From 3707f792fbfcf6367582684a9d2dd04be171f776 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Tue, 26 Feb 2019 23:52:37 +0000 Subject: [PATCH] new_backend: New interface Move more logic out of the backend. The backends are now more agnostic to what happens in compton. Signed-off-by: Yuxuan Shui --- src/backend/backend.c | 165 ++++++--- src/backend/backend.h | 137 ++++---- src/backend/backend_common.c | 16 + src/backend/backend_common.h | 3 + src/backend/gl/glx.c | 55 +-- src/backend/gl/glx.h | 31 +- src/backend/xrender.c | 624 ++++++++++++++++------------------- src/common.h | 5 +- src/compton.c | 108 ++++-- src/render.c | 24 +- src/win.c | 59 +++- src/win.h | 5 +- src/x.c | 59 ++++ src/x.h | 23 ++ 14 files changed, 742 insertions(+), 572 deletions(-) diff --git a/src/backend/backend.c b/src/backend/backend.c index 5e166da..c073f06 100644 --- a/src/backend/backend.c +++ b/src/backend/backend.c @@ -8,18 +8,19 @@ #include "config.h" #include "region.h" #include "win.h" +#include "log.h" -backend_info_t *backend_list[NUM_BKEND] = { - [BKEND_XRENDER] = &xrender_backend, +backend_init_fn backend_list[NUM_BKEND] = { + [BKEND_XRENDER] = backend_xrender_init, #ifdef CONFIG_OPENGL - [BKEND_GLX] = &glx_backend, + [BKEND_GLX] = backend_glx_init, #endif }; region_t get_damage(session_t *ps) { region_t region; - auto buffer_age_fn = backend_list[ps->o.backend]->buffer_age; - int buffer_age = buffer_age_fn ? buffer_age_fn(ps->backend_data, ps) : -1; + auto buffer_age_fn = ps->backend_data->ops->buffer_age; + int buffer_age = buffer_age_fn ? buffer_age_fn(ps->backend_data) : -1; pixman_region32_init(®ion); if (buffer_age == -1 || buffer_age > ps->ndamage) { @@ -48,8 +49,6 @@ void paint_all_new(session_t *ps, win *const t, bool ignore_damage) { pixman_region32_fini(®ion); return; } - auto bi = backend_list[ps->o.backend]; - assert(bi); #ifdef DEBUG_REPAINT static struct timespec last_paint = {0}; @@ -67,8 +66,16 @@ void paint_all_new(session_t *ps, win *const t, bool ignore_damage) { reg_paint = ®ion; } - if (bi->prepare) - bi->prepare(ps->backend_data, ps, reg_paint); + // TODO Bind root pixmap + + if (ps->backend_data->ops->prepare) { + ps->backend_data->ops->prepare(ps->backend_data, reg_paint); + } + + if (ps->root_image) { + ps->backend_data->ops->compose(ps->backend_data, ps->root_image, 0, 0, + reg_paint); + } // Windows are sorted from bottom to top // Each window has a reg_ignore, which is the region obscured by all the windows @@ -81,52 +88,124 @@ void paint_all_new(session_t *ps, win *const t, bool ignore_damage) { // XXX XXX pixman_region32_subtract(®_tmp, ®ion, w->reg_ignore); - if (pixman_region32_not_empty(®_tmp)) { - // Render window content - // XXX do this in preprocess? - bi->render_win(ps->backend_data, ps, w, w->win_data, ®_tmp); + if (!pixman_region32_not_empty(®_tmp)) { + continue; + } - // Blur window background - bool win_transparent = - bi->is_win_transparent(ps->backend_data, w, w->win_data); - bool frame_transparent = - bi->is_frame_transparent(ps->backend_data, w, w->win_data); - if (w->blur_background && - (win_transparent || - (ps->o.blur_background_frame && frame_transparent))) { - // Minimize the region we try to blur, if the window - // itself is not opaque, only the frame is. - region_t reg_blur = win_get_bounding_shape_global_by_val(w); - if (win_is_solid(ps, w)) { - region_t reg_noframe; - pixman_region32_init(®_noframe); - win_get_region_noframe_local(w, ®_noframe); - pixman_region32_translate(®_noframe, w->g.x, - w->g.y); - pixman_region32_subtract(®_blur, ®_blur, - ®_noframe); - pixman_region32_fini(®_noframe); - } - bi->blur(ps->backend_data, ps, w->opacity, ®_blur); - pixman_region32_fini(®_blur); + auto reg_bound = win_get_bounding_shape_global_by_val(w); + // Draw shadow on target + if (w->shadow) { + auto reg_shadow = win_extents_by_val(w); + pixman_region32_intersect(®_shadow, ®_shadow, ®_tmp); + + if (!ps->o.wintype_option[w->window_type].full_shadow) { + pixman_region32_subtract(®_shadow, ®_shadow, ®_bound); } - // Draw window on target - bi->compose(ps->backend_data, ps, w, w->win_data, w->g.x, w->g.y, - ®_tmp); + // Mask out the region we don't want shadow on + if (pixman_region32_not_empty(&ps->shadow_exclude_reg)) { + pixman_region32_subtract(®_tmp, ®_tmp, + &ps->shadow_exclude_reg); + } - if (bi->finish_render_win) - bi->finish_render_win(ps->backend_data, ps, w, w->win_data); + if (ps->o.xinerama_shadow_crop && w->xinerama_scr >= 0 && + w->xinerama_scr < ps->xinerama_nscrs) { + // There can be a window where number of screens is + // updated, but the screen number attached to the windows + // have not. + // + // Window screen number will be updated eventually, so + // here we just check to make sure we don't access out of + // bounds. + pixman_region32_intersect( + ®_shadow, ®_shadow, + &ps->xinerama_scr_regs[w->xinerama_scr]); + } + + assert(w->shadow_image); + ps->backend_data->ops->compose(ps->backend_data, w->shadow_image, + w->g.x + w->shadow_dx, + w->g.y + w->shadow_dy, ®_shadow); + pixman_region32_fini(®_shadow); + } + + pixman_region32_intersect(®_tmp, ®_tmp, ®_bound); + pixman_region32_fini(®_bound); + if (!pixman_region32_not_empty(®_tmp)) { + continue; + } + // Blur window background + bool win_transparent = ps->backend_data->ops->is_image_transparent( + ps->backend_data, w->win_image); + bool frame_transparent = w->frame_opacity != 1; + if (w->blur_background && + (win_transparent || (ps->o.blur_background_frame && frame_transparent))) { + // Minimize the region we try to blur, if the window + // itself is not opaque, only the frame is. + if (win_is_solid(ps, w)) { + region_t reg_blur; + pixman_region32_init(®_blur); + win_get_region_noframe_local(w, ®_blur); + pixman_region32_translate(®_blur, w->g.x, w->g.y); + pixman_region32_subtract(®_blur, ®_tmp, ®_blur); + ps->backend_data->ops->blur(ps->backend_data, w->opacity, + ®_blur); + pixman_region32_fini(®_blur); + } else { + ps->backend_data->ops->blur(ps->backend_data, w->opacity, + ®_tmp); + } + } + // Draw window on target + if (!w->invert_color && !w->dim && w->frame_opacity == 1 && w->opacity == 1) { + ps->backend_data->ops->compose(ps->backend_data, w->win_image, + w->g.x, w->g.y, ®_tmp); + } else { + region_t reg_local; + pixman_region32_init(®_local); + pixman_region32_copy(®_local, ®_tmp); + pixman_region32_translate(®_local, -w->g.x, -w->g.y); + auto new_img = ps->backend_data->ops->copy( + ps->backend_data, w->win_image, ®_local); + if (w->invert_color) { + ps->backend_data->ops->image_op(ps->backend_data, + IMAGE_OP_INVERT_COLOR, + new_img, ®_local, NULL); + } + if (w->dim) { + double dim_opacity = ps->o.inactive_dim; + if (!ps->o.inactive_dim_fixed) { + dim_opacity *= w->opacity; + } + ps->backend_data->ops->image_op( + ps->backend_data, IMAGE_OP_DIM, new_img, ®_local, + (double[]){dim_opacity}); + } + if (w->frame_opacity != 1) { + auto reg_frame = win_get_region_noframe_local_by_val(w); + pixman_region32_subtract(®_frame, ®_local, ®_frame); + ps->backend_data->ops->image_op( + ps->backend_data, IMAGE_OP_APPLY_ALPHA, new_img, + ®_frame, (double[]){w->frame_opacity}); + } + if (w->opacity != 1) { + ps->backend_data->ops->image_op( + ps->backend_data, IMAGE_OP_APPLY_ALPHA, new_img, NULL, + (double[]){w->opacity}); + } + ps->backend_data->ops->compose(ps->backend_data, new_img, w->g.x, + w->g.y, ®_tmp); + ps->backend_data->ops->release_image(ps->backend_data, new_img); } } // Free up all temporary regions pixman_region32_fini(®_tmp); - if (bi->present) { + if (ps->backend_data->ops->present) { // Present the rendered scene // Vsync is done here - bi->present(ps->backend_data, ps); + ps->backend_data->ops->present(ps->backend_data); } #ifdef DEBUG_REPAINT diff --git a/src/backend/backend.h b/src/backend/backend.h index acbbade..574cba2 100644 --- a/src/backend/backend.h +++ b/src/backend/backend.h @@ -5,12 +5,30 @@ #include -#include "region.h" #include "compiler.h" +#include "kernel.h" +#include "region.h" +#include "x.h" typedef struct session session_t; typedef struct win win; -typedef struct backend_info { + +struct backend_operations; + +typedef struct backend_base { + struct backend_operations *ops; + xcb_connection_t *c; + xcb_window_t root; + // ... +} backend_t; + +enum image_operations { + IMAGE_OP_INVERT_COLOR, + IMAGE_OP_DIM, + IMAGE_OP_APPLY_ALPHA, +}; + +struct backend_operations { // =========== Initialization =========== @@ -19,15 +37,14 @@ typedef struct backend_info { /// 1) if ps->overlay is not XCB_NONE, use that /// 2) use ps->root otherwise /// XXX make the target window a parameter - void *(*init)(session_t *ps) __attribute__((nonnull(1))); - void (*deinit)(void *backend_data, session_t *ps) __attribute__((nonnull(1, 2))); + void (*deinit)(backend_t *backend_data) __attribute__((nonnull(1))); /// Called when rendering will be stopped for an unknown amount of /// time (e.g. screen is unredirected). Free some resources. - void (*pause)(void *backend_data, session_t *ps); + void (*pause)(backend_t *backend_data, session_t *ps); /// Called before rendering is resumed - void (*resume)(void *backend_data, session_t *ps); + void (*resume)(backend_t *backend_data, session_t *ps); /// Called when root property changed, returns the new /// backend_data. Even if the backend_data changed, all @@ -35,12 +52,7 @@ typedef struct backend_info { /// remain valid. /// /// Optional - void *(*root_change)(void *backend_data, session_t *ps); - - /// Called when vsync is toggled after initialization. If vsync is enabled when init() - /// is called, these function won't be called - void (*vsync_start)(void *backend_data, session_t *ps); - void (*vsync_stop)(void *backend_data, session_t *ps); + void *(*root_change)(backend_t *backend_data, session_t *ps); // =========== Rendering ============ @@ -50,74 +62,55 @@ typedef struct backend_info { /// on the buffer (usually the wallpaper). /// /// Optional? - void (*prepare)(void *backend_data, session_t *ps, const region_t *reg_paint); + void (*prepare)(backend_t *backend_data, const region_t *reg_paint); - /// Paint the content of the window onto the (possibly buffered) + /// Paint the content of an imageonto the (possibly buffered) /// target picture. Always called after render_win(). Maybe called /// multiple times between render_win() and finish_render_win(). /// The origin is the top left of the window, exclude the shadow, /// (dst_x, dst_y) refers to where the origin should be in the target /// buffer. - void (*compose)(void *backend_data, session_t *ps, win *w, void *win_data, - int dst_x, int dst_y, const region_t *reg_paint); + void (*compose)(backend_t *backend_data, void *image_data, int dst_x, int dst_y, + const region_t *reg_paint); /// Blur a given region on of the target. - bool (*blur)(void *backend_data, session_t *ps, double opacity, const region_t *) - __attribute__((nonnull(1, 2, 4))); + bool (*blur)(backend_t *backend_data, double opacity, const region_t *) + __attribute__((nonnull(1, 3))); /// Present the buffered target picture onto the screen. If target /// is not buffered, this should be NULL. Otherwise, it should always /// be non-NULL. /// /// Optional - void (*present)(void *backend_data, session_t *ps) __attribute__((nonnull(1, 2))); + void (*present)(backend_t *backend_data) __attribute__((nonnull(1))); /** - * Render the content of a window into an opaque - * data structure. Dimming, shadow and color inversion is handled - * here. + * Bind a X pixmap to the backend's internal image data structure. * - * This function is allowed to allocate additional resource needed - * for rendering. - * - * Params: - * reg_paint = the paint region, meaning painting should only - * be happening within that region. It's in global - * coordinates. If NULL, the region of paint is the - * whole screen. + * @param backend_data backend data + * @param pixmap X pixmap to bind + * @param fmt information of the pixmap's visual + * @param owned whether the ownership of the pixmap is transfered to the backend + * @return backend internal data structure bound with this pixmap */ - void (*render_win)(void *backend_data, session_t *ps, win *w, void *win_data, - const region_t *reg_paint); + void *(*bind_pixmap)(backend_t *backend_data, xcb_pixmap_t pixmap, + struct xvisual_info fmt, bool owned); - /// Free resource allocated for rendering. After this function is - /// called, compose() won't be called before render_win is called - /// another time. - /// - /// Optional - void (*finish_render_win)(void *backend_data, session_t *ps, win *w, void *win_data); + /// Create a shadow image based on the parameters + void *(*render_shadow)(backend_t *backend_data, int width, int height, + const conv *kernel, double r, double g, double b); // ============ Resource management =========== - // XXX Thoughts: calling release_win and prepare_win for every config notify + // XXX Thoughts: calling release_image and render_* for every config notify // is wasteful, since there can be multiple such notifies per drawing. // But if we don't, it can mean there will be a state where is window is // mapped and visible, but there is no win_data attached to it. We don't // want to break that assumption as for now. We need to reconsider this. - /// Create a structure to stored additional data needed for rendering a - /// window, later used for render() and compose(). - /// - /// Backend can assume this function will only be called with visible - /// InputOutput windows, and only be called when screen is redirected. - /// - /// Backend can assume size, shape and visual of the window won't change between - /// prepare_win() and release_win(). - void *(*prepare_win)(void *backend_data, session_t *ps, win *w) - __attribute__((nonnull(1, 2, 3))); - - /// Free resources allocated by prepare_win() - void (*release_win)(void *backend_data, session_t *ps, win *w, void *win_data) - __attribute__((nonnull(1, 2, 3))); + /// Free resources associated with an image data structure + void (*release_image)(backend_t *backend_data, void *img_data) + __attribute__((nonnull(1, 2))); // =========== Query =========== @@ -127,32 +120,44 @@ typedef struct backend_info { /// This function is needed because some backend might change the content of the /// window (e.g. when using a custom shader with the glx backend), so we only now /// the transparency after the window is rendered - bool (*is_win_transparent)(void *backend_data, win *w, void *win_data) - __attribute__((nonnull(1, 2))); - - /// Return if the frame window has transparent content. Guaranteed to - /// only be called after render_win is called. - /// - /// Same logic as is_win_transparent applies here. - bool (*is_frame_transparent)(void *backend_data, win *w, void *win_data) + bool (*is_image_transparent)(backend_t *backend_data, void *image_data) __attribute__((nonnull(1, 2))); /// Get the age of the buffer content we are currently rendering ontop /// of. The buffer that has just been `present`ed has a buffer age of 1. /// Everytime `present` is called, buffers get older. Return -1 if the /// buffer is empty. - int (*buffer_age)(void *backend_data, session_t *); + int (*buffer_age)(backend_t *backend_data); /// The maximum number buffer_age might return. int max_buffer_age; + // =========== Post-processing ============ + /** + * Manipulate an image + * + * @param backend_data backend data + * @param op the operation to perform + * @param image_data an image data structure returned by the backend + * @param reg_paint the clip region, limit the region of the image to be + * manipulated + * @param args extra arguments, specific to each operation + * @return a new image data structure contains the same image as `image_data` + */ + void (*image_op)(backend_t *backend_data, enum image_operations op, + void *image_data, const region_t *reg_paint, void *args); + + void *(*copy)(backend_t *base, const void *image_data, const region_t *reg_copy); + // =========== Hooks ============ /// Let the backend hook into the event handling queue -} backend_info_t; +}; -extern backend_info_t xrender_backend; -extern backend_info_t glx_backend; -extern backend_info_t *backend_list[]; +typedef backend_t *(*backend_init_fn)(session_t *ps) __attribute__((nonnull(1))); + +extern backend_t *backend_xrender_init(session_t *ps); +extern backend_t *backend_glx_init(session_t *ps); +extern backend_init_fn backend_list[]; bool default_is_win_transparent(void *, win *, void *); bool default_is_frame_transparent(void *, win *, void *); diff --git a/src/backend/backend_common.c b/src/backend/backend_common.c index 2d8a933..57592d7 100644 --- a/src/backend/backend_common.c +++ b/src/backend/backend_common.c @@ -256,6 +256,22 @@ shadow_picture_err: return false; } +void *default_backend_render_shadow(backend_t *backend_data, int width, int height, + const conv *kernel, double r, double g, double b) { + xcb_pixmap_t shadow_pixel = solid_picture(backend_data->c, backend_data->root, + true, 1, r, g, b), + shadow = XCB_NONE; + xcb_render_picture_t pict = XCB_NONE; + + build_shadow(backend_data->c, backend_data->root, 1, width, height, kernel, + shadow_pixel, &shadow, &pict); + + auto visual = x_get_visual_for_standard(backend_data->c, XCB_PICT_STANDARD_ARGB_32); + void *ret = backend_data->ops->bind_pixmap(backend_data, shadow, x_get_visual_info(backend_data->c, visual), true); + xcb_render_free_picture(backend_data->c, pict); + return ret; +} + bool default_is_win_transparent(void *backend_data, win *w, void *win_data) { return w->mode != WMODE_SOLID; } diff --git a/src/backend/backend_common.h b/src/backend/backend_common.h index bca2076..2c4342a 100644 --- a/src/backend/backend_common.h +++ b/src/backend/backend_common.h @@ -30,3 +30,6 @@ bool default_is_win_transparent(void *, win *, void *); /// The default implementation of `is_frame_transparent`, it uses win::frame_opacity. Same /// caveat as `default_is_win_transparent` applies. bool default_is_frame_transparent(void *, win *, void *); + +void *default_backend_render_shadow(backend_t *backend_data, int width, int height, + const conv *kernel, double r, double g, double b); diff --git a/src/backend/gl/glx.c b/src/backend/gl/glx.c index f09e541..d2070e9 100644 --- a/src/backend/gl/glx.c +++ b/src/backend/gl/glx.c @@ -39,6 +39,7 @@ struct _glx_win_data { }; struct _glx_data { + backend_t base; int glx_event; int glx_error; GLXContext ctx; @@ -52,7 +53,7 @@ struct _glx_data { }; struct glx_fbconfig_info * -glx_find_fbconfig(Display *dpy, int screen, struct glx_fbconfig_criteria m) { +glx_find_fbconfig(Display *dpy, int screen, struct xvisual_info m) { log_debug("Looking for FBConfig for RGBA%d%d%d%d, depth %d", m.red_size, m.blue_size, m.green_size, m.alpha_size, m.visual_depth); @@ -181,7 +182,7 @@ static void glx_release_pixmap(struct _glx_data *gd, Display *dpy, struct _glx_w /** * Free a glx_texture_t. */ -static void glx_release_win(void *backend_data, session_t *ps, win *w, void *win_data) { +void glx_release_win(void *backend_data, session_t *ps, win *w, void *win_data) { struct _glx_win_data *wd = win_data; struct _glx_data *gd = backend_data; glx_release_pixmap(gd, ps->dpy, wd); @@ -203,7 +204,7 @@ static inline void free_win_res_glx(session_t *ps, win *w) { /** * Destroy GLX related resources. */ -static void glx_deinit(void *backend_data, session_t *ps) { +void glx_deinit(void *backend_data, session_t *ps) { struct _glx_data *gd = backend_data; // Free all GLX resources of windows @@ -231,10 +232,14 @@ static void glx_deinit(void *backend_data, session_t *ps) { /** * Initialize OpenGL. */ -static void *glx_init(session_t *ps) { +backend_t *backend_glx_init(session_t *ps) { bool success = false; glxext_init(ps->dpy, ps->scr); auto gd = ccalloc(1, struct _glx_data); + gd->base.c = ps->c; + gd->base.root = ps->root; + gd->base.ops = NULL; // TODO + XVisualInfo *pvis = NULL; // Check for GLX extension @@ -371,10 +376,10 @@ end: return NULL; } - return gd; + return &gd->base; } -static void *glx_prepare_win(void *backend_data, session_t *ps, win *w) { +void *glx_prepare_win(void *backend_data, session_t *ps, win *w) { struct _glx_data *gd = backend_data; // Retrieve pixmap parameters, if they aren't provided if (w->g.depth > OPENGL_MAX_DEPTH) { @@ -395,8 +400,8 @@ static void *glx_prepare_win(void *backend_data, session_t *ps, win *w) { goto err; } - auto criteria = x_visual_to_fbconfig_criteria(ps->c, w->a.visual); - auto fbcfg = glx_find_fbconfig(ps->dpy, ps->scr, criteria); + auto visual_info = x_get_visual_info(ps->c, w->a.visual); + auto fbcfg = glx_find_fbconfig(ps->dpy, ps->scr, visual_info); if (!fbcfg) { log_error("Couldn't find FBConfig with requested visual %x", w->a.visual); goto err; @@ -473,7 +478,7 @@ err: /** * Bind a X pixmap to an OpenGL texture. */ -static void glx_render_win(void *backend_data, session_t *ps, win *w, void *win_data, +void glx_render_win(void *backend_data, session_t *ps, win *w, void *win_data, const region_t *reg_paint) { struct _glx_data *gd = backend_data; struct _glx_win_data *wd = win_data; @@ -489,11 +494,11 @@ static void glx_render_win(void *backend_data, session_t *ps, win *w, void *win_ gl_check_err(); } -static void glx_present(void *backend_data, session_t *ps) { +void glx_present(void *backend_data, session_t *ps) { glXSwapBuffers(ps->dpy, ps->overlay != XCB_NONE ? ps->overlay : ps->root); } -static int glx_buffer_age(void *backend_data, session_t *ps) { +int glx_buffer_age(void *backend_data, session_t *ps) { if (ps->o.glx_swap_method == SWAPM_BUFFER_AGE) { unsigned int val; glXQueryDrawable(ps->dpy, get_tgt_window(ps), GLX_BACK_BUFFER_AGE_EXT, &val); @@ -503,7 +508,7 @@ static int glx_buffer_age(void *backend_data, session_t *ps) { } } -static void glx_compose(void *backend_data, session_t *ps, win *w, void *win_data, +void glx_compose(void *backend_data, session_t *ps, win *w, void *win_data, int dst_x, int dst_y, const region_t *region) { struct _glx_data *gd = backend_data; struct _glx_win_data *wd = win_data; @@ -531,19 +536,19 @@ static void glx_compose(void *backend_data, session_t *ps, win *w, void *win_dat pixman_region32_fini(®ion_yflipped); } -backend_info_t glx_backend = { - .init = glx_init, - .deinit = glx_deinit, - .prepare_win = glx_prepare_win, - .render_win = glx_render_win, - .release_win = glx_release_win, - .present = glx_present, - .compose = glx_compose, - .is_win_transparent = default_is_win_transparent, - .is_frame_transparent = default_is_frame_transparent, - .buffer_age = glx_buffer_age, - .max_buffer_age = 5, // XXX why? -}; +/* backend_info_t glx_backend = { */ +/* .init = glx_init, */ +/* .deinit = glx_deinit, */ +/* .prepare_win = glx_prepare_win, */ +/* .render_win = glx_render_win, */ +/* .release_win = glx_release_win, */ +/* .present = glx_present, */ +/* .compose = glx_compose, */ +/* .is_win_transparent = default_is_win_transparent, */ +/* .is_frame_transparent = default_is_frame_transparent, */ +/* .buffer_age = glx_buffer_age, */ +/* .max_buffer_age = 5, // XXX why? */ +/* }; */ /** * Check if a GLX extension exists. diff --git a/src/backend/gl/glx.h b/src/backend/gl/glx.h index 9eedf2f..9b7b955 100644 --- a/src/backend/gl/glx.h +++ b/src/backend/gl/glx.h @@ -40,37 +40,8 @@ struct glx_fbconfig_criteria { int visual_depth; }; -struct glx_fbconfig_info *glx_find_fbconfig(Display *, int screen, struct glx_fbconfig_criteria); +struct glx_fbconfig_info *glx_find_fbconfig(Display *, int screen, struct xvisual_info); -/// Generate a search criteria for fbconfig from a X visual. -/// Returns {-1, -1, -1, -1, -1} on failure -static inline struct glx_fbconfig_criteria -x_visual_to_fbconfig_criteria(xcb_connection_t *c, xcb_visualid_t visual) { - auto pictfmt = x_get_pictform_for_visual(c, visual); - auto depth = x_get_visual_depth(c, visual); - if (!pictfmt || depth == -1) { - log_error("Invalid visual %#03x", visual); - return (struct glx_fbconfig_criteria){-1, -1, -1, -1, -1}; - } - if (pictfmt->type != XCB_RENDER_PICT_TYPE_DIRECT) { - log_error("compton cannot handle non-DirectColor visuals. Report an " - "issue if you see this error message."); - return (struct glx_fbconfig_criteria){-1, -1, -1, -1, -1}; - } - - int red_size = popcountl(pictfmt->direct.red_mask), - blue_size = popcountl(pictfmt->direct.blue_mask), - green_size = popcountl(pictfmt->direct.green_mask), - alpha_size = popcountl(pictfmt->direct.alpha_mask); - - return (struct glx_fbconfig_criteria){ - .red_size = red_size, - .green_size = green_size, - .blue_size = blue_size, - .alpha_size = alpha_size, - .visual_depth = depth, - }; -} struct glxext_info { bool initialized; diff --git a/src/backend/xrender.c b/src/backend/xrender.c index db28bca..48ce910 100644 --- a/src/backend/xrender.c +++ b/src/backend/xrender.c @@ -25,8 +25,10 @@ #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 @@ -56,10 +58,8 @@ typedef struct _xrender_data { /// 1x1 black picture xcb_render_picture_t black_pixel; - /// 1x1 picture of the shadow color - xcb_render_picture_t shadow_pixel; - /// convolution kernel for the shadow - conv *shadow_kernel; + /// 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]; @@ -69,112 +69,39 @@ typedef struct _xrender_data { xcb_special_event_t *present_event; } xrender_data; -#if 0 -/** - * Paint root window content. - */ -static void -paint_root(session_t *ps, const region_t *reg_paint) { - if (!ps->root_tile_paint.pixmap) - get_root_tile(ps); - - paint_region(ps, NULL, 0, 0, ps->root_width, ps->root_height, 1.0, reg_paint, - ps->root_tile_paint.pict); -} -#endif - -struct _xrender_win_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; - // A buffer used for rendering - xcb_render_picture_t buffer; - // The rendered content of the window (dimmed, inverted - // color, etc.). This is either `buffer` or `pict` - xcb_render_picture_t rendered_pict; - xcb_pixmap_t shadow_pixmap; - xcb_render_picture_t shadow_pict; + long width, height; + bool has_alpha; + double opacity; + xcb_visualid_t visual; + uint8_t depth; + bool owned; }; -static void compose(void *backend_data, session_t *ps, win *w, void *win_data, int dst_x, - int dst_y, const region_t *reg_paint) { - struct _xrender_data *xd = backend_data; - struct _xrender_win_data *wd = win_data; - bool blend = default_is_frame_transparent(NULL, w, win_data) || - default_is_win_transparent(NULL, w, win_data); - int op = (blend ? XCB_RENDER_PICT_OP_OVER : XCB_RENDER_PICT_OP_SRC); - auto alpha_pict = xd->alpha_pict[(int)(w->opacity * 255.0)]; - - // XXX Move shadow drawing into a separate function, - // also do shadow excluding outside of backend - // XXX This is needed to implement full-shadow - if (w->shadow) { - // Put shadow on background - region_t shadow_reg = win_extents_by_val(w); - region_t bshape = win_get_bounding_shape_global_by_val(w); - region_t reg_tmp; - pixman_region32_init(®_tmp); - // Shadow doesn't need to be painted underneath the body of the window - // Because no one can see it - pixman_region32_subtract(®_tmp, &shadow_reg, w->reg_ignore); - - // Mask out the region we don't want shadow on - if (pixman_region32_not_empty(&ps->shadow_exclude_reg)) - pixman_region32_subtract(®_tmp, ®_tmp, &ps->shadow_exclude_reg); - - // Might be worth while to crop the region to shadow border - pixman_region32_intersect_rect(®_tmp, ®_tmp, w->g.x + w->shadow_dx, - w->g.y + w->shadow_dy, w->shadow_width, - w->shadow_height); - - // Crop the shadow to the damage region. If we draw out side of - // the damage region, we could be drawing over perfectly good - // content, and destroying it. - pixman_region32_intersect(®_tmp, ®_tmp, (region_t *)reg_paint); - - if (ps->o.xinerama_shadow_crop && w->xinerama_scr >= 0 && - w->xinerama_scr < ps->xinerama_nscrs) - // There can be a window where number of screens is updated, - // but the screen number attached to the windows have not. - // - // Window screen number will be updated eventually, so here we - // just check to make sure we don't access out of bounds. - pixman_region32_intersect( - ®_tmp, ®_tmp, &ps->xinerama_scr_regs[w->xinerama_scr]); - - // Mask out the body of the window from the shadow - // Doing it here instead of in make_shadow() for saving GPU - // power and handling shaped windows (XXX unconfirmed) - pixman_region32_subtract(®_tmp, ®_tmp, &bshape); - pixman_region32_fini(&bshape); - - // Detect if the region is empty before painting - if (pixman_region32_not_empty(®_tmp)) { - x_set_picture_clip_region(ps->c, xd->back[xd->curr_back], 0, 0, - ®_tmp); - xcb_render_composite( - ps->c, XCB_RENDER_PICT_OP_OVER, wd->shadow_pict, alpha_pict, - xd->back[xd->curr_back], 0, 0, 0, 0, dst_x + w->shadow_dx, - dst_y + w->shadow_dy, w->shadow_width, w->shadow_height); - } - pixman_region32_fini(®_tmp); - pixman_region32_fini(&shadow_reg); - } +static void +compose(backend_t *base, void *img_data, int dst_x, int dst_y, const region_t *reg_paint) { + struct _xrender_data *xd = (void *)base; + struct _xrender_image_data *img = img_data; + int 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)]; // 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(ps->c, wd->rendered_pict); + x_clear_picture_clip_region(base->c, img->pict); - x_set_picture_clip_region(ps->c, xd->back[xd->curr_back], 0, 0, reg_paint); - xcb_render_composite(ps->c, op, wd->rendered_pict, alpha_pict, xd->back[xd->curr_back], - 0, 0, 0, 0, dst_x, dst_y, w->widthb, w->heightb); + x_set_picture_clip_region(base->c, xd->back[xd->curr_back], 0, 0, reg_paint); + xcb_render_composite(base->c, op, img->pict, alpha_pict, xd->back[xd->curr_back], + 0, 0, 0, 0, dst_x, dst_y, img->width, img->height); } -static bool -blur(void *backend_data, session_t *ps, double opacity, const region_t *reg_paint) { - struct _xrender_data *xd = backend_data; +static bool blur(backend_t *backend_data, double opacity, const region_t *reg_paint) { + struct _xrender_data *xd = (void *)backend_data; + xcb_connection_t *c = xd->base.c; const pixman_box32_t *reg = pixman_region32_extents((region_t *)reg_paint); const int height = reg->y2 - reg->y1; const int width = reg->x2 - reg->x1; @@ -184,8 +111,10 @@ blur(void *backend_data, session_t *ps, double opacity, const region_t *reg_pain // 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(ps->c, ps->root, width, height, ps->vis, 0, NULL), - x_create_picture_with_visual(ps->c, ps->root, width, height, ps->vis, 0, NULL)}; + 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)}; region_t clip; pixman_region32_init(&clip); @@ -197,8 +126,8 @@ blur(void *backend_data, session_t *ps, double opacity, const region_t *reg_pain return false; } - x_set_picture_clip_region(ps->c, tmp_picture[0], 0, 0, &clip); - x_set_picture_clip_region(ps->c, tmp_picture[1], 0, 0, &clip); + x_set_picture_clip_region(c, tmp_picture[0], 0, 0, &clip); + x_set_picture_clip_region(c, tmp_picture[1], 0, 0, &clip); // The multipass blur implemented here is not correct, but this is what old // compton did anyway. XXX @@ -206,6 +135,7 @@ blur(void *backend_data, session_t *ps, double opacity, const region_t *reg_pain auto alpha_pict = xd->alpha_pict[(int)(opacity * 255)]; int current = 0; int src_x = reg->x1, src_y = reg->y1; + x_set_picture_clip_region(c, src_pict, 0, 0, reg_paint); // For more than 1 pass, we do: // back -(pass 1)-> tmp0 -(pass 2)-> tmp1 ... @@ -220,24 +150,23 @@ blur(void *backend_data, session_t *ps, double opacity, const region_t *reg_pain // be applied on source picture, to get the nearby pixels outside the // window. // TODO cache converted blur_kerns - xcb_render_set_picture_filter(ps->c, src_pict, strlen(filter), filter, + xcb_render_set_picture_filter(c, src_pict, strlen(filter), filter, xd->x_blur_kern_size[i], xd->x_blur_kern[i]); - if (ps->o.blur_kerns[i + 1] || i == 0) { + if (xd->x_blur_kern[i + 1] || i == 0) { // This is not the last pass, or this is the first pass - xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, src_pict, + 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(ps->c, XCB_RENDER_PICT_OP_OVER, src_pict, + xcb_render_composite(c, XCB_RENDER_PICT_OP_OVER, src_pict, alpha_pict, xd->back[xd->curr_back], 0, 0, 0, 0, reg->x1, reg->y1, width, height); } // reset filter - xcb_render_set_picture_filter(ps->c, src_pict, strlen(filter0), filter0, - 0, NULL); + xcb_render_set_picture_filter(c, src_pict, strlen(filter0), filter0, 0, NULL); src_pict = tmp_picture[current]; dst_pict = tmp_picture[!current]; @@ -248,90 +177,187 @@ blur(void *backend_data, session_t *ps, double opacity, const region_t *reg_pain // There is only 1 pass if (i == 1) { - xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_OVER, src_pict, alpha_pict, + xcb_render_composite(c, XCB_RENDER_PICT_OP_OVER, src_pict, alpha_pict, xd->back[xd->curr_back], 0, 0, 0, 0, reg->x1, reg->y1, width, height); } - xcb_render_free_picture(ps->c, tmp_picture[0]); - xcb_render_free_picture(ps->c, tmp_picture[1]); + xcb_render_free_picture(c, tmp_picture[0]); + xcb_render_free_picture(c, tmp_picture[1]); return true; } -static void render_win(void *backend_data, session_t *ps, win *w, void *win_data, - const region_t *reg_paint) { - struct _xrender_data *xd = backend_data; - struct _xrender_win_data *wd = win_data; - - w->pixmap_damaged = false; - - if (!w->invert_color && w->frame_opacity == 1 && !w->dim) { - // No extra processing needed - wd->rendered_pict = wd->pict; - return; +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; } - region_t reg_paint_local; - pixman_region32_init(®_paint_local); - pixman_region32_copy(®_paint_local, (region_t *)reg_paint); - pixman_region32_translate(®_paint_local, -w->g.x, -w->g.y); - - // We don't want to modify the content of the original window when we process - // it, so we create a buffer. - if (wd->buffer == XCB_NONE) { - wd->buffer = x_create_picture_with_pictfmt(ps->c, ps->root, w->widthb, w->heightb, - w->pictfmt, 0, NULL); + auto img = ccalloc(1, struct _xrender_image_data); + img->depth = fmt.visual_depth; + img->width = r->width; + img->height = 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; +} - // Copy the content of the window over to the buffer - x_clear_picture_clip_region(ps->c, wd->buffer); - wd->rendered_pict = wd->buffer; - xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, wd->pict, XCB_NONE, - wd->rendered_pict, 0, 0, 0, 0, 0, 0, w->widthb, w->heightb); - - if (w->invert_color) { - // Handle invert color - x_set_picture_clip_region(ps->c, wd->rendered_pict, 0, 0, ®_paint_local); - - xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_DIFFERENCE, - xd->white_pixel, XCB_NONE, wd->rendered_pict, 0, 0, - 0, 0, 0, 0, w->widthb, w->heightb); - // We use an extra PictOpInReverse operation to get correct pixel - // alpha. There could be a better solution. - if (win_has_alpha(w)) - xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_IN_REVERSE, - wd->pict, XCB_NONE, wd->rendered_pict, 0, 0, - 0, 0, 0, 0, w->widthb, w->heightb); +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); } +} - if (w->frame_opacity != 1) { - // Handle transparent frame - // Step 1: clip paint area to frame - region_t frame_reg; - pixman_region32_init(&frame_reg); - pixman_region32_copy(&frame_reg, &w->bounding_shape); - - region_t body_reg = win_get_region_noframe_local_by_val(w); - pixman_region32_subtract(&frame_reg, &frame_reg, &body_reg); - - // Draw the frame with frame opacity - xcb_render_picture_t alpha_pict = - xd->alpha_pict[(int)(w->frame_opacity * w->opacity * 255)]; - x_set_picture_clip_region(ps->c, wd->rendered_pict, 0, 0, &frame_reg); - - // Step 2: multiply alpha value - // XXX test - xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, xd->white_pixel, - alpha_pict, wd->rendered_pict, 0, 0, 0, 0, 0, 0, - w->widthb, w->heightb); +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); +} - if (w->dim) { - // Handle dimming +static void prepare(backend_t *base, const region_t *reg_paint) { + struct _xrender_data *xd = (void *)base; - double dim_opacity = ps->o.inactive_dim; - if (!ps->o.inactive_dim_fixed) - dim_opacity *= w->opacity; + // Paint the root pixmap (i.e. wallpaper) + // Limit the paint area + x_set_picture_clip_region(base->c, xd->back[xd->curr_back], 0, 0, reg_paint); + + xcb_render_composite(base->c, XCB_RENDER_PICT_OP_SRC, xd->root_pict, XCB_NONE, + xd->back[xd->curr_back], 0, 0, 0, 0, 0, 0, xd->target_width, + xd->target_height); +} + +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, xd->target_width, 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 void image_op(backend_t *base, enum image_operations op, void *image, + const region_t *reg, void *arg) { + struct _xrender_data *xd = (void *)base; + struct _xrender_image_data *img = image; + double dim_opacity; + double alpha_multiplier; + switch (op) { + case IMAGE_OP_INVERT_COLOR: + assert(reg); + x_set_picture_clip_region(base->c, img->pict, 0, 0, reg); + 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, + img->width, img->height); + + xcb_render_composite(base->c, XCB_RENDER_PICT_OP_DIFFERENCE, + xd->white_pixel, XCB_NONE, tmp_pict, 0, 0, 0, + 0, 0, 0, img->width, img->height); + // 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, img->width, img->height); + 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, img->width, img->height); + } + break; + case IMAGE_OP_DIM: + assert(reg); + x_set_picture_clip_region(base->c, img->pict, 0, 0, reg); + dim_opacity = *(double *)arg; xcb_render_color_t color = { .red = 0, .green = 0, .blue = 0, .alpha = 0xffff * dim_opacity}; @@ -340,101 +366,104 @@ static void render_win(void *backend_data, session_t *ps, win *w, void *win_data xcb_rectangle_t rect = { .x = 0, .y = 0, - .width = w->widthb, - .height = w->heightb, + .width = img->width, + .height = img->height, }; - x_clear_picture_clip_region(ps->c, wd->rendered_pict); - xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_OVER, - wd->rendered_pict, color, 1, &rect); - } + xcb_render_fill_rectangles(base->c, XCB_RENDER_PICT_OP_OVER, img->pict, + color, 1, &rect); + break; + case IMAGE_OP_APPLY_ALPHA: + alpha_multiplier = *(double *)arg; + if (alpha_multiplier == 1) { + break; + } + if (!reg) { + img->opacity *= alpha_multiplier; + img->has_alpha = true; + break; + } - pixman_region32_fini(®_paint_local); + auto alpha_pict = xd->alpha_pict[(int)(alpha_multiplier * 255)]; + x_set_picture_clip_region(base->c, img->pict, 0, 0, reg); + xcb_render_composite(base->c, XCB_RENDER_PICT_OP_IN, img->pict, XCB_NONE, + alpha_pict, 0, 0, 0, 0, 0, 0, img->width, img->height); + img->has_alpha = true; + break; + } } -static void *prepare_win(void *backend_data, session_t *ps, win *w) { - auto wd = ccalloc(1, struct _xrender_win_data); - struct _xrender_data *xd = backend_data; - assert(w->a.map_state == XCB_MAP_STATE_VIEWABLE); - if (ps->has_name_pixmap) { - wd->pixmap = xcb_generate_id(ps->c); - xcb_composite_name_window_pixmap_checked(ps->c, w->id, wd->pixmap); +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); + x_set_picture_clip_region(base->c, img->pict, 0, 0, reg); + new_img->has_alpha = img->has_alpha; + new_img->width = img->width; + new_img->height = img->height; + new_img->visual = img->visual; + 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) { + 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->pixmap == XCB_NONE) { + xcb_free_pixmap(base->c, new_img->pixmap); + free(new_img); + return NULL; } - xcb_drawable_t draw = wd->pixmap; - if (!draw) - draw = w->id; - - log_trace("%s %x", w->name, wd->pixmap); - wd->pict = x_create_picture_with_pictfmt_and_pixmap(ps->c, w->pictfmt, draw, 0, NULL); - wd->buffer = XCB_NONE; - - // XXX delay allocating shadow pict until compose() will dramatical - // improve performance, probably because otherwise shadow pict - // can be created and destroyed multiple times per draw. - // - // However doing that breaks a assumption the backend API makes (i.e. - // either all needed data is here, or none is), therefore we will - // leave this here until we have chance to re-think the backend API - if (w->shadow) { - xcb_pixmap_t pixmap; - build_shadow(ps->c, ps->root, 1, w->widthb, w->heightb, xd->shadow_kernel, - xd->shadow_pixel, &pixmap, &wd->shadow_pict); - xcb_free_pixmap(ps->c, pixmap); - } - return wd; + auto 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, img->width, img->height); + return new_img; } -static void release_win(void *backend_data, session_t *ps, win *w, void *win_data) { - struct _xrender_win_data *wd = win_data; - xcb_free_pixmap(ps->c, wd->pixmap); - // xcb_free_pixmap(ps->c, wd->shadow_pixmap); - xcb_render_free_picture(ps->c, wd->pict); - xcb_render_free_picture(ps->c, wd->shadow_pict); - if (wd->buffer != XCB_NONE) - xcb_render_free_picture(ps->c, wd->buffer); - free(wd); -} +static struct backend_operations xrender_ops = { + .deinit = deinit, + .blur = blur, + .present = present, + .prepare = prepare, + .compose = compose, + .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, -static void deinit(void *backend_data, session_t *ps) { - struct _xrender_data *xd = backend_data; - for (int i = 0; i < 256; i++) { - xcb_render_free_picture(ps->c, xd->alpha_pict[i]); - } - xcb_render_free_picture(ps->c, xd->target); - xcb_render_free_picture(ps->c, xd->root_pict); - for (int i = 0; i < 2; i++) { - xcb_render_free_picture(ps->c, xd->back[i]); - xcb_free_pixmap(ps->c, xd->back_pixmap[i]); - } - for (int i = 0; ps->o.blur_kerns[i]; i++) { - free(xd->x_blur_kern[i]); - } - if (xd->present_event) { - xcb_unregister_for_special_event(ps->c, xd->present_event); - } - xcb_render_free_picture(ps->c, xd->white_pixel); - xcb_render_free_picture(ps->c, xd->black_pixel); - xcb_render_free_picture(ps->c, xd->shadow_pixel); - free_conv(xd->shadow_kernel); - free(xd); -} + .image_op = image_op, + .copy = copy, +}; -static void *init(session_t *ps) { +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; + xd->base.ops = &xrender_ops; + 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); - xd->shadow_pixel = solid_picture(ps->c, ps->root, true, 1, ps->o.shadow_red, - ps->o.shadow_green, ps->o.shadow_blue); - xd->shadow_kernel = gaussian_kernel(ps->o.shadow_radius); - sum_kernel_preprocess(xd->shadow_kernel); if (ps->overlay != XCB_NONE) { xd->target = x_create_picture_with_visual_and_pixmap( @@ -506,103 +535,10 @@ static void *init(session_t *ps) { 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; + return &xd->base; err: - deinit(xd, ps); + deinit(&xd->base); return NULL; } -static void *root_change(void *backend_data, session_t *ps) { - deinit(backend_data, ps); - return init(ps); -} - -static void prepare(void *backend_data, session_t *ps, const region_t *reg_paint) { - struct _xrender_data *xd = backend_data; - - // Paint the root pixmap (i.e. wallpaper) - // Limit the paint area - x_set_picture_clip_region(ps->c, xd->back[xd->curr_back], 0, 0, reg_paint); - - xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, xd->root_pict, XCB_NONE, - xd->back[xd->curr_back], 0, 0, 0, 0, 0, 0, ps->root_width, - ps->root_height); -} - -static void present(void *backend_data, session_t *ps) { - struct _xrender_data *xd = backend_data; - - if (xd->vsync) { - // Make sure we got reply from PresentPixmap before waiting for events, - // to avoid deadlock - auto e = xcb_request_check( - ps->c, xcb_present_pixmap_checked( - ps->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(ps->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(ps->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(ps->c, XCB_RENDER_PICT_OP_SRC, - xd->back[xd->curr_back], XCB_NONE, xd->target, 0, 0, - 0, 0, 0, 0, ps->root_width, ps->root_height); - xd->buffer_age[xd->curr_back] = 1; - } -} - -static int buffer_age(void *backend_data, session_t *ps) { - struct _xrender_data *xd = backend_data; - return xd->buffer_age[xd->curr_back]; -} - -struct backend_info xrender_backend = { - .init = init, - .deinit = deinit, - .blur = blur, - .present = present, - .prepare = prepare, - .compose = compose, - .root_change = root_change, - .render_win = render_win, - .prepare_win = prepare_win, - .release_win = release_win, - .is_win_transparent = default_is_win_transparent, - .is_frame_transparent = default_is_frame_transparent, - .buffer_age = buffer_age, - .max_buffer_age = 2, -}; - // vim: set noet sw=8 ts=8: diff --git a/src/common.h b/src/common.h index 976d184..f19cd3e 100644 --- a/src/common.h +++ b/src/common.h @@ -86,6 +86,7 @@ #include "compiler.h" #include "utils.h" #include "x.h" +#include "backend/backend.h" // === Constants === @@ -303,7 +304,7 @@ typedef struct session { /// Signal handler for SIGINT ev_signal int_signal; /// backend data - void *backend_data; + backend_t *backend_data; /// libev mainloop struct ev_loop *loop; @@ -332,6 +333,8 @@ typedef struct session { bool root_tile_fill; /// Picture of the root window background. paint_t root_tile_paint; + /// The backend data the root pixmap bound to + void *root_image; /// A region of the size of the screen. region_t screen_reg; /// Picture of root window. Destination of painting in no-DBE painting diff --git a/src/compton.c b/src/compton.c index 6307114..348afd4 100644 --- a/src/compton.c +++ b/src/compton.c @@ -807,9 +807,9 @@ void configure_root(session_t *ps, int width, int height) { auto bi = backend_list[ps->o.backend]; if (ps->o.experimental_backends) { assert(bi); - if (!bi->root_change) { + if (!ps->backend_data->ops->root_change) { // deinit/reinit backend if the backend cannot handle root change - bi->deinit(ps->backend_data, ps); + ps->backend_data->ops->deinit(ps->backend_data); ps->backend_data = NULL; } } else { @@ -836,10 +836,10 @@ void configure_root(session_t *ps, int width, int height) { glx_on_root_change(ps); #endif if (ps->o.experimental_backends) { - if (bi->root_change) { - bi->root_change(ps->backend_data, ps); + if (ps->backend_data->ops->root_change) { + ps->backend_data->ops->root_change(ps->backend_data, ps); } else { - ps->backend_data = bi->init(ps); + ps->backend_data = backend_list[ps->o.backend](ps); if (!ps->backend_data) { log_fatal("Failed to re-initialize backend after root change, aborting..."); ps->quit = true; @@ -938,10 +938,21 @@ circulate_win(session_t *ps, xcb_circulate_notify_event_t *ce) { static inline void root_damaged(session_t *ps) { if (ps->root_tile_paint.pixmap) { - xcb_clear_area(ps->c, true, ps->root, 0, 0, 0, 0); free_root_tile(ps); } + if (ps->o.experimental_backends) { + if (ps->root_image) { + ps->backend_data->ops->release_image(ps->backend_data, ps->root_image); + } + auto pixmap = x_get_root_back_pixmap(ps); + if (pixmap != XCB_NONE) { + ps->root_image = + ps->backend_data->ops->bind_pixmap(ps->backend_data, pixmap, + x_get_visual_info(ps->c, ps->vis), false); + } + } + // Mark screen damaged force_repaint(ps); } @@ -1981,22 +1992,44 @@ redir_start(session_t *ps) { if (ps->o.experimental_backends) { // Reinitialize win_data - backend_info_t *bi = backend_list[ps->o.backend]; - assert(bi); - ps->backend_data = bi->init(ps); + ps->backend_data = backend_list[ps->o.backend](ps); if (!ps->backend_data) { log_fatal("Failed to initialize backend, aborting..."); ps->quit = true; ev_break(ps->loop, EVBREAK_ALL); return false; } + for (win *w = ps->list; w; w = w->next) { if (w->a.map_state == XCB_MAP_STATE_VIEWABLE) { - w->win_data = bi->prepare_win(ps->backend_data, ps, w); + w->pixmap = xcb_generate_id(ps->c); + xcb_composite_name_window_pixmap(ps->c, w->id, w->pixmap); + w->win_image = ps->backend_data->ops->bind_pixmap(ps->backend_data, w->pixmap, + x_get_visual_info(ps->c, w->a.visual), true); + if (w->shadow) { + w->shadow_image = + ps->backend_data->ops->render_shadow(ps->backend_data, w->widthb, + w->heightb, ps->gaussian_map, + ps->o.shadow_red, ps->o.shadow_green, + ps->o.shadow_blue); + } } } } + + if (ps->o.experimental_backends) { + ps->ndamage = ps->backend_data->ops->max_buffer_age; + } else { + ps->ndamage = maximum_buffer_age(ps); + } + ps->damage_ring = ccalloc(ps->ndamage, region_t); + ps->damage = ps->damage_ring + ps->ndamage - 1; + + for (int i = 0; i < ps->ndamage; i++) { + pixman_region32_init(&ps->damage_ring[i]); + } + /* // Unredirect GL context window as this may have an effect on VSync: // < http://dri.freedesktop.org/wiki/CompositeSwap > @@ -2011,6 +2044,8 @@ redir_start(session_t *ps) { ps->redirected = true; + root_damaged(ps); + // Repaint the whole screen force_repaint(ps); } @@ -2024,7 +2059,6 @@ static void redir_stop(session_t *ps) { if (ps->redirected) { log_debug("Screen unredirected."); - backend_info_t *bi = backend_list[ps->o.backend]; // Destroy all Pictures as they expire once windows are unredirected // If we don't destroy them here, looks like the resources are just // kept inaccessible somehow @@ -2038,13 +2072,23 @@ redir_stop(session_t *ps) { continue; } if (ps->o.experimental_backends) { - assert(bi); if (w->state == WSTATE_MAPPED) { - bi->release_win(ps->backend_data, ps, w, w->win_data); - w->win_data = NULL; + ps->backend_data->ops->release_image(ps->backend_data, w->win_image); + if (w->shadow_image) { + ps->backend_data->ops->release_image(ps->backend_data, w->shadow_image); + } + xcb_free_pixmap(ps->c, w->pixmap); + w->win_image = NULL; + w->shadow_image = NULL; + w->pixmap = XCB_NONE; } else { - assert(!w->win_data); + assert(!w->win_image); + assert(!w->shadow_image); } + if (ps->root_image) { + ps->backend_data->ops->release_image(ps->backend_data, ps->root_image); + ps->root_image = NULL; + } } else { free_paint(ps, &w->paint); } @@ -2057,10 +2101,18 @@ redir_stop(session_t *ps) { if (ps->o.experimental_backends) { // deinit backend - bi->deinit(ps->backend_data, ps); + ps->backend_data->ops->deinit(ps->backend_data); ps->backend_data = NULL; } + // Free the damage ring + for (int i = 0; i < ps->ndamage; ++i) { + pixman_region32_fini(&ps->damage_ring[i]); + } + ps->ndamage = 0; + free(ps->damage_ring); + ps->damage_ring = ps->damage = NULL; + // Must call XSync() here x_sync(ps->c); @@ -2560,6 +2612,9 @@ session_init(int argc, char **argv, Display *dpy, const char *config_file, log_error("Post-processing of conditionals failed, some of your rules might not work"); } + ps->gaussian_map = gaussian_kernel(ps->o.shadow_radius); + sum_kernel_preprocess(ps->gaussian_map); + rebuild_shadow_exclude_reg(ps); // Query X Shape @@ -2654,18 +2709,6 @@ session_init(int argc, char **argv, Display *dpy, const char *config_file, exit(1); } - if (ps->o.experimental_backends) { - ps->ndamage = backend_list[ps->o.backend]->max_buffer_age; - } else { - ps->ndamage = maximum_buffer_age(ps); - } - ps->damage_ring = ccalloc(ps->ndamage, region_t); - ps->damage = ps->damage_ring + ps->ndamage - 1; - - for (int i = 0; i < ps->ndamage; i++) { - pixman_region32_init(&ps->damage_ring[i]); - } - if (ps->o.print_diagnostics) { print_diagnostics(ps, config_file); free(config_file_to_free); @@ -2949,17 +2992,10 @@ session_destroy(session_t *ps) { deinit_render(ps); } - // Free the damage ring - for (int i = 0; i < ps->ndamage; ++i) { - pixman_region32_fini(&ps->damage_ring[i]); - } - ps->ndamage = 0; - free(ps->damage_ring); - ps->damage_ring = ps->damage = NULL; - // Flush all events x_sync(ps->c); ev_io_stop(ps->loop, &ps->xiow); + free_conv(ps->gaussian_map); #ifdef DEBUG_XRC // Report about resource leakage diff --git a/src/render.c b/src/render.c index 707c8f8..71b768b 100644 --- a/src/render.c +++ b/src/render.c @@ -47,13 +47,13 @@ paint_bind_tex(session_t *ps, paint_t *ppaint, unsigned wid, unsigned hei, bool if (!visual) { assert(depth == 32); if (!argb_fbconfig) { - argb_fbconfig = glx_find_fbconfig( - ps->dpy, ps->scr, - (struct glx_fbconfig_criteria){.red_size = 8, - .green_size = 8, - .blue_size = 8, - .alpha_size = 8, - .visual_depth = 32}); + argb_fbconfig = + glx_find_fbconfig(ps->dpy, ps->scr, + (struct xvisual_info){.red_size = 8, + .green_size = 8, + .blue_size = 8, + .alpha_size = 8, + .visual_depth = 32}); } if (!argb_fbconfig) { log_error("Failed to find appropriate FBConfig for 32 bit depth"); @@ -61,7 +61,7 @@ paint_bind_tex(session_t *ps, paint_t *ppaint, unsigned wid, unsigned hei, bool } fbcfg = argb_fbconfig; } else { - auto m = x_visual_to_fbconfig_criteria(ps->c, visual); + auto m = x_get_visual_info(ps->c, visual); if (m.visual_depth < 0) { return false; } @@ -278,8 +278,8 @@ void paint_one(session_t *ps, win *w, const region_t *reg_paint) { // Invert window color, if required if (bkend_use_xrender(ps) && w->invert_color) { - xcb_render_picture_t newpict = - x_create_picture_with_pictfmt(ps->c, ps->root, wid, hei, w->pictfmt, 0, NULL); + xcb_render_picture_t newpict = x_create_picture_with_pictfmt( + ps->c, ps->root, wid, hei, w->pictfmt, 0, NULL); if (newpict) { // Apply clipping region to save some CPU if (reg_paint) { @@ -1148,9 +1148,6 @@ bool init_render(session_t *ps) { } } - ps->gaussian_map = gaussian_kernel(ps->o.shadow_radius); - sum_kernel_preprocess(ps->gaussian_map); - ps->black_picture = solid_picture(ps->c, ps->root, true, 1, 0, 0, 0); ps->white_picture = solid_picture(ps->c, ps->root, true, 1, 1, 1, 1); @@ -1207,7 +1204,6 @@ void deinit_render(session_t *ps) { free_picture(ps->c, &ps->black_picture); free_picture(ps->c, &ps->white_picture); - free_conv(ps->gaussian_map); // Free other X resources free_root_tile(ps); diff --git a/src/win.c b/src/win.c index c6b9bfa..1a65833 100644 --- a/src/win.c +++ b/src/win.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -620,8 +621,19 @@ void win_on_win_size_change(session_t *ps, win *w) { if (w->state == WSTATE_MAPPED || w->state == WSTATE_MAPPING || w->state == WSTATE_FADING) { - backend_list[ps->o.backend]->release_win(ps->backend_data, ps, w, w->win_data); - w->win_data = backend_list[ps->o.backend]->prepare_win(ps->backend_data, ps, w); + ps->backend_data->ops->release_image(ps->backend_data, w->win_image); + if (w->shadow_image) { + ps->backend_data->ops->release_image(ps->backend_data, w->shadow_image); + } + w->pixmap = xcb_generate_id(ps->c); + xcb_composite_name_window_pixmap(ps->c, w->id, w->pixmap); + w->win_image = ps->backend_data->ops->bind_pixmap(ps->backend_data, w->pixmap, x_get_visual_info(ps->c, w->a.visual), true); + if (w->shadow) { + w->shadow_image = ps->backend_data->ops->render_shadow(ps->backend_data, w->widthb, + w->heightb, ps->gaussian_map, + ps->o.shadow_red, ps->o.shadow_green, + ps->o.shadow_blue); + } } else { assert(w->state == WSTATE_UNMAPPED); } @@ -756,7 +768,7 @@ void win_recheck_client(session_t *ps, win *w) { * Free all resources in a struct _win. */ void free_win_res(session_t *ps, win *w) { - // No need to call backend release_win here because + // No need to call backend release_image here because // finish_unmap_win should've done that for us. // XXX unless we are called by session_destroy // assert(w->win_data == NULL); @@ -823,7 +835,8 @@ void add_win(session_t *ps, xcb_window_t id, xcb_window_t prev) { // Not initialized until mapped, this variables // have no meaning or have no use until the window // is mapped - .win_data = NULL, + .win_image = NULL, + .shadow_image = NULL, .prev_trans = NULL, .shadow = false, .xinerama_scr = -1, @@ -1245,10 +1258,19 @@ void win_update_bounding_shape(session_t *ps, win *w) { if (ps->redirected && w->state != WSTATE_UNMAPPED) { // Note we only do this when screen is redirected, because // otherwise win_data is not valid - backend_info_t *bi = backend_list[ps->o.backend]; - bi->release_win(ps->backend_data, ps, w, w->win_data); - if (w->state != WSTATE_UNMAPPING && w->state != WSTATE_DESTROYING) { - w->win_data = bi->prepare_win(ps->backend_data, ps, w); + assert(w->state != WSTATE_UNMAPPING && w->state != WSTATE_DESTROYING); + ps->backend_data->ops->release_image(ps->backend_data, w->win_image); + if (w->shadow_image) { + ps->backend_data->ops->release_image(ps->backend_data, w->shadow_image); + } + w->pixmap = xcb_generate_id(ps->c); + xcb_composite_name_window_pixmap(ps->c, w->id, w->pixmap); + w->win_image = ps->backend_data->ops->bind_pixmap(ps->backend_data, w->pixmap, x_get_visual_info(ps->c, w->a.visual), true); + if (w->shadow) { + w->shadow_image = ps->backend_data->ops->render_shadow(ps->backend_data, w->widthb, + w->heightb, ps->gaussian_map, + ps->o.shadow_red, ps->o.shadow_green, + ps->o.shadow_blue); } } } else { @@ -1348,9 +1370,13 @@ finish_unmap_win(session_t *ps, win **_w) { if (ps->o.experimental_backends) { // We are in unmap_win, we definitely was viewable if (ps->redirected) { - assert(w->win_data); - backend_list[ps->o.backend]->release_win(ps->backend_data, ps, w, w->win_data); - w->win_data = NULL; + assert(w->win_image); + ps->backend_data->ops->release_image(ps->backend_data, w->win_image); + if (w->shadow_image) { + ps->backend_data->ops->release_image(ps->backend_data, w->shadow_image); + } + w->win_image = NULL; + w->shadow_image = NULL; } } else { free_paint(ps, &w->paint); @@ -1627,7 +1653,16 @@ void map_win(session_t *ps, win *w) { // TODO win_update_bounding_shape below will immediately // reinit w->win_data, not very efficient if (ps->redirected && ps->o.experimental_backends) { - w->win_data = backend_list[ps->o.backend]->prepare_win(ps->backend_data, ps, w); + w->pixmap = xcb_generate_id(ps->c); + xcb_composite_name_window_pixmap(ps->c, w->id, w->pixmap); + w->win_image = ps->backend_data->ops->bind_pixmap(ps->backend_data, w->pixmap, x_get_visual_info(ps->c, w->a.visual), true); + if (w->shadow) { + w->shadow_image = + ps->backend_data->ops->render_shadow(ps->backend_data, w->widthb, + w->heightb, ps->gaussian_map, + ps->o.shadow_red, ps->o.shadow_green, + ps->o.shadow_blue); + } } log_debug("Window %#010x has opacity %f, opacity target is %f", w->id, w->opacity, w->opacity_tgt); diff --git a/src/win.h b/src/win.h index 639da9f..e9bb577 100644 --- a/src/win.h +++ b/src/win.h @@ -119,7 +119,8 @@ typedef struct win win; struct win { /// backend data attached to this window. Only available when /// `state` is not UNMAPPED - void *win_data; + void *win_image; + void *shadow_image; /// Pointer to the next lower window in window stack. win *next; /// Pointer to the next higher window to paint. @@ -127,6 +128,8 @@ struct win { // TODO rethink reg_ignore // Core members + /// Named pixmap attached to this window + xcb_pixmap_t pixmap; /// ID of the top-level frame window. xcb_window_t id; /// The "mapped state" of this window, doesn't necessary diff --git a/src/x.c b/src/x.c index 2b2113b..663a986 100644 --- a/src/x.c +++ b/src/x.c @@ -145,6 +145,34 @@ x_get_pictform_for_visual(xcb_connection_t *c, xcb_visualid_t visual) { return NULL; } +static xcb_visualid_t attr_pure +x_get_visual_for_pictfmt(xcb_render_query_pict_formats_reply_t *r, + xcb_render_pictformat_t fmt) { + for (auto screen = xcb_render_query_pict_formats_screens_iterator(r); + screen.rem; xcb_render_pictscreen_next(&screen)) { + for (auto depth = xcb_render_pictscreen_depths_iterator(screen.data); + depth.rem; xcb_render_pictdepth_next(&depth)) { + for (auto pv = xcb_render_pictdepth_visuals_iterator(depth.data); pv.rem; + xcb_render_pictvisual_next(&pv)) { + if (pv.data->format == fmt) { + return pv.data->visual; + } + } + } + } + return XCB_NONE; +} + +xcb_visualid_t +x_get_visual_for_standard(xcb_connection_t *c, xcb_pict_standard_t std) { + x_get_server_pictfmts(c); + + auto pictfmt = + xcb_render_util_find_standard_format(g_pictfmts, std); + + return x_get_visual_for_pictfmt(g_pictfmts, pictfmt->id); +} + int x_get_visual_depth(xcb_connection_t *c, xcb_visualid_t visual) { auto setup = xcb_get_setup(c); for (auto screen = xcb_setup_roots_iterator(setup); screen.rem; xcb_screen_next(&screen)) { @@ -548,3 +576,34 @@ size_t x_picture_filter_from_conv(const conv *kernel, double center, xcb_render_ buf[kernel->h / 2 * kernel->w + kernel->w / 2 + 2] = DOUBLE_TO_XFIXED(center * factor); return kernel->w * kernel->h + 2; } + +/// Generate a search criteria for fbconfig from a X visual. +/// Returns {-1, -1, -1, -1, -1, -1} on failure +struct xvisual_info +x_get_visual_info(xcb_connection_t *c, xcb_visualid_t visual) { + auto pictfmt = x_get_pictform_for_visual(c, visual); + auto depth = x_get_visual_depth(c, visual); + if (!pictfmt || depth == -1) { + log_error("Invalid visual %#03x", visual); + return (struct xvisual_info){-1, -1, -1, -1, -1, -1}; + } + if (pictfmt->type != XCB_RENDER_PICT_TYPE_DIRECT) { + log_error("compton cannot handle non-DirectColor visuals. Report an " + "issue if you see this error message."); + return (struct xvisual_info){-1, -1, -1, -1, -1, -1}; + } + + int red_size = popcountl(pictfmt->direct.red_mask), + blue_size = popcountl(pictfmt->direct.blue_mask), + green_size = popcountl(pictfmt->direct.green_mask), + alpha_size = popcountl(pictfmt->direct.alpha_mask); + + return (struct xvisual_info){ + .red_size = red_size, + .green_size = green_size, + .blue_size = blue_size, + .alpha_size = alpha_size, + .visual_depth = depth, + .visual = visual, + }; +} diff --git a/src/x.h b/src/x.h index 3eb2252..2e8b258 100644 --- a/src/x.h +++ b/src/x.h @@ -32,6 +32,21 @@ typedef struct winprop { xcb_get_property_reply_t *r; } winprop_t; +struct xvisual_info { + /// Bit depth of the red component + int red_size; + /// Bit depth of the green component + int green_size; + /// Bit depth of the blue component + int blue_size; + /// Bit depth of the alpha component + int alpha_size; + /// The depth of X visual + int visual_depth; + + xcb_visualid_t visual; +}; + #define XCB_SYNCED_VOID(func, c, ...) \ xcb_request_check(c, func##_checked(c, __VA_ARGS__)); #define XCB_SYNCED(func, c, ...) \ @@ -188,3 +203,11 @@ bool x_fence_sync(xcb_connection_t *, xcb_sync_fence_t); */ size_t x_picture_filter_from_conv(const conv *kernel, double center, xcb_render_fixed_t **ret, size_t *size); + +/// Generate a search criteria for fbconfig from a X visual. +/// Returns {-1, -1, -1, -1, -1, -1} on failure +struct xvisual_info +x_get_visual_info(xcb_connection_t *c, xcb_visualid_t visual); + +xcb_visualid_t +x_get_visual_for_standard(xcb_connection_t *c, xcb_pict_standard_t std);