Merge pull request #125 from yshui/new-new-backend
new_backend: New interface
This commit is contained in:
commit
0d7f505dd6
|
@ -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(®ion);
|
pixman_region32_init(®ion);
|
||||||
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(®ion);
|
pixman_region32_fini(®ion);
|
||||||
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 = ®ion;
|
reg_paint = ®ion;
|
||||||
}
|
}
|
||||||
|
|
||||||
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(®_tmp, ®ion, w->reg_ignore);
|
pixman_region32_subtract(®_tmp, ®ion, w->reg_ignore);
|
||||||
|
|
||||||
if (pixman_region32_not_empty(®_tmp)) {
|
if (!pixman_region32_not_empty(®_tmp)) {
|
||||||
// Render window content
|
continue;
|
||||||
// XXX do this in preprocess?
|
}
|
||||||
bi->render_win(ps->backend_data, ps, w, w->win_data, ®_tmp);
|
|
||||||
|
|
||||||
// Blur window background
|
auto reg_bound = win_get_bounding_shape_global_by_val(w);
|
||||||
bool win_transparent =
|
// Draw shadow on target
|
||||||
bi->is_win_transparent(ps->backend_data, w, w->win_data);
|
if (w->shadow) {
|
||||||
bool frame_transparent =
|
auto reg_shadow = win_extents_by_val(w);
|
||||||
bi->is_frame_transparent(ps->backend_data, w, w->win_data);
|
pixman_region32_intersect(®_shadow, ®_shadow, ®_tmp);
|
||||||
if (w->blur_background &&
|
|
||||||
(win_transparent ||
|
if (!ps->o.wintype_option[w->window_type].full_shadow) {
|
||||||
(ps->o.blur_background_frame && frame_transparent))) {
|
pixman_region32_subtract(®_shadow, ®_shadow, ®_bound);
|
||||||
// Minimize the region we try to blur, if the window
|
|
||||||
// itself is not opaque, only the frame is.
|
|
||||||
region_t reg_blur = win_get_bounding_shape_global_by_val(w);
|
|
||||||
if (win_is_solid(ps, w)) {
|
|
||||||
region_t reg_noframe;
|
|
||||||
pixman_region32_init(®_noframe);
|
|
||||||
win_get_region_noframe_local(w, ®_noframe);
|
|
||||||
pixman_region32_translate(®_noframe, w->g.x,
|
|
||||||
w->g.y);
|
|
||||||
pixman_region32_subtract(®_blur, ®_blur,
|
|
||||||
®_noframe);
|
|
||||||
pixman_region32_fini(®_noframe);
|
|
||||||
}
|
|
||||||
bi->blur(ps->backend_data, ps, w->opacity, ®_blur);
|
|
||||||
pixman_region32_fini(®_blur);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw window on target
|
// Mask out the region we don't want shadow on
|
||||||
bi->compose(ps->backend_data, ps, w, w->win_data, w->g.x, w->g.y,
|
if (pixman_region32_not_empty(&ps->shadow_exclude_reg)) {
|
||||||
®_tmp);
|
pixman_region32_subtract(®_tmp, ®_tmp,
|
||||||
|
&ps->shadow_exclude_reg);
|
||||||
|
}
|
||||||
|
|
||||||
if (bi->finish_render_win)
|
if (ps->o.xinerama_shadow_crop && w->xinerama_scr >= 0 &&
|
||||||
bi->finish_render_win(ps->backend_data, ps, w, w->win_data);
|
w->xinerama_scr < ps->xinerama_nscrs) {
|
||||||
|
// There can be a window where number of screens is
|
||||||
|
// updated, but the screen number attached to the windows
|
||||||
|
// have not.
|
||||||
|
//
|
||||||
|
// Window screen number will be updated eventually, so
|
||||||
|
// here we just check to make sure we don't access out of
|
||||||
|
// bounds.
|
||||||
|
pixman_region32_intersect(
|
||||||
|
®_shadow, ®_shadow,
|
||||||
|
&ps->xinerama_scr_regs[w->xinerama_scr]);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(w->shadow_image);
|
||||||
|
ps->backend_data->ops->compose(ps->backend_data, w->shadow_image,
|
||||||
|
w->g.x + w->shadow_dx,
|
||||||
|
w->g.y + w->shadow_dy, ®_shadow);
|
||||||
|
pixman_region32_fini(®_shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
pixman_region32_intersect(®_tmp, ®_tmp, ®_bound);
|
||||||
|
pixman_region32_fini(®_bound);
|
||||||
|
if (!pixman_region32_not_empty(®_tmp)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Blur window background
|
||||||
|
bool win_transparent = ps->backend_data->ops->is_image_transparent(
|
||||||
|
ps->backend_data, w->win_image);
|
||||||
|
bool frame_transparent = w->frame_opacity != 1;
|
||||||
|
if (w->blur_background &&
|
||||||
|
(win_transparent || (ps->o.blur_background_frame && frame_transparent))) {
|
||||||
|
// Minimize the region we try to blur, if the window
|
||||||
|
// itself is not opaque, only the frame is.
|
||||||
|
if (win_is_solid(ps, w)) {
|
||||||
|
region_t reg_blur;
|
||||||
|
pixman_region32_init(®_blur);
|
||||||
|
win_get_region_noframe_local(w, ®_blur);
|
||||||
|
pixman_region32_translate(®_blur, w->g.x, w->g.y);
|
||||||
|
pixman_region32_subtract(®_blur, ®_tmp, ®_blur);
|
||||||
|
ps->backend_data->ops->blur(ps->backend_data, w->opacity,
|
||||||
|
®_blur);
|
||||||
|
pixman_region32_fini(®_blur);
|
||||||
|
} else {
|
||||||
|
ps->backend_data->ops->blur(ps->backend_data, w->opacity,
|
||||||
|
®_tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Draw window on target
|
||||||
|
if (!w->invert_color && !w->dim && w->frame_opacity == 1 && w->opacity == 1) {
|
||||||
|
ps->backend_data->ops->compose(ps->backend_data, w->win_image,
|
||||||
|
w->g.x, w->g.y, ®_tmp);
|
||||||
|
} else {
|
||||||
|
region_t reg_local;
|
||||||
|
pixman_region32_init(®_local);
|
||||||
|
pixman_region32_copy(®_local, ®_tmp);
|
||||||
|
pixman_region32_translate(®_local, -w->g.x, -w->g.y);
|
||||||
|
auto new_img = ps->backend_data->ops->copy(
|
||||||
|
ps->backend_data, w->win_image, ®_local);
|
||||||
|
if (w->invert_color) {
|
||||||
|
ps->backend_data->ops->image_op(ps->backend_data,
|
||||||
|
IMAGE_OP_INVERT_COLOR,
|
||||||
|
new_img, ®_local, NULL);
|
||||||
|
}
|
||||||
|
if (w->dim) {
|
||||||
|
double dim_opacity = ps->o.inactive_dim;
|
||||||
|
if (!ps->o.inactive_dim_fixed) {
|
||||||
|
dim_opacity *= w->opacity;
|
||||||
|
}
|
||||||
|
ps->backend_data->ops->image_op(
|
||||||
|
ps->backend_data, IMAGE_OP_DIM, new_img, ®_local,
|
||||||
|
(double[]){dim_opacity});
|
||||||
|
}
|
||||||
|
if (w->frame_opacity != 1) {
|
||||||
|
auto reg_frame = win_get_region_noframe_local_by_val(w);
|
||||||
|
pixman_region32_subtract(®_frame, ®_local, ®_frame);
|
||||||
|
ps->backend_data->ops->image_op(
|
||||||
|
ps->backend_data, IMAGE_OP_APPLY_ALPHA, new_img,
|
||||||
|
®_frame, (double[]){w->frame_opacity});
|
||||||
|
}
|
||||||
|
if (w->opacity != 1) {
|
||||||
|
ps->backend_data->ops->image_op(
|
||||||
|
ps->backend_data, IMAGE_OP_APPLY_ALPHA, new_img, NULL,
|
||||||
|
(double[]){w->opacity});
|
||||||
|
}
|
||||||
|
ps->backend_data->ops->compose(ps->backend_data, new_img, w->g.x,
|
||||||
|
w->g.y, ®_tmp);
|
||||||
|
ps->backend_data->ops->release_image(ps->backend_data, new_img);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free up all temporary regions
|
// Free up all temporary regions
|
||||||
pixman_region32_fini(®_tmp);
|
pixman_region32_fini(®_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
|
||||||
|
|
|
@ -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 *);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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(®ion_yflipped);
|
pixman_region32_fini(®ion_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.
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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(®_tmp);
|
|
||||||
// Shadow doesn't need to be painted underneath the body of the window
|
|
||||||
// Because no one can see it
|
|
||||||
pixman_region32_subtract(®_tmp, &shadow_reg, w->reg_ignore);
|
|
||||||
|
|
||||||
// Mask out the region we don't want shadow on
|
|
||||||
if (pixman_region32_not_empty(&ps->shadow_exclude_reg))
|
|
||||||
pixman_region32_subtract(®_tmp, ®_tmp, &ps->shadow_exclude_reg);
|
|
||||||
|
|
||||||
// Might be worth while to crop the region to shadow border
|
|
||||||
pixman_region32_intersect_rect(®_tmp, ®_tmp, w->g.x + w->shadow_dx,
|
|
||||||
w->g.y + w->shadow_dy, w->shadow_width,
|
|
||||||
w->shadow_height);
|
|
||||||
|
|
||||||
// Crop the shadow to the damage region. If we draw out side of
|
|
||||||
// the damage region, we could be drawing over perfectly good
|
|
||||||
// content, and destroying it.
|
|
||||||
pixman_region32_intersect(®_tmp, ®_tmp, (region_t *)reg_paint);
|
|
||||||
|
|
||||||
if (ps->o.xinerama_shadow_crop && w->xinerama_scr >= 0 &&
|
|
||||||
w->xinerama_scr < ps->xinerama_nscrs)
|
|
||||||
// There can be a window where number of screens is updated,
|
|
||||||
// but the screen number attached to the windows have not.
|
|
||||||
//
|
|
||||||
// Window screen number will be updated eventually, so here we
|
|
||||||
// just check to make sure we don't access out of bounds.
|
|
||||||
pixman_region32_intersect(
|
|
||||||
®_tmp, ®_tmp, &ps->xinerama_scr_regs[w->xinerama_scr]);
|
|
||||||
|
|
||||||
// Mask out the body of the window from the shadow
|
|
||||||
// Doing it here instead of in make_shadow() for saving GPU
|
|
||||||
// power and handling shaped windows (XXX unconfirmed)
|
|
||||||
pixman_region32_subtract(®_tmp, ®_tmp, &bshape);
|
|
||||||
pixman_region32_fini(&bshape);
|
|
||||||
|
|
||||||
// Detect if the region is empty before painting
|
|
||||||
if (pixman_region32_not_empty(®_tmp)) {
|
|
||||||
x_set_picture_clip_region(ps->c, xd->back[xd->curr_back], 0, 0,
|
|
||||||
®_tmp);
|
|
||||||
xcb_render_composite(
|
|
||||||
ps->c, XCB_RENDER_PICT_OP_OVER, wd->shadow_pict, alpha_pict,
|
|
||||||
xd->back[xd->curr_back], 0, 0, 0, 0, dst_x + w->shadow_dx,
|
|
||||||
dst_y + w->shadow_dy, w->shadow_width, w->shadow_height);
|
|
||||||
}
|
|
||||||
pixman_region32_fini(®_tmp);
|
|
||||||
pixman_region32_fini(&shadow_reg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) {
|
||||||
w->pixmap_damaged = false;
|
log_error("Invalid pixmap: %#010x", pixmap);
|
||||||
|
x_print_error(e->full_sequence, e->major_code, e->minor_code, e->error_code);
|
||||||
if (!w->invert_color && w->frame_opacity == 1 && !w->dim) {
|
return NULL;
|
||||||
// No extra processing needed
|
|
||||||
wd->rendered_pict = wd->pict;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
region_t reg_paint_local;
|
auto img = ccalloc(1, struct _xrender_image_data);
|
||||||
pixman_region32_init(®_paint_local);
|
img->depth = fmt.visual_depth;
|
||||||
pixman_region32_copy(®_paint_local, (region_t *)reg_paint);
|
img->width = r->width;
|
||||||
pixman_region32_translate(®_paint_local, -w->g.x, -w->g.y);
|
img->height = r->height;
|
||||||
|
img->pixmap = pixmap;
|
||||||
// We don't want to modify the content of the original window when we process
|
img->opacity = 1;
|
||||||
// it, so we create a buffer.
|
img->has_alpha = fmt.alpha_size != 0;
|
||||||
if (wd->buffer == XCB_NONE) {
|
img->pict =
|
||||||
wd->buffer = x_create_picture_with_pictfmt(ps->c, ps->root, w->widthb, w->heightb,
|
x_create_picture_with_visual_and_pixmap(base->c, fmt.visual, pixmap, 0, NULL);
|
||||||
w->pictfmt, 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
|
static void release_image(backend_t *base, void *image) {
|
||||||
x_clear_picture_clip_region(ps->c, wd->buffer);
|
struct _xrender_image_data *img = image;
|
||||||
wd->rendered_pict = wd->buffer;
|
xcb_render_free_picture(base->c, img->pict);
|
||||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, wd->pict, XCB_NONE,
|
if (img->owned) {
|
||||||
wd->rendered_pict, 0, 0, 0, 0, 0, 0, w->widthb, w->heightb);
|
xcb_free_pixmap(base->c, img->pixmap);
|
||||||
|
|
||||||
if (w->invert_color) {
|
|
||||||
// Handle invert color
|
|
||||||
x_set_picture_clip_region(ps->c, wd->rendered_pict, 0, 0, ®_paint_local);
|
|
||||||
|
|
||||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_DIFFERENCE,
|
|
||||||
xd->white_pixel, XCB_NONE, wd->rendered_pict, 0, 0,
|
|
||||||
0, 0, 0, 0, w->widthb, w->heightb);
|
|
||||||
// We use an extra PictOpInReverse operation to get correct pixel
|
|
||||||
// alpha. There could be a better solution.
|
|
||||||
if (win_has_alpha(w))
|
|
||||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_IN_REVERSE,
|
|
||||||
wd->pict, XCB_NONE, wd->rendered_pict, 0, 0,
|
|
||||||
0, 0, 0, 0, w->widthb, w->heightb);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (w->frame_opacity != 1) {
|
static void deinit(backend_t *backend_data) {
|
||||||
// Handle transparent frame
|
struct _xrender_data *xd = (void *)backend_data;
|
||||||
// Step 1: clip paint area to frame
|
for (int i = 0; i < 256; i++) {
|
||||||
region_t frame_reg;
|
xcb_render_free_picture(xd->base.c, xd->alpha_pict[i]);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
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) {
|
static void prepare(backend_t *base, const region_t *reg_paint) {
|
||||||
// Handle dimming
|
struct _xrender_data *xd = (void *)base;
|
||||||
|
|
||||||
double dim_opacity = ps->o.inactive_dim;
|
// Paint the root pixmap (i.e. wallpaper)
|
||||||
if (!ps->o.inactive_dim_fixed)
|
// Limit the paint area
|
||||||
dim_opacity *= w->opacity;
|
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 = {
|
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(®_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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
108
src/compton.c
108
src/compton.c
|
@ -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,22 +1992,44 @@ 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:
|
||||||
// < http://dri.freedesktop.org/wiki/CompositeSwap >
|
// < http://dri.freedesktop.org/wiki/CompositeSwap >
|
||||||
|
@ -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,13 +2072,23 @@ 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
|
||||||
|
|
24
src/render.c
24
src/render.c
|
@ -47,13 +47,13 @@ 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,
|
||||||
.visual_depth = 32});
|
.visual_depth = 32});
|
||||||
}
|
}
|
||||||
if (!argb_fbconfig) {
|
if (!argb_fbconfig) {
|
||||||
log_error("Failed to find appropriate FBConfig for 32 bit depth");
|
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;
|
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);
|
||||||
|
|
59
src/win.c
59
src/win.c
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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
59
src/x.c
|
@ -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
23
src/x.h
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue