Merge pull request #125 from yshui/new-new-backend

new_backend: New interface
This commit is contained in:
yshui 2019-03-03 22:21:12 +00:00 committed by GitHub
commit 0d7f505dd6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 742 additions and 572 deletions

View File

@ -8,18 +8,19 @@
#include "config.h" #include "config.h"
#include "region.h" #include "region.h"
#include "win.h" #include "win.h"
#include "log.h"
backend_info_t *backend_list[NUM_BKEND] = { backend_init_fn backend_list[NUM_BKEND] = {
[BKEND_XRENDER] = &xrender_backend, [BKEND_XRENDER] = backend_xrender_init,
#ifdef CONFIG_OPENGL #ifdef CONFIG_OPENGL
[BKEND_GLX] = &glx_backend, [BKEND_GLX] = backend_glx_init,
#endif #endif
}; };
region_t get_damage(session_t *ps) { region_t get_damage(session_t *ps) {
region_t region; region_t region;
auto buffer_age_fn = backend_list[ps->o.backend]->buffer_age; auto buffer_age_fn = ps->backend_data->ops->buffer_age;
int buffer_age = buffer_age_fn ? buffer_age_fn(ps->backend_data, ps) : -1; int buffer_age = buffer_age_fn ? buffer_age_fn(ps->backend_data) : -1;
pixman_region32_init(&region); pixman_region32_init(&region);
if (buffer_age == -1 || buffer_age > ps->ndamage) { 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); pixman_region32_fini(&region);
return; return;
} }
auto bi = backend_list[ps->o.backend];
assert(bi);
#ifdef DEBUG_REPAINT #ifdef DEBUG_REPAINT
static struct timespec last_paint = {0}; 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; reg_paint = &region;
} }
if (bi->prepare) // TODO Bind root pixmap
bi->prepare(ps->backend_data, ps, reg_paint);
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 // Windows are sorted from bottom to top
// Each window has a reg_ignore, which is the region obscured by all the windows // 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 // XXX XXX
pixman_region32_subtract(&reg_tmp, &region, w->reg_ignore); pixman_region32_subtract(&reg_tmp, &region, w->reg_ignore);
if (pixman_region32_not_empty(&reg_tmp)) { if (!pixman_region32_not_empty(&reg_tmp)) {
// Render window content continue;
// XXX do this in preprocess? }
bi->render_win(ps->backend_data, ps, w, w->win_data, &reg_tmp);
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);
}
// 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 (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 // Blur window background
bool win_transparent = bool win_transparent = ps->backend_data->ops->is_image_transparent(
bi->is_win_transparent(ps->backend_data, w, w->win_data); ps->backend_data, w->win_image);
bool frame_transparent = bool frame_transparent = w->frame_opacity != 1;
bi->is_frame_transparent(ps->backend_data, w, w->win_data);
if (w->blur_background && if (w->blur_background &&
(win_transparent || (win_transparent || (ps->o.blur_background_frame && frame_transparent))) {
(ps->o.blur_background_frame && frame_transparent))) {
// Minimize the region we try to blur, if the window // Minimize the region we try to blur, if the window
// itself is not opaque, only the frame is. // 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)) { if (win_is_solid(ps, w)) {
region_t reg_noframe; region_t reg_blur;
pixman_region32_init(&reg_noframe); pixman_region32_init(&reg_blur);
win_get_region_noframe_local(w, &reg_noframe); win_get_region_noframe_local(w, &reg_blur);
pixman_region32_translate(&reg_noframe, w->g.x, pixman_region32_translate(&reg_blur, w->g.x, w->g.y);
w->g.y); pixman_region32_subtract(&reg_blur, &reg_tmp, &reg_blur);
pixman_region32_subtract(&reg_blur, &reg_blur, ps->backend_data->ops->blur(ps->backend_data, w->opacity,
&reg_noframe); &reg_blur);
pixman_region32_fini(&reg_noframe);
}
bi->blur(ps->backend_data, ps, w->opacity, &reg_blur);
pixman_region32_fini(&reg_blur); pixman_region32_fini(&reg_blur);
} } else {
ps->backend_data->ops->blur(ps->backend_data, w->opacity,
// Draw window on target
bi->compose(ps->backend_data, ps, w, w->win_data, w->g.x, w->g.y,
&reg_tmp); &reg_tmp);
}
if (bi->finish_render_win) }
bi->finish_render_win(ps->backend_data, ps, w, w->win_data); // 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 // Free up all temporary regions
pixman_region32_fini(&reg_tmp); pixman_region32_fini(&reg_tmp);
if (bi->present) { if (ps->backend_data->ops->present) {
// Present the rendered scene // Present the rendered scene
// Vsync is done here // Vsync is done here
bi->present(ps->backend_data, ps); ps->backend_data->ops->present(ps->backend_data);
} }
#ifdef DEBUG_REPAINT #ifdef DEBUG_REPAINT

View File

@ -5,12 +5,30 @@
#include <stdbool.h> #include <stdbool.h>
#include "region.h"
#include "compiler.h" #include "compiler.h"
#include "kernel.h"
#include "region.h"
#include "x.h"
typedef struct session session_t; typedef struct session session_t;
typedef struct win win; 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 =========== // =========== Initialization ===========
@ -19,15 +37,14 @@ typedef struct backend_info {
/// 1) if ps->overlay is not XCB_NONE, use that /// 1) if ps->overlay is not XCB_NONE, use that
/// 2) use ps->root otherwise /// 2) use ps->root otherwise
/// XXX make the target window a parameter /// XXX make the target window a parameter
void *(*init)(session_t *ps) __attribute__((nonnull(1))); void (*deinit)(backend_t *backend_data) __attribute__((nonnull(1)));
void (*deinit)(void *backend_data, session_t *ps) __attribute__((nonnull(1, 2)));
/// Called when rendering will be stopped for an unknown amount of /// Called when rendering will be stopped for an unknown amount of
/// time (e.g. screen is unredirected). Free some resources. /// 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 /// 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 /// Called when root property changed, returns the new
/// backend_data. Even if the backend_data changed, all /// backend_data. Even if the backend_data changed, all
@ -35,12 +52,7 @@ typedef struct backend_info {
/// remain valid. /// remain valid.
/// ///
/// Optional /// Optional
void *(*root_change)(void *backend_data, session_t *ps); void *(*root_change)(backend_t *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);
// =========== Rendering ============ // =========== Rendering ============
@ -50,74 +62,55 @@ typedef struct backend_info {
/// on the buffer (usually the wallpaper). /// on the buffer (usually the wallpaper).
/// ///
/// Optional? /// 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 /// target picture. Always called after render_win(). Maybe called
/// multiple times between render_win() and finish_render_win(). /// multiple times between render_win() and finish_render_win().
/// The origin is the top left of the window, exclude the shadow, /// 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 /// (dst_x, dst_y) refers to where the origin should be in the target
/// buffer. /// buffer.
void (*compose)(void *backend_data, session_t *ps, win *w, void *win_data, void (*compose)(backend_t *backend_data, void *image_data, int dst_x, int dst_y,
int dst_x, int dst_y, const region_t *reg_paint); const region_t *reg_paint);
/// Blur a given region on of the target. /// Blur a given region on of the target.
bool (*blur)(void *backend_data, session_t *ps, double opacity, const region_t *) bool (*blur)(backend_t *backend_data, double opacity, const region_t *)
__attribute__((nonnull(1, 2, 4))); __attribute__((nonnull(1, 3)));
/// Present the buffered target picture onto the screen. If target /// Present the buffered target picture onto the screen. If target
/// is not buffered, this should be NULL. Otherwise, it should always /// is not buffered, this should be NULL. Otherwise, it should always
/// be non-NULL. /// be non-NULL.
/// ///
/// Optional /// 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 * Bind a X pixmap to the backend's internal image data structure.
* data structure. Dimming, shadow and color inversion is handled
* here.
* *
* This function is allowed to allocate additional resource needed * @param backend_data backend data
* for rendering. * @param pixmap X pixmap to bind
* * @param fmt information of the pixmap's visual
* Params: * @param owned whether the ownership of the pixmap is transfered to the backend
* reg_paint = the paint region, meaning painting should only * @return backend internal data structure bound with this pixmap
* be happening within that region. It's in global
* coordinates. If NULL, the region of paint is the
* whole screen.
*/ */
void (*render_win)(void *backend_data, session_t *ps, win *w, void *win_data, void *(*bind_pixmap)(backend_t *backend_data, xcb_pixmap_t pixmap,
const region_t *reg_paint); struct xvisual_info fmt, bool owned);
/// Free resource allocated for rendering. After this function is /// Create a shadow image based on the parameters
/// called, compose() won't be called before render_win is called void *(*render_shadow)(backend_t *backend_data, int width, int height,
/// another time. const conv *kernel, double r, double g, double b);
///
/// Optional
void (*finish_render_win)(void *backend_data, session_t *ps, win *w, void *win_data);
// ============ Resource management =========== // ============ 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. // 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 // 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 // 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. // want to break that assumption as for now. We need to reconsider this.
/// Create a structure to stored additional data needed for rendering a /// Free resources associated with an image data structure
/// window, later used for render() and compose(). void (*release_image)(backend_t *backend_data, void *img_data)
/// __attribute__((nonnull(1, 2)));
/// 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)));
// =========== Query =========== // =========== Query ===========
@ -127,32 +120,44 @@ typedef struct backend_info {
/// This function is needed because some backend might change the content of the /// 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 /// window (e.g. when using a custom shader with the glx backend), so we only now
/// the transparency after the window is rendered /// the transparency after the window is rendered
bool (*is_win_transparent)(void *backend_data, win *w, void *win_data) bool (*is_image_transparent)(backend_t *backend_data, void *image_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)
__attribute__((nonnull(1, 2))); __attribute__((nonnull(1, 2)));
/// Get the age of the buffer content we are currently rendering ontop /// 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. /// 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 /// Everytime `present` is called, buffers get older. Return -1 if the
/// buffer is empty. /// 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. /// The maximum number buffer_age might return.
int max_buffer_age; 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 ============ // =========== Hooks ============
/// Let the backend hook into the event handling queue /// Let the backend hook into the event handling queue
} backend_info_t; };
extern backend_info_t xrender_backend; typedef backend_t *(*backend_init_fn)(session_t *ps) __attribute__((nonnull(1)));
extern backend_info_t glx_backend;
extern backend_info_t *backend_list[]; 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_win_transparent(void *, win *, void *);
bool default_is_frame_transparent(void *, win *, void *); bool default_is_frame_transparent(void *, win *, void *);

View File

@ -256,6 +256,22 @@ shadow_picture_err:
return false; 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) { bool default_is_win_transparent(void *backend_data, win *w, void *win_data) {
return w->mode != WMODE_SOLID; 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 /// The default implementation of `is_frame_transparent`, it uses win::frame_opacity. Same
/// caveat as `default_is_win_transparent` applies. /// caveat as `default_is_win_transparent` applies.
bool default_is_frame_transparent(void *, win *, void *); 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 { struct _glx_data {
backend_t base;
int glx_event; int glx_event;
int glx_error; int glx_error;
GLXContext ctx; GLXContext ctx;
@ -52,7 +53,7 @@ struct _glx_data {
}; };
struct glx_fbconfig_info * 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, 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); 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. * 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_win_data *wd = win_data;
struct _glx_data *gd = backend_data; struct _glx_data *gd = backend_data;
glx_release_pixmap(gd, ps->dpy, wd); 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. * 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; struct _glx_data *gd = backend_data;
// Free all GLX resources of windows // Free all GLX resources of windows
@ -231,10 +232,14 @@ static void glx_deinit(void *backend_data, session_t *ps) {
/** /**
* Initialize OpenGL. * Initialize OpenGL.
*/ */
static void *glx_init(session_t *ps) { backend_t *backend_glx_init(session_t *ps) {
bool success = false; bool success = false;
glxext_init(ps->dpy, ps->scr); glxext_init(ps->dpy, ps->scr);
auto gd = ccalloc(1, struct _glx_data); 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; XVisualInfo *pvis = NULL;
// Check for GLX extension // Check for GLX extension
@ -371,10 +376,10 @@ end:
return NULL; 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; struct _glx_data *gd = backend_data;
// Retrieve pixmap parameters, if they aren't provided // Retrieve pixmap parameters, if they aren't provided
if (w->g.depth > OPENGL_MAX_DEPTH) { 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; goto err;
} }
auto criteria = x_visual_to_fbconfig_criteria(ps->c, w->a.visual); auto visual_info = x_get_visual_info(ps->c, w->a.visual);
auto fbcfg = glx_find_fbconfig(ps->dpy, ps->scr, criteria); auto fbcfg = glx_find_fbconfig(ps->dpy, ps->scr, visual_info);
if (!fbcfg) { if (!fbcfg) {
log_error("Couldn't find FBConfig with requested visual %x", w->a.visual); log_error("Couldn't find FBConfig with requested visual %x", w->a.visual);
goto err; goto err;
@ -473,7 +478,7 @@ err:
/** /**
* Bind a X pixmap to an OpenGL texture. * 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) { const region_t *reg_paint) {
struct _glx_data *gd = backend_data; struct _glx_data *gd = backend_data;
struct _glx_win_data *wd = win_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(); 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); 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) { if (ps->o.glx_swap_method == SWAPM_BUFFER_AGE) {
unsigned int val; unsigned int val;
glXQueryDrawable(ps->dpy, get_tgt_window(ps), GLX_BACK_BUFFER_AGE_EXT, &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) { int dst_x, int dst_y, const region_t *region) {
struct _glx_data *gd = backend_data; struct _glx_data *gd = backend_data;
struct _glx_win_data *wd = win_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); pixman_region32_fini(&region_yflipped);
} }
backend_info_t glx_backend = { /* backend_info_t glx_backend = { */
.init = glx_init, /* .init = glx_init, */
.deinit = glx_deinit, /* .deinit = glx_deinit, */
.prepare_win = glx_prepare_win, /* .prepare_win = glx_prepare_win, */
.render_win = glx_render_win, /* .render_win = glx_render_win, */
.release_win = glx_release_win, /* .release_win = glx_release_win, */
.present = glx_present, /* .present = glx_present, */
.compose = glx_compose, /* .compose = glx_compose, */
.is_win_transparent = default_is_win_transparent, /* .is_win_transparent = default_is_win_transparent, */
.is_frame_transparent = default_is_frame_transparent, /* .is_frame_transparent = default_is_frame_transparent, */
.buffer_age = glx_buffer_age, /* .buffer_age = glx_buffer_age, */
.max_buffer_age = 5, // XXX why? /* .max_buffer_age = 5, // XXX why? */
}; /* }; */
/** /**
* Check if a GLX extension exists. * Check if a GLX extension exists.

View File

@ -40,37 +40,8 @@ struct glx_fbconfig_criteria {
int visual_depth; 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 { struct glxext_info {
bool initialized; bool initialized;

View File

@ -25,8 +25,10 @@
#define auto __auto_type #define auto __auto_type
typedef struct _xrender_data { typedef struct _xrender_data {
backend_t base;
/// If vsync is enabled and supported by the current system /// If vsync is enabled and supported by the current system
bool vsync; bool vsync;
xcb_visualid_t default_visual;
/// The idle fence for the present extension /// The idle fence for the present extension
xcb_sync_fence_t idle_fence; xcb_sync_fence_t idle_fence;
/// The target window /// The target window
@ -56,10 +58,8 @@ typedef struct _xrender_data {
/// 1x1 black picture /// 1x1 black picture
xcb_render_picture_t black_pixel; xcb_render_picture_t black_pixel;
/// 1x1 picture of the shadow color /// Width and height of the target pixmap
xcb_render_picture_t shadow_pixel; int target_width, target_height;
/// convolution kernel for the shadow
conv *shadow_kernel;
/// Blur kernels converted to X format /// Blur kernels converted to X format
xcb_render_fixed_t *x_blur_kern[MAX_BLUR_PASS]; xcb_render_fixed_t *x_blur_kern[MAX_BLUR_PASS];
@ -69,112 +69,39 @@ typedef struct _xrender_data {
xcb_special_event_t *present_event; xcb_special_event_t *present_event;
} xrender_data; } xrender_data;
#if 0 struct _xrender_image_data {
/**
* 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 {
// Pixmap that the client window draws to, // Pixmap that the client window draws to,
// it will contain the content of client window. // it will contain the content of client window.
xcb_pixmap_t pixmap; xcb_pixmap_t pixmap;
// A Picture links to the Pixmap // A Picture links to the Pixmap
xcb_render_picture_t pict; xcb_render_picture_t pict;
// A buffer used for rendering long width, height;
xcb_render_picture_t buffer; bool has_alpha;
// The rendered content of the window (dimmed, inverted double opacity;
// color, etc.). This is either `buffer` or `pict` xcb_visualid_t visual;
xcb_render_picture_t rendered_pict; uint8_t depth;
xcb_pixmap_t shadow_pixmap; bool owned;
xcb_render_picture_t shadow_pict;
}; };
static void compose(void *backend_data, session_t *ps, win *w, void *win_data, int dst_x, static void
int dst_y, const region_t *reg_paint) { compose(backend_t *base, void *img_data, int dst_x, int dst_y, const region_t *reg_paint) {
struct _xrender_data *xd = backend_data; struct _xrender_data *xd = (void *)base;
struct _xrender_win_data *wd = win_data; struct _xrender_image_data *img = img_data;
bool blend = default_is_frame_transparent(NULL, w, win_data) || int op = (img->has_alpha ? XCB_RENDER_PICT_OP_OVER : XCB_RENDER_PICT_OP_SRC);
default_is_win_transparent(NULL, w, win_data); auto alpha_pict = xd->alpha_pict[(int)(img->opacity * 255.0)];
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);
}
// Clip region of rendered_pict might be set during rendering, clear it to make // Clip region of rendered_pict might be set during rendering, clear it to make
// sure we get everything into the buffer // 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); x_set_picture_clip_region(base->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], xcb_render_composite(base->c, op, img->pict, alpha_pict, xd->back[xd->curr_back],
0, 0, 0, 0, dst_x, dst_y, w->widthb, w->heightb); 0, 0, 0, 0, dst_x, dst_y, img->width, img->height);
} }
static bool static bool blur(backend_t *backend_data, double opacity, const region_t *reg_paint) {
blur(void *backend_data, session_t *ps, double opacity, const region_t *reg_paint) { struct _xrender_data *xd = (void *)backend_data;
struct _xrender_data *xd = backend_data; xcb_connection_t *c = xd->base.c;
const pixman_box32_t *reg = pixman_region32_extents((region_t *)reg_paint); const pixman_box32_t *reg = pixman_region32_extents((region_t *)reg_paint);
const int height = reg->y2 - reg->y1; const int height = reg->y2 - reg->y1;
const int width = reg->x2 - reg->x1; 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 // Create a buffer for storing blurred picture, make it just big enough
// for the blur region // for the blur region
xcb_render_picture_t tmp_picture[2] = { 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(xd->base.c, xd->base.root, width, height,
x_create_picture_with_visual(ps->c, ps->root, width, height, ps->vis, 0, NULL)}; 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; region_t clip;
pixman_region32_init(&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; return false;
} }
x_set_picture_clip_region(ps->c, tmp_picture[0], 0, 0, &clip); x_set_picture_clip_region(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[1], 0, 0, &clip);
// The multipass blur implemented here is not correct, but this is what old // The multipass blur implemented here is not correct, but this is what old
// compton did anyway. XXX // 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)]; auto alpha_pict = xd->alpha_pict[(int)(opacity * 255)];
int current = 0; int current = 0;
int src_x = reg->x1, src_y = reg->y1; 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: // For more than 1 pass, we do:
// back -(pass 1)-> tmp0 -(pass 2)-> tmp1 ... // 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 // be applied on source picture, to get the nearby pixels outside the
// window. // window.
// TODO cache converted blur_kerns // 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]); 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 // 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, XCB_NONE, dst_pict, src_x, src_y, 0, 0, 0, 0,
width, height); width, height);
} else { } else {
// This is the last pass, and this is also not the first // 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, alpha_pict, xd->back[xd->curr_back], 0, 0, 0,
0, reg->x1, reg->y1, width, height); 0, reg->x1, reg->y1, width, height);
} }
// reset filter // reset filter
xcb_render_set_picture_filter(ps->c, src_pict, strlen(filter0), filter0, xcb_render_set_picture_filter(c, src_pict, strlen(filter0), filter0, 0, NULL);
0, NULL);
src_pict = tmp_picture[current]; src_pict = tmp_picture[current];
dst_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 // There is only 1 pass
if (i == 1) { 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, xd->back[xd->curr_back], 0, 0, 0, 0, reg->x1,
reg->y1, width, height); reg->y1, width, height);
} }
xcb_render_free_picture(ps->c, tmp_picture[0]); xcb_render_free_picture(c, tmp_picture[0]);
xcb_render_free_picture(ps->c, tmp_picture[1]); xcb_render_free_picture(c, tmp_picture[1]);
return true; return true;
} }
static void render_win(void *backend_data, session_t *ps, win *w, void *win_data, static void *
const region_t *reg_paint) { bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, bool owned) {
struct _xrender_data *xd = backend_data; xcb_generic_error_t *e;
struct _xrender_win_data *wd = win_data; 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;
}
w->pixmap_damaged = false; 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;
}
if (!w->invert_color && w->frame_opacity == 1 && !w->dim) { static void release_image(backend_t *base, void *image) {
// No extra processing needed struct _xrender_image_data *img = image;
wd->rendered_pict = wd->pict; xcb_render_free_picture(base->c, img->pict);
if (img->owned) {
xcb_free_pixmap(base->c, img->pixmap);
}
}
static void deinit(backend_t *backend_data) {
struct _xrender_data *xd = (void *)backend_data;
for (int i = 0; i < 256; i++) {
xcb_render_free_picture(xd->base.c, xd->alpha_pict[i]);
}
xcb_render_free_picture(xd->base.c, xd->target);
xcb_render_free_picture(xd->base.c, xd->root_pict);
for (int i = 0; i < 2; i++) {
xcb_render_free_picture(xd->base.c, xd->back[i]);
xcb_free_pixmap(xd->base.c, xd->back_pixmap[i]);
}
for (int i = 0; xd->x_blur_kern[i]; i++) {
free(xd->x_blur_kern[i]);
}
if (xd->present_event) {
xcb_unregister_for_special_event(xd->base.c, xd->present_event);
}
xcb_render_free_picture(xd->base.c, xd->white_pixel);
xcb_render_free_picture(xd->base.c, xd->black_pixel);
free(xd);
}
static void prepare(backend_t *base, const region_t *reg_paint) {
struct _xrender_data *xd = (void *)base;
// 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; 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;
region_t reg_paint_local; // buffer_age < 0 means that back buffer is empty
pixman_region32_init(&reg_paint_local); if (xd->buffer_age[1 - xd->curr_back] > 0) {
pixman_region32_copy(&reg_paint_local, (region_t *)reg_paint); xd->buffer_age[1 - xd->curr_back]++;
pixman_region32_translate(&reg_paint_local, -w->g.x, -w->g.y); }
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]);
// We don't want to modify the content of the original window when we process // TODO buffer-age-like optimization might be possible here.
// it, so we create a buffer. // but that will require a different backend API
if (wd->buffer == XCB_NONE) { xcb_render_composite(base->c, XCB_RENDER_PICT_OP_SRC,
wd->buffer = x_create_picture_with_pictfmt(ps->c, ps->root, w->widthb, w->heightb, xd->back[xd->curr_back], XCB_NONE, xd->target, 0, 0,
w->pictfmt, 0, NULL); 0, 0, 0, 0, xd->target_width, xd->target_height);
xd->buffer_age[xd->curr_back] = 1;
}
} }
// Copy the content of the window over to the buffer static int buffer_age(backend_t *backend_data) {
x_clear_picture_clip_region(ps->c, wd->buffer); struct _xrender_data *xd = (void *)backend_data;
wd->rendered_pict = wd->buffer; return xd->buffer_age[xd->curr_back];
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) { static bool is_image_transparent(backend_t *bd, void *image) {
// Handle invert color struct _xrender_image_data *img = image;
x_set_picture_clip_region(ps->c, wd->rendered_pict, 0, 0, &reg_paint_local); return img->has_alpha;
}
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_DIFFERENCE, static void image_op(backend_t *base, enum image_operations op, void *image,
xd->white_pixel, XCB_NONE, wd->rendered_pict, 0, 0, const region_t *reg, void *arg) {
0, 0, 0, 0, w->widthb, w->heightb); 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 // We use an extra PictOpInReverse operation to get correct pixel
// alpha. There could be a better solution. // alpha. There could be a better solution.
if (win_has_alpha(w)) xcb_render_composite(base->c, XCB_RENDER_PICT_OP_IN_REVERSE,
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_IN_REVERSE, tmp_pict, XCB_NONE, img->pict, 0, 0, 0, 0, 0,
wd->pict, XCB_NONE, wd->rendered_pict, 0, 0, 0, img->width, img->height);
0, 0, 0, 0, w->widthb, w->heightb); 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;
if (w->frame_opacity != 1) { case IMAGE_OP_DIM:
// Handle transparent frame assert(reg);
// Step 1: clip paint area to frame x_set_picture_clip_region(base->c, img->pict, 0, 0, reg);
region_t frame_reg; dim_opacity = *(double *)arg;
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);
}
if (w->dim) {
// Handle dimming
double dim_opacity = ps->o.inactive_dim;
if (!ps->o.inactive_dim_fixed)
dim_opacity *= w->opacity;
xcb_render_color_t color = { xcb_render_color_t color = {
.red = 0, .green = 0, .blue = 0, .alpha = 0xffff * dim_opacity}; .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 = { xcb_rectangle_t rect = {
.x = 0, .x = 0,
.y = 0, .y = 0,
.width = w->widthb, .width = img->width,
.height = w->heightb, .height = img->height,
}; };
x_clear_picture_clip_region(ps->c, wd->rendered_pict); xcb_render_fill_rectangles(base->c, XCB_RENDER_PICT_OP_OVER, img->pict,
xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_OVER, color, 1, &rect);
wd->rendered_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) { static void *copy(backend_t *base, const void *image, const region_t *reg) {
auto wd = ccalloc(1, struct _xrender_win_data); const struct _xrender_image_data *img = image;
struct _xrender_data *xd = backend_data; struct _xrender_data *xd = (void *)base;
assert(w->a.map_state == XCB_MAP_STATE_VIEWABLE); auto new_img = ccalloc(1, struct _xrender_image_data);
if (ps->has_name_pixmap) { assert(img->visual != XCB_NONE);
wd->pixmap = xcb_generate_id(ps->c); log_trace("xrender: copying %#010x visual %#x", img->pixmap, img->visual);
xcb_composite_name_window_pixmap_checked(ps->c, w->id, wd->pixmap); 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; auto alpha_pict =
if (!draw) img->opacity == 1 ? XCB_NONE : xd->alpha_pict[(int)(img->opacity * 255)];
draw = w->id; 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);
log_trace("%s %x", w->name, wd->pixmap); return new_img;
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;
} }
static void release_win(void *backend_data, session_t *ps, win *w, void *win_data) { static struct backend_operations xrender_ops = {
struct _xrender_win_data *wd = win_data; .deinit = deinit,
xcb_free_pixmap(ps->c, wd->pixmap); .blur = blur,
// xcb_free_pixmap(ps->c, wd->shadow_pixmap); .present = present,
xcb_render_free_picture(ps->c, wd->pict); .prepare = prepare,
xcb_render_free_picture(ps->c, wd->shadow_pict); .compose = compose,
if (wd->buffer != XCB_NONE) .bind_pixmap = bind_pixmap,
xcb_render_free_picture(ps->c, wd->buffer); .release_image = release_image,
free(wd); .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) { .image_op = image_op,
struct _xrender_data *xd = backend_data; .copy = copy,
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);
}
static void *init(session_t *ps) { backend_t *backend_xrender_init(session_t *ps) {
auto xd = ccalloc(1, struct _xrender_data); 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) { for (int i = 0; i < 256; ++i) {
double o = (double)i / 255.0; double o = (double)i / 255.0;
xd->alpha_pict[i] = solid_picture(ps->c, ps->root, false, o, 0, 0, 0); xd->alpha_pict[i] = solid_picture(ps->c, ps->root, false, o, 0, 0, 0);
assert(xd->alpha_pict[i] != XCB_NONE); 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->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->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) { if (ps->overlay != XCB_NONE) {
xd->target = x_create_picture_with_visual_and_pixmap( 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( 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}); ps->o.blur_kerns[i], 1, &xd->x_blur_kern[i], (size_t[]){0});
} }
return xd; return &xd->base;
err: err:
deinit(xd, ps); deinit(&xd->base);
return NULL; 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: // vim: set noet sw=8 ts=8:

View File

@ -86,6 +86,7 @@
#include "compiler.h" #include "compiler.h"
#include "utils.h" #include "utils.h"
#include "x.h" #include "x.h"
#include "backend/backend.h"
// === Constants === // === Constants ===
@ -303,7 +304,7 @@ typedef struct session {
/// Signal handler for SIGINT /// Signal handler for SIGINT
ev_signal int_signal; ev_signal int_signal;
/// backend data /// backend data
void *backend_data; backend_t *backend_data;
/// libev mainloop /// libev mainloop
struct ev_loop *loop; struct ev_loop *loop;
@ -332,6 +333,8 @@ typedef struct session {
bool root_tile_fill; bool root_tile_fill;
/// Picture of the root window background. /// Picture of the root window background.
paint_t root_tile_paint; paint_t root_tile_paint;
/// The backend data the root pixmap bound to
void *root_image;
/// A region of the size of the screen. /// A region of the size of the screen.
region_t screen_reg; region_t screen_reg;
/// Picture of root window. Destination of painting in no-DBE painting /// 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]; auto bi = backend_list[ps->o.backend];
if (ps->o.experimental_backends) { if (ps->o.experimental_backends) {
assert(bi); assert(bi);
if (!bi->root_change) { if (!ps->backend_data->ops->root_change) {
// deinit/reinit backend if the backend cannot handle 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; ps->backend_data = NULL;
} }
} else { } else {
@ -836,10 +836,10 @@ void configure_root(session_t *ps, int width, int height) {
glx_on_root_change(ps); glx_on_root_change(ps);
#endif #endif
if (ps->o.experimental_backends) { if (ps->o.experimental_backends) {
if (bi->root_change) { if (ps->backend_data->ops->root_change) {
bi->root_change(ps->backend_data, ps); ps->backend_data->ops->root_change(ps->backend_data, ps);
} else { } else {
ps->backend_data = bi->init(ps); ps->backend_data = backend_list[ps->o.backend](ps);
if (!ps->backend_data) { if (!ps->backend_data) {
log_fatal("Failed to re-initialize backend after root change, aborting..."); log_fatal("Failed to re-initialize backend after root change, aborting...");
ps->quit = true; ps->quit = true;
@ -938,10 +938,21 @@ circulate_win(session_t *ps, xcb_circulate_notify_event_t *ce) {
static inline void static inline void
root_damaged(session_t *ps) { root_damaged(session_t *ps) {
if (ps->root_tile_paint.pixmap) { if (ps->root_tile_paint.pixmap) {
xcb_clear_area(ps->c, true, ps->root, 0, 0, 0, 0);
free_root_tile(ps); 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 // Mark screen damaged
force_repaint(ps); force_repaint(ps);
} }
@ -1981,21 +1992,43 @@ redir_start(session_t *ps) {
if (ps->o.experimental_backends) { if (ps->o.experimental_backends) {
// Reinitialize win_data // Reinitialize win_data
backend_info_t *bi = backend_list[ps->o.backend]; ps->backend_data = backend_list[ps->o.backend](ps);
assert(bi);
ps->backend_data = bi->init(ps);
if (!ps->backend_data) { if (!ps->backend_data) {
log_fatal("Failed to initialize backend, aborting..."); log_fatal("Failed to initialize backend, aborting...");
ps->quit = true; ps->quit = true;
ev_break(ps->loop, EVBREAK_ALL); ev_break(ps->loop, EVBREAK_ALL);
return false; return false;
} }
for (win *w = ps->list; w; w = w->next) { for (win *w = ps->list; w; w = w->next) {
if (w->a.map_state == XCB_MAP_STATE_VIEWABLE) { 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: // Unredirect GL context window as this may have an effect on VSync:
@ -2011,6 +2044,8 @@ redir_start(session_t *ps) {
ps->redirected = true; ps->redirected = true;
root_damaged(ps);
// Repaint the whole screen // Repaint the whole screen
force_repaint(ps); force_repaint(ps);
} }
@ -2024,7 +2059,6 @@ static void
redir_stop(session_t *ps) { redir_stop(session_t *ps) {
if (ps->redirected) { if (ps->redirected) {
log_debug("Screen unredirected."); log_debug("Screen unredirected.");
backend_info_t *bi = backend_list[ps->o.backend];
// Destroy all Pictures as they expire once windows are unredirected // Destroy all Pictures as they expire once windows are unredirected
// If we don't destroy them here, looks like the resources are just // If we don't destroy them here, looks like the resources are just
// kept inaccessible somehow // kept inaccessible somehow
@ -2038,12 +2072,22 @@ redir_stop(session_t *ps) {
continue; continue;
} }
if (ps->o.experimental_backends) { if (ps->o.experimental_backends) {
assert(bi);
if (w->state == WSTATE_MAPPED) { if (w->state == WSTATE_MAPPED) {
bi->release_win(ps->backend_data, ps, w, w->win_data); ps->backend_data->ops->release_image(ps->backend_data, w->win_image);
w->win_data = NULL; 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 { } 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 { } else {
free_paint(ps, &w->paint); free_paint(ps, &w->paint);
@ -2057,10 +2101,18 @@ redir_stop(session_t *ps) {
if (ps->o.experimental_backends) { if (ps->o.experimental_backends) {
// deinit backend // deinit backend
bi->deinit(ps->backend_data, ps); ps->backend_data->ops->deinit(ps->backend_data);
ps->backend_data = NULL; 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 // Must call XSync() here
x_sync(ps->c); 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"); 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); rebuild_shadow_exclude_reg(ps);
// Query X Shape // Query X Shape
@ -2654,18 +2709,6 @@ session_init(int argc, char **argv, Display *dpy, const char *config_file,
exit(1); 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) { if (ps->o.print_diagnostics) {
print_diagnostics(ps, config_file); print_diagnostics(ps, config_file);
free(config_file_to_free); free(config_file_to_free);
@ -2949,17 +2992,10 @@ session_destroy(session_t *ps) {
deinit_render(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 // Flush all events
x_sync(ps->c); x_sync(ps->c);
ev_io_stop(ps->loop, &ps->xiow); ev_io_stop(ps->loop, &ps->xiow);
free_conv(ps->gaussian_map);
#ifdef DEBUG_XRC #ifdef DEBUG_XRC
// Report about resource leakage // Report about resource leakage

View File

@ -47,9 +47,9 @@ paint_bind_tex(session_t *ps, paint_t *ppaint, unsigned wid, unsigned hei, bool
if (!visual) { if (!visual) {
assert(depth == 32); assert(depth == 32);
if (!argb_fbconfig) { if (!argb_fbconfig) {
argb_fbconfig = glx_find_fbconfig( argb_fbconfig =
ps->dpy, ps->scr, glx_find_fbconfig(ps->dpy, ps->scr,
(struct glx_fbconfig_criteria){.red_size = 8, (struct xvisual_info){.red_size = 8,
.green_size = 8, .green_size = 8,
.blue_size = 8, .blue_size = 8,
.alpha_size = 8, .alpha_size = 8,
@ -61,7 +61,7 @@ paint_bind_tex(session_t *ps, paint_t *ppaint, unsigned wid, unsigned hei, bool
} }
fbcfg = argb_fbconfig; fbcfg = argb_fbconfig;
} else { } 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) { if (m.visual_depth < 0) {
return false; return false;
} }
@ -278,8 +278,8 @@ void paint_one(session_t *ps, win *w, const region_t *reg_paint) {
// Invert window color, if required // Invert window color, if required
if (bkend_use_xrender(ps) && w->invert_color) { if (bkend_use_xrender(ps) && w->invert_color) {
xcb_render_picture_t newpict = xcb_render_picture_t newpict = x_create_picture_with_pictfmt(
x_create_picture_with_pictfmt(ps->c, ps->root, wid, hei, w->pictfmt, 0, NULL); ps->c, ps->root, wid, hei, w->pictfmt, 0, NULL);
if (newpict) { if (newpict) {
// Apply clipping region to save some CPU // Apply clipping region to save some CPU
if (reg_paint) { 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->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); 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->black_picture);
free_picture(ps->c, &ps->white_picture); free_picture(ps->c, &ps->white_picture);
free_conv(ps->gaussian_map);
// Free other X resources // Free other X resources
free_root_tile(ps); free_root_tile(ps);

View File

@ -7,6 +7,7 @@
#include <xcb/xcb.h> #include <xcb/xcb.h>
#include <xcb/render.h> #include <xcb/render.h>
#include <xcb/damage.h> #include <xcb/damage.h>
#include <xcb/composite.h>
#include <xcb/xcb_renderutil.h> #include <xcb/xcb_renderutil.h>
#include <stdbool.h> #include <stdbool.h>
#include <math.h> #include <math.h>
@ -620,8 +621,19 @@ void win_on_win_size_change(session_t *ps, win *w) {
if (w->state == WSTATE_MAPPED || if (w->state == WSTATE_MAPPED ||
w->state == WSTATE_MAPPING || w->state == WSTATE_MAPPING ||
w->state == WSTATE_FADING) { w->state == WSTATE_FADING) {
backend_list[ps->o.backend]->release_win(ps->backend_data, ps, w, w->win_data); ps->backend_data->ops->release_image(ps->backend_data, w->win_image);
w->win_data = backend_list[ps->o.backend]->prepare_win(ps->backend_data, ps, w); 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 { } else {
assert(w->state == WSTATE_UNMAPPED); 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>. * Free all resources in a <code>struct _win</code>.
*/ */
void free_win_res(session_t *ps, win *w) { 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. // finish_unmap_win should've done that for us.
// XXX unless we are called by session_destroy // XXX unless we are called by session_destroy
// assert(w->win_data == NULL); // 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 // Not initialized until mapped, this variables
// have no meaning or have no use until the window // have no meaning or have no use until the window
// is mapped // is mapped
.win_data = NULL, .win_image = NULL,
.shadow_image = NULL,
.prev_trans = NULL, .prev_trans = NULL,
.shadow = false, .shadow = false,
.xinerama_scr = -1, .xinerama_scr = -1,
@ -1245,10 +1258,19 @@ void win_update_bounding_shape(session_t *ps, win *w) {
if (ps->redirected && w->state != WSTATE_UNMAPPED) { if (ps->redirected && w->state != WSTATE_UNMAPPED) {
// Note we only do this when screen is redirected, because // Note we only do this when screen is redirected, because
// otherwise win_data is not valid // otherwise win_data is not valid
backend_info_t *bi = backend_list[ps->o.backend]; assert(w->state != WSTATE_UNMAPPING && w->state != WSTATE_DESTROYING);
bi->release_win(ps->backend_data, ps, w, w->win_data); ps->backend_data->ops->release_image(ps->backend_data, w->win_image);
if (w->state != WSTATE_UNMAPPING && w->state != WSTATE_DESTROYING) { if (w->shadow_image) {
w->win_data = bi->prepare_win(ps->backend_data, ps, w); 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 { } else {
@ -1348,9 +1370,13 @@ finish_unmap_win(session_t *ps, win **_w) {
if (ps->o.experimental_backends) { if (ps->o.experimental_backends) {
// We are in unmap_win, we definitely was viewable // We are in unmap_win, we definitely was viewable
if (ps->redirected) { if (ps->redirected) {
assert(w->win_data); assert(w->win_image);
backend_list[ps->o.backend]->release_win(ps->backend_data, ps, w, w->win_data); ps->backend_data->ops->release_image(ps->backend_data, w->win_image);
w->win_data = NULL; 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 { } else {
free_paint(ps, &w->paint); 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 // TODO win_update_bounding_shape below will immediately
// reinit w->win_data, not very efficient // reinit w->win_data, not very efficient
if (ps->redirected && ps->o.experimental_backends) { 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); 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 { struct win {
/// backend data attached to this window. Only available when /// backend data attached to this window. Only available when
/// `state` is not UNMAPPED /// `state` is not UNMAPPED
void *win_data; void *win_image;
void *shadow_image;
/// Pointer to the next lower window in window stack. /// Pointer to the next lower window in window stack.
win *next; win *next;
/// Pointer to the next higher window to paint. /// Pointer to the next higher window to paint.
@ -127,6 +128,8 @@ struct win {
// TODO rethink reg_ignore // TODO rethink reg_ignore
// Core members // Core members
/// Named pixmap attached to this window
xcb_pixmap_t pixmap;
/// ID of the top-level frame window. /// ID of the top-level frame window.
xcb_window_t id; xcb_window_t id;
/// The "mapped state" of this window, doesn't necessary /// 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; 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) { int x_get_visual_depth(xcb_connection_t *c, xcb_visualid_t visual) {
auto setup = xcb_get_setup(c); auto setup = xcb_get_setup(c);
for (auto screen = xcb_setup_roots_iterator(setup); screen.rem; xcb_screen_next(&screen)) { 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); buf[kernel->h / 2 * kernel->w + kernel->w / 2 + 2] = DOUBLE_TO_XFIXED(center * factor);
return kernel->w * kernel->h + 2; 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; xcb_get_property_reply_t *r;
} winprop_t; } 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, ...) \ #define XCB_SYNCED_VOID(func, c, ...) \
xcb_request_check(c, func##_checked(c, __VA_ARGS__)); xcb_request_check(c, func##_checked(c, __VA_ARGS__));
#define XCB_SYNCED(func, c, ...) \ #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, size_t x_picture_filter_from_conv(const conv *kernel, double center,
xcb_render_fixed_t **ret, size_t *size); 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);