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 <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui 2019-02-26 23:52:37 +00:00
parent 11c8655206
commit 3707f792fb
No known key found for this signature in database
GPG Key ID: 37C999F617EA1A47
14 changed files with 742 additions and 572 deletions

View File

@ -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(&region);
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(&region);
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 = &region;
}
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(&reg_tmp, &region, w->reg_ignore);
if (pixman_region32_not_empty(&reg_tmp)) {
// Render window content
// XXX do this in preprocess?
bi->render_win(ps->backend_data, ps, w, w->win_data, &reg_tmp);
if (!pixman_region32_not_empty(&reg_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(&reg_noframe);
win_get_region_noframe_local(w, &reg_noframe);
pixman_region32_translate(&reg_noframe, w->g.x,
w->g.y);
pixman_region32_subtract(&reg_blur, &reg_blur,
&reg_noframe);
pixman_region32_fini(&reg_noframe);
}
bi->blur(ps->backend_data, ps, w->opacity, &reg_blur);
pixman_region32_fini(&reg_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(&reg_shadow, &reg_shadow, &reg_tmp);
if (!ps->o.wintype_option[w->window_type].full_shadow) {
pixman_region32_subtract(&reg_shadow, &reg_shadow, &reg_bound);
}
// Draw window on target
bi->compose(ps->backend_data, ps, w, w->win_data, w->g.x, w->g.y,
&reg_tmp);
// Mask out the region we don't want shadow on
if (pixman_region32_not_empty(&ps->shadow_exclude_reg)) {
pixman_region32_subtract(&reg_tmp, &reg_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(
&reg_shadow, &reg_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, &reg_shadow);
pixman_region32_fini(&reg_shadow);
}
pixman_region32_intersect(&reg_tmp, &reg_tmp, &reg_bound);
pixman_region32_fini(&reg_bound);
if (!pixman_region32_not_empty(&reg_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(&reg_blur);
win_get_region_noframe_local(w, &reg_blur);
pixman_region32_translate(&reg_blur, w->g.x, w->g.y);
pixman_region32_subtract(&reg_blur, &reg_tmp, &reg_blur);
ps->backend_data->ops->blur(ps->backend_data, w->opacity,
&reg_blur);
pixman_region32_fini(&reg_blur);
} else {
ps->backend_data->ops->blur(ps->backend_data, w->opacity,
&reg_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, &reg_tmp);
} else {
region_t reg_local;
pixman_region32_init(&reg_local);
pixman_region32_copy(&reg_local, &reg_tmp);
pixman_region32_translate(&reg_local, -w->g.x, -w->g.y);
auto new_img = ps->backend_data->ops->copy(
ps->backend_data, w->win_image, &reg_local);
if (w->invert_color) {
ps->backend_data->ops->image_op(ps->backend_data,
IMAGE_OP_INVERT_COLOR,
new_img, &reg_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, &reg_local,
(double[]){dim_opacity});
}
if (w->frame_opacity != 1) {
auto reg_frame = win_get_region_noframe_local_by_val(w);
pixman_region32_subtract(&reg_frame, &reg_local, &reg_frame);
ps->backend_data->ops->image_op(
ps->backend_data, IMAGE_OP_APPLY_ALPHA, new_img,
&reg_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, &reg_tmp);
ps->backend_data->ops->release_image(ps->backend_data, new_img);
}
}
// Free up all temporary regions
pixman_region32_fini(&reg_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

View File

@ -5,12 +5,30 @@
#include <stdbool.h>
#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 *);

View File

@ -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;
}

View File

@ -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);

View File

@ -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(&region_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.

View File

@ -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;

View File

@ -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(&reg_tmp);
// Shadow doesn't need to be painted underneath the body of the window
// Because no one can see it
pixman_region32_subtract(&reg_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(&reg_tmp, &reg_tmp, &ps->shadow_exclude_reg);
// Might be worth while to crop the region to shadow border
pixman_region32_intersect_rect(&reg_tmp, &reg_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(&reg_tmp, &reg_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(
&reg_tmp, &reg_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(&reg_tmp, &reg_tmp, &bshape);
pixman_region32_fini(&bshape);
// Detect if the region is empty before painting
if (pixman_region32_not_empty(&reg_tmp)) {
x_set_picture_clip_region(ps->c, xd->back[xd->curr_back], 0, 0,
&reg_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(&reg_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(&reg_paint_local);
pixman_region32_copy(&reg_paint_local, (region_t *)reg_paint);
pixman_region32_translate(&reg_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, &reg_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(&reg_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:

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -7,6 +7,7 @@
#include <xcb/xcb.h>
#include <xcb/render.h>
#include <xcb/damage.h>
#include <xcb/composite.h>
#include <xcb/xcb_renderutil.h>
#include <stdbool.h>
#include <math.h>
@ -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 <code>struct _win</code>.
*/
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);

View File

@ -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

59
src/x.c
View File

@ -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,
};
}

23
src/x.h
View File

@ -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);