diff options
| author | allusive-dev <[email protected]> | 2023-09-19 17:46:20 +1000 |
|---|---|---|
| committer | allusive-dev <[email protected]> | 2023-09-19 17:46:20 +1000 |
| commit | 5650d887357bf2a3fac8c5fd4f467bf8795b5fc4 (patch) | |
| tree | 4b825dc642cb6eb9a060e54bf8d69288fbee4904 /src/backend/xrender | |
| parent | Update picom.sample.conf (diff) | |
| download | compfy-5650d887357bf2a3fac8c5fd4f467bf8795b5fc4.tar.xz compfy-5650d887357bf2a3fac8c5fd4f467bf8795b5fc4.zip | |
reset
Diffstat (limited to 'src/backend/xrender')
| -rw-r--r-- | src/backend/xrender/xrender.c | 779 |
1 files changed, 0 insertions, 779 deletions
diff --git a/src/backend/xrender/xrender.c b/src/backend/xrender/xrender.c deleted file mode 100644 index ccf358b..0000000 --- a/src/backend/xrender/xrender.c +++ /dev/null @@ -1,779 +0,0 @@ -// SPDX-License-Identifier: MPL-2.0 -// Copyright (c) Yuxuan Shui <[email protected]> -#include <assert.h> -#include <math.h> -#include <stdlib.h> -#include <string.h> - -#include <xcb/composite.h> -#include <xcb/present.h> -#include <xcb/render.h> -#include <xcb/sync.h> -#include <xcb/xcb.h> - -#include "backend/backend.h" -#include "backend/backend_common.h" -#include "common.h" -#include "config.h" -#include "kernel.h" -#include "log.h" -#include "picom.h" -#include "region.h" -#include "types.h" -#include "utils.h" -#include "win.h" -#include "x.h" - -typedef struct _xrender_data { - backend_t base; - /// If vsync is enabled and supported by the current system - bool vsync; - xcb_visualid_t default_visual; - /// Target window - xcb_window_t target_win; - /// Painting target, it is either the root or the overlay - xcb_render_picture_t target; - /// Back buffers. Double buffer, with 1 for temporary render use - xcb_render_picture_t back[3]; - /// The back buffer that is for temporary use - /// Age of each back buffer. - int buffer_age[3]; - /// The back buffer we should be painting into - int curr_back; - /// The corresponding pixmap to the back buffer - xcb_pixmap_t back_pixmap[3]; - /// Pictures of pixel of different alpha value, used as a mask to - /// paint transparent images - xcb_render_picture_t alpha_pict[256]; - - // XXX don't know if these are really needed - - /// 1x1 white picture - xcb_render_picture_t white_pixel; - /// 1x1 black picture - xcb_render_picture_t black_pixel; - - /// Width and height of the target pixmap - int target_width, target_height; - - xcb_special_event_t *present_event; -} xrender_data; - -struct _xrender_blur_context { - enum blur_method method; - /// Blur kernels converted to X format - struct x_convolution_kernel **x_blur_kernel; - - int resize_width, resize_height; - - /// Number of blur kernels - int x_blur_kernel_count; -}; - -struct _xrender_image_data_inner { - // struct backend_image_inner_base - int refcount; - bool has_alpha; - - // Pixmap that the client window draws to, - // it will contain the content of client window. - xcb_pixmap_t pixmap; - // A Picture links to the Pixmap - xcb_render_picture_t pict; - int width, height; - xcb_visualid_t visual; - uint8_t depth; - // Whether we own this image, e.g. we allocated it; - // or not, e.g. this is a named pixmap of a X window. - bool owned; -}; - -static void compose_impl(struct _xrender_data *xd, const struct backend_image *img, - int dst_x1, int dst_y1, int dst_x2, int dst_y2, - const region_t *reg_paint, const region_t *reg_visible, - xcb_render_picture_t result) { - auto alpha_pict = xd->alpha_pict[(int)(img->opacity * MAX_ALPHA)]; - auto inner = (struct _xrender_image_data_inner *)img->inner; - region_t reg; - - bool has_alpha = inner->has_alpha || img->opacity != 1; - const auto tmpw = to_u16_checked(dst_x2 - dst_x1); - const auto tmph = to_u16_checked(dst_y2 - dst_y1); - const auto tmpew = to_u16_checked(dst_x2 - dst_x1); - const auto tmpeh = to_u16_checked(dst_y2 - dst_y1); - const xcb_render_color_t dim_color = { - .red = 0, .green = 0, .blue = 0, .alpha = (uint16_t)(0xffff * img->dim)}; - - // Clip region of rendered_pict might be set during rendering, clear it to - // make sure we get everything into the buffer - x_clear_picture_clip_region(xd->base.c, inner->pict); - - pixman_region32_init(®); - pixman_region32_intersect(®, (region_t *)reg_paint, (region_t *)reg_visible); - x_set_picture_clip_region(xd->base.c, result, 0, 0, ®); - -#define DOUBLE_TO_XFIXED(value) ((xcb_render_fixed_t)(((double)(value)) * 65536)) - { - const xcb_render_transform_t transform = { - DOUBLE_TO_XFIXED((double)img->ewidth / (double)tmpew), DOUBLE_TO_XFIXED(0.0), DOUBLE_TO_XFIXED(0.0), - DOUBLE_TO_XFIXED(0.0), DOUBLE_TO_XFIXED((double)img->eheight / (double)tmpeh), DOUBLE_TO_XFIXED(0.0), - DOUBLE_TO_XFIXED(0.0), DOUBLE_TO_XFIXED(0.0), DOUBLE_TO_XFIXED(1.0), - }; - xcb_render_set_picture_transform(xd->base.c, inner->pict, transform); - xcb_render_set_picture_filter(xd->base.c, inner->pict, 7, "nearest", 0, NULL); - } -#undef DOUBLE_TO_XFIXED - - if ((img->color_inverted || img->dim != 0) && has_alpha) { - // Apply image properties using a temporary image, because the source - // image is transparent. Otherwise the properties can be applied directly - // on the target image. - auto tmp_pict = - x_create_picture_with_visual(xd->base.c, xd->base.root, inner->width, - inner->height, inner->visual, 0, NULL); - - // Set clip region translated to source coordinate - x_set_picture_clip_region(xd->base.c, tmp_pict, to_i16_checked(-dst_x1), - to_i16_checked(-dst_y1), ®); - // Copy source -> tmp - xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_OVER, inner->pict, - XCB_NONE, tmp_pict, 0, 0, 0, 0, 0, 0, tmpw, tmph); - if (img->color_inverted) { - if (inner->has_alpha) { - auto tmp_pict2 = x_create_picture_with_visual( - xd->base.c, xd->base.root, tmpw, tmph, inner->visual, - 0, NULL); - xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_OVER, - tmp_pict, XCB_NONE, tmp_pict2, 0, 0, - 0, 0, 0, 0, tmpw, tmph); - - xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_DIFFERENCE, - xd->white_pixel, XCB_NONE, tmp_pict, - 0, 0, 0, 0, 0, 0, tmpw, tmph); - xcb_render_composite( - xd->base.c, XCB_RENDER_PICT_OP_IN_REVERSE, tmp_pict2, - XCB_NONE, tmp_pict, 0, 0, 0, 0, 0, 0, tmpw, tmph); - xcb_render_free_picture(xd->base.c, tmp_pict2); - } else { - xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_DIFFERENCE, - xd->white_pixel, XCB_NONE, tmp_pict, - 0, 0, 0, 0, 0, 0, tmpw, tmph); - } - } - if (img->dim != 0) { - // Dim the actually content of window - xcb_rectangle_t rect = { - .x = 0, - .y = 0, - .width = tmpw, - .height = tmph, - }; - - xcb_render_fill_rectangles(xd->base.c, XCB_RENDER_PICT_OP_OVER, - tmp_pict, dim_color, 1, &rect); - } - - xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_OVER, tmp_pict, - alpha_pict, result, 0, 0, 0, 0, to_i16_checked(dst_x1), - to_i16_checked(dst_y1), tmpew, tmpeh); - xcb_render_free_picture(xd->base.c, tmp_pict); - } else { - uint8_t op = (has_alpha ? XCB_RENDER_PICT_OP_OVER : XCB_RENDER_PICT_OP_SRC); - - xcb_render_composite(xd->base.c, op, inner->pict, alpha_pict, result, 0, - 0, 0, 0, to_i16_checked(dst_x1), - to_i16_checked(dst_y1), tmpew, tmpeh); - if (img->dim != 0 || img->color_inverted) { - // Apply properties, if we reach here, then has_alpha == false - assert(!has_alpha); - if (img->color_inverted) { - xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_DIFFERENCE, - xd->white_pixel, XCB_NONE, result, 0, - 0, 0, 0, to_i16_checked(dst_x1), - to_i16_checked(dst_y1), tmpew, tmpeh); - } - - if (img->dim != 0) { - // Dim the actually content of window - xcb_rectangle_t rect = { - .x = to_i16_checked(dst_x1), - .y = to_i16_checked(dst_y1), - .width = tmpew, - .height = tmpeh, - }; - - xcb_render_fill_rectangles(xd->base.c, XCB_RENDER_PICT_OP_OVER, - result, dim_color, 1, &rect); - } - } - } - pixman_region32_fini(®); -} - -static void compose(backend_t *base, void *img_data, - int dst_x1, int dst_y1, int dst_x2, int dst_y2, - const region_t *reg_paint, const region_t *reg_visible) { - // TODO(dccsillag): use dst_{x,y}2 - struct _xrender_data *xd = (void *)base; - return compose_impl(xd, img_data, dst_x1, dst_y1, dst_x2, dst_y2, reg_paint, reg_visible, xd->back[2]); -} - -static void fill(backend_t *base, struct color c, const region_t *clip) { - struct _xrender_data *xd = (void *)base; - const rect_t *extent = pixman_region32_extents((region_t *)clip); - x_set_picture_clip_region(base->c, xd->back[2], 0, 0, clip); - // color is in X fixed point representation - xcb_render_fill_rectangles( - base->c, XCB_RENDER_PICT_OP_OVER, xd->back[2], - (xcb_render_color_t){.red = (uint16_t)(c.red * 0xffff), - .green = (uint16_t)(c.green * 0xffff), - .blue = (uint16_t)(c.blue * 0xffff), - .alpha = (uint16_t)(c.alpha * 0xffff)}, - 1, - (xcb_rectangle_t[]){{.x = to_i16_checked(extent->x1), - .y = to_i16_checked(extent->y1), - .width = to_u16_checked(extent->x2 - extent->x1), - .height = to_u16_checked(extent->y2 - extent->y1)}}); -} - -static bool blur(backend_t *backend_data, double opacity, void *ctx_, - const region_t *reg_blur, const region_t *reg_visible) { - struct _xrender_blur_context *bctx = ctx_; - if (bctx->method == BLUR_METHOD_NONE) { - return true; - } - - struct _xrender_data *xd = (void *)backend_data; - xcb_connection_t *c = xd->base.c; - region_t reg_op; - pixman_region32_init(®_op); - pixman_region32_intersect(®_op, (region_t *)reg_blur, (region_t *)reg_visible); - if (!pixman_region32_not_empty(®_op)) { - pixman_region32_fini(®_op); - return true; - } - - region_t reg_op_resized = - resize_region(®_op, bctx->resize_width, bctx->resize_height); - - const pixman_box32_t *extent_resized = pixman_region32_extents(®_op_resized); - const auto height_resized = to_u16_checked(extent_resized->y2 - extent_resized->y1); - const auto width_resized = to_u16_checked(extent_resized->x2 - extent_resized->x1); - static const char *filter0 = "Nearest"; // The "null" filter - static const char *filter = "convolution"; - - // Create a buffer for storing blurred picture, make it just big enough - // for the blur region - const uint32_t pic_attrs_mask = XCB_RENDER_CP_REPEAT; - const xcb_render_create_picture_value_list_t pic_attrs = {.repeat = XCB_RENDER_REPEAT_PAD}; - xcb_render_picture_t tmp_picture[2] = { - x_create_picture_with_visual(xd->base.c, xd->base.root, width_resized, height_resized, - xd->default_visual, pic_attrs_mask, &pic_attrs), - x_create_picture_with_visual(xd->base.c, xd->base.root, width_resized, height_resized, - xd->default_visual, pic_attrs_mask, &pic_attrs)}; - - if (!tmp_picture[0] || !tmp_picture[1]) { - log_error("Failed to build intermediate Picture."); - pixman_region32_fini(®_op); - pixman_region32_fini(®_op_resized); - return false; - } - - region_t clip; - pixman_region32_init(&clip); - pixman_region32_copy(&clip, ®_op_resized); - pixman_region32_translate(&clip, -extent_resized->x1, -extent_resized->y1); - x_set_picture_clip_region(c, tmp_picture[0], 0, 0, &clip); - x_set_picture_clip_region(c, tmp_picture[1], 0, 0, &clip); - pixman_region32_fini(&clip); - - xcb_render_picture_t src_pict = xd->back[2], dst_pict = tmp_picture[0]; - auto alpha_pict = xd->alpha_pict[(int)(opacity * MAX_ALPHA)]; - int current = 0; - x_set_picture_clip_region(c, src_pict, 0, 0, ®_op_resized); - - // For more than 1 pass, we do: - // back -(pass 1)-> tmp0 -(pass 2)-> tmp1 ... - // -(pass n-1)-> tmp0 or tmp1 -(pass n)-> back - // For 1 pass, we do - // back -(pass 1)-> tmp0 -(copy)-> target_buffer - int i; - for (i = 0; i < bctx->x_blur_kernel_count; i++) { - // Copy from source picture to destination. The filter must - // be applied on source picture, to get the nearby pixels outside the - // window. - xcb_render_set_picture_filter(c, src_pict, to_u16_checked(strlen(filter)), - filter, - to_u32_checked(bctx->x_blur_kernel[i]->size), - bctx->x_blur_kernel[i]->kernel); - - if (i == 0) { - // First pass, back buffer -> tmp picture - // (we do this even if this is also the last pass, because we - // cannot do back buffer -> back buffer) - xcb_render_composite(c, XCB_RENDER_PICT_OP_SRC, src_pict, XCB_NONE, - dst_pict, to_i16_checked(extent_resized->x1), - to_i16_checked(extent_resized->y1), 0, 0, 0, - 0, width_resized, height_resized); - } else if (i < bctx->x_blur_kernel_count - 1) { - // This is not the last pass or the first pass, - // tmp picture 1 -> tmp picture 2 - xcb_render_composite(c, XCB_RENDER_PICT_OP_SRC, src_pict, - XCB_NONE, dst_pict, 0, 0, 0, 0, 0, 0, - width_resized, height_resized); - } else { - x_set_picture_clip_region(c, xd->back[2], 0, 0, ®_op); - // This is the last pass, and we are doing more than 1 pass - xcb_render_composite(c, XCB_RENDER_PICT_OP_OVER, src_pict, - alpha_pict, xd->back[2], 0, 0, 0, 0, - to_i16_checked(extent_resized->x1), - to_i16_checked(extent_resized->y1), - width_resized, height_resized); - } - - // reset filter - xcb_render_set_picture_filter( - c, src_pict, to_u16_checked(strlen(filter0)), filter0, 0, NULL); - - src_pict = tmp_picture[current]; - dst_pict = tmp_picture[!current]; - current = !current; - } - - // There is only 1 pass - if (i == 1) { - x_set_picture_clip_region(c, xd->back[2], 0, 0, ®_op); - xcb_render_composite( - c, XCB_RENDER_PICT_OP_OVER, src_pict, alpha_pict, xd->back[2], 0, 0, - 0, 0, to_i16_checked(extent_resized->x1), - to_i16_checked(extent_resized->y1), width_resized, height_resized); - } - - xcb_render_free_picture(c, tmp_picture[0]); - xcb_render_free_picture(c, tmp_picture[1]); - pixman_region32_fini(®_op); - pixman_region32_fini(®_op_resized); - return true; -} - -static void * -bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, bool owned) { - xcb_generic_error_t *e; - auto r = xcb_get_geometry_reply(base->c, xcb_get_geometry(base->c, pixmap), &e); - if (!r) { - log_error("Invalid pixmap: %#010x", pixmap); - x_print_error(e->full_sequence, e->major_code, e->minor_code, e->error_code); - return NULL; - } - - auto img = ccalloc(1, struct backend_image); - auto inner = ccalloc(1, struct _xrender_image_data_inner); - inner->depth = (uint8_t)fmt.visual_depth; - inner->width = img->ewidth = r->width; - inner->height = img->eheight = r->height; - inner->pixmap = pixmap; - inner->has_alpha = fmt.alpha_size != 0; - inner->pict = - x_create_picture_with_visual_and_pixmap(base->c, fmt.visual, pixmap, 0, NULL); - inner->owned = owned; - inner->visual = fmt.visual; - inner->refcount = 1; - - img->inner = (struct backend_image_inner_base *)inner; - img->opacity = 1; - free(r); - - if (inner->pict == XCB_NONE) { - free(inner); - free(img); - return NULL; - } - return img; -} -static void release_image_inner(backend_t *base, struct _xrender_image_data_inner *inner) { - xcb_render_free_picture(base->c, inner->pict); - if (inner->owned) { - xcb_free_pixmap(base->c, inner->pixmap); - } - free(inner); -} -static void release_image(backend_t *base, void *image) { - struct backend_image *img = image; - img->inner->refcount--; - if (img->inner->refcount == 0) { - release_image_inner(base, (void *)img->inner); - } - free(img); -} - -static void deinit(backend_t *backend_data) { - struct _xrender_data *xd = (void *)backend_data; - for (int i = 0; i < 256; i++) { - xcb_render_free_picture(xd->base.c, xd->alpha_pict[i]); - } - xcb_render_free_picture(xd->base.c, xd->target); - for (int i = 0; i < 2; i++) { - xcb_render_free_picture(xd->base.c, xd->back[i]); - xcb_free_pixmap(xd->base.c, xd->back_pixmap[i]); - } - if (xd->present_event) { - xcb_unregister_for_special_event(xd->base.c, xd->present_event); - } - xcb_render_free_picture(xd->base.c, xd->white_pixel); - xcb_render_free_picture(xd->base.c, xd->black_pixel); - free(xd); -} - -static void present(backend_t *base, const region_t *region) { - struct _xrender_data *xd = (void *)base; - const rect_t *extent = pixman_region32_extents((region_t *)region); - int16_t orig_x = to_i16_checked(extent->x1), orig_y = to_i16_checked(extent->y1); - uint16_t region_width = to_u16_checked(extent->x2 - extent->x1), - region_height = to_u16_checked(extent->y2 - extent->y1); - - // compose() sets clip region on the back buffer, so clear it first - x_clear_picture_clip_region(base->c, xd->back[xd->curr_back]); - - // limit the region of update - x_set_picture_clip_region(base->c, xd->back[2], 0, 0, region); - - if (xd->vsync) { - // Update the back buffer first, then present - xcb_render_composite(base->c, XCB_RENDER_PICT_OP_SRC, xd->back[2], - XCB_NONE, xd->back[xd->curr_back], orig_x, orig_y, 0, - 0, orig_x, orig_y, region_width, region_height); - - // Make sure we got reply from PresentPixmap before waiting for events, - // to avoid deadlock - auto e = xcb_request_check( - base->c, xcb_present_pixmap_checked( - xd->base.c, xd->target_win, - xd->back_pixmap[xd->curr_back], 0, XCB_NONE, XCB_NONE, 0, - 0, XCB_NONE, XCB_NONE, XCB_NONE, 0, 0, 0, 0, 0, NULL)); - if (e) { - log_error("Failed to present pixmap"); - free(e); - return; - } - // TODO(yshui) don't block wait for present completion - xcb_present_generic_event_t *pev = - (void *)xcb_wait_for_special_event(base->c, xd->present_event); - if (!pev) { - // We don't know what happened, maybe X died - // But reset buffer age, so in case we do recover, we will - // render correctly. - xd->buffer_age[0] = xd->buffer_age[1] = -1; - return; - } - assert(pev->evtype == XCB_PRESENT_COMPLETE_NOTIFY); - xcb_present_complete_notify_event_t *pcev = (void *)pev; - // log_trace("Present complete: %d %ld", pcev->mode, pcev->msc); - xd->buffer_age[xd->curr_back] = 1; - - // buffer_age < 0 means that back buffer is empty - if (xd->buffer_age[1 - xd->curr_back] > 0) { - xd->buffer_age[1 - xd->curr_back]++; - } - if (pcev->mode == XCB_PRESENT_COMPLETE_MODE_FLIP) { - // We cannot use the pixmap we used anymore - xd->curr_back = 1 - xd->curr_back; - } - free(pev); - } else { - // No vsync needed, draw into the target picture directly - xcb_render_composite(base->c, XCB_RENDER_PICT_OP_SRC, xd->back[2], - XCB_NONE, xd->target, orig_x, orig_y, 0, 0, orig_x, - orig_y, region_width, region_height); - } -} - -static int buffer_age(backend_t *backend_data) { - struct _xrender_data *xd = (void *)backend_data; - if (!xd->vsync) { - // Only the target picture really holds the screen content, and its - // content is always up to date. So buffer age is always 1. - return 1; - } - return xd->buffer_age[xd->curr_back]; -} - -static struct _xrender_image_data_inner * -new_inner(backend_t *base, int w, int h, xcb_visualid_t visual, uint8_t depth) { - auto new_inner = ccalloc(1, struct _xrender_image_data_inner); - new_inner->pixmap = x_create_pixmap(base->c, depth, base->root, w, h); - if (new_inner->pixmap == XCB_NONE) { - log_error("Failed to create pixmap for copy"); - free(new_inner); - return NULL; - } - new_inner->pict = x_create_picture_with_visual_and_pixmap( - base->c, visual, new_inner->pixmap, 0, NULL); - if (new_inner->pict == XCB_NONE) { - log_error("Failed to create picture for copy"); - xcb_free_pixmap(base->c, new_inner->pixmap); - free(new_inner); - return NULL; - } - new_inner->width = w; - new_inner->height = h; - new_inner->visual = visual; - new_inner->depth = depth; - new_inner->refcount = 1; - new_inner->owned = true; - return new_inner; -} - -static bool decouple_image(backend_t *base, struct backend_image *img, const region_t *reg) { - if (img->inner->refcount == 1) { - return true; - } - auto inner = (struct _xrender_image_data_inner *)img->inner; - // Force new pixmap to a 32-bit ARGB visual to allow for transparent frames around - // non-transparent windows - auto visual = (inner->depth == 32) - ? inner->visual - : x_get_visual_for_standard(base->c, XCB_PICT_STANDARD_ARGB_32); - auto inner2 = new_inner(base, inner->width, inner->height, visual, 32); - if (!inner2) { - return false; - } - - x_set_picture_clip_region(base->c, inner->pict, 0, 0, reg); - xcb_render_composite(base->c, XCB_RENDER_PICT_OP_SRC, inner->pict, XCB_NONE, - inner2->pict, 0, 0, 0, 0, 0, 0, to_u16_checked(inner->width), - to_u16_checked(inner->height)); - - img->inner = (struct backend_image_inner_base *)inner2; - inner->refcount--; - return true; -} - -static bool image_op(backend_t *base, enum image_operations op, void *image, - const region_t *reg_op, const region_t *reg_visible, void *arg) { - struct _xrender_data *xd = (void *)base; - struct backend_image *img = image; - region_t reg; - double *dargs = arg; - - pixman_region32_init(®); - pixman_region32_intersect(®, (region_t *)reg_op, (region_t *)reg_visible); - - switch (op) { - case IMAGE_OP_APPLY_ALPHA: - assert(reg_op); - - if (!pixman_region32_not_empty(®)) { - break; - } - - if (dargs[0] == 1) { - break; - } - - if (!decouple_image(base, img, reg_visible)) { - pixman_region32_fini(®); - return false; - } - - auto inner = (struct _xrender_image_data_inner *)img->inner; - auto alpha_pict = xd->alpha_pict[(int)((1 - dargs[0]) * MAX_ALPHA)]; - x_set_picture_clip_region(base->c, inner->pict, 0, 0, ®); - xcb_render_composite(base->c, XCB_RENDER_PICT_OP_OUT_REVERSE, alpha_pict, - XCB_NONE, inner->pict, 0, 0, 0, 0, 0, 0, - to_u16_checked(inner->width), - to_u16_checked(inner->height)); - inner->has_alpha = true; - break; - } - pixman_region32_fini(®); - return true; -} - -static void * -create_blur_context(backend_t *base attr_unused, enum blur_method method, void *args) { - auto ret = ccalloc(1, struct _xrender_blur_context); - if (!method || method >= BLUR_METHOD_INVALID) { - ret->method = BLUR_METHOD_NONE; - return ret; - } - if (method == BLUR_METHOD_DUAL_KAWASE) { - log_warn("Blur method 'dual_kawase' is not compatible with the 'xrender' " - "backend."); - ret->method = BLUR_METHOD_NONE; - return ret; - } - - ret->method = BLUR_METHOD_KERNEL; - struct conv **kernels; - int kernel_count; - if (method == BLUR_METHOD_KERNEL) { - kernels = ((struct kernel_blur_args *)args)->kernels; - kernel_count = ((struct kernel_blur_args *)args)->kernel_count; - } else { - kernels = generate_blur_kernel(method, args, &kernel_count); - } - - ret->x_blur_kernel = ccalloc(kernel_count, struct x_convolution_kernel *); - for (int i = 0; i < kernel_count; i++) { - int center = kernels[i]->h * kernels[i]->w / 2; - x_create_convolution_kernel(kernels[i], kernels[i]->data[center], - &ret->x_blur_kernel[i]); - ret->resize_width += kernels[i]->w / 2; - ret->resize_height += kernels[i]->h / 2; - } - ret->x_blur_kernel_count = kernel_count; - - if (method != BLUR_METHOD_KERNEL) { - // Kernels generated by generate_blur_kernel, so we need to free them. - for (int i = 0; i < kernel_count; i++) { - free(kernels[i]); - } - free(kernels); - } - return ret; -} - -static void destroy_blur_context(backend_t *base attr_unused, void *ctx_) { - struct _xrender_blur_context *ctx = ctx_; - for (int i = 0; i < ctx->x_blur_kernel_count; i++) { - free(ctx->x_blur_kernel[i]); - } - free(ctx->x_blur_kernel); - free(ctx); -} - -static void get_blur_size(void *blur_context, int *width, int *height) { - struct _xrender_blur_context *ctx = blur_context; - *width = ctx->resize_width; - *height = ctx->resize_height; -} - -static bool -read_pixel(backend_t *backend_data, void *image_data, int x, int y, struct color *output) { - auto xd = (struct _xrender_data *)backend_data; - auto img = (struct backend_image *)image_data; - auto inner = (struct _xrender_image_data_inner *)img->inner; - - auto r = XCB_AWAIT(xcb_get_image, xd->base.c, XCB_IMAGE_FORMAT_XY_PIXMAP, inner->pixmap, - to_i16_checked(x), to_i16_checked(y), 1, 1, (uint32_t)-1L); - - if (!r) { - return false; - } - - // Color format seems to be BGRA8888, see glamor_format_for_pixmap from the - // Xserver codebase. - uint8_t *pixels = xcb_get_image_data(r); - output->blue = pixels[0] / 255.0; - output->green = pixels[1] / 255.0; - output->red = pixels[2] / 255.0; - output->alpha = pixels[3] / 255.0; - - return true; -} - -static backend_t *backend_xrender_init(session_t *ps) { - auto xd = ccalloc(1, struct _xrender_data); - init_backend_base(&xd->base, ps); - - for (int i = 0; i <= MAX_ALPHA; ++i) { - double o = (double)i / (double)MAX_ALPHA; - xd->alpha_pict[i] = solid_picture(ps->c, ps->root, false, o, 0, 0, 0); - assert(xd->alpha_pict[i] != XCB_NONE); - } - - xd->target_width = ps->root_width; - xd->target_height = ps->root_height; - xd->default_visual = ps->vis; - xd->black_pixel = solid_picture(ps->c, ps->root, true, 1, 0, 0, 0); - xd->white_pixel = solid_picture(ps->c, ps->root, true, 1, 1, 1, 1); - - xd->target_win = session_get_target_window(ps); - xcb_render_create_picture_value_list_t pa = { - .subwindowmode = XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS, - }; - xd->target = x_create_picture_with_visual_and_pixmap( - ps->c, ps->vis, xd->target_win, XCB_RENDER_CP_SUBWINDOW_MODE, &pa); - - auto pictfmt = x_get_pictform_for_visual(ps->c, ps->vis); - if (!pictfmt) { - log_fatal("Default visual is invalid"); - abort(); - } - - xd->vsync = ps->o.vsync; - if (ps->present_exists) { - auto eid = x_new_id(ps->c); - auto e = - xcb_request_check(ps->c, xcb_present_select_input_checked( - ps->c, eid, xd->target_win, - XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY)); - if (e) { - log_error("Cannot select present input, vsync will be disabled"); - xd->vsync = false; - free(e); - } - - xd->present_event = - xcb_register_for_special_xge(ps->c, &xcb_present_id, eid, NULL); - if (!xd->present_event) { - log_error("Cannot register for special XGE, vsync will be " - "disabled"); - xd->vsync = false; - } - } else { - xd->vsync = false; - } - - // We might need to do double buffering for vsync, and buffer 0 and 1 are for - // double buffering. - int first_buffer_index = xd->vsync ? 0 : 2; - for (int i = first_buffer_index; i < 3; i++) { - xd->back_pixmap[i] = x_create_pixmap(ps->c, pictfmt->depth, ps->root, - to_u16_checked(ps->root_width), - to_u16_checked(ps->root_height)); - const uint32_t pic_attrs_mask = XCB_RENDER_CP_REPEAT; - const xcb_render_create_picture_value_list_t pic_attrs = { - .repeat = XCB_RENDER_REPEAT_PAD}; - xd->back[i] = x_create_picture_with_pictfmt_and_pixmap( - ps->c, pictfmt, xd->back_pixmap[i], pic_attrs_mask, &pic_attrs); - xd->buffer_age[i] = -1; - if (xd->back_pixmap[i] == XCB_NONE || xd->back[i] == XCB_NONE) { - log_error("Cannot create pixmap for rendering"); - goto err; - } - } - xd->curr_back = 0; - - return &xd->base; -err: - deinit(&xd->base); - return NULL; -} - -struct backend_operations xrender_ops = { - .init = backend_xrender_init, - .deinit = deinit, - .blur = blur, - .present = present, - .compose = compose, - .fill = fill, - .bind_pixmap = bind_pixmap, - .release_image = release_image, - .render_shadow = default_backend_render_shadow, - //.prepare_win = prepare_win, - //.release_win = release_win, - .is_image_transparent = default_is_image_transparent, - .buffer_age = buffer_age, - .max_buffer_age = 2, - - .image_op = image_op, - .read_pixel = read_pixel, - .clone_image = default_clone_image, - .set_image_property = default_set_image_property, - .create_blur_context = create_blur_context, - .destroy_blur_context = destroy_blur_context, - .get_blur_size = get_blur_size, -}; - -// vim: set noet sw=8 ts=8: |