diff --git a/src/common.h b/src/common.h
index 2577e06..60e7139 100644
--- a/src/common.h
+++ b/src/common.h
@@ -138,6 +138,7 @@
#include "log.h"
#include "utils.h"
#include "compiler.h"
+#include "kernel.h"
// === Constants ===
@@ -354,7 +355,7 @@ typedef struct {
GLint unifm_factor_center;
} glx_blur_pass_t;
-typedef struct {
+typedef struct glx_prog_main {
/// GLSL program.
GLuint prog;
/// Location of uniform "opacity" in window GLSL program.
@@ -379,11 +380,6 @@ typedef uint32_t glx_prog_main_t;
#define PAINT_INIT { .pixmap = None, .pict = None }
-typedef struct conv {
- int size;
- double data[];
-} conv;
-
/// Linked list type of atoms.
typedef struct _latom {
Atom atom;
@@ -1308,18 +1304,6 @@ bkend_use_glx(session_t *ps) {
|| BKEND_XR_GLX_HYBRID == ps->o.backend;
}
-/**
- * Check if there's a GLX context.
- */
-static inline bool
-glx_has_context(session_t *ps) {
-#ifdef CONFIG_OPENGL
- return ps->psglx && ps->psglx->context;
-#else
- return false;
-#endif
-}
-
/**
* Check if a window is really focused.
*/
@@ -1483,53 +1467,6 @@ vsync_deinit(session_t *ps);
*/
///@{
-/**
- * Free a GLX texture.
- */
-static inline void
-free_texture_r(session_t *ps, GLuint *ptexture) {
- if (*ptexture) {
- assert(glx_has_context(ps));
- glDeleteTextures(1, ptexture);
- *ptexture = 0;
- }
-}
-
-/**
- * Free a GLX Framebuffer object.
- */
-static inline void
-free_glx_fbo(session_t *ps, GLuint *pfbo) {
-#ifdef CONFIG_OPENGL
- if (*pfbo) {
- glDeleteFramebuffers(1, pfbo);
- *pfbo = 0;
- }
-#endif
- assert(!*pfbo);
-}
-
-#ifdef CONFIG_OPENGL
-/**
- * Free data in glx_blur_cache_t on resize.
- */
-static inline void
-free_glx_bc_resize(session_t *ps, glx_blur_cache_t *pbc) {
- free_texture_r(ps, &pbc->textures[0]);
- free_texture_r(ps, &pbc->textures[1]);
- pbc->width = 0;
- pbc->height = 0;
-}
-
-/**
- * Free a glx_blur_cache_t
- */
-static inline void
-free_glx_bc(session_t *ps, glx_blur_cache_t *pbc) {
- free_glx_fbo(ps, &pbc->fbo);
- free_glx_bc_resize(ps, pbc);
-}
-#endif
#endif
/**
diff --git a/src/compton.c b/src/compton.c
index b9236ec..176792f 100644
--- a/src/compton.c
+++ b/src/compton.c
@@ -31,7 +31,10 @@
#include "config.h"
#include "diagnostic.h"
#include "string_utils.h"
+#include "render.h"
#include "utils.h"
+#include "kernel.h"
+#include "vsync.h"
#include "log.h"
#define auto __auto_type
@@ -68,40 +71,6 @@ static void
cxinerama_upd_scrs(session_t *ps);
#endif
-static bool
-vsync_drm_init(session_t *ps);
-
-#ifdef CONFIG_VSYNC_DRM
-static int
-vsync_drm_wait(session_t *ps);
-#endif
-
-static bool
-vsync_opengl_init(session_t *ps);
-
-static bool
-vsync_opengl_oml_init(session_t *ps);
-
-static bool
-vsync_opengl_swc_init(session_t *ps);
-
-static bool
-vsync_opengl_mswc_init(session_t *ps);
-
-#ifdef CONFIG_OPENGL
-static int
-vsync_opengl_wait(session_t *ps);
-
-static int
-vsync_opengl_oml_wait(session_t *ps);
-
-static void
-vsync_opengl_swc_deinit(session_t *ps);
-#endif
-
-static void
-vsync_wait(session_t *ps);
-
static void
redir_start(session_t *ps);
@@ -111,9 +80,6 @@ redir_stop(session_t *ps);
static win *
recheck_focus(session_t *ps);
-static bool
-get_root_tile(session_t *ps);
-
static double
get_opacity_percent(win *w);
@@ -126,12 +92,6 @@ update_ewmh_active_win(session_t *ps);
static void
draw_callback(EV_P_ ev_idle *w, int revents);
-static void
-render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei,
- double opacity, bool argb, bool neg,
- xcb_render_picture_t pict, glx_texture_t *ptex,
- const region_t *reg_paint, const glx_prog_main_t *pprogram);
-
// === Global constants ===
/// Name strings for window types.
@@ -172,37 +132,9 @@ const char * const BACKEND_STRS[NUM_BKEND + 1] = {
NULL
};
-/// Function pointers to init VSync modes.
-static bool (* const (VSYNC_FUNCS_INIT[NUM_VSYNC]))(session_t *ps) = {
- [VSYNC_DRM ] = vsync_drm_init,
- [VSYNC_OPENGL ] = vsync_opengl_init,
- [VSYNC_OPENGL_OML ] = vsync_opengl_oml_init,
- [VSYNC_OPENGL_SWC ] = vsync_opengl_swc_init,
- [VSYNC_OPENGL_MSWC ] = vsync_opengl_mswc_init,
-};
-
-/// Function pointers to wait for VSync.
-static int (* const (VSYNC_FUNCS_WAIT[NUM_VSYNC]))(session_t *ps) = {
-#ifdef CONFIG_VSYNC_DRM
- [VSYNC_DRM ] = vsync_drm_wait,
-#endif
-#ifdef CONFIG_OPENGL
- [VSYNC_OPENGL ] = vsync_opengl_wait,
- [VSYNC_OPENGL_OML ] = vsync_opengl_oml_wait,
-#endif
-};
-
-/// Function pointers to deinitialize VSync.
-static void (* const (VSYNC_FUNCS_DEINIT[NUM_VSYNC]))(session_t *ps) = {
-#ifdef CONFIG_OPENGL
- [VSYNC_OPENGL_SWC ] = vsync_opengl_swc_deinit,
- [VSYNC_OPENGL_MSWC ] = vsync_opengl_swc_deinit,
-#endif
-};
-
/// Names of root window properties that could point to a pixmap of
/// background.
-static const char *background_props_str[] = {
+const char *background_props_str[] = {
"_XROOTPMAP_ID",
"_XSETROOT_ID",
0,
@@ -259,7 +191,7 @@ free_win_res(session_t *ps, win *w) {
*/
static inline void
free_root_tile(session_t *ps) {
- free_picture(ps, &ps->root_tile_paint.pict);
+ free_picture(ps->c, &ps->root_tile_paint.pict);
free_texture(ps, &ps->root_tile_paint.ptex);
if (ps->root_tile_fill) {
xcb_free_pixmap(ps->c, ps->root_tile_paint.pixmap);
@@ -313,24 +245,6 @@ resize_region(session_t *ps, region_t *region, short mod) {
free(newrects);
}
-static inline void
-__attribute__((nonnull(1, 2)))
-set_tgt_clip(session_t *ps, region_t *reg) {
- switch (ps->o.backend) {
- case BKEND_XRENDER:
- case BKEND_XR_GLX_HYBRID:
- x_set_picture_clip_region(ps, ps->tgt_buffer.pict, 0, 0, reg);
- break;
-#ifdef CONFIG_OPENGL
- case BKEND_GLX:
- glx_set_clip(ps, reg);
- break;
-#endif
- default:
- assert(false);
- }
-}
-
/**
* Get the Xinerama screen a window is on.
*
@@ -417,19 +331,6 @@ void queue_redraw(session_t *ps) {
ps->redraw_needed = true;
}
-static inline void
-paint_region(session_t *ps, win *w, int x, int y, int wid, int hei,
- double opacity, const region_t *reg_paint, xcb_render_picture_t pict) {
- const int dx = (w ? w->g.x: 0) + x;
- const int dy = (w ? w->g.y: 0) + y;
- const bool argb = (w && (win_has_alpha(w) || ps->o.force_win_blend));
- const bool neg = (w && w->invert_color);
-
- render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg,
- pict, (w ? w->paint.ptex: ps->root_tile_paint.ptex),
- reg_paint, (w ? &ps->o.glx_prog_win: NULL));
-}
-
/**
* Get a region of the screen size.
*/
@@ -444,36 +345,6 @@ get_screen_region(session_t *ps, region_t *res) {
pixman_region32_init_rects(res, &b, 1);
}
-/**
- * Check if current backend uses XRender for rendering.
- */
-static inline bool
-bkend_use_xrender(session_t *ps) {
- return BKEND_XRENDER == ps->o.backend
- || BKEND_XR_GLX_HYBRID == ps->o.backend;
-}
-
-/**
- * Check whether a paint_t contains enough data.
- */
-static inline bool
-paint_isvalid(session_t *ps, const paint_t *ppaint) {
- // Don't check for presence of Pixmap here, because older X Composite doesn't
- // provide it
- if (!ppaint)
- return false;
-
- if (bkend_use_xrender(ps) && !ppaint->pict)
- return false;
-
-#ifdef CONFIG_OPENGL
- if (BKEND_GLX == ps->o.backend && !glx_tex_binded(ppaint->ptex, None))
- return false;
-#endif
-
- return true;
-}
-
void add_damage(session_t *ps, const region_t *damage) {
// Ignore damage when screen isn't redirected
if (!ps->redirected)
@@ -532,151 +403,8 @@ run_fade(session_t *ps, win *w, unsigned steps) {
}
}
-/**
- * Set fade callback of a window, and possibly execute the previous
- * callback.
- *
- * If a callback can cause rendering result to change, it should call
- * `queue_redraw`.
- *
- * @param exec_callback whether the previous callback is to be executed
- */
-void set_fade_callback(session_t *ps, win **_w,
- void (*callback) (session_t *ps, win **w), bool exec_callback) {
- win *w = *_w;
- void (*old_callback) (session_t *ps, win **w) = w->fade_callback;
-
- w->fade_callback = callback;
- // Must be the last line as the callback could destroy w!
- if (exec_callback && old_callback)
- old_callback(ps, _w);
-}
-
-/**
- * Execute fade callback of a window if fading finished.
- * XXX should be in win.c
- */
-static inline void
-check_fade_fin(session_t *ps, win **_w) {
- win *w = *_w;
- if (w->fade_callback && w->opacity == w->opacity_tgt) {
- // Must be the last line as the callback could destroy w!
- set_fade_callback(ps, _w, NULL, true);
- }
-}
-
// === Shadows ===
-static double __attribute__((const))
-gaussian(double r, double x, double y) {
- // Formula can be found here:
- // https://en.wikipedia.org/wiki/Gaussian_blur#Mathematics
- // Except a special case for r == 0 to produce sharp shadows
- if (r == 0)
- return 1;
- return exp(-0.5*(x*x+y*y)/(r*r))/(2*M_PI*r*r);
-}
-
-static conv *
-make_gaussian_map(double r) {
- conv *c;
- int size = r*2+1;
- int center = size/2;
- double t;
-
- c = cvalloc(sizeof(conv)+size*size*sizeof(double));
- c->size = size;
- t = 0.0;
-
- /*printf_errf("(): %f", r);*/
- for (int y = 0; y < size; y++) {
- for (int x = 0; x < size; x++) {
- double g = gaussian(r, x-center, y-center);
- t += g;
- c->data[y*size+x] = g;
- /*printf("%f ", c->data[y*size+x]);*/
- }
- /*printf("\n");*/
- }
-
- for (int y = 0; y < size; y++) {
- for (int x = 0; x < size; x++) {
- c->data[y*size+x] /= t;
- /*printf("%f ", c->data[y*size+x]);*/
- }
- /*printf("\n");*/
- }
-
- return c;
-}
-
-/*
- * A picture will help
- *
- * -center 0 width width+center
- * -center +-----+-------------------+-----+
- * | | | |
- * | | | |
- * 0 +-----+-------------------+-----+
- * | | | |
- * | | | |
- * | | | |
- * height +-----+-------------------+-----+
- * | | | |
- * height+ | | | |
- * center +-----+-------------------+-----+
- */
-
-static unsigned char
-sum_gaussian(conv *map, double opacity,
- int x, int y, int width, int height) {
- int fx, fy;
- double *g_data;
- double *g_line = map->data;
- int g_size = map->size;
- int center = g_size / 2;
- int fx_start, fx_end;
- int fy_start, fy_end;
- double v;
-
- /*
- * Compute set of filter values which are "in range",
- * that's the set with:
- * 0 <= x + (fx-center) && x + (fx-center) < width &&
- * 0 <= y + (fy-center) && y + (fy-center) < height
- *
- * 0 <= x + (fx - center) x + fx - center < width
- * center - x <= fx fx < width + center - x
- */
-
- fx_start = center - x;
- if (fx_start < 0) fx_start = 0;
- fx_end = width + center - x;
- if (fx_end > g_size) fx_end = g_size;
-
- fy_start = center - y;
- if (fy_start < 0) fy_start = 0;
- fy_end = height + center - y;
- if (fy_end > g_size) fy_end = g_size;
-
- g_line = g_line + fy_start * g_size + fx_start;
-
- v = 0;
-
- for (fy = fy_start; fy < fy_end; fy++) {
- g_data = g_line;
- g_line += g_size;
-
- for (fx = fx_start; fx < fx_end; fx++) {
- v += *g_data++;
- }
- }
-
- if (v > 1) v = 1;
-
- return ((unsigned char) (v * opacity * 255.0));
-}
-
/* precompute shadow corners and sides
to save time for large windows */
@@ -696,8 +424,8 @@ presum_gaussian(session_t *ps, conv *map) {
ps->shadow_top = cvalloc((ps->cgsize + 1) * 26);
for (x = 0; x <= ps->cgsize; x++) {
- ps->shadow_top[25 * (ps->cgsize + 1) + x] =
- sum_gaussian(map, 1, x - center, center, ps->cgsize * 2, ps->cgsize * 2);
+ ps->shadow_top[25 * (ps->cgsize + 1) + x] = (unsigned char)
+ (sum_kernel(map, x - center, center, ps->cgsize * 2, ps->cgsize * 2) * 255.0);
for (opacity = 0; opacity < 25; opacity++) {
ps->shadow_top[opacity * (ps->cgsize + 1) + x] =
@@ -706,7 +434,8 @@ presum_gaussian(session_t *ps, conv *map) {
for (y = 0; y <= x; y++) {
ps->shadow_corner[25 * (ps->cgsize + 1) * (ps->cgsize + 1) + y * (ps->cgsize + 1) + x]
- = sum_gaussian(map, 1, x - center, y - center, ps->cgsize * 2, ps->cgsize * 2);
+ = (unsigned char)
+ (sum_kernel(map, x - center, y - center, ps->cgsize * 2, ps->cgsize * 2) * 255.0);
ps->shadow_corner[25 * (ps->cgsize + 1) * (ps->cgsize + 1) + x * (ps->cgsize + 1) + y]
= ps->shadow_corner[25 * (ps->cgsize + 1) * (ps->cgsize + 1) + y * (ps->cgsize + 1) + x];
@@ -722,189 +451,6 @@ presum_gaussian(session_t *ps, conv *map) {
}
}
-static xcb_image_t *
-make_shadow(session_t *ps, double opacity,
- int width, int height) {
- xcb_image_t *ximage;
- int ylimit, xlimit;
- int swidth = width + ps->cgsize;
- int sheight = height + ps->cgsize;
- int center = ps->cgsize / 2;
- int x, y;
- unsigned char d;
- int x_diff;
- int opacity_int = (int)(opacity * 25);
-
- ximage = xcb_image_create_native(ps->c, swidth, sheight, XCB_IMAGE_FORMAT_Z_PIXMAP, 8,
- 0, 0, NULL);
-
- if (!ximage) {
- printf_errf("(): failed to create an X image");
- return 0;
- }
-
- unsigned char *data = ximage->data;
- uint32_t sstride = ximage->stride;
-
- /*
- * Build the gaussian in sections
- */
-
- /*
- * center (fill the complete data array)
- */
-
- // XXX If the center part of the shadow would be entirely covered by
- // the body of the window, we shouldn't need to fill the center here.
- // XXX In general, we want to just fill the part that is not behind
- // the window, in order to reduce CPU load and make transparent window
- // look correct
- if (ps->cgsize > 0) {
- d = ps->shadow_top[opacity_int * (ps->cgsize + 1) + ps->cgsize];
- } else {
- d = sum_gaussian(ps->gaussian_map,
- opacity, center, center, width, height);
- }
- memset(data, d, sheight * swidth);
-
- /*
- * corners
- */
-
- ylimit = ps->cgsize;
- if (ylimit > sheight / 2) ylimit = (sheight + 1) / 2;
-
- xlimit = ps->cgsize;
- if (xlimit > swidth / 2) xlimit = (swidth + 1) / 2;
-
- for (y = 0; y < ylimit; y++) {
- for (x = 0; x < xlimit; x++) {
- if (xlimit == ps->cgsize && ylimit == ps->cgsize) {
- d = ps->shadow_corner[opacity_int * (ps->cgsize + 1) * (ps->cgsize + 1)
- + y * (ps->cgsize + 1) + x];
- } else {
- d = sum_gaussian(ps->gaussian_map,
- opacity, x - center, y - center, width, height);
- }
- data[y * sstride + x] = d;
- data[(sheight - y - 1) * sstride + x] = d;
- data[(sheight - y - 1) * sstride + (swidth - x - 1)] = d;
- data[y * sstride + (swidth - x - 1)] = d;
- }
- }
-
- /*
- * top/bottom
- */
-
- x_diff = swidth - (ps->cgsize * 2);
- if (x_diff > 0 && ylimit > 0) {
- for (y = 0; y < ylimit; y++) {
- if (ylimit == ps->cgsize) {
- d = ps->shadow_top[opacity_int * (ps->cgsize + 1) + y];
- } else {
- d = sum_gaussian(ps->gaussian_map,
- opacity, center, y - center, width, height);
- }
- memset(&data[y * sstride + ps->cgsize], d, x_diff);
- memset(&data[(sheight - y - 1) * sstride + ps->cgsize], d, x_diff);
- }
- }
-
- /*
- * sides
- */
-
- for (x = 0; x < xlimit; x++) {
- if (xlimit == ps->cgsize) {
- d = ps->shadow_top[opacity_int * (ps->cgsize + 1) + x];
- } else {
- d = sum_gaussian(ps->gaussian_map,
- opacity, x - center, center, width, height);
- }
- for (y = ps->cgsize; y < sheight - ps->cgsize; y++) {
- data[y * sstride + x] = d;
- data[y * sstride + (swidth - x - 1)] = d;
- }
- }
-
- return ximage;
-}
-
-/**
- * Generate shadow Picture
for a window.
- */
-static bool
-win_build_shadow(session_t *ps, win *w, double opacity) {
- const int width = w->widthb;
- const int height = w->heightb;
- //printf_errf("(): building shadow for %s %d %d", w->name, width, height);
-
- xcb_image_t *shadow_image = NULL;
- xcb_pixmap_t shadow_pixmap = None, shadow_pixmap_argb = None;
- xcb_render_picture_t shadow_picture = None, shadow_picture_argb = None;
- xcb_gcontext_t gc = None;
-
- shadow_image = make_shadow(ps, opacity, width, height);
- if (!shadow_image) {
- printf_errf("(): failed to make shadow");
- return None;
- }
-
- shadow_pixmap = x_create_pixmap(ps, 8, ps->root, shadow_image->width, shadow_image->height);
- shadow_pixmap_argb = x_create_pixmap(ps, 32, ps->root, shadow_image->width, shadow_image->height);
-
- if (!shadow_pixmap || !shadow_pixmap_argb) {
- printf_errf("(): failed to create shadow pixmaps");
- goto shadow_picture_err;
- }
-
- shadow_picture = x_create_picture_with_standard_and_pixmap(ps,
- XCB_PICT_STANDARD_A_8, shadow_pixmap, 0, NULL);
- shadow_picture_argb = x_create_picture_with_standard_and_pixmap(ps,
- XCB_PICT_STANDARD_ARGB_32, shadow_pixmap_argb, 0, NULL);
- if (!shadow_picture || !shadow_picture_argb)
- goto shadow_picture_err;
-
- gc = xcb_generate_id(ps->c);
- xcb_create_gc(ps->c, gc, shadow_pixmap, 0, NULL);
-
- xcb_image_put(ps->c, shadow_pixmap, gc, shadow_image, 0, 0, 0);
- xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, ps->cshadow_picture, shadow_picture,
- shadow_picture_argb, 0, 0, 0, 0, 0, 0,
- shadow_image->width, shadow_image->height);
-
- assert(!w->shadow_paint.pixmap);
- w->shadow_paint.pixmap = shadow_pixmap_argb;
- assert(!w->shadow_paint.pict);
- w->shadow_paint.pict = shadow_picture_argb;
-
- // Sync it once and only once
- xr_sync(ps, w->shadow_paint.pixmap, NULL);
-
- xcb_free_gc(ps->c, gc);
- xcb_image_destroy(shadow_image);
- xcb_free_pixmap(ps->c, shadow_pixmap);
- xcb_render_free_picture(ps->c, shadow_picture);
-
- return true;
-
-shadow_picture_err:
- if (shadow_image)
- xcb_image_destroy(shadow_image);
- if (shadow_pixmap)
- xcb_free_pixmap(ps->c, shadow_pixmap);
- if (shadow_pixmap_argb)
- xcb_free_pixmap(ps->c, shadow_pixmap_argb);
- if (shadow_picture)
- xcb_render_free_picture(ps->c, shadow_picture);
- if (shadow_picture_argb)
- xcb_render_free_picture(ps->c, shadow_picture_argb);
- if (gc)
- xcb_free_gc(ps->c, gc);
-
- return false;
-}
/**
* Generate a 1x1 Picture
of a particular color.
@@ -1074,90 +620,6 @@ recheck_focus(session_t *ps) {
return NULL;
}
-static bool
-get_root_tile(session_t *ps) {
- /*
- if (ps->o.paint_on_overlay) {
- return ps->root_picture;
- } */
-
- assert(!ps->root_tile_paint.pixmap);
- ps->root_tile_fill = false;
-
- bool fill = false;
- xcb_pixmap_t pixmap = None;
-
- // Get the values of background attributes
- for (int p = 0; background_props_str[p]; p++) {
- winprop_t prop = wid_get_prop(ps, ps->root,
- get_atom(ps, background_props_str[p]),
- 1L, XCB_ATOM_PIXMAP, 32);
- if (prop.nitems) {
- pixmap = *prop.p32;
- fill = false;
- free_winprop(&prop);
- break;
- }
- free_winprop(&prop);
- }
-
- // Make sure the pixmap we got is valid
- if (pixmap && !validate_pixmap(ps, pixmap))
- pixmap = None;
-
- // Create a pixmap if there isn't any
- if (!pixmap) {
- pixmap = x_create_pixmap(ps, ps->depth, ps->root, 1, 1);
- if (pixmap == XCB_NONE) {
- fprintf(stderr, "Failed to create some pixmap\n");
- exit(1);
- }
- fill = true;
- }
-
- // Create Picture
- xcb_render_create_picture_value_list_t pa = {
- .repeat = True,
- };
- ps->root_tile_paint.pict = x_create_picture_with_visual_and_pixmap(
- ps, ps->vis, pixmap, XCB_RENDER_CP_REPEAT, &pa);
-
- // Fill pixmap if needed
- if (fill) {
- xcb_render_color_t col;
- xcb_rectangle_t rect;
-
- col.red = col.green = col.blue = 0x8080;
- col.alpha = 0xffff;
-
- rect.x = rect.y = 0;
- rect.width = rect.height = 1;
-
- xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_SRC, ps->root_tile_paint.pict, col, 1, &rect);
- }
-
- ps->root_tile_fill = fill;
- ps->root_tile_paint.pixmap = pixmap;
-#ifdef CONFIG_OPENGL
- if (BKEND_GLX == ps->o.backend)
- return glx_bind_pixmap(ps, &ps->root_tile_paint.ptex, ps->root_tile_paint.pixmap, 0, 0, 0);
-#endif
-
- return true;
-}
-
-/**
- * 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);
-}
-
/**
* Look for the client window of a particular window.
*/
@@ -1346,7 +808,7 @@ paint_preprocess(session_t *ps, win *list) {
w->reg_ignore_valid = true;
assert(w->destroyed == (w->fade_callback == finish_destroy_win));
- check_fade_fin(ps, &w);
+ win_check_fade_finished(ps, &w);
// Avoid setting w->to_paint if w is freed
if (w) {
@@ -1389,91 +851,6 @@ paint_preprocess(session_t *ps, win *list) {
return t;
}
-/**
- * Paint the shadow of a window.
- */
-static inline void
-win_paint_shadow(session_t *ps, win *w, region_t *reg_paint) {
- // Bind shadow pixmap to GLX texture if needed
- paint_bind_tex(ps, &w->shadow_paint, 0, 0, 32, false);
-
- if (!paint_isvalid(ps, &w->shadow_paint)) {
- printf_errf("(%#010lx): Missing shadow data.", w->id);
- return;
- }
-
- render(ps, 0, 0, w->g.x + w->shadow_dx, w->g.y + w->shadow_dy,
- w->shadow_width, w->shadow_height, w->shadow_opacity, true, false,
- w->shadow_paint.pict, w->shadow_paint.ptex, reg_paint, NULL);
-}
-
-/**
- * @brief Blur an area on a buffer.
- *
- * @param ps current session
- * @param tgt_buffer a buffer as both source and destination
- * @param x x pos
- * @param y y pos
- * @param wid width
- * @param hei height
- * @param blur_kerns blur kernels, ending with a NULL, guaranteed to have at
- * least one kernel
- * @param reg_clip a clipping region to be applied on intermediate buffers
- *
- * @return true if successful, false otherwise
- */
-static bool
-xr_blur_dst(session_t *ps, xcb_render_picture_t tgt_buffer,
- int x, int y, int wid, int hei, xcb_render_fixed_t **blur_kerns,
- const region_t *reg_clip) {
- assert(blur_kerns[0]);
-
- // Directly copying from tgt_buffer to it does not work, so we create a
- // Picture in the middle.
- xcb_render_picture_t tmp_picture = x_create_picture(ps, wid, hei, NULL, 0, NULL);
-
- if (!tmp_picture) {
- printf_errf("(): Failed to build intermediate Picture.");
- return false;
- }
-
- if (reg_clip && tmp_picture)
- x_set_picture_clip_region(ps, tmp_picture, 0, 0, reg_clip);
-
- xcb_render_picture_t src_pict = tgt_buffer, dst_pict = tmp_picture;
- for (int i = 0; blur_kerns[i]; ++i) {
- assert(i < MAX_BLUR_PASS - 1);
- xcb_render_fixed_t *convolution_blur = blur_kerns[i];
- int kwid = XFIXED_TO_DOUBLE(convolution_blur[0]),
- khei = XFIXED_TO_DOUBLE(convolution_blur[1]);
- bool rd_from_tgt = (tgt_buffer == src_pict);
-
- // Copy from source picture to destination. The filter must
- // be applied on source picture, to get the nearby pixels outside the
- // window.
- xcb_render_set_picture_filter(ps->c, src_pict, strlen(XRFILTER_CONVOLUTION), XRFILTER_CONVOLUTION,
- kwid * khei + 2, convolution_blur);
- xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, src_pict, None, dst_pict,
- (rd_from_tgt ? x: 0), (rd_from_tgt ? y: 0), 0, 0,
- (rd_from_tgt ? 0: x), (rd_from_tgt ? 0: y), wid, hei);
- xrfilter_reset(ps, src_pict);
-
- {
- xcb_render_picture_t tmp = src_pict;
- src_pict = dst_pict;
- dst_pict = tmp;
- }
- }
-
- if (src_pict != tgt_buffer)
- xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, src_pict, None, tgt_buffer,
- 0, 0, 0, 0, x, y, wid, hei);
-
- free_picture(ps, &tmp_picture);
-
- return true;
-}
-
/*
* WORK-IN-PROGRESS!
static void
@@ -1488,322 +865,6 @@ xr_take_screenshot(session_t *ps) {
}
*/
-/**
- * Blur the background of a window.
- */
-static inline void
-win_blur_background(session_t *ps, win *w, xcb_render_picture_t tgt_buffer,
- const region_t *reg_paint) {
- const int x = w->g.x;
- const int y = w->g.y;
- const int wid = w->widthb;
- const int hei = w->heightb;
-
- double factor_center = 1.0;
- // Adjust blur strength according to window opacity, to make it appear
- // better during fading
- if (!ps->o.blur_background_fixed) {
- double pct = 1.0 - get_opacity_percent(w) * (1.0 - 1.0 / 9.0);
- factor_center = pct * 8.0 / (1.1 - pct);
- }
-
- switch (ps->o.backend) {
- case BKEND_XRENDER:
- case BKEND_XR_GLX_HYBRID:
- {
- // Normalize blur kernels
- for (int i = 0; i < MAX_BLUR_PASS; ++i) {
- xcb_render_fixed_t *kern_src = ps->o.blur_kerns[i];
- xcb_render_fixed_t *kern_dst = ps->blur_kerns_cache[i];
- assert(i < MAX_BLUR_PASS);
- if (!kern_src) {
- assert(!kern_dst);
- break;
- }
-
- assert(!kern_dst
- || (kern_src[0] == kern_dst[0] && kern_src[1] == kern_dst[1]));
-
- // Skip for fixed factor_center if the cache exists already
- if (ps->o.blur_background_fixed && kern_dst) continue;
-
- int kwid = XFIXED_TO_DOUBLE(kern_src[0]),
- khei = XFIXED_TO_DOUBLE(kern_src[1]);
-
- // Allocate cache space if needed
- if (!kern_dst) {
- kern_dst = ccalloc(kwid * khei + 2, xcb_render_fixed_t);
- if (!kern_dst) {
- printf_errf("(): Failed to allocate memory for blur kernel.");
- return;
- }
- ps->blur_kerns_cache[i] = kern_dst;
- }
-
- // Modify the factor of the center pixel
- kern_src[2 + (khei / 2) * kwid + kwid / 2] =
- DOUBLE_TO_XFIXED(factor_center);
-
- // Copy over
- memcpy(kern_dst, kern_src, (kwid * khei + 2) * sizeof(xcb_render_fixed_t));
- normalize_conv_kern(kwid, khei, kern_dst + 2);
- }
-
- // 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);
- }
- // Translate global coordinates to local ones
- pixman_region32_translate(®_blur, -x, -y);
- xr_blur_dst(ps, tgt_buffer, x, y, wid, hei, ps->blur_kerns_cache,
- ®_blur);
- pixman_region32_clear(®_blur);
- }
- break;
-#ifdef CONFIG_OPENGL
- case BKEND_GLX:
- // TODO: Handle frame opacity
- glx_blur_dst(ps, x, y, wid, hei, ps->psglx->z - 0.5, factor_center,
- reg_paint, &w->glx_blur_cache);
- break;
-#endif
- default:
- assert(0);
- }
-}
-
-static void
-render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei,
- double opacity, bool argb, bool neg,
- xcb_render_picture_t pict, glx_texture_t *ptex,
- const region_t *reg_paint, const glx_prog_main_t *pprogram)
-{
- switch (ps->o.backend) {
- case BKEND_XRENDER:
- case BKEND_XR_GLX_HYBRID:
- {
- int alpha_step = opacity * MAX_ALPHA;
- xcb_render_picture_t alpha_pict = ps->alpha_picts[alpha_step];
- if (alpha_step != 0) {
- int op = ((!argb && !alpha_pict) ? XCB_RENDER_PICT_OP_SRC: XCB_RENDER_PICT_OP_OVER);
- xcb_render_composite(ps->c, op, pict, alpha_pict,
- ps->tgt_buffer.pict, x, y, 0, 0, dx, dy, wid, hei);
- }
- break;
- }
-#ifdef CONFIG_OPENGL
- case BKEND_GLX:
- glx_render(ps, ptex, x, y, dx, dy, wid, hei,
- ps->psglx->z, opacity, argb, neg, reg_paint, pprogram);
- ps->psglx->z += 1;
- break;
-#endif
- default:
- assert(0);
- }
-}
-
-/**
- * Paint a window itself and dim it if asked.
- */
-static inline void
-paint_one(session_t *ps, win *w, const region_t *reg_paint) {
- glx_mark(ps, w->id, true);
-
- // Fetch Pixmap
- if (!w->paint.pixmap && ps->has_name_pixmap) {
- w->paint.pixmap = xcb_generate_id(ps->c);
- set_ignore_cookie(ps,
- xcb_composite_name_window_pixmap(ps->c, w->id, w->paint.pixmap));
- if (w->paint.pixmap)
- free_fence(ps, &w->fence);
- }
-
- Drawable draw = w->paint.pixmap;
- if (!draw)
- draw = w->id;
-
- // XRender: Build picture
- if (bkend_use_xrender(ps) && !w->paint.pict) {
- xcb_render_create_picture_value_list_t pa = {
- .subwindowmode = IncludeInferiors,
- };
-
- w->paint.pict = x_create_picture_with_pictfmt_and_pixmap(ps, w->pictfmt,
- draw, XCB_RENDER_CP_SUBWINDOW_MODE, &pa);
- }
-
- if (IsViewable == w->a.map_state)
- xr_sync(ps, draw, &w->fence);
-
- // GLX: Build texture
- // Let glx_bind_pixmap() determine pixmap size, because if the user
- // is resizing windows, the width and height we get may not be up-to-date,
- // causing the jittering issue M4he reported in #7.
- if (!paint_bind_tex(ps, &w->paint, 0, 0, 0,
- (!ps->o.glx_no_rebind_pixmap && w->pixmap_damaged))) {
- printf_errf("(%#010lx): Failed to bind texture. Expect troubles.", w->id);
- }
- w->pixmap_damaged = false;
-
- if (!paint_isvalid(ps, &w->paint)) {
- printf_errf("(%#010lx): Missing painting data. This is a bad sign.", w->id);
- return;
- }
-
- const int x = w->g.x;
- const int y = w->g.y;
- const int wid = w->widthb;
- const int hei = w->heightb;
-
- xcb_render_picture_t pict = w->paint.pict;
-
- // Invert window color, if required
- if (bkend_use_xrender(ps) && w->invert_color) {
- xcb_render_picture_t newpict = x_create_picture(ps, wid, hei, w->pictfmt, 0, NULL);
- if (newpict) {
- // Apply clipping region to save some CPU
- if (reg_paint) {
- region_t reg;
- pixman_region32_init(®);
- pixman_region32_copy(®, (region_t *)reg_paint);
- pixman_region32_translate(®, -x, -y);
- // FIXME XFixesSetPictureClipRegion(ps->dpy, newpict, 0, 0, reg);
- pixman_region32_fini(®);
- }
-
- xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, pict, None,
- newpict, 0, 0, 0, 0, 0, 0, wid, hei);
- xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_DIFFERENCE, ps->white_picture, None,
- newpict, 0, 0, 0, 0, 0, 0, wid, hei);
- // 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, pict, None,
- newpict, 0, 0, 0, 0, 0, 0, wid, hei);
- pict = newpict;
- }
- }
-
- const double dopacity = get_opacity_percent(w);
-
- if (w->frame_opacity == 1) {
- paint_region(ps, w, 0, 0, wid, hei, dopacity, reg_paint, pict);
- } else {
- // Painting parameters
- const margin_t extents = win_calc_frame_extents(w);
- const int t = extents.top;
- const int l = extents.left;
- const int b = extents.bottom;
- const int r = extents.right;
-
-#define COMP_BDR(cx, cy, cwid, chei) \
-paint_region(ps, w, (cx), (cy), (cwid), (chei), w->frame_opacity * dopacity, \
- reg_paint, pict)
-
- // Sanitize the margins, in case some broken WM makes
- // top_width + bottom_width > height in some cases.
-
- do {
- // top
- int body_height = hei;
- // ctop = checked top
- int ctop = min_i(body_height, t); // Make sure top margin is smaller than height
- if (ctop > 0)
- COMP_BDR(0, 0, wid, ctop);
-
- body_height -= ctop;
- if (body_height <= 0)
- break;
-
- // bottom
- // cbot = checked bottom
- int cbot = min_i(body_height, b); // Make sure bottom margin is not too large
- if (cbot > 0)
- COMP_BDR(0, hei-cbot, wid, cbot);
-
- body_height -= cbot; // Height of window exclude the margin
- if (body_height <= 0)
- break;
-
- // left
- int body_width = wid;
- int cleft = min_i(body_width, l);
- if (cleft > 0)
- COMP_BDR(0, ctop, cleft, body_height);
-
- body_width -= cleft;
- if (body_width <= 0)
- break;
-
- // right
- int cright = min_i(body_width, r);
- if (cright > 0)
- COMP_BDR(wid - cright, ctop, cright, body_height);
-
- body_width -= cright;
- if (body_width <= 0)
- break;
-
- // body
- paint_region(ps, w, cleft, ctop, body_width, body_height, dopacity, reg_paint, pict);
- } while (0);
- }
-
-#undef COMP_BDR
-
- if (pict != w->paint.pict)
- free_picture(ps, &pict);
-
- // Dimming the window if needed
- if (w->dim) {
- double dim_opacity = ps->o.inactive_dim;
- if (!ps->o.inactive_dim_fixed)
- dim_opacity *= get_opacity_percent(w);
-
- switch (ps->o.backend) {
- case BKEND_XRENDER:
- case BKEND_XR_GLX_HYBRID:
- {
- unsigned short cval = 0xffff * dim_opacity;
-
- // Premultiply color
- xcb_render_color_t color = {
- .red = 0, .green = 0, .blue = 0, .alpha = cval,
- };
-
- xcb_rectangle_t rect = {
- .x = x,
- .y = y,
- .width = wid,
- .height = hei,
- };
-
- xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_OVER, ps->tgt_buffer.pict,
- color, 1, &rect);
- }
- break;
-#ifdef CONFIG_OPENGL
- case BKEND_GLX:
- glx_dim_dst(ps, x, y, wid, hei, ps->psglx->z - 0.7, dim_opacity,
- reg_paint);
- break;
-#endif
- default:
- assert(false);
- }
- }
-
- glx_mark(ps, w->id, false);
-}
-
/**
* Rebuild cached screen_reg
.
*/
@@ -1823,267 +884,6 @@ rebuild_shadow_exclude_reg(session_t *ps) {
exit(1);
}
-/// paint all windows
-/// region = ??
-/// region_real = the damage region
-static void
-paint_all(session_t *ps, region_t *region, const region_t *region_real, win * const t) {
- if (!region_real)
- region_real = region;
-
-#ifdef DEBUG_REPAINT
- static struct timespec last_paint = { 0 };
-#endif
-
- if (!region)
- region_real = region = &ps->screen_reg;
- else
- // Remove the damaged area out of screen
- pixman_region32_intersect(region, region, &ps->screen_reg);
-
-#ifdef CONFIG_OPENGL
- if (bkend_use_glx(ps))
- glx_paint_pre(ps, region);
-#endif
-
- if (!paint_isvalid(ps, &ps->tgt_buffer)) {
- if (!ps->tgt_buffer.pixmap) {
- free_paint(ps, &ps->tgt_buffer);
- ps->tgt_buffer.pixmap = x_create_pixmap(ps, ps->depth, ps->root, ps->root_width, ps->root_height);
- if (ps->tgt_buffer.pixmap == XCB_NONE) {
- fprintf(stderr, "Failed to allocate a screen-sized pixmap\n");
- exit(1);
- }
- }
-
- if (BKEND_GLX != ps->o.backend)
- ps->tgt_buffer.pict = x_create_picture_with_visual_and_pixmap(
- ps, ps->vis, ps->tgt_buffer.pixmap, 0, 0);
- }
-
- if (BKEND_XRENDER == ps->o.backend) {
- x_set_picture_clip_region(ps, ps->tgt_picture, 0, 0, region_real);
- }
-
- region_t reg_tmp, *reg_paint;
- pixman_region32_init(®_tmp);
- if (t) {
- // Calculate the region upon which the root window is to be painted
- // based on the ignore region of the lowest window, if available
- pixman_region32_subtract(®_tmp, region, t->reg_ignore);
- reg_paint = ®_tmp;
- } else {
- reg_paint = region;
- }
-
- set_tgt_clip(ps, reg_paint);
- paint_root(ps, reg_paint);
-
- // Windows are sorted from bottom to top
- // Each window has a reg_ignore, which is the region obscured by all the windows
- // on top of that window. This is used to reduce the number of pixels painted.
- //
- // Whether this is beneficial is to be determined XXX
- for (win *w = t; w; w = w->prev_trans) {
- region_t bshape = win_get_bounding_shape_global_by_val(w);
- // Painting shadow
- if (w->shadow) {
- // Lazy shadow building
- if (!w->shadow_paint.pixmap)
- if (!win_build_shadow(ps, w, 1))
- printf_errf("(): build shadow failed");
-
- // Shadow doesn't need to be painted underneath the body of the window
- // Because no one can see it
- pixman_region32_subtract(®_tmp, region, 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);
-
- // Mask out the body of the window from the shadow if needed
- // Doing it here instead of in make_shadow() for saving GPU
- // power and handling shaped windows (XXX unconfirmed)
- if (!ps->o.wintype_option[w->window_type].full_shadow)
- pixman_region32_subtract(®_tmp, ®_tmp, &bshape);
-
-#ifdef CONFIG_XINERAMA
- 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]);
-#endif
-
- // Detect if the region is empty before painting
- if (pixman_region32_not_empty(®_tmp)) {
- set_tgt_clip(ps, ®_tmp);
- win_paint_shadow(ps, w, ®_tmp);
- }
- }
-
- // Calculate the region based on the reg_ignore of the next (higher)
- // window and the bounding region
- // XXX XXX
- pixman_region32_subtract(®_tmp, region, w->reg_ignore);
- pixman_region32_intersect(®_tmp, ®_tmp, &bshape);
- pixman_region32_fini(&bshape);
-
- if (pixman_region32_not_empty(®_tmp)) {
- set_tgt_clip(ps, ®_tmp);
- // Blur window background
- if (w->blur_background && (!win_is_solid(ps, w)
- || (ps->o.blur_background_frame && w->frame_opacity != 1)))
- win_blur_background(ps, w, ps->tgt_buffer.pict, ®_tmp);
-
- // Painting the window
- paint_one(ps, w, ®_tmp);
- }
- }
-
- // Free up all temporary regions
- pixman_region32_fini(®_tmp);
-
- // Do this as early as possible
- set_tgt_clip(ps, &ps->screen_reg);
-
- if (ps->o.vsync) {
- // Make sure all previous requests are processed to achieve best
- // effect
- x_sync(ps->c);
-#ifdef CONFIG_OPENGL
- if (glx_has_context(ps)) {
- if (ps->o.vsync_use_glfinish)
- glFinish();
- else
- glFlush();
- glXWaitX();
- }
-#endif
- }
-
- // Wait for VBlank. We could do it aggressively (send the painting
- // request and XFlush() on VBlank) or conservatively (send the request
- // only on VBlank).
- if (!ps->o.vsync_aggressive)
- vsync_wait(ps);
-
- switch (ps->o.backend) {
- case BKEND_XRENDER:
- if (ps->o.monitor_repaint) {
- // Copy the screen content to a new picture, and highlight the paint
- // region. This is not very efficient, but since it's for debug only,
- // we don't really care
-
- // First, we clear tgt_buffer.pict's clip region, since we want to copy
- // everything
- x_set_picture_clip_region(ps, ps->tgt_buffer.pict, 0, 0, &ps->screen_reg);
-
- // Then we create a new picture, and copy content to it
- xcb_render_pictforminfo_t *pictfmt = x_get_pictform_for_visual(ps, ps->vis);
- xcb_render_picture_t new_pict = x_create_picture(
- ps, ps->root_width, ps->root_height, pictfmt, 0, NULL);
- xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, ps->tgt_buffer.pict,
- None, new_pict, 0, 0, 0, 0, 0, 0, ps->root_width, ps->root_height);
-
- // Next, we set the region of paint and highlight it
- x_set_picture_clip_region(ps, new_pict, 0, 0, region_real);
- xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_OVER, ps->white_picture,
- ps->alpha_picts[MAX_ALPHA / 2],
- new_pict, 0, 0, 0, 0, 0, 0,
- ps->root_width, ps->root_height);
-
- // Finally, clear clip region and put the whole thing on screen
- x_set_picture_clip_region(ps, new_pict, 0, 0, &ps->screen_reg);
- xcb_render_composite(
- ps->c, XCB_RENDER_PICT_OP_SRC, new_pict, None,
- ps->tgt_picture, 0, 0, 0, 0,
- 0, 0, ps->root_width, ps->root_height);
- xcb_render_free_picture(ps->c, new_pict);
- } else
- xcb_render_composite(
- ps->c, XCB_RENDER_PICT_OP_SRC, ps->tgt_buffer.pict, None,
- ps->tgt_picture, 0, 0, 0, 0,
- 0, 0, ps->root_width, ps->root_height);
- break;
-#ifdef CONFIG_OPENGL
- case BKEND_XR_GLX_HYBRID:
- x_sync(ps->c);
- if (ps->o.vsync_use_glfinish)
- glFinish();
- else
- glFlush();
- glXWaitX();
- assert(ps->tgt_buffer.pixmap);
- xr_sync(ps, ps->tgt_buffer.pixmap, &ps->tgt_buffer_fence);
- paint_bind_tex(ps, &ps->tgt_buffer,
- ps->root_width, ps->root_height, ps->depth,
- !ps->o.glx_no_rebind_pixmap);
- // See #163
- xr_sync(ps, ps->tgt_buffer.pixmap, &ps->tgt_buffer_fence);
- if (ps->o.vsync_use_glfinish)
- glFinish();
- else
- glFlush();
- glXWaitX();
- glx_render(ps, ps->tgt_buffer.ptex, 0, 0, 0, 0,
- ps->root_width, ps->root_height, 0, 1.0, false, false,
- region_real, NULL);
- // falls through
- case BKEND_GLX:
- glXSwapBuffers(ps->dpy, get_tgt_window(ps));
- break;
-#endif
- default:
- assert(0);
- }
- glx_mark_frame(ps);
-
- if (ps->o.vsync_aggressive)
- vsync_wait(ps);
-
- XFlush(ps->dpy);
-
-#ifdef CONFIG_OPENGL
- if (glx_has_context(ps)) {
- glFlush();
- glXWaitX();
- }
-#endif
-
-#ifdef DEBUG_REPAINT
- print_timestamp(ps);
- struct timespec now = get_time_timespec();
- struct timespec diff = { 0 };
- timespec_subtract(&diff, &now, &last_paint);
- printf("[ %5ld:%09ld ] ", diff.tv_sec, diff.tv_nsec);
- last_paint = now;
- printf("paint:");
- for (win *w = t; w; w = w->prev_trans)
- printf(" %#010lx", w->id);
- putchar('\n');
- fflush(stdout);
-#endif
-
- // Check if fading is finished on all painted windows
- {
- win *pprev = NULL;
- for (win *w = t; w; w = pprev) {
- pprev = w->prev_trans;
- check_fade_fin(ps, &w);
- }
- }
-}
-
static void
repair_win(session_t *ps, win *w) {
if (w->a.map_state != XCB_MAP_STATE_VIEWABLE)
@@ -2215,7 +1015,7 @@ map_win(session_t *ps, Window id) {
// Set fading state
w->in_openclose = true;
- set_fade_callback(ps, &w, finish_map_win, true);
+ win_set_fade_callback(ps, &w, finish_map_win, true);
win_determine_fade(ps, w);
win_determine_blur_background(ps, w);
@@ -2275,7 +1075,7 @@ unmap_win(session_t *ps, win **_w) {
// Fading out
w->flags |= WFLAG_OPCT_CHANGE;
- set_fade_callback(ps, _w, finish_unmap_win, false);
+ win_set_fade_callback(ps, _w, finish_unmap_win, false);
w->in_openclose = true;
win_determine_fade(ps, w);
@@ -2553,7 +1353,7 @@ destroy_win(session_t *ps, Window id) {
win_determine_fade(ps, w);
// Set fading callback
- set_fade_callback(ps, &w, finish_destroy_win, false);
+ win_set_fade_callback(ps, &w, finish_destroy_win, false);
#ifdef CONFIG_DBUS
// Send D-Bus signal
@@ -4412,258 +3212,6 @@ swopti_handle_timeout(session_t *ps) {
return (ps->refresh_intv - offset) / 1e6;
}
-/**
- * Initialize DRM VSync.
- *
- * @return true for success, false otherwise
- */
-static bool
-vsync_drm_init(session_t *ps) {
-#ifdef CONFIG_VSYNC_DRM
- // Should we always open card0?
- if (ps->drm_fd < 0 && (ps->drm_fd = open("/dev/dri/card0", O_RDWR)) < 0) {
- printf_errf("(): Failed to open device.");
- return false;
- }
-
- if (vsync_drm_wait(ps))
- return false;
-
- return true;
-#else
- printf_errf("(): Program not compiled with DRM VSync support.");
- return false;
-#endif
-}
-
-#ifdef CONFIG_VSYNC_DRM
-/**
- * Wait for next VSync, DRM method.
- *
- * Stolen from: https://github.com/MythTV/mythtv/blob/master/mythtv/libs/libmythtv/vsync.cpp
- */
-static int
-vsync_drm_wait(session_t *ps) {
- int ret = -1;
- drm_wait_vblank_t vbl;
-
- vbl.request.type = _DRM_VBLANK_RELATIVE,
- vbl.request.sequence = 1;
-
- do {
- ret = ioctl(ps->drm_fd, DRM_IOCTL_WAIT_VBLANK, &vbl);
- vbl.request.type &= ~_DRM_VBLANK_RELATIVE;
- } while (ret && errno == EINTR);
-
- if (ret)
- fprintf(stderr, "vsync_drm_wait(): VBlank ioctl did not work, "
- "unimplemented in this drmver?\n");
-
- return ret;
-
-}
-#endif
-
-/**
- * Initialize OpenGL VSync.
- *
- * Stolen from: http://git.tuxfamily.org/?p=ccm/cairocompmgr.git;a=commitdiff;h=efa4ceb97da501e8630ca7f12c99b1dce853c73e
- * Possible original source: http://www.inb.uni-luebeck.de/~boehme/xvideo_sync.html
- *
- * @return true for success, false otherwise
- */
-static bool
-vsync_opengl_init(session_t *ps) {
-#ifdef CONFIG_OPENGL
- if (!ensure_glx_context(ps))
- return false;
-
- if (!glx_hasglxext(ps, "GLX_SGI_video_sync")) {
- printf_errf("(): Your driver doesn't support SGI_video_sync, giving up.");
- return false;
- }
-
- // Get video sync functions
- if (!ps->psglx->glXGetVideoSyncSGI)
- ps->psglx->glXGetVideoSyncSGI = (f_GetVideoSync)
- glXGetProcAddress((const GLubyte *) "glXGetVideoSyncSGI");
- if (!ps->psglx->glXWaitVideoSyncSGI)
- ps->psglx->glXWaitVideoSyncSGI = (f_WaitVideoSync)
- glXGetProcAddress((const GLubyte *) "glXWaitVideoSyncSGI");
- if (!ps->psglx->glXWaitVideoSyncSGI || !ps->psglx->glXGetVideoSyncSGI) {
- printf_errf("(): Failed to get glXWait/GetVideoSyncSGI function.");
- return false;
- }
-
- return true;
-#else
- printf_errf("(): Program not compiled with OpenGL VSync support.");
- return false;
-#endif
-}
-
-static bool
-vsync_opengl_oml_init(session_t *ps) {
-#ifdef CONFIG_OPENGL
- if (!ensure_glx_context(ps))
- return false;
-
- if (!glx_hasglxext(ps, "GLX_OML_sync_control")) {
- printf_errf("(): Your driver doesn't support OML_sync_control, giving up.");
- return false;
- }
-
- // Get video sync functions
- if (!ps->psglx->glXGetSyncValuesOML)
- ps->psglx->glXGetSyncValuesOML = (f_GetSyncValuesOML)
- glXGetProcAddress ((const GLubyte *) "glXGetSyncValuesOML");
- if (!ps->psglx->glXWaitForMscOML)
- ps->psglx->glXWaitForMscOML = (f_WaitForMscOML)
- glXGetProcAddress ((const GLubyte *) "glXWaitForMscOML");
- if (!ps->psglx->glXGetSyncValuesOML || !ps->psglx->glXWaitForMscOML) {
- printf_errf("(): Failed to get OML_sync_control functions.");
- return false;
- }
-
- return true;
-#else
- printf_errf("(): Program not compiled with OpenGL VSync support.");
- return false;
-#endif
-}
-
-static bool
-vsync_opengl_swc_swap_interval(session_t *ps, unsigned int interval) {
-#ifdef CONFIG_OPENGL
- if (!ensure_glx_context(ps))
- return false;
-
- if (!ps->psglx->glXSwapIntervalProc && !ps->psglx->glXSwapIntervalMESAProc) {
- if (glx_hasglxext(ps, "GLX_MESA_swap_control")) {
- ps->psglx->glXSwapIntervalMESAProc = (f_SwapIntervalMESA)
- glXGetProcAddress ((const GLubyte *) "glXSwapIntervalMESA");
- } else if (glx_hasglxext(ps, "GLX_SGI_swap_control")) {
- ps->psglx->glXSwapIntervalProc = (f_SwapIntervalSGI)
- glXGetProcAddress ((const GLubyte *) "glXSwapIntervalSGI");
- } else {
- printf_errf("(): Your driver doesn't support SGI_swap_control nor MESA_swap_control, giving up.");
- return false;
- }
- }
-
- if (ps->psglx->glXSwapIntervalMESAProc)
- ps->psglx->glXSwapIntervalMESAProc(interval);
- else if (ps->psglx->glXSwapIntervalProc)
- ps->psglx->glXSwapIntervalProc(interval);
- else
- return false;
-#endif
-
- return true;
-}
-
-static bool
-vsync_opengl_swc_init(session_t *ps) {
-#ifdef CONFIG_OPENGL
- if (!bkend_use_glx(ps)) {
- printf_errf("(): OpenGL swap control requires the GLX backend.");
- return false;
- }
-
- if (!vsync_opengl_swc_swap_interval(ps, 1)) {
- printf_errf("(): Failed to load a swap control extension.");
- return false;
- }
-
- return true;
-#else
- printf_errf("(): Program not compiled with OpenGL VSync support.");
- return false;
-#endif
-}
-
-static bool
-vsync_opengl_mswc_init(session_t *ps) {
- printf_errf("(): opengl-mswc is deprecated, please use opengl-swc instead.");
- return vsync_opengl_swc_init(ps);
-}
-
-#ifdef CONFIG_OPENGL
-/**
- * Wait for next VSync, OpenGL method.
- */
-static int
-vsync_opengl_wait(session_t *ps) {
- unsigned vblank_count = 0;
-
- ps->psglx->glXGetVideoSyncSGI(&vblank_count);
- ps->psglx->glXWaitVideoSyncSGI(2, (vblank_count + 1) % 2, &vblank_count);
- // I see some code calling glXSwapIntervalSGI(1) afterwards, is it required?
-
- return 0;
-}
-
-/**
- * Wait for next VSync, OpenGL OML method.
- *
- * https://mail.gnome.org/archives/clutter-list/2012-November/msg00031.html
- */
-static int
-vsync_opengl_oml_wait(session_t *ps) {
- int64_t ust = 0, msc = 0, sbc = 0;
-
- ps->psglx->glXGetSyncValuesOML(ps->dpy, ps->reg_win, &ust, &msc, &sbc);
- ps->psglx->glXWaitForMscOML(ps->dpy, ps->reg_win, 0, 2, (msc + 1) % 2,
- &ust, &msc, &sbc);
-
- return 0;
-}
-
-static void
-vsync_opengl_swc_deinit(session_t *ps) {
- vsync_opengl_swc_swap_interval(ps, 0);
-}
-#endif
-
-/**
- * Initialize current VSync method.
- */
-bool
-vsync_init(session_t *ps) {
- // Mesa turns on swap control by default, undo that
- if (bkend_use_glx(ps))
- vsync_opengl_swc_swap_interval(ps, 0);
-
- if (ps->o.vsync && VSYNC_FUNCS_INIT[ps->o.vsync]
- && !VSYNC_FUNCS_INIT[ps->o.vsync](ps)) {
- ps->o.vsync = VSYNC_NONE;
- return false;
- }
- else
- return true;
-}
-
-/**
- * Wait for next VSync.
- */
-static void
-vsync_wait(session_t *ps) {
- if (!ps->o.vsync)
- return;
-
- if (VSYNC_FUNCS_WAIT[ps->o.vsync])
- VSYNC_FUNCS_WAIT[ps->o.vsync](ps);
-}
-
-/**
- * Deinitialize current VSync method.
- */
-void
-vsync_deinit(session_t *ps) {
- if (ps->o.vsync && VSYNC_FUNCS_DEINIT[ps->o.vsync])
- VSYNC_FUNCS_DEINIT[ps->o.vsync](ps);
-}
-
/**
* Pregenerate alpha pictures.
*/
@@ -5428,7 +3976,7 @@ session_init(session_t *ps_old, int argc, char **argv) {
init_atoms(ps);
init_alpha_picts(ps);
- ps->gaussian_map = make_gaussian_map(ps->o.shadow_radius);
+ ps->gaussian_map = gaussian_kernel(ps->o.shadow_radius);
presum_gaussian(ps, ps->gaussian_map);
{
@@ -5606,7 +4154,7 @@ session_destroy(session_t *ps) {
// Free alpha_picts
{
for (int i = 0; i <= MAX_ALPHA; ++i)
- free_picture(ps, &ps->alpha_picts[i]);
+ free_picture(ps->c, &ps->alpha_picts[i]);
free(ps->alpha_picts);
ps->alpha_picts = NULL;
}
@@ -5650,10 +4198,10 @@ session_destroy(session_t *ps) {
if (ps->cshadow_picture == ps->black_picture)
ps->cshadow_picture = None;
else
- free_picture(ps, &ps->cshadow_picture);
+ free_picture(ps->c, &ps->cshadow_picture);
- free_picture(ps, &ps->black_picture);
- free_picture(ps, &ps->white_picture);
+ free_picture(ps->c, &ps->black_picture);
+ free_picture(ps->c, &ps->white_picture);
// Free tgt_{buffer,picture} and root_picture
if (ps->tgt_buffer.pict == ps->tgt_picture)
@@ -5662,10 +4210,10 @@ session_destroy(session_t *ps) {
if (ps->tgt_picture == ps->root_picture)
ps->tgt_picture = None;
else
- free_picture(ps, &ps->tgt_picture);
+ free_picture(ps->c, &ps->tgt_picture);
free_fence(ps, &ps->tgt_buffer_fence);
- free_picture(ps, &ps->root_picture);
+ free_picture(ps->c, &ps->root_picture);
free_paint(ps, &ps->tgt_buffer);
// Free other X resources
diff --git a/src/compton.h b/src/compton.h
index 62e68d4..db66bef 100644
--- a/src/compton.h
+++ b/src/compton.h
@@ -34,6 +34,7 @@
#include "x.h"
#include "c2.h"
#include "log.h" // XXX clean up
+#include "render.h"
// == Functions ==
// TODO move static inline functions that are only used in compton.c, into
@@ -53,16 +54,6 @@ win *find_toplevel2(session_t *ps, Window wid);
void map_win(session_t *ps, Window id);
-/**
- * Reset filter on a Picture
.
- */
-static inline void
-xrfilter_reset(session_t *ps, xcb_render_picture_t p) {
-#define FILTER "Nearest"
- xcb_render_set_picture_filter(ps->c, p, strlen(FILTER), FILTER, 0, NULL);
-#undef FILTER
-}
-
/**
* Subtract two unsigned long values.
*
@@ -103,17 +94,6 @@ array_wid_exists(const Window *arr, int count, Window wid) {
return false;
}
-/**
- * Destroy a Picture
.
- */
-inline static void
-free_picture(session_t *ps, xcb_render_picture_t *p) {
- if (*p) {
- xcb_render_free_picture(ps->c, *p);
- *p = None;
- }
-}
-
/**
* Destroy a condition list.
*/
@@ -123,29 +103,7 @@ free_wincondlst(c2_lptr_t **pcondlst) {
continue;
}
-#ifdef CONFIG_OPENGL
-/**
- * Bind texture in paint_t if we are using GLX backend.
- */
-static inline bool
-paint_bind_tex(session_t *ps, paint_t *ppaint,
- unsigned wid, unsigned hei, unsigned depth, bool force)
-{
- if (!ppaint->pixmap)
- return false;
-
- if (force || !glx_tex_binded(ppaint->ptex, ppaint->pixmap))
- return glx_bind_pixmap(ps, &ppaint->ptex, ppaint->pixmap, wid, hei, depth);
-
- return true;
-}
-#else
-static inline bool
-paint_bind_tex(session_t *ps, paint_t *ppaint,
- unsigned wid, unsigned hei, unsigned depth, bool force)
-{
- return true;
-}
+#ifndef CONFIG_OPENGL
static inline void
free_paint_glx(session_t *ps, paint_t *p) {}
static inline void
@@ -156,18 +114,6 @@ free_texture(session_t *ps, glx_texture_t **t) {
}
#endif
-/**
- * Free paint_t.
- */
-static inline void
-free_paint(session_t *ps, paint_t *ppaint) {
- free_paint_glx(ps, ppaint);
- free_picture(ps, &ppaint->pict);
- if (ppaint->pixmap)
- xcb_free_pixmap(ps->c, ppaint->pixmap);
- ppaint->pixmap = XCB_NONE;
-}
-
/**
* Create a XTextProperty of a single string.
*/
@@ -220,59 +166,14 @@ dump_drawable(session_t *ps, Drawable drawable) {
}
}
-
-/**
- * Validate a pixmap.
- *
- * Detect whether the pixmap is valid with XGetGeometry. Well, maybe there
- * are better ways.
- */
-static inline bool
-validate_pixmap(session_t *ps, xcb_pixmap_t pxmap) {
- if (!pxmap) return false;
-
- Window rroot = None;
- int rx = 0, ry = 0;
- unsigned rwid = 0, rhei = 0, rborder = 0, rdepth = 0;
- return XGetGeometry(ps->dpy, pxmap, &rroot, &rx, &ry,
- &rwid, &rhei, &rborder, &rdepth) && rwid && rhei;
-}
-
/**
* Validate pixmap of a window, and destroy pixmap and picture if invalid.
*/
static inline void
win_validate_pixmap(session_t *ps, win *w) {
// Destroy pixmap and picture, if invalid
- if (!validate_pixmap(ps, w->paint.pixmap))
+ if (!x_validate_pixmap(ps, w->paint.pixmap))
free_paint(ps, &w->paint);
}
-/**
- * Normalize a convolution kernel.
- */
-static inline void
-normalize_conv_kern(int wid, int hei, xcb_render_fixed_t *kern) {
- double sum = 0.0;
- for (int i = 0; i < wid * hei; ++i)
- sum += XFIXED_TO_DOUBLE(kern[i]);
- double factor = 1.0 / sum;
- for (int i = 0; i < wid * hei; ++i)
- kern[i] = DOUBLE_TO_XFIXED(XFIXED_TO_DOUBLE(kern[i]) * factor);
-}
-
-#ifdef CONFIG_OPENGL
-/**
- * Ensure we have a GLX context.
- */
-static inline bool
-ensure_glx_context(session_t *ps) {
- // Create GLX context
- if (!glx_has_context(ps))
- glx_init(ps, false);
-
- return ps->psglx->context;
-}
-#endif
-
// vim: set et sw=2 :
diff --git a/src/kernel.c b/src/kernel.c
new file mode 100644
index 0000000..5c955ec
--- /dev/null
+++ b/src/kernel.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: MPL-2.0
+// Copyright (c) Yuxuan Shui
+
+#include
+
+#include "utils.h"
+#include "kernel.h"
+
+/*
+ * A picture will help
+ *
+ * -center 0 width width+center
+ * -center +-----+-------------------+-----+
+ * | | | |
+ * | | | |
+ * 0 +-----+-------------------+-----+
+ * | | | |
+ * | | | |
+ * | | | |
+ * height +-----+-------------------+-----+
+ * | | | |
+ * height+ | | | |
+ * center +-----+-------------------+-----+
+ */
+
+double
+sum_kernel(conv *map, int x, int y, int width, int height) {
+ int fx, fy;
+ double *g_data;
+ double *g_line = map->data;
+ int g_size = map->size;
+ int center = g_size / 2;
+ int fx_start, fx_end;
+ int fy_start, fy_end;
+ double v;
+
+ /*
+ * Compute set of filter values which are "in range",
+ * that's the set with:
+ * 0 <= x + (fx-center) && x + (fx-center) < width &&
+ * 0 <= y + (fy-center) && y + (fy-center) < height
+ *
+ * 0 <= x + (fx - center) x + fx - center < width
+ * center - x <= fx fx < width + center - x
+ */
+
+ fx_start = center - x;
+ if (fx_start < 0)
+ fx_start = 0;
+ fx_end = width + center - x;
+ if (fx_end > g_size)
+ fx_end = g_size;
+
+ fy_start = center - y;
+ if (fy_start < 0)
+ fy_start = 0;
+ fy_end = height + center - y;
+ if (fy_end > g_size)
+ fy_end = g_size;
+
+ g_line = g_line + fy_start * g_size + fx_start;
+
+ v = 0;
+
+ for (fy = fy_start; fy < fy_end; fy++) {
+ g_data = g_line;
+ g_line += g_size;
+
+ for (fx = fx_start; fx < fx_end; fx++) {
+ v += *g_data++;
+ }
+ }
+
+ if (v > 1)
+ v = 1;
+
+ return v;
+}
+
+static double __attribute__((const)) gaussian(double r, double x, double y) {
+ // Formula can be found here:
+ // https://en.wikipedia.org/wiki/Gaussian_blur#Mathematics
+ // Except a special case for r == 0 to produce sharp shadows
+ if (r == 0)
+ return 1;
+ return exp(-0.5 * (x * x + y * y) / (r * r)) / (2 * M_PI * r * r);
+}
+
+conv *gaussian_kernel(double r) {
+ conv *c;
+ int size = r * 2 + 1;
+ int center = size / 2;
+ double t;
+
+ c = cvalloc(sizeof(conv) + size * size * sizeof(double));
+ c->size = size;
+ t = 0.0;
+
+ /*printf_errf("(): %f", r);*/
+ for (int y = 0; y < size; y++) {
+ for (int x = 0; x < size; x++) {
+ double g = gaussian(r, x - center, y - center);
+ t += g;
+ c->data[y * size + x] = g;
+ /*printf("%f ", c->data[y*size+x]);*/
+ }
+ /*printf("\n");*/
+ }
+
+ for (int y = 0; y < size; y++) {
+ for (int x = 0; x < size; x++) {
+ c->data[y * size + x] /= t;
+ /*printf("%f ", c->data[y*size+x]);*/
+ }
+ /*printf("\n");*/
+ }
+
+ return c;
+}
+
+// vim: set noet sw=8 ts=8 :
diff --git a/src/kernel.h b/src/kernel.h
new file mode 100644
index 0000000..7881a1e
--- /dev/null
+++ b/src/kernel.h
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: MPL-2.0
+// Copyright (c) Yuxuan Shui
+
+#pragma once
+
+/// Code for generating convolution kernels
+
+typedef struct conv {
+ int size;
+ double data[];
+} conv;
+
+/// Calculate the sum of a rectangle part of the convolution kernel
+/// the rectangle is defined by top left (x, y), and a size (width x height)
+double
+sum_kernel(conv *map, int x, int y, int width, int height);
+
+/// Create a kernel with gaussian distribution of radius r
+conv *gaussian_kernel(double r);
diff --git a/src/meson.build b/src/meson.build
index 58f323e..5404cf3 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -4,7 +4,8 @@ deps = [
dependency('xcb', version: '>=1.9.2'),
]
-srcs = ['compton.c', 'win.c', 'c2.c', 'x.c', 'config.c', 'diagnostic.c', 'string_utils.c']
+srcs = [ files('compton.c', 'win.c', 'c2.c', 'x.c', 'config.c', 'vsync.c',
+ 'diagnostic.c', 'string_utils.c', 'render.c', 'kernel.c')]
cflags = []
diff --git a/src/opengl.h b/src/opengl.h
index 326b5e8..e6ad57d 100644
--- a/src/opengl.h
+++ b/src/opengl.h
@@ -199,6 +199,70 @@ glx_create_program_from_str(const char *vert_shader_str,
unsigned char *
glx_take_screenshot(session_t *ps, int *out_length);
+/**
+ * Check if there's a GLX context.
+ */
+static inline bool
+glx_has_context(session_t *ps) {
+ return ps->psglx && ps->psglx->context;
+}
+
+/**
+ * Ensure we have a GLX context.
+ */
+static inline bool
+ensure_glx_context(session_t *ps) {
+ // Create GLX context
+ if (!glx_has_context(ps))
+ glx_init(ps, false);
+
+ return ps->psglx->context;
+}
+
+/**
+ * Free a GLX texture.
+ */
+static inline void
+free_texture_r(session_t *ps, GLuint *ptexture) {
+ if (*ptexture) {
+ assert(glx_has_context(ps));
+ glDeleteTextures(1, ptexture);
+ *ptexture = 0;
+ }
+}
+
+/**
+ * Free a GLX Framebuffer object.
+ */
+static inline void
+free_glx_fbo(session_t *ps, GLuint *pfbo) {
+ if (*pfbo) {
+ glDeleteFramebuffers(1, pfbo);
+ *pfbo = 0;
+ }
+ assert(!*pfbo);
+}
+
+/**
+ * Free data in glx_blur_cache_t on resize.
+ */
+static inline void
+free_glx_bc_resize(session_t *ps, glx_blur_cache_t *pbc) {
+ free_texture_r(ps, &pbc->textures[0]);
+ free_texture_r(ps, &pbc->textures[1]);
+ pbc->width = 0;
+ pbc->height = 0;
+}
+
+/**
+ * Free a glx_blur_cache_t
+ */
+static inline void
+free_glx_bc(session_t *ps, glx_blur_cache_t *pbc) {
+ free_glx_fbo(ps, &pbc->fbo);
+ free_glx_bc_resize(ps, pbc);
+}
+
/**
* Free a glx_texture_t.
*/
diff --git a/src/render.c b/src/render.c
new file mode 100644
index 0000000..3ff4186
--- /dev/null
+++ b/src/render.c
@@ -0,0 +1,1090 @@
+// SPDX-License-Identifier: MPL-2.0
+// Copyright (c) Yuxuan Shui
+
+#include
+
+#include "common.h"
+#include "opengl.h"
+#include "vsync.h"
+#include "win.h"
+
+#include "render.h"
+
+#ifdef CONFIG_OPENGL
+/**
+ * Bind texture in paint_t if we are using GLX backend.
+ */
+static inline bool paint_bind_tex(session_t *ps, paint_t *ppaint, unsigned wid,
+ unsigned hei, unsigned depth, bool force) {
+ if (!ppaint->pixmap)
+ return false;
+
+ if (force || !glx_tex_binded(ppaint->ptex, ppaint->pixmap))
+ return glx_bind_pixmap(ps, &ppaint->ptex, ppaint->pixmap, wid, hei,
+ depth);
+
+ return true;
+}
+#else
+static inline bool paint_bind_tex(session_t *ps, paint_t *ppaint, unsigned wid,
+ unsigned hei, unsigned depth, bool force) {
+ return true;
+}
+#endif
+
+/**
+ * Check if current backend uses XRender for rendering.
+ */
+static inline bool bkend_use_xrender(session_t *ps) {
+ return BKEND_XRENDER == ps->o.backend || BKEND_XR_GLX_HYBRID == ps->o.backend;
+}
+
+/**
+ * Reset filter on a Picture
.
+ */
+static inline void xrfilter_reset(session_t *ps, xcb_render_picture_t p) {
+#define FILTER "Nearest"
+ xcb_render_set_picture_filter(ps->c, p, strlen(FILTER), FILTER, 0, NULL);
+#undef FILTER
+}
+
+static inline void __attribute__((nonnull(1, 2)))
+set_tgt_clip(session_t *ps, region_t *reg) {
+ switch (ps->o.backend) {
+ case BKEND_XRENDER:
+ case BKEND_XR_GLX_HYBRID:
+ x_set_picture_clip_region(ps, ps->tgt_buffer.pict, 0, 0, reg);
+ break;
+#ifdef CONFIG_OPENGL
+ case BKEND_GLX: glx_set_clip(ps, reg); break;
+#endif
+ default: assert(false);
+ }
+}
+
+/**
+ * Destroy a Picture
.
+ */
+void free_picture(xcb_connection_t *c, xcb_render_picture_t *p) {
+ if (*p) {
+ xcb_render_free_picture(c, *p);
+ *p = None;
+ }
+}
+
+/**
+ * Free paint_t.
+ */
+void free_paint(session_t *ps, paint_t *ppaint) {
+ free_paint_glx(ps, ppaint);
+ free_picture(ps->c, &ppaint->pict);
+ if (ppaint->pixmap)
+ xcb_free_pixmap(ps->c, ppaint->pixmap);
+ ppaint->pixmap = XCB_NONE;
+}
+
+void render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei,
+ double opacity, bool argb, bool neg, xcb_render_picture_t pict,
+ glx_texture_t *ptex, const region_t *reg_paint,
+ const glx_prog_main_t *pprogram) {
+ switch (ps->o.backend) {
+ case BKEND_XRENDER:
+ case BKEND_XR_GLX_HYBRID: {
+ int alpha_step = opacity * MAX_ALPHA;
+ xcb_render_picture_t alpha_pict = ps->alpha_picts[alpha_step];
+ if (alpha_step != 0) {
+ int op = ((!argb && !alpha_pict) ? XCB_RENDER_PICT_OP_SRC
+ : XCB_RENDER_PICT_OP_OVER);
+ xcb_render_composite(ps->c, op, pict, alpha_pict,
+ ps->tgt_buffer.pict, x, y, 0, 0, dx, dy,
+ wid, hei);
+ }
+ break;
+ }
+#ifdef CONFIG_OPENGL
+ case BKEND_GLX:
+ glx_render(ps, ptex, x, y, dx, dy, wid, hei, ps->psglx->z, opacity,
+ argb, neg, reg_paint, pprogram);
+ ps->psglx->z += 1;
+ break;
+#endif
+ default: assert(0);
+ }
+}
+
+static inline void
+paint_region(session_t *ps, win *w, int x, int y, int wid, int hei, double opacity,
+ const region_t *reg_paint, xcb_render_picture_t pict) {
+ const int dx = (w ? w->g.x : 0) + x;
+ const int dy = (w ? w->g.y : 0) + y;
+ const bool argb = (w && (win_has_alpha(w) || ps->o.force_win_blend));
+ const bool neg = (w && w->invert_color);
+
+ render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, pict,
+ (w ? w->paint.ptex : ps->root_tile_paint.ptex), reg_paint,
+ (w ? &ps->o.glx_prog_win : NULL));
+}
+
+/**
+ * Check whether a paint_t contains enough data.
+ */
+static inline bool paint_isvalid(session_t *ps, const paint_t *ppaint) {
+ // Don't check for presence of Pixmap here, because older X Composite doesn't
+ // provide it
+ if (!ppaint)
+ return false;
+
+ if (bkend_use_xrender(ps) && !ppaint->pict)
+ return false;
+
+#ifdef CONFIG_OPENGL
+ if (BKEND_GLX == ps->o.backend && !glx_tex_binded(ppaint->ptex, None))
+ return false;
+#endif
+
+ return true;
+}
+
+/**
+ * Paint a window itself and dim it if asked.
+ */
+void paint_one(session_t *ps, win *w, const region_t *reg_paint) {
+ glx_mark(ps, w->id, true);
+
+ // Fetch Pixmap
+ if (!w->paint.pixmap && ps->has_name_pixmap) {
+ w->paint.pixmap = xcb_generate_id(ps->c);
+ set_ignore_cookie(ps, xcb_composite_name_window_pixmap(
+ ps->c, w->id, w->paint.pixmap));
+ if (w->paint.pixmap)
+ free_fence(ps, &w->fence);
+ }
+
+ Drawable draw = w->paint.pixmap;
+ if (!draw)
+ draw = w->id;
+
+ // XRender: Build picture
+ if (bkend_use_xrender(ps) && !w->paint.pict) {
+ xcb_render_create_picture_value_list_t pa = {
+ .subwindowmode = IncludeInferiors,
+ };
+
+ w->paint.pict = x_create_picture_with_pictfmt_and_pixmap(
+ ps, w->pictfmt, draw, XCB_RENDER_CP_SUBWINDOW_MODE, &pa);
+ }
+
+ if (IsViewable == w->a.map_state)
+ xr_sync(ps, draw, &w->fence);
+
+ // GLX: Build texture
+ // Let glx_bind_pixmap() determine pixmap size, because if the user
+ // is resizing windows, the width and height we get may not be up-to-date,
+ // causing the jittering issue M4he reported in #7.
+ if (!paint_bind_tex(ps, &w->paint, 0, 0, 0,
+ (!ps->o.glx_no_rebind_pixmap && w->pixmap_damaged))) {
+ printf_errf("(%#010lx): Failed to bind texture. Expect troubles.",
+ w->id);
+ }
+ w->pixmap_damaged = false;
+
+ if (!paint_isvalid(ps, &w->paint)) {
+ printf_errf("(%#010lx): Missing painting data. This is a bad sign.",
+ w->id);
+ return;
+ }
+
+ const int x = w->g.x;
+ const int y = w->g.y;
+ const int wid = w->widthb;
+ const int hei = w->heightb;
+
+ xcb_render_picture_t pict = w->paint.pict;
+
+ // Invert window color, if required
+ if (bkend_use_xrender(ps) && w->invert_color) {
+ xcb_render_picture_t newpict =
+ x_create_picture(ps, wid, hei, w->pictfmt, 0, NULL);
+ if (newpict) {
+ // Apply clipping region to save some CPU
+ if (reg_paint) {
+ region_t reg;
+ pixman_region32_init(®);
+ pixman_region32_copy(®, (region_t *)reg_paint);
+ pixman_region32_translate(®, -x, -y);
+ // FIXME XFixesSetPictureClipRegion(ps->dpy, newpict, 0, 0, reg);
+ pixman_region32_fini(®);
+ }
+
+ xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, pict,
+ None, newpict, 0, 0, 0, 0, 0, 0, wid,
+ hei);
+ xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_DIFFERENCE,
+ ps->white_picture, None, newpict, 0, 0,
+ 0, 0, 0, 0, wid, hei);
+ // 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, pict, None,
+ newpict, 0, 0, 0, 0, 0, 0, wid, hei);
+ pict = newpict;
+ }
+ }
+
+ const double dopacity = get_opacity_percent(w);
+
+ if (w->frame_opacity == 1) {
+ paint_region(ps, w, 0, 0, wid, hei, dopacity, reg_paint, pict);
+ } else {
+ // Painting parameters
+ const margin_t extents = win_calc_frame_extents(w);
+ const int t = extents.top;
+ const int l = extents.left;
+ const int b = extents.bottom;
+ const int r = extents.right;
+
+#define COMP_BDR(cx, cy, cwid, chei) \
+ paint_region(ps, w, (cx), (cy), (cwid), (chei), w->frame_opacity *dopacity, \
+ reg_paint, pict)
+
+ // Sanitize the margins, in case some broken WM makes
+ // top_width + bottom_width > height in some cases.
+
+ do {
+ // top
+ int body_height = hei;
+ // ctop = checked top
+ int ctop = min_i(
+ body_height,
+ t); // Make sure top margin is smaller than height
+ if (ctop > 0)
+ COMP_BDR(0, 0, wid, ctop);
+
+ body_height -= ctop;
+ if (body_height <= 0)
+ break;
+
+ // bottom
+ // cbot = checked bottom
+ int cbot = min_i(
+ body_height,
+ b); // Make sure bottom margin is not too large
+ if (cbot > 0)
+ COMP_BDR(0, hei - cbot, wid, cbot);
+
+ body_height -=
+ cbot; // Height of window exclude the margin
+ if (body_height <= 0)
+ break;
+
+ // left
+ int body_width = wid;
+ int cleft = min_i(body_width, l);
+ if (cleft > 0)
+ COMP_BDR(0, ctop, cleft, body_height);
+
+ body_width -= cleft;
+ if (body_width <= 0)
+ break;
+
+ // right
+ int cright = min_i(body_width, r);
+ if (cright > 0)
+ COMP_BDR(wid - cright, ctop, cright, body_height);
+
+ body_width -= cright;
+ if (body_width <= 0)
+ break;
+
+ // body
+ paint_region(ps, w, cleft, ctop, body_width, body_height,
+ dopacity, reg_paint, pict);
+ } while (0);
+ }
+
+#undef COMP_BDR
+
+ if (pict != w->paint.pict)
+ free_picture(ps->c, &pict);
+
+ // Dimming the window if needed
+ if (w->dim) {
+ double dim_opacity = ps->o.inactive_dim;
+ if (!ps->o.inactive_dim_fixed)
+ dim_opacity *= get_opacity_percent(w);
+
+ switch (ps->o.backend) {
+ case BKEND_XRENDER:
+ case BKEND_XR_GLX_HYBRID: {
+ unsigned short cval = 0xffff * dim_opacity;
+
+ // Premultiply color
+ xcb_render_color_t color = {
+ .red = 0,
+ .green = 0,
+ .blue = 0,
+ .alpha = cval,
+ };
+
+ xcb_rectangle_t rect = {
+ .x = x,
+ .y = y,
+ .width = wid,
+ .height = hei,
+ };
+
+ xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_OVER,
+ ps->tgt_buffer.pict, color, 1,
+ &rect);
+ } break;
+#ifdef CONFIG_OPENGL
+ case BKEND_GLX:
+ glx_dim_dst(ps, x, y, wid, hei, ps->psglx->z - 0.7,
+ dim_opacity, reg_paint);
+ break;
+#endif
+ default: assert(false);
+ }
+ }
+
+ glx_mark(ps, w->id, false);
+}
+
+extern const char *background_props_str[];
+
+static bool get_root_tile(session_t *ps) {
+ /*
+ if (ps->o.paint_on_overlay) {
+ return ps->root_picture;
+ } */
+
+ assert(!ps->root_tile_paint.pixmap);
+ ps->root_tile_fill = false;
+
+ bool fill = false;
+ xcb_pixmap_t pixmap = None;
+
+ // Get the values of background attributes
+ for (int p = 0; background_props_str[p]; p++) {
+ winprop_t prop =
+ wid_get_prop(ps, ps->root, get_atom(ps, background_props_str[p]),
+ 1L, XCB_ATOM_PIXMAP, 32);
+ if (prop.nitems) {
+ pixmap = *prop.p32;
+ fill = false;
+ free_winprop(&prop);
+ break;
+ }
+ free_winprop(&prop);
+ }
+
+ // Make sure the pixmap we got is valid
+ if (pixmap && !x_validate_pixmap(ps, pixmap))
+ pixmap = None;
+
+ // Create a pixmap if there isn't any
+ if (!pixmap) {
+ pixmap = x_create_pixmap(ps, ps->depth, ps->root, 1, 1);
+ if (pixmap == XCB_NONE) {
+ fprintf(stderr, "Failed to create some pixmap\n");
+ exit(1);
+ }
+ fill = true;
+ }
+
+ // Create Picture
+ xcb_render_create_picture_value_list_t pa = {
+ .repeat = True,
+ };
+ ps->root_tile_paint.pict = x_create_picture_with_visual_and_pixmap(
+ ps, ps->vis, pixmap, XCB_RENDER_CP_REPEAT, &pa);
+
+ // Fill pixmap if needed
+ if (fill) {
+ xcb_render_color_t col;
+ xcb_rectangle_t rect;
+
+ col.red = col.green = col.blue = 0x8080;
+ col.alpha = 0xffff;
+
+ rect.x = rect.y = 0;
+ rect.width = rect.height = 1;
+
+ xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_SRC,
+ ps->root_tile_paint.pict, col, 1, &rect);
+ }
+
+ ps->root_tile_fill = fill;
+ ps->root_tile_paint.pixmap = pixmap;
+#ifdef CONFIG_OPENGL
+ if (BKEND_GLX == ps->o.backend)
+ return glx_bind_pixmap(ps, &ps->root_tile_paint.ptex,
+ ps->root_tile_paint.pixmap, 0, 0, 0);
+#endif
+
+ return true;
+}
+
+/**
+ * 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);
+}
+
+static xcb_image_t *make_shadow(session_t *ps, double opacity, int width, int height) {
+ xcb_image_t *ximage;
+ int ylimit, xlimit;
+ int swidth = width + ps->cgsize;
+ int sheight = height + ps->cgsize;
+ int center = ps->cgsize / 2;
+ int x, y;
+ unsigned char d;
+ int x_diff;
+ int opacity_int = (int)(opacity * 25);
+
+ ximage = xcb_image_create_native(ps->c, swidth, sheight,
+ XCB_IMAGE_FORMAT_Z_PIXMAP, 8, 0, 0, NULL);
+
+ if (!ximage) {
+ printf_errf("(): failed to create an X image");
+ return 0;
+ }
+
+ unsigned char *data = ximage->data;
+ uint32_t sstride = ximage->stride;
+
+ /*
+ * Build the gaussian in sections
+ */
+
+ /*
+ * center (fill the complete data array)
+ */
+
+ // XXX If the center part of the shadow would be entirely covered by
+ // the body of the window, we shouldn't need to fill the center here.
+ // XXX In general, we want to just fill the part that is not behind
+ // the window, in order to reduce CPU load and make transparent window
+ // look correct
+ if (ps->cgsize > 0) {
+ d = ps->shadow_top[opacity_int * (ps->cgsize + 1) + ps->cgsize];
+ } else {
+ d = (unsigned char)(sum_kernel(ps->gaussian_map, center, center,
+ width, height) *
+ opacity * 255.0);
+ }
+ memset(data, d, sheight * swidth);
+
+ /*
+ * corners
+ */
+
+ ylimit = ps->cgsize;
+ if (ylimit > sheight / 2)
+ ylimit = (sheight + 1) / 2;
+
+ xlimit = ps->cgsize;
+ if (xlimit > swidth / 2)
+ xlimit = (swidth + 1) / 2;
+
+ for (y = 0; y < ylimit; y++) {
+ for (x = 0; x < xlimit; x++) {
+ if (xlimit == ps->cgsize && ylimit == ps->cgsize) {
+ d = ps->shadow_corner[opacity_int * (ps->cgsize + 1) *
+ (ps->cgsize + 1) +
+ y * (ps->cgsize + 1) + x];
+ } else {
+ d = (unsigned char)(sum_kernel(ps->gaussian_map,
+ x - center, y - center,
+ width, height) *
+ opacity * 255.0);
+ }
+ data[y * sstride + x] = d;
+ data[(sheight - y - 1) * sstride + x] = d;
+ data[(sheight - y - 1) * sstride + (swidth - x - 1)] = d;
+ data[y * sstride + (swidth - x - 1)] = d;
+ }
+ }
+
+ /*
+ * top/bottom
+ */
+
+ x_diff = swidth - (ps->cgsize * 2);
+ if (x_diff > 0 && ylimit > 0) {
+ for (y = 0; y < ylimit; y++) {
+ if (ylimit == ps->cgsize) {
+ d = ps->shadow_top[opacity_int * (ps->cgsize + 1) + y];
+ } else {
+ d = (unsigned char)(sum_kernel(ps->gaussian_map,
+ center, y - center,
+ width, height) *
+ opacity * 255.0);
+ }
+ memset(&data[y * sstride + ps->cgsize], d, x_diff);
+ memset(&data[(sheight - y - 1) * sstride + ps->cgsize], d,
+ x_diff);
+ }
+ }
+
+ /*
+ * sides
+ */
+
+ for (x = 0; x < xlimit; x++) {
+ if (xlimit == ps->cgsize) {
+ d = ps->shadow_top[opacity_int * (ps->cgsize + 1) + x];
+ } else {
+ d = (unsigned char)(sum_kernel(ps->gaussian_map, x - center,
+ center, width, height) *
+ opacity * 255.0);
+ }
+ for (y = ps->cgsize; y < sheight - ps->cgsize; y++) {
+ data[y * sstride + x] = d;
+ data[y * sstride + (swidth - x - 1)] = d;
+ }
+ }
+
+ return ximage;
+}
+
+/**
+ * Generate shadow Picture
for a window.
+ */
+static bool win_build_shadow(session_t *ps, win *w, double opacity) {
+ const int width = w->widthb;
+ const int height = w->heightb;
+ // printf_errf("(): building shadow for %s %d %d", w->name, width, height);
+
+ xcb_image_t *shadow_image = NULL;
+ xcb_pixmap_t shadow_pixmap = None, shadow_pixmap_argb = None;
+ xcb_render_picture_t shadow_picture = None, shadow_picture_argb = None;
+ xcb_gcontext_t gc = None;
+
+ shadow_image = make_shadow(ps, opacity, width, height);
+ if (!shadow_image) {
+ printf_errf("(): failed to make shadow");
+ return None;
+ }
+
+ shadow_pixmap = x_create_pixmap(ps, 8, ps->root, shadow_image->width,
+ shadow_image->height);
+ shadow_pixmap_argb = x_create_pixmap(ps, 32, ps->root, shadow_image->width,
+ shadow_image->height);
+
+ if (!shadow_pixmap || !shadow_pixmap_argb) {
+ printf_errf("(): failed to create shadow pixmaps");
+ goto shadow_picture_err;
+ }
+
+ shadow_picture = x_create_picture_with_standard_and_pixmap(
+ ps, XCB_PICT_STANDARD_A_8, shadow_pixmap, 0, NULL);
+ shadow_picture_argb = x_create_picture_with_standard_and_pixmap(
+ ps, XCB_PICT_STANDARD_ARGB_32, shadow_pixmap_argb, 0, NULL);
+ if (!shadow_picture || !shadow_picture_argb)
+ goto shadow_picture_err;
+
+ gc = xcb_generate_id(ps->c);
+ xcb_create_gc(ps->c, gc, shadow_pixmap, 0, NULL);
+
+ xcb_image_put(ps->c, shadow_pixmap, gc, shadow_image, 0, 0, 0);
+ xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, ps->cshadow_picture,
+ shadow_picture, shadow_picture_argb, 0, 0, 0, 0, 0, 0,
+ shadow_image->width, shadow_image->height);
+
+ assert(!w->shadow_paint.pixmap);
+ w->shadow_paint.pixmap = shadow_pixmap_argb;
+ assert(!w->shadow_paint.pict);
+ w->shadow_paint.pict = shadow_picture_argb;
+
+ // Sync it once and only once
+ xr_sync(ps, w->shadow_paint.pixmap, NULL);
+
+ xcb_free_gc(ps->c, gc);
+ xcb_image_destroy(shadow_image);
+ xcb_free_pixmap(ps->c, shadow_pixmap);
+ xcb_render_free_picture(ps->c, shadow_picture);
+
+ return true;
+
+shadow_picture_err:
+ if (shadow_image)
+ xcb_image_destroy(shadow_image);
+ if (shadow_pixmap)
+ xcb_free_pixmap(ps->c, shadow_pixmap);
+ if (shadow_pixmap_argb)
+ xcb_free_pixmap(ps->c, shadow_pixmap_argb);
+ if (shadow_picture)
+ xcb_render_free_picture(ps->c, shadow_picture);
+ if (shadow_picture_argb)
+ xcb_render_free_picture(ps->c, shadow_picture_argb);
+ if (gc)
+ xcb_free_gc(ps->c, gc);
+
+ return false;
+}
+
+/**
+ * Paint the shadow of a window.
+ */
+static inline void win_paint_shadow(session_t *ps, win *w, region_t *reg_paint) {
+ // Bind shadow pixmap to GLX texture if needed
+ paint_bind_tex(ps, &w->shadow_paint, 0, 0, 32, false);
+
+ if (!paint_isvalid(ps, &w->shadow_paint)) {
+ printf_errf("(%#010lx): Missing shadow data.", w->id);
+ return;
+ }
+
+ render(ps, 0, 0, w->g.x + w->shadow_dx, w->g.y + w->shadow_dy,
+ w->shadow_width, w->shadow_height, w->shadow_opacity, true, false,
+ w->shadow_paint.pict, w->shadow_paint.ptex, reg_paint, NULL);
+}
+
+/**
+ * Normalize a convolution kernel.
+ */
+static inline void normalize_conv_kern(int wid, int hei, xcb_render_fixed_t *kern) {
+ double sum = 0.0;
+ for (int i = 0; i < wid * hei; ++i)
+ sum += XFIXED_TO_DOUBLE(kern[i]);
+ double factor = 1.0 / sum;
+ for (int i = 0; i < wid * hei; ++i)
+ kern[i] = DOUBLE_TO_XFIXED(XFIXED_TO_DOUBLE(kern[i]) * factor);
+}
+
+/**
+ * @brief Blur an area on a buffer.
+ *
+ * @param ps current session
+ * @param tgt_buffer a buffer as both source and destination
+ * @param x x pos
+ * @param y y pos
+ * @param wid width
+ * @param hei height
+ * @param blur_kerns blur kernels, ending with a NULL, guaranteed to have at
+ * least one kernel
+ * @param reg_clip a clipping region to be applied on intermediate buffers
+ *
+ * @return true if successful, false otherwise
+ */
+static bool
+xr_blur_dst(session_t *ps, xcb_render_picture_t tgt_buffer, int x, int y, int wid,
+ int hei, xcb_render_fixed_t **blur_kerns, const region_t *reg_clip) {
+ assert(blur_kerns[0]);
+
+ // Directly copying from tgt_buffer to it does not work, so we create a
+ // Picture in the middle.
+ xcb_render_picture_t tmp_picture =
+ x_create_picture(ps, wid, hei, NULL, 0, NULL);
+
+ if (!tmp_picture) {
+ printf_errf("(): Failed to build intermediate Picture.");
+ return false;
+ }
+
+ if (reg_clip && tmp_picture)
+ x_set_picture_clip_region(ps, tmp_picture, 0, 0, reg_clip);
+
+ xcb_render_picture_t src_pict = tgt_buffer, dst_pict = tmp_picture;
+ for (int i = 0; blur_kerns[i]; ++i) {
+ assert(i < MAX_BLUR_PASS - 1);
+ xcb_render_fixed_t *convolution_blur = blur_kerns[i];
+ int kwid = XFIXED_TO_DOUBLE(convolution_blur[0]),
+ khei = XFIXED_TO_DOUBLE(convolution_blur[1]);
+ bool rd_from_tgt = (tgt_buffer == src_pict);
+
+ // Copy from source picture to destination. The filter must
+ // be applied on source picture, to get the nearby pixels outside the
+ // window.
+ xcb_render_set_picture_filter(
+ ps->c, src_pict, strlen(XRFILTER_CONVOLUTION),
+ XRFILTER_CONVOLUTION, kwid * khei + 2, convolution_blur);
+ xcb_render_composite(
+ ps->c, XCB_RENDER_PICT_OP_SRC, src_pict, None, dst_pict,
+ (rd_from_tgt ? x : 0), (rd_from_tgt ? y : 0), 0, 0,
+ (rd_from_tgt ? 0 : x), (rd_from_tgt ? 0 : y), wid, hei);
+ xrfilter_reset(ps, src_pict);
+
+ {
+ xcb_render_picture_t tmp = src_pict;
+ src_pict = dst_pict;
+ dst_pict = tmp;
+ }
+ }
+
+ if (src_pict != tgt_buffer)
+ xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, src_pict, None,
+ tgt_buffer, 0, 0, 0, 0, x, y, wid, hei);
+
+ free_picture(ps->c, &tmp_picture);
+
+ return true;
+}
+
+/**
+ * Blur the background of a window.
+ */
+static inline void
+win_blur_background(session_t *ps, win *w, xcb_render_picture_t tgt_buffer,
+ const region_t *reg_paint) {
+ const int x = w->g.x;
+ const int y = w->g.y;
+ const int wid = w->widthb;
+ const int hei = w->heightb;
+
+ double factor_center = 1.0;
+ // Adjust blur strength according to window opacity, to make it appear
+ // better during fading
+ if (!ps->o.blur_background_fixed) {
+ double pct = 1.0 - get_opacity_percent(w) * (1.0 - 1.0 / 9.0);
+ factor_center = pct * 8.0 / (1.1 - pct);
+ }
+
+ switch (ps->o.backend) {
+ case BKEND_XRENDER:
+ case BKEND_XR_GLX_HYBRID: {
+ // Normalize blur kernels
+ for (int i = 0; i < MAX_BLUR_PASS; ++i) {
+ xcb_render_fixed_t *kern_src = ps->o.blur_kerns[i];
+ xcb_render_fixed_t *kern_dst = ps->blur_kerns_cache[i];
+ assert(i < MAX_BLUR_PASS);
+ if (!kern_src) {
+ assert(!kern_dst);
+ break;
+ }
+
+ assert(!kern_dst || (kern_src[0] == kern_dst[0] &&
+ kern_src[1] == kern_dst[1]));
+
+ // Skip for fixed factor_center if the cache exists already
+ if (ps->o.blur_background_fixed && kern_dst)
+ continue;
+
+ int kwid = XFIXED_TO_DOUBLE(kern_src[0]),
+ khei = XFIXED_TO_DOUBLE(kern_src[1]);
+
+ // Allocate cache space if needed
+ if (!kern_dst) {
+ kern_dst =
+ ccalloc(kwid * khei + 2, xcb_render_fixed_t);
+ if (!kern_dst) {
+ printf_errf("(): Failed to allocate memory "
+ "for blur kernel.");
+ return;
+ }
+ ps->blur_kerns_cache[i] = kern_dst;
+ }
+
+ // Modify the factor of the center pixel
+ kern_src[2 + (khei / 2) * kwid + kwid / 2] =
+ DOUBLE_TO_XFIXED(factor_center);
+
+ // Copy over
+ memcpy(kern_dst, kern_src,
+ (kwid * khei + 2) * sizeof(xcb_render_fixed_t));
+ normalize_conv_kern(kwid, khei, kern_dst + 2);
+ }
+
+ // 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);
+ }
+ // Translate global coordinates to local ones
+ pixman_region32_translate(®_blur, -x, -y);
+ xr_blur_dst(ps, tgt_buffer, x, y, wid, hei, ps->blur_kerns_cache,
+ ®_blur);
+ pixman_region32_clear(®_blur);
+ } break;
+#ifdef CONFIG_OPENGL
+ case BKEND_GLX:
+ // TODO: Handle frame opacity
+ glx_blur_dst(ps, x, y, wid, hei, ps->psglx->z - 0.5, factor_center,
+ reg_paint, &w->glx_blur_cache);
+ break;
+#endif
+ default: assert(0);
+ }
+}
+
+/// paint all windows
+/// region = ??
+/// region_real = the damage region
+void paint_all(session_t *ps, region_t *region, const region_t *region_real,
+ win *const t) {
+ if (!region_real)
+ region_real = region;
+
+#ifdef DEBUG_REPAINT
+ static struct timespec last_paint = {0};
+#endif
+
+ if (!region)
+ region_real = region = &ps->screen_reg;
+ else
+ // Remove the damaged area out of screen
+ pixman_region32_intersect(region, region, &ps->screen_reg);
+
+#ifdef CONFIG_OPENGL
+ if (bkend_use_glx(ps))
+ glx_paint_pre(ps, region);
+#endif
+
+ if (!paint_isvalid(ps, &ps->tgt_buffer)) {
+ if (!ps->tgt_buffer.pixmap) {
+ free_paint(ps, &ps->tgt_buffer);
+ ps->tgt_buffer.pixmap = x_create_pixmap(
+ ps, ps->depth, ps->root, ps->root_width, ps->root_height);
+ if (ps->tgt_buffer.pixmap == XCB_NONE) {
+ fprintf(stderr, "Failed to allocate a screen-sized "
+ "pixmap\n");
+ exit(1);
+ }
+ }
+
+ if (BKEND_GLX != ps->o.backend)
+ ps->tgt_buffer.pict = x_create_picture_with_visual_and_pixmap(
+ ps, ps->vis, ps->tgt_buffer.pixmap, 0, 0);
+ }
+
+ if (BKEND_XRENDER == ps->o.backend) {
+ x_set_picture_clip_region(ps, ps->tgt_picture, 0, 0, region_real);
+ }
+
+ region_t reg_tmp, *reg_paint;
+ pixman_region32_init(®_tmp);
+ if (t) {
+ // Calculate the region upon which the root window is to be painted
+ // based on the ignore region of the lowest window, if available
+ pixman_region32_subtract(®_tmp, region, t->reg_ignore);
+ reg_paint = ®_tmp;
+ } else {
+ reg_paint = region;
+ }
+
+ set_tgt_clip(ps, reg_paint);
+ paint_root(ps, reg_paint);
+
+ // Windows are sorted from bottom to top
+ // Each window has a reg_ignore, which is the region obscured by all the windows
+ // on top of that window. This is used to reduce the number of pixels painted.
+ //
+ // Whether this is beneficial is to be determined XXX
+ for (win *w = t; w; w = w->prev_trans) {
+ region_t bshape = win_get_bounding_shape_global_by_val(w);
+ // Painting shadow
+ if (w->shadow) {
+ // Lazy shadow building
+ if (!w->shadow_paint.pixmap)
+ if (!win_build_shadow(ps, w, 1))
+ printf_errf("(): build shadow failed");
+
+ // Shadow doesn't need to be painted underneath the body of
+ // the window Because no one can see it
+ pixman_region32_subtract(®_tmp, region, 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);
+
+ // Mask out the body of the window from the shadow if needed
+ // Doing it here instead of in make_shadow() for saving GPU
+ // power and handling shaped windows (XXX unconfirmed)
+ if (!ps->o.wintype_option[w->window_type].full_shadow)
+ pixman_region32_subtract(®_tmp, ®_tmp, &bshape);
+
+#ifdef CONFIG_XINERAMA
+ 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]);
+#endif
+
+ // Detect if the region is empty before painting
+ if (pixman_region32_not_empty(®_tmp)) {
+ set_tgt_clip(ps, ®_tmp);
+ win_paint_shadow(ps, w, ®_tmp);
+ }
+ }
+
+ // Calculate the region based on the reg_ignore of the next (higher)
+ // window and the bounding region
+ // XXX XXX
+ pixman_region32_subtract(®_tmp, region, w->reg_ignore);
+ pixman_region32_intersect(®_tmp, ®_tmp, &bshape);
+ pixman_region32_fini(&bshape);
+
+ if (pixman_region32_not_empty(®_tmp)) {
+ set_tgt_clip(ps, ®_tmp);
+ // Blur window background
+ if (w->blur_background &&
+ (!win_is_solid(ps, w) ||
+ (ps->o.blur_background_frame && w->frame_opacity != 1)))
+ win_blur_background(ps, w, ps->tgt_buffer.pict,
+ ®_tmp);
+
+ // Painting the window
+ paint_one(ps, w, ®_tmp);
+ }
+ }
+
+ // Free up all temporary regions
+ pixman_region32_fini(®_tmp);
+
+ // Do this as early as possible
+ set_tgt_clip(ps, &ps->screen_reg);
+
+ if (ps->o.vsync) {
+ // Make sure all previous requests are processed to achieve best
+ // effect
+ x_sync(ps->c);
+#ifdef CONFIG_OPENGL
+ if (glx_has_context(ps)) {
+ if (ps->o.vsync_use_glfinish)
+ glFinish();
+ else
+ glFlush();
+ glXWaitX();
+ }
+#endif
+ }
+
+ // Wait for VBlank. We could do it aggressively (send the painting
+ // request and XFlush() on VBlank) or conservatively (send the request
+ // only on VBlank).
+ if (!ps->o.vsync_aggressive)
+ vsync_wait(ps);
+
+ switch (ps->o.backend) {
+ case BKEND_XRENDER:
+ if (ps->o.monitor_repaint) {
+ // Copy the screen content to a new picture, and highlight
+ // the paint region. This is not very efficient, but since
+ // it's for debug only, we don't really care
+
+ // First, we clear tgt_buffer.pict's clip region, since we
+ // want to copy everything
+ x_set_picture_clip_region(ps, ps->tgt_buffer.pict, 0, 0,
+ &ps->screen_reg);
+
+ // Then we create a new picture, and copy content to it
+ xcb_render_pictforminfo_t *pictfmt =
+ x_get_pictform_for_visual(ps, ps->vis);
+ xcb_render_picture_t new_pict = x_create_picture(
+ ps, ps->root_width, ps->root_height, pictfmt, 0, NULL);
+ xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC,
+ ps->tgt_buffer.pict, None, new_pict, 0,
+ 0, 0, 0, 0, 0, ps->root_width,
+ ps->root_height);
+
+ // Next, we set the region of paint and highlight it
+ x_set_picture_clip_region(ps, new_pict, 0, 0, region_real);
+ xcb_render_composite(
+ ps->c, XCB_RENDER_PICT_OP_OVER, ps->white_picture,
+ ps->alpha_picts[MAX_ALPHA / 2], new_pict, 0, 0, 0, 0, 0,
+ 0, ps->root_width, ps->root_height);
+
+ // Finally, clear clip region and put the whole thing on screen
+ x_set_picture_clip_region(ps, new_pict, 0, 0, &ps->screen_reg);
+ xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, new_pict,
+ None, ps->tgt_picture, 0, 0, 0, 0, 0, 0,
+ ps->root_width, ps->root_height);
+ xcb_render_free_picture(ps->c, new_pict);
+ } else
+ xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC,
+ ps->tgt_buffer.pict, None,
+ ps->tgt_picture, 0, 0, 0, 0, 0, 0,
+ ps->root_width, ps->root_height);
+ break;
+#ifdef CONFIG_OPENGL
+ case BKEND_XR_GLX_HYBRID:
+ x_sync(ps->c);
+ if (ps->o.vsync_use_glfinish)
+ glFinish();
+ else
+ glFlush();
+ glXWaitX();
+ assert(ps->tgt_buffer.pixmap);
+ xr_sync(ps, ps->tgt_buffer.pixmap, &ps->tgt_buffer_fence);
+ paint_bind_tex(ps, &ps->tgt_buffer, ps->root_width, ps->root_height,
+ ps->depth, !ps->o.glx_no_rebind_pixmap);
+ // See #163
+ xr_sync(ps, ps->tgt_buffer.pixmap, &ps->tgt_buffer_fence);
+ if (ps->o.vsync_use_glfinish)
+ glFinish();
+ else
+ glFlush();
+ glXWaitX();
+ glx_render(ps, ps->tgt_buffer.ptex, 0, 0, 0, 0, ps->root_width,
+ ps->root_height, 0, 1.0, false, false, region_real, NULL);
+ // falls through
+ case BKEND_GLX: glXSwapBuffers(ps->dpy, get_tgt_window(ps)); break;
+#endif
+ default: assert(0);
+ }
+ glx_mark_frame(ps);
+
+ if (ps->o.vsync_aggressive)
+ vsync_wait(ps);
+
+ XFlush(ps->dpy);
+
+#ifdef CONFIG_OPENGL
+ if (glx_has_context(ps)) {
+ glFlush();
+ glXWaitX();
+ }
+#endif
+
+#ifdef DEBUG_REPAINT
+ print_timestamp(ps);
+ struct timespec now = get_time_timespec();
+ struct timespec diff = {0};
+ timespec_subtract(&diff, &now, &last_paint);
+ printf("[ %5ld:%09ld ] ", diff.tv_sec, diff.tv_nsec);
+ last_paint = now;
+ printf("paint:");
+ for (win *w = t; w; w = w->prev_trans)
+ printf(" %#010lx", w->id);
+ putchar('\n');
+ fflush(stdout);
+#endif
+
+ // Check if fading is finished on all painted windows
+ {
+ win *pprev = NULL;
+ for (win *w = t; w; w = pprev) {
+ pprev = w->prev_trans;
+ win_check_fade_finished(ps, &w);
+ }
+ }
+}
+
+// vim: set ts=8 sw=8 noet :
diff --git a/src/render.h b/src/render.h
new file mode 100644
index 0000000..1709d6e
--- /dev/null
+++ b/src/render.h
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: MPL-2.0
+// Copyright (c) Yuxuan Shui
+#pragma once
+
+#include
+#include "region.h"
+
+typedef struct _glx_texture glx_texture_t;
+typedef struct glx_prog_main glx_prog_main_t;
+typedef struct win win;
+typedef struct session session_t;
+
+typedef struct paint {
+ xcb_pixmap_t pixmap;
+ xcb_render_picture_t pict;
+ glx_texture_t *ptex;
+} paint_t;
+
+void
+render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei,
+ double opacity, bool argb, bool neg,
+ xcb_render_picture_t pict, glx_texture_t *ptex,
+ const region_t *reg_paint, const glx_prog_main_t *pprogram);
+void
+paint_one(session_t *ps, win *w, const region_t *reg_paint);
+
+void
+paint_all(session_t *ps, region_t *region, const region_t *region_real, win * const t);
+
+void free_picture(xcb_connection_t *c, xcb_render_picture_t *p);
+
+void free_paint(session_t *ps, paint_t *ppaint);
diff --git a/src/string_utils.c b/src/string_utils.c
index 8dca93d..064db9c 100644
--- a/src/string_utils.c
+++ b/src/string_utils.c
@@ -1,3 +1,6 @@
+// SPDX-License-Identifier: MPL-2.0
+// Copyright (c) Yuxuan Shui
+
#include
#include "compiler.h"
diff --git a/src/vsync.c b/src/vsync.c
new file mode 100644
index 0000000..a1ae076
--- /dev/null
+++ b/src/vsync.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: MPL-2.0
+// Copyright (c) Yuxuan Shui
+
+/// Function pointers to init VSync modes.
+
+#include "common.h"
+#include "log.h"
+#include "opengl.h"
+
+#include "vsync.h"
+
+/**
+ * Initialize DRM VSync.
+ *
+ * @return true for success, false otherwise
+ */
+static bool
+vsync_drm_init(session_t *ps) {
+#ifdef CONFIG_VSYNC_DRM
+ // Should we always open card0?
+ if (ps->drm_fd < 0 && (ps->drm_fd = open("/dev/dri/card0", O_RDWR)) < 0) {
+ printf_errf("(): Failed to open device.");
+ return false;
+ }
+
+ if (vsync_drm_wait(ps))
+ return false;
+
+ return true;
+#else
+ printf_errf("(): Program not compiled with DRM VSync support.");
+ return false;
+#endif
+}
+
+/**
+ * Initialize OpenGL VSync.
+ *
+ * Stolen from: http://git.tuxfamily.org/?p=ccm/cairocompmgr.git;a=commitdiff;h=efa4ceb97da501e8630ca7f12c99b1dce853c73e
+ * Possible original source: http://www.inb.uni-luebeck.de/~boehme/xvideo_sync.html
+ *
+ * @return true for success, false otherwise
+ */
+static bool
+vsync_opengl_init(session_t *ps) {
+#ifdef CONFIG_OPENGL
+ if (!ensure_glx_context(ps))
+ return false;
+
+ if (!glx_hasglxext(ps, "GLX_SGI_video_sync")) {
+ printf_errf("(): Your driver doesn't support SGI_video_sync, giving up.");
+ return false;
+ }
+
+ // Get video sync functions
+ if (!ps->psglx->glXGetVideoSyncSGI)
+ ps->psglx->glXGetVideoSyncSGI = (f_GetVideoSync)
+ glXGetProcAddress((const GLubyte *) "glXGetVideoSyncSGI");
+ if (!ps->psglx->glXWaitVideoSyncSGI)
+ ps->psglx->glXWaitVideoSyncSGI = (f_WaitVideoSync)
+ glXGetProcAddress((const GLubyte *) "glXWaitVideoSyncSGI");
+ if (!ps->psglx->glXWaitVideoSyncSGI || !ps->psglx->glXGetVideoSyncSGI) {
+ printf_errf("(): Failed to get glXWait/GetVideoSyncSGI function.");
+ return false;
+ }
+
+ return true;
+#else
+ printf_errf("(): Program not compiled with OpenGL VSync support.");
+ return false;
+#endif
+}
+
+static bool
+vsync_opengl_oml_init(session_t *ps) {
+#ifdef CONFIG_OPENGL
+ if (!ensure_glx_context(ps))
+ return false;
+
+ if (!glx_hasglxext(ps, "GLX_OML_sync_control")) {
+ printf_errf("(): Your driver doesn't support OML_sync_control, giving up.");
+ return false;
+ }
+
+ // Get video sync functions
+ if (!ps->psglx->glXGetSyncValuesOML)
+ ps->psglx->glXGetSyncValuesOML = (f_GetSyncValuesOML)
+ glXGetProcAddress ((const GLubyte *) "glXGetSyncValuesOML");
+ if (!ps->psglx->glXWaitForMscOML)
+ ps->psglx->glXWaitForMscOML = (f_WaitForMscOML)
+ glXGetProcAddress ((const GLubyte *) "glXWaitForMscOML");
+ if (!ps->psglx->glXGetSyncValuesOML || !ps->psglx->glXWaitForMscOML) {
+ printf_errf("(): Failed to get OML_sync_control functions.");
+ return false;
+ }
+
+ return true;
+#else
+ printf_errf("(): Program not compiled with OpenGL VSync support.");
+ return false;
+#endif
+}
+
+static bool
+vsync_opengl_swc_swap_interval(session_t *ps, unsigned int interval) {
+#ifdef CONFIG_OPENGL
+ if (!ensure_glx_context(ps))
+ return false;
+
+ if (!ps->psglx->glXSwapIntervalProc && !ps->psglx->glXSwapIntervalMESAProc) {
+ if (glx_hasglxext(ps, "GLX_MESA_swap_control")) {
+ ps->psglx->glXSwapIntervalMESAProc = (f_SwapIntervalMESA)
+ glXGetProcAddress ((const GLubyte *) "glXSwapIntervalMESA");
+ } else if (glx_hasglxext(ps, "GLX_SGI_swap_control")) {
+ ps->psglx->glXSwapIntervalProc = (f_SwapIntervalSGI)
+ glXGetProcAddress ((const GLubyte *) "glXSwapIntervalSGI");
+ } else {
+ printf_errf("(): Your driver doesn't support SGI_swap_control nor MESA_swap_control, giving up.");
+ return false;
+ }
+ }
+
+ if (ps->psglx->glXSwapIntervalMESAProc)
+ ps->psglx->glXSwapIntervalMESAProc(interval);
+ else if (ps->psglx->glXSwapIntervalProc)
+ ps->psglx->glXSwapIntervalProc(interval);
+ else
+ return false;
+#endif
+
+ return true;
+}
+
+static bool
+vsync_opengl_swc_init(session_t *ps) {
+#ifdef CONFIG_OPENGL
+ if (!bkend_use_glx(ps)) {
+ printf_errf("(): OpenGL swap control requires the GLX backend.");
+ return false;
+ }
+
+ if (!vsync_opengl_swc_swap_interval(ps, 1)) {
+ printf_errf("(): Failed to load a swap control extension.");
+ return false;
+ }
+
+ return true;
+#else
+ printf_errf("(): Program not compiled with OpenGL VSync support.");
+ return false;
+#endif
+}
+
+static bool
+vsync_opengl_mswc_init(session_t *ps) {
+ printf_errf("(): opengl-mswc is deprecated, please use opengl-swc instead.");
+ return vsync_opengl_swc_init(ps);
+}
+
+bool (*const VSYNC_FUNCS_INIT[])(session_t *ps) = {
+ [VSYNC_DRM ] = vsync_drm_init,
+ [VSYNC_OPENGL ] = vsync_opengl_init,
+ [VSYNC_OPENGL_OML ] = vsync_opengl_oml_init,
+ [VSYNC_OPENGL_SWC ] = vsync_opengl_swc_init,
+ [VSYNC_OPENGL_MSWC ] = vsync_opengl_mswc_init,
+};
+
+#ifdef CONFIG_VSYNC_DRM
+/**
+ * Wait for next VSync, DRM method.
+ *
+ * Stolen from: https://github.com/MythTV/mythtv/blob/master/mythtv/libs/libmythtv/vsync.cpp
+ */
+static int
+vsync_drm_wait(session_t *ps) {
+ int ret = -1;
+ drm_wait_vblank_t vbl;
+
+ vbl.request.type = _DRM_VBLANK_RELATIVE,
+ vbl.request.sequence = 1;
+
+ do {
+ ret = ioctl(ps->drm_fd, DRM_IOCTL_WAIT_VBLANK, &vbl);
+ vbl.request.type &= ~_DRM_VBLANK_RELATIVE;
+ } while (ret && errno == EINTR);
+
+ if (ret)
+ fprintf(stderr, "vsync_drm_wait(): VBlank ioctl did not work, "
+ "unimplemented in this drmver?\n");
+
+ return ret;
+
+}
+#endif
+
+#ifdef CONFIG_OPENGL
+/**
+ * Wait for next VSync, OpenGL method.
+ */
+static int
+vsync_opengl_wait(session_t *ps) {
+ unsigned vblank_count = 0;
+
+ ps->psglx->glXGetVideoSyncSGI(&vblank_count);
+ ps->psglx->glXWaitVideoSyncSGI(2, (vblank_count + 1) % 2, &vblank_count);
+ // I see some code calling glXSwapIntervalSGI(1) afterwards, is it required?
+
+ return 0;
+}
+
+/**
+ * Wait for next VSync, OpenGL OML method.
+ *
+ * https://mail.gnome.org/archives/clutter-list/2012-November/msg00031.html
+ */
+static int
+vsync_opengl_oml_wait(session_t *ps) {
+ int64_t ust = 0, msc = 0, sbc = 0;
+
+ ps->psglx->glXGetSyncValuesOML(ps->dpy, ps->reg_win, &ust, &msc, &sbc);
+ ps->psglx->glXWaitForMscOML(ps->dpy, ps->reg_win, 0, 2, (msc + 1) % 2,
+ &ust, &msc, &sbc);
+
+ return 0;
+}
+
+#endif
+
+/// Function pointers to wait for VSync.
+int (*const VSYNC_FUNCS_WAIT[])(session_t *ps) = {
+#ifdef CONFIG_VSYNC_DRM
+ [VSYNC_DRM ] = vsync_drm_wait,
+#endif
+#ifdef CONFIG_OPENGL
+ [VSYNC_OPENGL ] = vsync_opengl_wait,
+ [VSYNC_OPENGL_OML ] = vsync_opengl_oml_wait,
+#endif
+};
+
+#ifdef CONFIG_OPENGL
+static void
+vsync_opengl_swc_deinit(session_t *ps) {
+ vsync_opengl_swc_swap_interval(ps, 0);
+}
+#endif
+
+
+/// Function pointers to deinitialize VSync.
+void (*const VSYNC_FUNCS_DEINIT[])(session_t *ps) = {
+#ifdef CONFIG_OPENGL
+ [VSYNC_OPENGL_SWC ] = vsync_opengl_swc_deinit,
+ [VSYNC_OPENGL_MSWC ] = vsync_opengl_swc_deinit,
+#endif
+};
+
+/**
+ * Initialize current VSync method.
+ */
+bool vsync_init(session_t *ps) {
+ // Mesa turns on swap control by default, undo that
+ if (bkend_use_glx(ps))
+ vsync_opengl_swc_swap_interval(ps, 0);
+
+ if (ps->o.vsync && VSYNC_FUNCS_INIT[ps->o.vsync]
+ && !VSYNC_FUNCS_INIT[ps->o.vsync](ps)) {
+ ps->o.vsync = VSYNC_NONE;
+ return false;
+ }
+ else
+ return true;
+}
+
+/**
+ * Wait for next VSync.
+ */
+void vsync_wait(session_t *ps) {
+ if (!ps->o.vsync)
+ return;
+
+ if (VSYNC_FUNCS_WAIT[ps->o.vsync])
+ VSYNC_FUNCS_WAIT[ps->o.vsync](ps);
+}
+
+/**
+ * Deinitialize current VSync method.
+ */
+void vsync_deinit(session_t *ps) {
+ if (ps->o.vsync && VSYNC_FUNCS_DEINIT[ps->o.vsync])
+ VSYNC_FUNCS_DEINIT[ps->o.vsync](ps);
+}
diff --git a/src/vsync.h b/src/vsync.h
new file mode 100644
index 0000000..e50f6d0
--- /dev/null
+++ b/src/vsync.h
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: MPL-2.0
+// Copyright (c) Yuxuan Shui
+#include
+
+typedef struct session session_t;
+
+bool vsync_init(session_t *ps);
+void vsync_wait(session_t *ps);
+void vsync_deinit(session_t *ps);
diff --git a/src/win.c b/src/win.c
index 33261ce..d0845ad 100644
--- a/src/win.c
+++ b/src/win.c
@@ -1289,3 +1289,35 @@ void win_ev_stop(session_t *ps, win *w) {
xcb_shape_select_input(ps->c, w->id, 0));
}
}
+
+/**
+ * Set fade callback of a window, and possibly execute the previous
+ * callback.
+ *
+ * If a callback can cause rendering result to change, it should call
+ * `queue_redraw`.
+ *
+ * @param exec_callback whether the previous callback is to be executed
+ */
+void win_set_fade_callback(session_t *ps, win **_w,
+ void (*callback) (session_t *ps, win **w), bool exec_callback) {
+ win *w = *_w;
+ void (*old_callback) (session_t *ps, win **w) = w->fade_callback;
+
+ w->fade_callback = callback;
+ // Must be the last line as the callback could destroy w!
+ if (exec_callback && old_callback)
+ old_callback(ps, _w);
+}
+
+/**
+ * Execute fade callback of a window if fading finished.
+ */
+void
+win_check_fade_finished(session_t *ps, win **_w) {
+ win *w = *_w;
+ if (w->fade_callback && w->opacity == w->opacity_tgt) {
+ // Must be the last line as the callback could destroy w!
+ win_set_fade_callback(ps, _w, NULL, true);
+ }
+}
diff --git a/src/win.h b/src/win.h
index 75a205b..787e5fc 100644
--- a/src/win.h
+++ b/src/win.h
@@ -16,17 +16,11 @@
#include "x.h"
#include "types.h"
#include "c2.h"
+#include "render.h"
typedef struct session session_t;
typedef struct _glx_texture glx_texture_t;
-// FIXME not the best place for this type
-typedef struct {
- xcb_pixmap_t pixmap;
- xcb_render_picture_t pict;
- glx_texture_t *ptex;
-} paint_t;
-
#ifdef CONFIG_OPENGL
// FIXME this type should be in opengl.h
// it is very unideal for it to be here
@@ -335,6 +329,24 @@ void
win_update_frame_extents(session_t *ps, win *w, Window client);
bool add_win(session_t *ps, Window id, Window prev);
+/**
+ * Set fade callback of a window, and possibly execute the previous
+ * callback.
+ *
+ * If a callback can cause rendering result to change, it should call
+ * `queue_redraw`.
+ *
+ * @param exec_callback whether the previous callback is to be executed
+ */
+void win_set_fade_callback(session_t *ps, win **_w,
+ void (*callback) (session_t *ps, win **w), bool exec_callback);
+
+/**
+ * Execute fade callback of a window if fading finished.
+ */
+void
+win_check_fade_finished(session_t *ps, win **_w);
+
// Stop receiving events (except ConfigureNotify, XXX why?) from a window
void win_ev_stop(session_t *ps, win *w);
diff --git a/src/x.c b/src/x.c
index 47f17a1..22f1273 100644
--- a/src/x.c
+++ b/src/x.c
@@ -377,3 +377,20 @@ x_create_pixmap(session_t *ps, uint8_t depth, xcb_drawable_t drawable, uint16_t
free(err);
return XCB_NONE;
}
+
+/**
+ * Validate a pixmap.
+ *
+ * Detect whether the pixmap is valid with XGetGeometry. Well, maybe there
+ * are better ways.
+ */
+bool
+x_validate_pixmap(session_t *ps, xcb_pixmap_t pxmap) {
+ if (!pxmap) return false;
+
+ Window rroot = None;
+ int rx = 0, ry = 0;
+ unsigned rwid = 0, rhei = 0, rborder = 0, rdepth = 0;
+ return XGetGeometry(ps->dpy, pxmap, &rroot, &rx, &ry,
+ &rwid, &rhei, &rborder, &rdepth) && rwid && rhei;
+}
diff --git a/src/x.h b/src/x.h
index 3e1cde3..9e22bab 100644
--- a/src/x.h
+++ b/src/x.h
@@ -137,6 +137,9 @@ x_print_error(unsigned long serial, uint8_t major, uint8_t minor, uint8_t error_
xcb_pixmap_t
x_create_pixmap(session_t *ps, uint8_t depth, xcb_drawable_t drawable, uint16_t width, uint16_t height);
+bool
+x_validate_pixmap(session_t *ps, xcb_pixmap_t pxmap);
+
/**
* Free a winprop_t
.
*