abeeb36bb2
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
590 lines
20 KiB
C
590 lines
20 KiB
C
// SPDX-License-Identifier: MPL-2.0
|
|
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
|
|
#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 "region.h"
|
|
#include "utils.h"
|
|
#include "win.h"
|
|
#include "x.h"
|
|
|
|
#define auto __auto_type
|
|
|
|
typedef struct _xrender_data {
|
|
backend_t base;
|
|
/// If vsync is enabled and supported by the current system
|
|
bool vsync;
|
|
xcb_visualid_t default_visual;
|
|
/// The idle fence for the present extension
|
|
xcb_sync_fence_t idle_fence;
|
|
/// The target window
|
|
xcb_window_t target_win;
|
|
/// The painting target, it is either the root or the overlay
|
|
xcb_render_picture_t target;
|
|
/// A back buffer
|
|
xcb_render_picture_t back[2];
|
|
/// Age of each back buffer
|
|
int buffer_age[2];
|
|
/// The back buffer we should be painting into
|
|
int curr_back;
|
|
/// The corresponding pixmap to the back buffer
|
|
xcb_pixmap_t back_pixmap[2];
|
|
/// The original root window content, usually the wallpaper.
|
|
/// We save it so we don't loss the wallpaper when we paint over
|
|
/// it.
|
|
xcb_render_picture_t root_pict;
|
|
/// 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;
|
|
|
|
/// Blur kernels converted to X format
|
|
xcb_render_fixed_t *x_blur_kern[MAX_BLUR_PASS];
|
|
/// Number of elements in each blur kernel
|
|
int x_blur_kern_size[MAX_BLUR_PASS];
|
|
|
|
xcb_special_event_t *present_event;
|
|
} xrender_data;
|
|
|
|
struct _xrender_image_data {
|
|
// 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;
|
|
// The effective size of the image
|
|
int ewidth, eheight;
|
|
bool has_alpha;
|
|
double opacity;
|
|
xcb_visualid_t visual;
|
|
uint8_t depth;
|
|
bool owned;
|
|
};
|
|
|
|
static void compose(backend_t *base, void *img_data, int dst_x, int dst_y,
|
|
const region_t *reg_paint, const region_t *reg_visible) {
|
|
struct _xrender_data *xd = (void *)base;
|
|
struct _xrender_image_data *img = img_data;
|
|
uint8_t op = (img->has_alpha ? XCB_RENDER_PICT_OP_OVER : XCB_RENDER_PICT_OP_SRC);
|
|
auto alpha_pict = xd->alpha_pict[(int)(img->opacity * 255.0)];
|
|
region_t reg;
|
|
pixman_region32_init(®);
|
|
pixman_region32_intersect(®, (region_t *)reg_paint, (region_t *)reg_visible);
|
|
|
|
// 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(base->c, img->pict);
|
|
|
|
x_set_picture_clip_region(base->c, xd->back[xd->curr_back], 0, 0, ®);
|
|
xcb_render_composite(base->c, op, img->pict, alpha_pict, xd->back[xd->curr_back],
|
|
0, 0, 0, 0, to_i16_checked(dst_x), to_i16_checked(dst_y),
|
|
to_u16_checked(img->ewidth), to_u16_checked(img->eheight));
|
|
pixman_region32_fini(®);
|
|
}
|
|
|
|
static void
|
|
fill(backend_t *base, double r, double g, double b, double a, 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[xd->curr_back], 0, 0, clip);
|
|
// color is in X fixed point representation
|
|
xcb_render_fill_rectangles(
|
|
base->c, XCB_RENDER_PICT_OP_OVER, xd->back[xd->curr_back],
|
|
(xcb_render_color_t){.red = (uint16_t)(r * 0xffff),
|
|
.green = (uint16_t)(g * 0xffff),
|
|
.blue = (uint16_t)(b * 0xffff),
|
|
.alpha = (uint16_t)(a * 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, const region_t *reg_blur,
|
|
const region_t *reg_visible) {
|
|
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;
|
|
}
|
|
|
|
const pixman_box32_t *extent = pixman_region32_extents(®_op);
|
|
const auto height = to_u16_checked(extent->y2 - extent->y1);
|
|
const auto width = to_u16_checked(extent->x2 - extent->x1);
|
|
auto src_x = to_i16_checked(extent->x1);
|
|
auto src_y = to_i16_checked(extent->y1);
|
|
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
|
|
xcb_render_picture_t tmp_picture[2] = {
|
|
x_create_picture_with_visual(xd->base.c, xd->base.root, width, height,
|
|
xd->default_visual, 0, NULL),
|
|
x_create_picture_with_visual(xd->base.c, xd->base.root, width, height,
|
|
xd->default_visual, 0, NULL)};
|
|
|
|
if (!tmp_picture[0] || !tmp_picture[1]) {
|
|
log_error("Failed to build intermediate Picture.");
|
|
pixman_region32_fini(®_op);
|
|
return false;
|
|
}
|
|
|
|
region_t clip;
|
|
pixman_region32_init(&clip);
|
|
pixman_region32_copy(&clip, ®_op);
|
|
pixman_region32_translate(&clip, -src_x, -src_y);
|
|
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);
|
|
|
|
// The multipass blur implemented here is not correct, but this is what old
|
|
// compton did anyway. XXX
|
|
xcb_render_picture_t src_pict = xd->back[xd->curr_back], dst_pict = tmp_picture[0];
|
|
auto alpha_pict = xd->alpha_pict[(int)(opacity * 255)];
|
|
int current = 0;
|
|
x_set_picture_clip_region(c, src_pict, 0, 0, ®_op);
|
|
|
|
// 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; xd->x_blur_kern[i]; i++) {
|
|
assert(i < MAX_BLUR_PASS - 1);
|
|
|
|
// Copy from source picture to destination. The filter must
|
|
// be applied on source picture, to get the nearby pixels outside the
|
|
// window.
|
|
// TODO cache converted blur_kerns
|
|
xcb_render_set_picture_filter(
|
|
c, src_pict, to_u16_checked(strlen(filter)), filter,
|
|
to_u32_checked(xd->x_blur_kern_size[i]), xd->x_blur_kern[i]);
|
|
|
|
if (xd->x_blur_kern[i + 1] || i == 0) {
|
|
// This is not the last pass, or this is the first pass
|
|
xcb_render_composite(c, XCB_RENDER_PICT_OP_SRC, src_pict,
|
|
XCB_NONE, dst_pict, src_x, src_y, 0, 0, 0, 0,
|
|
width, height);
|
|
} else {
|
|
// This is the last pass, and this is also not the first
|
|
xcb_render_composite(c, XCB_RENDER_PICT_OP_OVER, src_pict,
|
|
alpha_pict, xd->back[xd->curr_back], 0, 0, 0,
|
|
0, src_x, src_y, width, height);
|
|
}
|
|
|
|
// 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];
|
|
src_x = 0;
|
|
src_y = 0;
|
|
current = !current;
|
|
}
|
|
|
|
// There is only 1 pass
|
|
if (i == 1) {
|
|
xcb_render_composite(c, XCB_RENDER_PICT_OP_OVER, src_pict, alpha_pict,
|
|
xd->back[xd->curr_back], 0, 0, 0, 0,
|
|
to_i16_checked(extent->x1),
|
|
to_i16_checked(extent->y1), width, height);
|
|
}
|
|
|
|
xcb_render_free_picture(c, tmp_picture[0]);
|
|
xcb_render_free_picture(c, tmp_picture[1]);
|
|
pixman_region32_fini(®_op);
|
|
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 _xrender_image_data);
|
|
img->depth = (uint8_t)fmt.visual_depth;
|
|
img->width = img->ewidth = r->width;
|
|
img->height = img->eheight = r->height;
|
|
img->pixmap = pixmap;
|
|
img->opacity = 1;
|
|
img->has_alpha = fmt.alpha_size != 0;
|
|
img->pict =
|
|
x_create_picture_with_visual_and_pixmap(base->c, fmt.visual, pixmap, 0, NULL);
|
|
img->owned = owned;
|
|
img->visual = fmt.visual;
|
|
if (img->pict == XCB_NONE) {
|
|
free(img);
|
|
return NULL;
|
|
}
|
|
return img;
|
|
}
|
|
|
|
static void release_image(backend_t *base, void *image) {
|
|
struct _xrender_image_data *img = image;
|
|
xcb_render_free_picture(base->c, img->pict);
|
|
if (img->owned) {
|
|
xcb_free_pixmap(base->c, img->pixmap);
|
|
}
|
|
}
|
|
|
|
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);
|
|
xcb_render_free_picture(xd->base.c, xd->root_pict);
|
|
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]);
|
|
}
|
|
for (int i = 0; xd->x_blur_kern[i]; i++) {
|
|
free(xd->x_blur_kern[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) {
|
|
struct _xrender_data *xd = (void *)base;
|
|
|
|
if (xd->vsync) {
|
|
// 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 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 {
|
|
// compose() sets clip region, so clear it first to make
|
|
// sure we update the whole screen.
|
|
x_clear_picture_clip_region(xd->base.c, xd->back[xd->curr_back]);
|
|
|
|
// TODO buffer-age-like optimization might be possible here.
|
|
// but that will require a different backend API
|
|
xcb_render_composite(base->c, XCB_RENDER_PICT_OP_SRC,
|
|
xd->back[xd->curr_back], XCB_NONE, xd->target, 0, 0,
|
|
0, 0, 0, 0, to_u16_checked(xd->target_width),
|
|
to_u16_checked(xd->target_height));
|
|
xd->buffer_age[xd->curr_back] = 1;
|
|
}
|
|
}
|
|
|
|
static int buffer_age(backend_t *backend_data) {
|
|
struct _xrender_data *xd = (void *)backend_data;
|
|
return xd->buffer_age[xd->curr_back];
|
|
}
|
|
|
|
static bool is_image_transparent(backend_t *bd, void *image) {
|
|
struct _xrender_image_data *img = image;
|
|
return img->has_alpha;
|
|
}
|
|
|
|
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 _xrender_image_data *img = image;
|
|
region_t reg;
|
|
double *dargs = arg;
|
|
int *iargs = arg;
|
|
if (op == IMAGE_OP_APPLY_ALPHA_ALL) {
|
|
img->opacity *= dargs[0];
|
|
img->has_alpha = true;
|
|
return true;
|
|
}
|
|
|
|
pixman_region32_init(®);
|
|
|
|
const auto tmpw = to_u16_checked(img->width);
|
|
const auto tmph = to_u16_checked(img->height);
|
|
switch (op) {
|
|
case IMAGE_OP_INVERT_COLOR_ALL:
|
|
x_set_picture_clip_region(base->c, img->pict, 0, 0, reg_visible);
|
|
if (img->has_alpha) {
|
|
auto tmp_pict =
|
|
x_create_picture_with_visual(base->c, base->root, img->width,
|
|
img->height, img->visual, 0, NULL);
|
|
xcb_render_composite(base->c, XCB_RENDER_PICT_OP_SRC, img->pict,
|
|
XCB_NONE, tmp_pict, 0, 0, 0, 0, 0, 0, tmpw, tmph);
|
|
|
|
xcb_render_composite(base->c, XCB_RENDER_PICT_OP_DIFFERENCE,
|
|
xd->white_pixel, XCB_NONE, tmp_pict, 0, 0, 0,
|
|
0, 0, 0, tmpw, tmph);
|
|
// We use an extra PictOpInReverse operation to get correct pixel
|
|
// alpha. There could be a better solution.
|
|
xcb_render_composite(base->c, XCB_RENDER_PICT_OP_IN_REVERSE,
|
|
tmp_pict, XCB_NONE, img->pict, 0, 0, 0, 0, 0,
|
|
0, tmpw, tmph);
|
|
xcb_render_free_picture(base->c, tmp_pict);
|
|
} else {
|
|
xcb_render_composite(base->c, XCB_RENDER_PICT_OP_DIFFERENCE,
|
|
xd->white_pixel, XCB_NONE, img->pict, 0, 0,
|
|
0, 0, 0, 0, tmpw, tmph);
|
|
}
|
|
break;
|
|
case IMAGE_OP_DIM_ALL:
|
|
x_set_picture_clip_region(base->c, img->pict, 0, 0, reg_visible);
|
|
|
|
xcb_render_color_t color = {
|
|
.red = 0, .green = 0, .blue = 0, .alpha = (uint16_t)(0xffff * dargs[0])};
|
|
|
|
// Dim the actually content of window
|
|
xcb_rectangle_t rect = {
|
|
.x = 0,
|
|
.y = 0,
|
|
.width = tmpw,
|
|
.height = tmph,
|
|
};
|
|
|
|
xcb_render_fill_rectangles(base->c, XCB_RENDER_PICT_OP_OVER, img->pict,
|
|
color, 1, &rect);
|
|
break;
|
|
case IMAGE_OP_APPLY_ALPHA:
|
|
assert(reg_op);
|
|
pixman_region32_intersect(®, (region_t *)reg_op, (region_t *)reg_visible);
|
|
if (!pixman_region32_not_empty(®)) {
|
|
break;
|
|
}
|
|
|
|
if (dargs[0] == 1) {
|
|
break;
|
|
}
|
|
|
|
auto alpha_pict = xd->alpha_pict[(int)(dargs[0] * 255)];
|
|
x_set_picture_clip_region(base->c, img->pict, 0, 0, ®);
|
|
xcb_render_composite(base->c, XCB_RENDER_PICT_OP_IN, img->pict, XCB_NONE,
|
|
alpha_pict, 0, 0, 0, 0, 0, 0, tmpw, tmph);
|
|
img->has_alpha = true;
|
|
break;
|
|
case IMAGE_OP_RESIZE_TILE:
|
|
img->ewidth = iargs[0];
|
|
img->eheight = iargs[1];
|
|
break;
|
|
case IMAGE_OP_APPLY_ALPHA_ALL: assert(false);
|
|
}
|
|
pixman_region32_fini(®);
|
|
return true;
|
|
}
|
|
|
|
static void *copy(backend_t *base, const void *image, const region_t *reg) {
|
|
const struct _xrender_image_data *img = image;
|
|
struct _xrender_data *xd = (void *)base;
|
|
auto new_img = ccalloc(1, struct _xrender_image_data);
|
|
assert(img->visual != XCB_NONE);
|
|
log_trace("xrender: copying %#010x visual %#x", img->pixmap, img->visual);
|
|
*new_img = *img;
|
|
x_set_picture_clip_region(base->c, img->pict, 0, 0, reg);
|
|
new_img->pixmap =
|
|
x_create_pixmap(base->c, img->depth, base->root, img->width, img->height);
|
|
new_img->opacity = 1;
|
|
new_img->owned = true;
|
|
if (new_img->pixmap == XCB_NONE) {
|
|
log_error("Failed to create pixmap for copy");
|
|
free(new_img);
|
|
return NULL;
|
|
}
|
|
new_img->pict = x_create_picture_with_visual_and_pixmap(base->c, img->visual,
|
|
new_img->pixmap, 0, NULL);
|
|
if (new_img->pict == XCB_NONE) {
|
|
log_error("Failed to create picture for copy");
|
|
xcb_free_pixmap(base->c, new_img->pixmap);
|
|
free(new_img);
|
|
return NULL;
|
|
}
|
|
|
|
xcb_render_picture_t alpha_pict =
|
|
img->opacity == 1 ? XCB_NONE : xd->alpha_pict[(int)(img->opacity * 255)];
|
|
xcb_render_composite(base->c, XCB_RENDER_PICT_OP_SRC, img->pict, alpha_pict,
|
|
new_img->pict, 0, 0, 0, 0, 0, 0, to_u16_checked(img->width),
|
|
to_u16_checked(img->height));
|
|
return new_img;
|
|
}
|
|
|
|
backend_t *backend_xrender_init(session_t *ps) {
|
|
auto xd = ccalloc(1, struct _xrender_data);
|
|
|
|
xd->base.c = ps->c;
|
|
xd->base.root = ps->root;
|
|
|
|
for (int i = 0; i < 256; ++i) {
|
|
double o = (double)i / 255.0;
|
|
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);
|
|
|
|
if (ps->overlay != XCB_NONE) {
|
|
xd->target = x_create_picture_with_visual_and_pixmap(
|
|
ps->c, ps->vis, ps->overlay, 0, NULL);
|
|
xd->target_win = ps->overlay;
|
|
} else {
|
|
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, ps->root, XCB_RENDER_CP_SUBWINDOW_MODE, &pa);
|
|
xd->target_win = ps->root;
|
|
}
|
|
|
|
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
|
|
int pixmap_needed = xd->vsync ? 2 : 1;
|
|
for (int i = 0; i < pixmap_needed; 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));
|
|
xd->back[i] = x_create_picture_with_pictfmt_and_pixmap(
|
|
ps->c, pictfmt, xd->back_pixmap[i], 0, NULL);
|
|
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;
|
|
|
|
xcb_pixmap_t root_pixmap = x_get_root_back_pixmap(ps);
|
|
if (root_pixmap == XCB_NONE) {
|
|
xd->root_pict = solid_picture(ps->c, ps->root, false, 1, 0.5, 0.5, 0.5);
|
|
} else {
|
|
xd->root_pict = x_create_picture_with_visual_and_pixmap(
|
|
ps->c, ps->vis, root_pixmap, 0, NULL);
|
|
}
|
|
for (int i = 0; ps->o.blur_kerns[i]; i++) {
|
|
assert(i < MAX_BLUR_PASS - 1);
|
|
xd->x_blur_kern_size[i] = x_picture_filter_from_conv(
|
|
ps->o.blur_kerns[i], 1, &xd->x_blur_kern[i], (size_t[]){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 = is_image_transparent,
|
|
.buffer_age = buffer_age,
|
|
.max_buffer_age = 2,
|
|
|
|
.image_op = image_op,
|
|
.copy = copy,
|
|
};
|
|
|
|
// vim: set noet sw=8 ts=8:
|