From b68985f96fd2b4737fd2168f27105b9b662fcc19 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sun, 19 Jan 2014 08:04:14 +0800 Subject: [PATCH 01/25] Bug fix #163: xr_glx_hybrid: Flickering issue xr_glx_hybrid backend: Attempt to fix flickering issue. Thanks to cju for testing. --- compton.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/compton.c b/compton.c index 3ddf46f4c..f539daa2c 100644 --- a/compton.c +++ b/compton.c @@ -1950,6 +1950,13 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t paint_bind_tex_real(ps, &ps->tgt_buffer, ps->root_width, ps->root_height, ps->depth, !ps->o.glx_no_rebind_pixmap); + // See #163 + XSync(ps->dpy, False); + 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, region_real, NULL); // No break here! From 53cb5d3a46dfd13c1185f005674224b3c9ad0a3d Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Tue, 21 Jan 2014 22:13:06 +0800 Subject: [PATCH 02/25] Bug fix: Fix access to freed memory due to invalid w->prev_trans - Fix a bug that w->prev_trans sometimes points to freed memory. Probably related to #165. - Add some more debugging printf()-s under DEBUG_EVENTS. --- compton.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/compton.c b/compton.c index f539daa2c..3a14d82b7 100644 --- a/compton.c +++ b/compton.c @@ -1245,6 +1245,7 @@ paint_preprocess(session_t *ps, win *list) { t = w; } else { + assert(w->destroyed == (w->fade_callback == destroy_callback)); check_fade_fin(ps, w); } @@ -2089,6 +2090,10 @@ map_win(session_t *ps, Window id) { win *w = find_win(ps, id); +#ifdef DEBUG_EVENTS + printf_dbgf("(%#010lx \"%s\"): %p\n", id, (w ? w->name: NULL), w); +#endif + // Don't care about window mapping if it's an InputOnly window // Try avoiding mapping a window twice if (!w || InputOnly == w->a.class @@ -2824,6 +2829,10 @@ add_win(session_t *ps, Window id, Window prev) { // Allocate and initialize the new win structure win *new = malloc(sizeof(win)); +#ifdef DEBUG_EVENTS + printf_dbgf("(%#010lx): %p\n", id, new); +#endif + if (!new) { printf_errf("(%#010lx): Failed to allocate memory for the new window.", id); return false; @@ -3078,10 +3087,18 @@ circulate_win(session_t *ps, XCirculateEvent *ce) { static void finish_destroy_win(session_t *ps, Window id) { - win **prev, *w; + win **prev = NULL, *w = NULL; + +#ifdef DEBUG_EVENTS + printf_dbgf("(%#010lx): Starting...\n", id); +#endif for (prev = &ps->list; (w = *prev); prev = &w->next) { if (w->id == id && w->destroyed) { +#ifdef DEBUG_EVENTS + printf_dbgf("(%#010lx \"%s\"): %p\n", id, w->name, w); +#endif + finish_unmap_win(ps, w); *prev = w->next; @@ -3091,6 +3108,12 @@ finish_destroy_win(session_t *ps, Window id) { free_win_res(ps, w); + // Drop w from all prev_trans to avoid accessing freed memory in + // repair_win() + for (win *w2 = ps->list; w2; w2 = w2->next) + if (w == w2->prev_trans) + w2->prev_trans = NULL; + free(w); break; } @@ -3106,6 +3129,10 @@ static void destroy_win(session_t *ps, Window id) { win *w = find_win(ps, id); +#ifdef DEBUG_EVENTS + printf_dbgf("(%#010lx \"%s\"): %p\n", id, (w ? w->name: NULL), w); +#endif + if (w) { unmap_win(ps, w); From 9745bfe765a42d4183dbcf343e889e1cb7cf18a4 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Thu, 27 Feb 2014 22:08:30 +0800 Subject: [PATCH 03/25] Bug fix: Fix -S Fix the broken -S. --- compton.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compton.c b/compton.c index 3a14d82b7..9d058767c 100644 --- a/compton.c +++ b/compton.c @@ -5480,6 +5480,8 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { ps->o.config_file = mstrcpy(optarg); else if ('d' == o) ps->o.display = mstrcpy(optarg); + else if ('S' == o) + ps->o.synchronize = true; else if ('?' == o || ':' == o) usage(1); } @@ -5532,6 +5534,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { usage(0); break; case 'd': + case 'S': break; P_CASELONG('D', fade_delta); case 'I': @@ -5556,7 +5559,6 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { case 'F': fading_enable = true; break; - P_CASEBOOL('S', synchronize); P_CASELONG('r', shadow_radius); case 'o': ps->o.shadow_opacity = atof(optarg); From 53d0c5c015d438172957a1ca0f31e1fb6fa9b150 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Tue, 11 Mar 2014 07:22:23 +0800 Subject: [PATCH 04/25] Misc: xr-glx-hybrid alias & minor fixes - Add "xr-glx-hybrid" as an alias of "xr_glx_hybrid". (#163) - Clear damage history in expose_root() and when root window size changes. Unfortunately this doesn't fix #181. --- common.h | 14 ++++++++++++++ compton.c | 2 ++ 2 files changed, 16 insertions(+) diff --git a/common.h b/common.h index 7786f82b8..e87651c9e 100644 --- a/common.h +++ b/common.h @@ -1499,6 +1499,11 @@ parse_backend(session_t *ps, const char *str) { ps->o.backend = BKEND_XR_GLX_HYBRID; return true; } + // cju wants to use dashes + if (!strcasecmp(str, "xr-glx-hybrid")) { + ps->o.backend = BKEND_XR_GLX_HYBRID; + return true; + } printf_errf("(\"%s\"): Invalid backend argument.", str); return false; } @@ -1788,6 +1793,15 @@ free_region(session_t *ps, XserverRegion *p) { } } +/** + * Free all regions in ps->all_damage_last . + */ +static inline void +free_all_damage_last(session_t *ps) { + for (int i = 0; i < CGLX_MAX_BUFFER_AGE; ++i) + free_region(ps, &ps->all_damage_last[i]); +} + /** * Crop a rectangle by another rectangle. * diff --git a/compton.c b/compton.c index 9d058767c..645aa5c57 100644 --- a/compton.c +++ b/compton.c @@ -2983,6 +2983,7 @@ configure_win(session_t *ps, XConfigureEvent *ce) { rebuild_screen_reg(ps); rebuild_shadow_exclude_reg(ps); + free_all_damage_last(ps); #ifdef CONFIG_VSYNC_OPENGL if (BKEND_GLX == ps->o.backend) @@ -3275,6 +3276,7 @@ error(Display __attribute__((unused)) *dpy, XErrorEvent *ev) { static void expose_root(session_t *ps, XRectangle *rects, int nrects) { + free_all_damage_last(ps); XserverRegion region = XFixesCreateRegion(ps->dpy, rects, nrects); add_damage(ps, region); } From 15bde6a8f5c4dfa13da4444e25239a9de102fe0c Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Mon, 17 Mar 2014 23:25:34 +0800 Subject: [PATCH 05/25] Bug fix #181: Add --xrender-sync{,-fence} - Add --xrender-sync{,-fence} to deal with redraw lag issue on GLX backend. --xrender-sync-fence requires a sufficiently new xorg-server and libXext. NO_XSYNC=1 may be used to disable it at compile time. Thanks to tchebb for reporting and everybody else for testing. (#181) - A bit code clean-up. Replace a few XSync() with XFlush() to minimize the latency. --- common.h | 171 +++++++++++++++++++++++++++++++++++++++++++++++++++++- compton.c | 119 ++++++++++++++++++++++++++++--------- compton.h | 11 +++- opengl.c | 127 +++++++++++++++++++++++++++++++++------- opengl.h | 2 + 5 files changed, 379 insertions(+), 51 deletions(-) diff --git a/common.h b/common.h index e87651c9e..d243dc077 100644 --- a/common.h +++ b/common.h @@ -55,11 +55,19 @@ // #define CONFIG_DBUS 1 // Whether to enable condition support. // #define CONFIG_C2 1 +// Whether to enable X Sync support. +// #define CONFIG_XSYNC 1 +// Whether to enable GLX Sync support. +// #define CONFIG_GLX_XSYNC 1 #if !defined(CONFIG_C2) && defined(DEBUG_C2) #error Cannot enable c2 debugging without c2 support. #endif +#if (!defined(CONFIG_XSYNC) || !defined(CONFIG_VSYNC_OPENGL)) && defined(CONFIG_GLX_SYNC) +#error Cannot enable GL sync without X Sync / OpenGL support. +#endif + #ifndef COMPTON_VERSION #define COMPTON_VERSION "unknown" #endif @@ -89,6 +97,9 @@ #include #include #include +#ifdef CONFIG_XSYNC +#include +#endif #ifdef CONFIG_XINERAMA #include @@ -338,6 +349,16 @@ enum { typedef struct _glx_texture glx_texture_t; #ifdef CONFIG_VSYNC_OPENGL +#ifdef DEBUG_GLX_DEBUG_CONTEXT +typedef GLXContext (*f_glXCreateContextAttribsARB) (Display *dpy, + GLXFBConfig config, GLXContext share_context, Bool direct, + const int *attrib_list); +typedef void (*GLDEBUGPROC) (GLenum source, GLenum type, + GLuint id, GLenum severity, GLsizei length, const GLchar* message, + GLvoid* userParam); +typedef void (*f_DebugMessageCallback) (GLDEBUGPROC, void *userParam); +#endif + typedef int (*f_WaitVideoSync) (int, int, unsigned *); typedef int (*f_GetVideoSync) (unsigned *); @@ -352,6 +373,47 @@ typedef void (*f_ReleaseTexImageEXT) (Display *display, GLXDrawable drawable, in typedef void (*f_CopySubBuffer) (Display *dpy, GLXDrawable drawable, int x, int y, int width, int height); +#ifdef CONFIG_GLX_SYNC +// Looks like duplicate typedef of the same type is safe? +typedef int64_t GLint64; +typedef uint64_t GLuint64; +typedef struct __GLsync *GLsync; + +#ifndef GL_SYNC_FLUSH_COMMANDS_BIT +#define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001 +#endif + +#ifndef GL_TIMEOUT_IGNORED +#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFFull +#endif + +#ifndef GL_ALREADY_SIGNALED +#define GL_ALREADY_SIGNALED 0x911A +#endif + +#ifndef GL_TIMEOUT_EXPIRED +#define GL_TIMEOUT_EXPIRED 0x911B +#endif + +#ifndef GL_CONDITION_SATISFIED +#define GL_CONDITION_SATISFIED 0x911C +#endif + +#ifndef GL_WAIT_FAILED +#define GL_WAIT_FAILED 0x911D +#endif + +typedef GLsync (*f_FenceSync) (GLenum condition, GLbitfield flags); +typedef GLboolean (*f_IsSync) (GLsync sync); +typedef void (*f_DeleteSync) (GLsync sync); +typedef GLenum (*f_ClientWaitSync) (GLsync sync, GLbitfield flags, + GLuint64 timeout); +typedef void (*f_WaitSync) (GLsync sync, GLbitfield flags, + GLuint64 timeout); +typedef GLsync (*f_ImportSyncEXT) (GLenum external_sync_type, + GLintptr external_sync, GLbitfield flags); +#endif + #ifdef DEBUG_GLX_MARK typedef void (*f_StringMarkerGREMEDY) (GLsizei len, const void *string); typedef void (*f_FrameTerminatorGREMEDY) (void); @@ -438,7 +500,7 @@ struct _win; typedef struct _c2_lptr c2_lptr_t; /// Structure representing all options. -typedef struct { +typedef struct _options_t { // === General === /// The configuration file we used. char *config_file; @@ -451,6 +513,11 @@ typedef struct { char *display_repr; /// The backend in use. enum backend backend; + /// Whether to sync X drawing to avoid certain delay issues with + /// GLX backend. + bool xrender_sync; + /// Whether to sync X drawing with X Sync fence. + bool xrender_sync_fence; /// Whether to avoid using stencil buffer under GLX backend. Might be /// unsafe. bool glx_no_stencil; @@ -617,7 +684,7 @@ typedef struct { } options_t; /// Structure containing all necessary data for a compton session. -typedef struct { +typedef struct _session_t { // === Display related === /// Display in use. Display *dpy; @@ -650,6 +717,9 @@ typedef struct { Picture tgt_picture; /// Temporary buffer to paint to before sending to display. paint_t tgt_buffer; +#ifdef CONFIG_XSYNC + XSyncFence tgt_buffer_fence; +#endif /// DBE back buffer for root window. Used in DBE painting mode. XdbeBackBuffer root_dbe; /// Window ID of the window we register as a symbol. @@ -783,6 +853,20 @@ typedef struct { f_ReleaseTexImageEXT glXReleaseTexImageProc; /// Pointer to glXCopySubBufferMESA function. f_CopySubBuffer glXCopySubBufferProc; +#ifdef CONFIG_GLX_SYNC + /// Pointer to the glFenceSync() function. + f_FenceSync glFenceSyncProc; + /// Pointer to the glIsSync() function. + f_IsSync glIsSyncProc; + /// Pointer to the glDeleteSync() function. + f_DeleteSync glDeleteSyncProc; + /// Pointer to the glClientWaitSync() function. + f_ClientWaitSync glClientWaitSyncProc; + /// Pointer to the glWaitSync() function. + f_WaitSync glWaitSyncProc; + /// Pointer to the glImportSyncEXT() function. + f_ImportSyncEXT glImportSyncEXT; +#endif #ifdef DEBUG_GLX_MARK /// Pointer to StringMarkerGREMEDY function. f_StringMarkerGREMEDY glStringMarkerGREMEDY; @@ -849,6 +933,14 @@ typedef struct { XserverRegion *xinerama_scr_regs; /// Number of Xinerama screens. int xinerama_nscrs; +#endif +#ifdef CONFIG_XSYNC + /// Whether X Sync extension exists. + bool xsync_exists; + /// Event base number for X Sync extension. + int xsync_event; + /// Error base number for X Sync extension. + int xsync_error; #endif /// Whether X Render convolution filter exists. bool xrfilter_convolution_exists; @@ -915,6 +1007,10 @@ typedef struct _win { winmode_t mode; /// Whether the window has been damaged at least once. bool damaged; +#ifdef CONFIG_XSYNC + /// X Sync fence of drawable. + XSyncFence fence; +#endif /// Whether the window was damaged after last paint. bool pixmap_damaged; /// Damage of the window. @@ -1802,6 +1898,20 @@ free_all_damage_last(session_t *ps) { free_region(ps, &ps->all_damage_last[i]); } +#ifdef CONFIG_XSYNC +/** + * Free a XSync fence. + */ +static inline void +free_fence(session_t *ps, XSyncFence *pfence) { + if (*pfence) + XSyncDestroyFence(ps->dpy, *pfence); + *pfence = None; +} +#else +#define free_fence(ps, pfence) ((void) 0) +#endif + /** * Crop a rectangle by another rectangle. * @@ -1927,6 +2037,11 @@ vsync_deinit(session_t *ps); */ ///@{ +#ifdef CONFIG_GLX_SYNC +void +xr_glx_sync(session_t *ps, Drawable d, XSyncFence *pfence); +#endif + bool glx_init(session_t *ps, bool need_render); @@ -2096,6 +2211,58 @@ glx_mark_frame(session_t *ps) { ///@} +#ifdef CONFIG_XSYNC +#define xr_sync(ps, d, pfence) xr_sync_(ps, d, pfence) +#else +#define xr_sync(ps, d, pfence) xr_sync_(ps, d) +#endif + +/** + * Synchronizes a X Render drawable to ensure all pending painting requests + * are completed. + */ +static inline void +xr_sync_(session_t *ps, Drawable d +#ifdef CONFIG_XSYNC + , XSyncFence *pfence +#endif + ) { + if (!ps->o.xrender_sync) + return; + +#ifdef CONFIG_XSYNC + if (ps->o.xrender_sync_fence && ps->xsync_exists) { + // TODO: If everybody just follows the rules stated in X Sync prototype, + // we need only one fence per screen, but let's stay a bit cautious right + // now + XSyncFence tmp_fence = None; + if (!pfence) + pfence = &tmp_fence; + assert(pfence); + if (!*pfence) + *pfence = XSyncCreateFence(ps->dpy, d, False); + if (*pfence) { + Bool triggered = False; + // The fence may fail to be created (e.g. because of died drawable) + assert(!XSyncQueryFence(ps->dpy, *pfence, &triggered) || !triggered); + XSyncTriggerFence(ps->dpy, *pfence); + XSyncAwaitFence(ps->dpy, pfence, 1); + assert(!XSyncQueryFence(ps->dpy, *pfence, &triggered) || triggered); + } + else { + printf_errf("(%#010lx): Failed to create X Sync fence.", d); + } + free_fence(ps, &tmp_fence); + if (*pfence) + XSyncResetFence(ps->dpy, *pfence); + } +#endif + XSync(ps->dpy, False); +#ifdef CONFIG_GLX_SYNC + xr_glx_sync(ps, d, pfence); +#endif +} + /** @name DBus handling */ ///@{ diff --git a/compton.c b/compton.c index 645aa5c57..1efb05d62 100644 --- a/compton.c +++ b/compton.c @@ -489,6 +489,9 @@ win_build_shadow(session_t *ps, win *w, double opacity) { w->shadow_paint.pixmap = shadow_pixmap_argb; w->shadow_paint.pict = shadow_picture_argb; + // Sync it once and only once + xr_sync(ps, w->shadow_paint.pixmap, NULL); + bool success = paint_bind_tex(ps, &w->shadow_paint, shadow_image->width, shadow_image->height, 32, true); XFreeGC(ps->dpy, gc); @@ -1513,12 +1516,16 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint, if (!w->paint.pixmap && ps->has_name_pixmap) { set_ignore_next(ps); w->paint.pixmap = XCompositeNameWindowPixmap(ps->dpy, w->id); + 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) { - Drawable draw = w->paint.pixmap; - if (!draw) - draw = w->id; { XRenderPictureAttributes pa = { .subwindow_mode = IncludeInferiors, @@ -1528,6 +1535,10 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint, CPSubwindowMode, &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, @@ -1948,11 +1959,13 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t else glFlush(); glXWaitX(); + assert(ps->tgt_buffer.pixmap); + xr_sync(ps, ps->tgt_buffer.pixmap, &ps->tgt_buffer_fence); paint_bind_tex_real(ps, &ps->tgt_buffer, ps->root_width, ps->root_height, ps->depth, !ps->o.glx_no_rebind_pixmap); // See #163 - XSync(ps->dpy, False); + xr_sync(ps, ps->tgt_buffer.pixmap, &ps->tgt_buffer_fence); if (ps->o.vsync_use_glfinish) glFinish(); else @@ -2116,7 +2129,7 @@ map_win(session_t *ps, Window id) { } // Make sure the XSelectInput() requests are sent - XSync(ps->dpy, False); + XFlush(ps->dpy); // Update window mode here to check for ARGB windows win_determine_mode(ps, w); @@ -2205,7 +2218,7 @@ finish_unmap_win(session_t *ps, win *w) { w->extents = None; } - free_paint(ps, &w->paint); + free_wpaint(ps, w); free_region(ps, &w->border_size); free_paint(ps, &w->shadow_paint); } @@ -2219,6 +2232,11 @@ static void unmap_win(session_t *ps, win *w) { if (!w || IsUnmapped == w->a.map_state) return; + // One last synchronization + if (w->paint.pixmap) + xr_sync(ps, w->paint.pixmap, &w->fence); + free_fence(ps, &w->fence); + // Set focus out win_set_focused(ps, w, false); @@ -2654,7 +2672,7 @@ win_mark_client(session_t *ps, win *w, Window client) { determine_evmask(ps, client, WIN_EVMODE_CLIENT)); // Make sure the XSelectInput() requests are sent - XSync(ps->dpy, False); + XFlush(ps->dpy); win_upd_wintype(ps, w); @@ -3037,7 +3055,7 @@ configure_win(session_t *ps, XConfigureEvent *ce) { if (w->a.width != ce->width || w->a.height != ce->height || w->a.border_width != ce->border_width) - free_paint(ps, &w->paint); + free_wpaint(ps, w); if (w->a.width != ce->width || w->a.height != ce->height || w->a.border_width != ce->border_width) { @@ -3097,7 +3115,7 @@ finish_destroy_win(session_t *ps, Window id) { for (prev = &ps->list; (w = *prev); prev = &w->next) { if (w->id == id && w->destroyed) { #ifdef DEBUG_EVENTS - printf_dbgf("(%#010lx \"%s\"): %p\n", id, w->name, w); + printf_dbgf("(%#010lx \"%s\"): %p\n", id, w->name, w); #endif finish_unmap_win(ps, w); @@ -3188,10 +3206,10 @@ damage_win(session_t *ps, XDamageNotifyEvent *de) { * Xlib error handler function. */ static int -error(Display __attribute__((unused)) *dpy, XErrorEvent *ev) { +xerror(Display __attribute__((unused)) *dpy, XErrorEvent *ev) { session_t * const ps = ps_g; - int o; + int o = 0; const char *name = "Unknown"; if (should_ignore(ps, ev->serial)) { @@ -3240,6 +3258,17 @@ error(Display __attribute__((unused)) *dpy, XErrorEvent *ev) { } #endif +#ifdef CONFIG_XSYNC + if (ps->xsync_exists) { + o = ev->error_code - ps->xsync_error; + switch (o) { + CASESTRRET2(XSyncBadCounter); + CASESTRRET2(XSyncBadAlarm); + CASESTRRET2(XSyncBadFence); + } + } +#endif + switch (ev->error_code) { CASESTRRET2(BadAccess); CASESTRRET2(BadAlloc); @@ -3771,18 +3800,27 @@ ev_name(session_t *ps, XEvent *ev) { CASESTRRET(Expose); CASESTRRET(PropertyNotify); CASESTRRET(ClientMessage); - default: - if (isdamagenotify(ps, ev)) - return "Damage"; + } - if (ps->shape_exists && ev->type == ps->shape_event) { - return "ShapeNotify"; - } + if (isdamagenotify(ps, ev)) + return "Damage"; - sprintf(buf, "Event %d", ev->type); + if (ps->shape_exists && ev->type == ps->shape_event) + return "ShapeNotify"; - return buf; +#ifdef CONFIG_XSYNC + if (ps->xsync_exists) { + int o = ev->type - ps->xsync_event; + switch (o) { + CASESTRRET(CounterNotify); + CASESTRRET(AlarmNotify); + } } +#endif + + sprintf(buf, "Event %d", ev->type); + + return buf; } static Window @@ -5464,6 +5502,8 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "unredir-if-possible-delay", required_argument, NULL, 309 }, { "write-pid-path", required_argument, NULL, 310 }, { "vsync-use-glfinish", no_argument, NULL, 311 }, + { "xrender-sync", no_argument, NULL, 312 }, + { "xrender-sync-fence", no_argument, NULL, 313 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -5714,6 +5754,8 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { ps->o.write_pid_path = mstrcpy(optarg); break; P_CASEBOOL(311, vsync_use_glfinish); + P_CASEBOOL(312, xrender_sync); + P_CASEBOOL(313, xrender_sync_fence); default: usage(1); break; @@ -5761,6 +5803,9 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { if (ps->o.blur_background_frame) ps->o.blur_background = true; + if (ps->o.xrender_sync_fence) + ps->o.xrender_sync = true; + // Other variables determined by options // Determine whether we need to track focus changes @@ -6478,7 +6523,7 @@ redir_stop(session_t *ps) { // If we don't destroy them here, looks like the resources are just // kept inaccessible somehow for (win *w = ps->list; w; w = w->next) - free_paint(ps, &w->paint); + free_wpaint(ps, w); XCompositeUnredirectSubwindows(ps->dpy, ps->root, CompositeRedirectManual); // Unmap overlay window @@ -6852,7 +6897,7 @@ session_init(session_t *ps_old, int argc, char **argv) { } } - XSetErrorHandler(error); + XSetErrorHandler(xerror); if (ps->o.synchronize) { XSynchronize(ps->dpy, 1); } @@ -6907,11 +6952,6 @@ session_init(session_t *ps_old, int argc, char **argv) { exit(1); } - // Query X Shape - if (XShapeQueryExtension(ps->dpy, &ps->shape_event, &ps->shape_error)) { - ps->shape_exists = true; - } - // Build a safe representation of display name { char *display_repr = DisplayString(ps->dpy); @@ -6936,6 +6976,32 @@ session_init(session_t *ps_old, int argc, char **argv) { // Second pass get_cfg(ps, argc, argv, false); + // Query X Shape + if (XShapeQueryExtension(ps->dpy, &ps->shape_event, &ps->shape_error)) { + ps->shape_exists = true; + } + + if (ps->o.xrender_sync_fence) { +#ifdef CONFIG_XSYNC + // Query X Sync + if (XSyncQueryExtension(ps->dpy, &ps->xsync_event, &ps->xsync_error)) { + // TODO: Fencing may require version >= 3.0? + int major_version_return = 0, minor_version_return = 0; + if (XSyncInitialize(ps->dpy, &major_version_return, &minor_version_return)) + ps->xsync_exists = true; + } + if (!ps->xsync_exists) { + printf_errf("(): X Sync extension not found. No X Sync fence sync is " + "possible."); + exit(1); + } +#else + printf_errf("(): X Sync support not compiled in. --xrender-sync-fence" + "can't work."); + exit(1); +#endif + } + // Query X RandR if ((ps->o.sw_opti && !ps->o.refresh_rate) || ps->o.xinerama_shadow_crop) { if (XRRQueryExtension(ps->dpy, &ps->randr_event, &ps->randr_error)) @@ -7219,6 +7285,7 @@ session_destroy(session_t *ps) { ps->tgt_picture = None; else free_picture(ps, &ps->tgt_picture); + free_fence(ps, &ps->tgt_buffer_fence); free_picture(ps, &ps->root_picture); free_paint(ps, &ps->tgt_buffer); diff --git a/compton.h b/compton.h index 22ee195dd..a4e838efb 100644 --- a/compton.h +++ b/compton.h @@ -275,6 +275,15 @@ free_paint(session_t *ps, paint_t *ppaint) { free_pixmap(ps, &ppaint->pixmap); } +/** + * Free w->paint. + */ +static inline void +free_wpaint(session_t *ps, win *w) { + free_paint(ps, &w->paint); + free_fence(ps, &w->fence); +} + /** * Destroy all resources in a struct _win. */ @@ -883,7 +892,7 @@ static void damage_win(session_t *ps, XDamageNotifyEvent *de); static int -error(Display *dpy, XErrorEvent *ev); +xerror(Display *dpy, XErrorEvent *ev); static void expose_root(session_t *ps, XRectangle *rects, int nrects); diff --git a/opengl.c b/opengl.c index d557aea63..39aac33c2 100644 --- a/opengl.c +++ b/opengl.c @@ -10,6 +10,50 @@ #include "opengl.h" +#ifdef CONFIG_GLX_SYNC +void +xr_glx_sync(session_t *ps, Drawable d, XSyncFence *pfence) { + if (*pfence) { + // GLsync sync = ps->glFenceSyncProc(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + GLsync sync = ps->glImportSyncEXT(GL_SYNC_X11_FENCE_EXT, *pfence, 0); + XSync(ps->dpy, False); + glx_check_err(ps); + /* GLenum ret = ps->glClientWaitSyncProc(sync, GL_SYNC_FLUSH_COMMANDS_BIT, + 1000); + assert(GL_CONDITION_SATISFIED == ret); */ + ps->glWaitSyncProc(sync, 0, GL_TIMEOUT_IGNORED); + // ps->glDeleteSyncProc(sync); + // XSyncResetFence(ps->dpy, *pfence); + } + glx_check_err(ps); +} +#endif + +static inline GLXFBConfig +get_fbconfig_from_visualinfo(session_t *ps, const XVisualInfo *visualinfo) { + int nelements = 0; + GLXFBConfig *fbconfigs = glXGetFBConfigs(ps->dpy, visualinfo->screen, + &nelements); + for (int i = 0; i < nelements; ++i) { + int visual_id = 0; + if (Success == glXGetFBConfigAttrib(ps->dpy, fbconfigs[i], GLX_VISUAL_ID, &visual_id) + && visual_id == visualinfo->visualid) + return fbconfigs[i]; + } + + return NULL; +} + +#ifdef DEBUG_GLX_DEBUG_CONTEXT +static void +glx_debug_msg_callback(GLenum source, GLenum type, + GLuint id, GLenum severity, GLsizei length, const GLchar *message, + GLvoid *userParam) { + printf_dbgf("(): source 0x%04X, type 0x%04X, id %u, severity 0x%0X, \"%s\"\n", + source, type, id, severity, message); +} +#endif + /** * Initialize OpenGL. */ @@ -56,7 +100,33 @@ glx_init(session_t *ps, bool need_render) { if (!ps->glx_context) { // Get GLX context +#ifndef DEBUG_GLX_DEBUG_CONTEXT ps->glx_context = glXCreateContext(ps->dpy, pvis, None, GL_TRUE); +#else + { + GLXFBConfig fbconfig = get_fbconfig_from_visualinfo(ps, pvis); + if (!fbconfig) { + printf_errf("(): Failed to get GLXFBConfig for root visual %#lx.", + pvis->visualid); + goto glx_init_end; + } + + f_glXCreateContextAttribsARB p_glXCreateContextAttribsARB = + (f_glXCreateContextAttribsARB) + glXGetProcAddress((const GLubyte *) "glXCreateContextAttribsARB"); + if (!p_glXCreateContextAttribsARB) { + printf_errf("(): Failed to get glXCreateContextAttribsARB()."); + goto glx_init_end; + } + + static const int attrib_list[] = { + GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB, + None + }; + ps->glx_context = p_glXCreateContextAttribsARB(ps->dpy, fbconfig, NULL, + GL_TRUE, attrib_list); + } +#endif if (!ps->glx_context) { printf_errf("(): Failed to get GLX context."); @@ -68,6 +138,20 @@ glx_init(session_t *ps, bool need_render) { printf_errf("(): Failed to attach GLX context."); goto glx_init_end; } + +#ifdef DEBUG_GLX_DEBUG_CONTEXT + { + f_DebugMessageCallback p_DebugMessageCallback = + (f_DebugMessageCallback) + glXGetProcAddress((const GLubyte *) "glDebugMessageCallback"); + if (!p_DebugMessageCallback) { + printf_errf("(): Failed to get glDebugMessageCallback(0."); + goto glx_init_end; + } + p_DebugMessageCallback(glx_debug_msg_callback, ps); + } +#endif + } // Ensure we have a stencil buffer. X Fixes does not guarantee rectangles @@ -114,6 +198,27 @@ glx_init(session_t *ps, bool need_render) { goto glx_init_end; } } + +#ifdef CONFIG_GLX_SYNC + ps->glFenceSyncProc = (f_FenceSync) + glXGetProcAddress((const GLubyte *) "glFenceSync"); + ps->glIsSyncProc = (f_IsSync) + glXGetProcAddress((const GLubyte *) "glIsSync"); + ps->glDeleteSyncProc = (f_DeleteSync) + glXGetProcAddress((const GLubyte *) "glDeleteSync"); + ps->glClientWaitSyncProc = (f_ClientWaitSync) + glXGetProcAddress((const GLubyte *) "glClientWaitSync"); + ps->glWaitSyncProc = (f_WaitSync) + glXGetProcAddress((const GLubyte *) "glWaitSync"); + ps->glImportSyncEXT = (f_ImportSyncEXT) + glXGetProcAddress((const GLubyte *) "glImportSyncEXT"); + if (!ps->glFenceSyncProc || !ps->glIsSyncProc || !ps->glDeleteSyncProc + || !ps->glClientWaitSyncProc || !ps->glWaitSyncProc + || !ps->glImportSyncEXT) { + printf_errf("(): Failed to acquire GLX sync functions."); + goto glx_init_end; + } +#endif } // Acquire FBConfigs @@ -344,9 +449,7 @@ glx_init_blur(session_t *ps) { } -#ifdef DEBUG_GLX_ERR glx_check_err(ps); -#endif return true; #else @@ -655,9 +758,7 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, glBindTexture(ptex->target, 0); glDisable(ptex->target); -#ifdef DEBUG_GLX_ERR glx_check_err(ps); -#endif return true; } @@ -680,9 +781,7 @@ glx_release_pixmap(session_t *ps, glx_texture_t *ptex) { ptex->glpixmap = 0; } -#ifdef DEBUG_GLX_ERR glx_check_err(ps); -#endif } /** @@ -803,9 +902,7 @@ glx_paint_pre(session_t *ps, XserverRegion *preg) { glx_render_color(ps, 0, 0, ps->root_width, ps->root_height, 0, *preg, NULL); #endif -#ifdef DEBUG_GLX_ERR glx_check_err(ps); -#endif } /** @@ -886,9 +983,7 @@ glx_set_clip(session_t *ps, XserverRegion reg, const reg_data_t *pcache_reg) { cxfree(rects_free); -#ifdef DEBUG_GLX_ERR glx_check_err(ps); -#endif } #define P_PAINTREG_START() \ @@ -1174,9 +1269,7 @@ glx_blur_dst_end: free_glx_bc(ps, pbc); } -#ifdef DEBUG_GLX_ERR glx_check_err(ps); -#endif return ret; } @@ -1212,9 +1305,7 @@ glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, float z, glColor4f(0.0f, 0.0f, 0.0f, 0.0f); glDisable(GL_BLEND); -#ifdef DEBUG_GLX_ERR glx_check_err(ps); -#endif return true; } @@ -1412,9 +1503,7 @@ glx_render(session_t *ps, const glx_texture_t *ptex, glActiveTexture(GL_TEXTURE0); } -#ifdef DEBUG_GLX_ERR glx_check_err(ps); -#endif return true; } @@ -1452,9 +1541,7 @@ glx_render_color(session_t *ps, int dx, int dy, int width, int height, int z, } glColor4f(0.0f, 0.0f, 0.0f, 0.0f); -#ifdef DEBUG_GLX_ERR glx_check_err(ps); -#endif } /** @@ -1492,9 +1579,7 @@ glx_render_dots(session_t *ps, int dx, int dy, int width, int height, int z, } glColor4f(0.0f, 0.0f, 0.0f, 0.0f); -#ifdef DEBUG_GLX_ERR glx_check_err(ps); -#endif } /** @@ -1524,9 +1609,7 @@ glx_swap_copysubbuffermesa(session_t *ps, XserverRegion reg) { } } -#ifdef DEBUG_GLX_ERR glx_check_err(ps); -#endif cxfree(rects); } diff --git a/opengl.h b/opengl.h index 564b7e20a..8628e36d3 100644 --- a/opengl.h +++ b/opengl.h @@ -59,6 +59,8 @@ glx_check_err_(session_t *ps, const char *func, int line) { } #define glx_check_err(ps) glx_check_err_(ps, __func__, __LINE__) +#else +#define glx_check_err(ps) ((void) 0) #endif /** From dbba28f946bac1a73b31f326f41ff282994d8176 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Wed, 26 Mar 2014 22:27:25 +0800 Subject: [PATCH 06/25] Misc: Add --xrender-sync{,-fence} as configuration file option - Add --xrender-sync{,-fence} as configuration file option. - Quit on encountering invalid opacity rule. - Other small changes. --- common.h | 4 +++- compton.c | 22 ++++++++++++++++++++-- opengl.c | 4 ++-- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/common.h b/common.h index d243dc077..525b9a96b 100644 --- a/common.h +++ b/common.h @@ -2230,6 +2230,7 @@ xr_sync_(session_t *ps, Drawable d if (!ps->o.xrender_sync) return; + XSync(ps->dpy, False); #ifdef CONFIG_XSYNC if (ps->o.xrender_sync_fence && ps->xsync_exists) { // TODO: If everybody just follows the rules stated in X Sync prototype, @@ -2243,6 +2244,8 @@ xr_sync_(session_t *ps, Drawable d *pfence = XSyncCreateFence(ps->dpy, d, False); if (*pfence) { Bool triggered = False; + /* if (XSyncQueryFence(ps->dpy, *pfence, &triggered) && triggered) + XSyncResetFence(ps->dpy, *pfence); */ // The fence may fail to be created (e.g. because of died drawable) assert(!XSyncQueryFence(ps->dpy, *pfence, &triggered) || !triggered); XSyncTriggerFence(ps->dpy, *pfence); @@ -2257,7 +2260,6 @@ xr_sync_(session_t *ps, Drawable d XSyncResetFence(ps->dpy, *pfence); } #endif - XSync(ps->dpy, False); #ifdef CONFIG_GLX_SYNC xr_glx_sync(ps, d, pfence); #endif diff --git a/compton.c b/compton.c index 1efb05d62..1da926b5d 100644 --- a/compton.c +++ b/compton.c @@ -4596,6 +4596,18 @@ usage(int ret) { "--glx-use-gpushader4\n" " GLX backend: Use GL_EXT_gpu_shader4 for some optimization on blur\n" " GLSL code. My tests on GTX 670 show no noticeable effect.\n" + "--xrender-sync\n" + " Attempt to synchronize client applications' draw calls with XSync(),\n" + " used on GLX backend to ensure up-to-date window content is painted.\n" +#undef WARNING +#ifndef CONFIG_XSYNC +#define WARNING WARNING_DISABLED +#else +#define WARNING +#endif + "--xrender-sync-fence\n" + " Additionally use X Sync fence to sync clients' draw calls. Needed\n" + " on nvidia-drivers with GLX backend for some users." WARNING "\n" #undef WARNING #ifndef CONFIG_DBUS #define WARNING WARNING_DISABLED @@ -5186,7 +5198,9 @@ parse_cfg_condlst_opct(session_t *ps, const config_t *pcfg, const char *name) { if (config_setting_is_array(setting)) { int i = config_setting_length(setting); while (i--) - parse_rule_opacity(ps, config_setting_get_string_elem(setting, i)); + if (!parse_rule_opacity(ps, config_setting_get_string_elem(setting, + i))) + exit(1); } // Treat it as a single pattern if it's a string else if (CONFIG_TYPE_STRING == config_setting_type(setting)) { @@ -5399,6 +5413,10 @@ parse_config(session_t *ps, struct options_tmp *pcfgtmp) { exit(1); // --glx-use-gpushader4 lcfg_lookup_bool(&cfg, "glx-use-gpushader4", &ps->o.glx_use_gpushader4); + // --xrender-sync + lcfg_lookup_bool(&cfg, "xrender-sync", &ps->o.xrender_sync); + // --xrender-sync-fence + lcfg_lookup_bool(&cfg, "xrender-sync-fence", &ps->o.xrender_sync_fence); // Wintype settings { wintype_t i; @@ -6996,7 +7014,7 @@ session_init(session_t *ps_old, int argc, char **argv) { exit(1); } #else - printf_errf("(): X Sync support not compiled in. --xrender-sync-fence" + printf_errf("(): X Sync support not compiled in. --xrender-sync-fence " "can't work."); exit(1); #endif diff --git a/opengl.c b/opengl.c index 39aac33c2..951d8e50a 100644 --- a/opengl.c +++ b/opengl.c @@ -16,11 +16,11 @@ xr_glx_sync(session_t *ps, Drawable d, XSyncFence *pfence) { if (*pfence) { // GLsync sync = ps->glFenceSyncProc(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); GLsync sync = ps->glImportSyncEXT(GL_SYNC_X11_FENCE_EXT, *pfence, 0); - XSync(ps->dpy, False); - glx_check_err(ps); /* GLenum ret = ps->glClientWaitSyncProc(sync, GL_SYNC_FLUSH_COMMANDS_BIT, 1000); assert(GL_CONDITION_SATISFIED == ret); */ + XSyncTriggerFence(ps->dpy, *pfence); + XFlush(ps->dpy); ps->glWaitSyncProc(sync, 0, GL_TIMEOUT_IGNORED); // ps->glDeleteSyncProc(sync); // XSyncResetFence(ps->dpy, *pfence); From b175464e8c88cc911dcc11198f247fa47c9586a5 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sat, 19 Apr 2014 19:41:26 +0800 Subject: [PATCH 07/25] Misc: Try to avoid evaluating conditions after window unmap & others - Try to avoid evaluating conditions after window unmap/destruction. Unfortunately, frequently this doesn't work due to the asynchronous nature of X. - Add _GTK_FRAME_EXTENTS exclusion rules to compton.sample.conf. Thanks to memeplex, hexchain, and others for info. (#189) - Add debugging option --show-all-xerrors, and other debugging changes. Doc update. --- c2.c | 2 ++ common.h | 19 ++++++++++++++---- compton.c | 58 +++++++++++++++++++++++++++++++++++++------------------ 3 files changed, 56 insertions(+), 23 deletions(-) diff --git a/c2.c b/c2.c index de221c01d..f70dcb5a7 100644 --- a/c2.c +++ b/c2.c @@ -1292,6 +1292,8 @@ c2_match_once(session_t *ps, win *w, const c2_ptr_t cond) { bool c2_matchd(session_t *ps, win *w, const c2_lptr_t *condlst, const c2_lptr_t **cache, void **pdata) { + assert(IsViewable == w->a.map_state); + // Check if the cached entry matches firstly if (cache && *cache && c2_match_once(ps, w, (*cache)->ptr)) { if (pdata) diff --git a/common.h b/common.h index 525b9a96b..5b6a266a6 100644 --- a/common.h +++ b/common.h @@ -14,6 +14,7 @@ // === Options === // Debug options, enable them using -D in CFLAGS +// #define DEBUG_BACKTRACE 1 // #define DEBUG_REPAINT 1 // #define DEBUG_EVENTS 1 // #define DEBUG_RESTACK 1 @@ -72,6 +73,10 @@ #define COMPTON_VERSION "unknown" #endif +#if defined(DEBUG_ALLOC_REG) +#define DEBUG_BACKTRACE 1 +#endif + // === Includes === // For some special functions @@ -568,6 +573,8 @@ typedef struct _options_t { c2_lptr_t *paint_blacklist; /// Whether to work under synchronized mode for debugging. bool synchronize; + /// Whether to show all X errors. + bool show_all_xerrors; // === VSync & software optimization === /// User-specified refresh rate. @@ -1192,13 +1199,13 @@ extern session_t *ps_g; static inline void print_timestamp(session_t *ps); -#ifdef DEBUG_ALLOC_REG +#ifdef DEBUG_BACKTRACE #include -#define BACKTRACE_SIZE 5 +#define BACKTRACE_SIZE 25 /** - * Print current backtrace, excluding the first two items. + * Print current backtrace. * * Stolen from glibc manual. */ @@ -1211,12 +1218,14 @@ print_backtrace(void) { size = backtrace(array, BACKTRACE_SIZE); strings = backtrace_symbols(array, size); - for (size_t i = 2; i < size; i++) + for (size_t i = 0; i < size; i++) printf ("%s\n", strings[i]); free(strings); } +#ifdef DEBUG_ALLOC_REG + /** * Wrapper of XFixesCreateRegion, for debugging. */ @@ -1247,6 +1256,8 @@ XFixesDestroyRegion_(Display *dpy, XserverRegion reg, #define XFixesDestroyRegion(dpy, reg) XFixesDestroyRegion_(dpy, reg, __func__, __LINE__) #endif +#endif + // === Functions === /** diff --git a/compton.c b/compton.c index 1da926b5d..f58661b7d 100644 --- a/compton.c +++ b/compton.c @@ -576,6 +576,9 @@ discard_ignore(session_t *ps, unsigned long sequence) { static void set_ignore(session_t *ps, unsigned long sequence) { + if (ps->o.show_all_xerrors) + return; + ignore_t *i = malloc(sizeof(ignore_t)); if (!i) return; @@ -2374,8 +2377,13 @@ static void win_determine_fade(session_t *ps, win *w) { if (UNSET != w->fade_force) w->fade = w->fade_force; - else if ((ps->o.no_fading_openclose && w->in_openclose) - || win_match(ps, w, ps->o.fade_blacklist, &w->cache_fblst)) + else if (ps->o.no_fading_openclose && w->in_openclose) + w->fade = false; + // Ignore other possible causes of fading state changes after window + // gets unmapped + else if (IsViewable != w->a.map_state) { + } + else if (win_match(ps, w, ps->o.fade_blacklist, &w->cache_fblst)) w->fade = false; else w->fade = ps->o.wintype_fade[w->window_type]; @@ -2403,8 +2411,7 @@ win_update_shape(session_t *ps, win *w) { win_update_shape_raw(ps, w); - // Shadow state could be changed - win_determine_shadow(ps, w); + win_on_factor_change(ps, w); /* // If clear_shadow state on the window possibly changed, destroy the old @@ -2457,13 +2464,14 @@ static void win_determine_shadow(session_t *ps, win *w) { bool shadow_old = w->shadow; - w->shadow = (UNSET == w->shadow_force ? - (ps->o.wintype_shadow[w->window_type] - && !win_match(ps, w, ps->o.shadow_blacklist, &w->cache_sblst) - && !(ps->o.shadow_ignore_shaped && w->bounding_shaped - && !w->rounded_corners) - && !(ps->o.respect_prop_shadow && 0 == w->prop_shadow)) - : w->shadow_force); + if (UNSET != w->shadow_force) + w->shadow = w->shadow_force; + else if (IsViewable == w->a.map_state) + w->shadow = (ps->o.wintype_shadow[w->window_type] + && !win_match(ps, w, ps->o.shadow_blacklist, &w->cache_sblst) + && !(ps->o.shadow_ignore_shaped && w->bounding_shaped + && !w->rounded_corners) + && !(ps->o.respect_prop_shadow && 0 == w->prop_shadow)); // Window extents need update on shadow state change if (w->shadow != shadow_old) { @@ -2488,17 +2496,13 @@ win_determine_shadow(session_t *ps, win *w) { */ static void win_determine_invert_color(session_t *ps, win *w) { - // Do not change window invert color state when the window is unmapped, - // unless it comes from w->invert_color_force. - if (UNSET == w->invert_color_force && IsViewable != w->a.map_state) - return; - bool invert_color_old = w->invert_color; if (UNSET != w->invert_color_force) w->invert_color = w->invert_color_force; - else - w->invert_color = win_match(ps, w, ps->o.invert_color_list, &w->cache_ivclst); + else if (IsViewable == w->a.map_state) + w->invert_color = win_match(ps, w, ps->o.invert_color_list, + &w->cache_ivclst); if (w->invert_color != invert_color_old) add_damage_win(ps, w); @@ -2509,6 +2513,9 @@ win_determine_invert_color(session_t *ps, win *w) { */ static void win_determine_blur_background(session_t *ps, win *w) { + if (IsViewable != w->a.map_state) + return; + bool blur_background_old = w->blur_background; w->blur_background = ps->o.blur_background @@ -2526,6 +2533,9 @@ win_determine_blur_background(session_t *ps, win *w) { */ static void win_update_opacity_rule(session_t *ps, win *w) { + if (IsViewable != w->a.map_state) + return; + // If long is 32-bit, unfortunately there's no way could we express "unset", // so we just entirely don't distinguish "unset" and OPAQUE opacity_t opacity = OPAQUE; @@ -3295,11 +3305,13 @@ xerror(Display __attribute__((unused)) *dpy, XErrorEvent *ev) { { char buf[BUF_LEN] = ""; XGetErrorText(ps->dpy, ev->error_code, buf, BUF_LEN); - printf("error %d (%s) request %d minor %d serial %lu (\"%s\")\n", + printf("error %4d %-12s request %4d minor %4d serial %6lu: \"%s\"\n", ev->error_code, name, ev->request_code, ev->minor_code, ev->serial, buf); } + // print_backtrace(); + return 0; } @@ -3904,10 +3916,12 @@ ev_focus_report(XFocusChangeEvent* ev) { * Determine whether we should respond to a FocusIn/Out * event. */ +/* inline static bool ev_focus_accept(XFocusChangeEvent *ev) { return NotifyNormal == ev->mode || NotifyUngrab == ev->mode; } +*/ static inline void ev_focus_in(session_t *ps, XFocusChangeEvent *ev) { @@ -4401,6 +4415,8 @@ usage(int ret) { " Daemonize process.\n" "-S\n" " Enable synchronous operation (for debugging).\n" + "--show-all-xerrors\n" + " Show all X errors (for debugging).\n" "--config path\n" " Look for configuration file at the path.\n" "--write-pid-path path\n" @@ -5522,6 +5538,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "vsync-use-glfinish", no_argument, NULL, 311 }, { "xrender-sync", no_argument, NULL, 312 }, { "xrender-sync-fence", no_argument, NULL, 313 }, + { "show-all-xerrors", no_argument, NULL, 314 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -5542,6 +5559,8 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { ps->o.display = mstrcpy(optarg); else if ('S' == o) ps->o.synchronize = true; + else if (314 == o) + ps->o.show_all_xerrors = true; else if ('?' == o || ':' == o) usage(1); } @@ -5595,6 +5614,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { break; case 'd': case 'S': + case 314: break; P_CASELONG('D', fade_delta); case 'I': From 5794e0988f13a75e15812930f70795c5f38449e0 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sat, 19 Apr 2014 21:52:20 +0800 Subject: [PATCH 08/25] Bug fix #190: Copy shadow/fade state from last paint on unmapped wins Copy shadow/fade/color-inversion/background-blur state from last paint on unmapped windows. I hope it doesn't have unexpected side effects. (#190) --- common.h | 8 +++++ compton.c | 103 +++++++++++++++++++++++++++++++++++++----------------- compton.h | 9 +++++ 3 files changed, 88 insertions(+), 32 deletions(-) diff --git a/common.h b/common.h index 5b6a266a6..d563c3764 100644 --- a/common.h +++ b/common.h @@ -1113,6 +1113,8 @@ typedef struct _win { /// Do not fade if it's false. Change on window type change. /// Used by fading blacklist in the future. bool fade; + /// Fade state on last paint. + bool fade_last; /// Override value of window fade state. Set by D-Bus method calls. switch_t fade_force; /// Callback to be called after fading completed. @@ -1127,6 +1129,8 @@ typedef struct _win { // Shadow-related members /// Whether a window has shadow. Calculated. bool shadow; + /// Shadow state on last paint. + bool shadow_last; /// Override value of window shadow state. Set by D-Bus method calls. switch_t shadow_force; /// Opacity of the shadow. Affected by window opacity and frame opacity. @@ -1151,12 +1155,16 @@ typedef struct _win { /// Whether to invert window color. bool invert_color; + /// Color inversion state on last paint. + bool invert_color_last; /// Override value of window color inversion state. Set by D-Bus method /// calls. switch_t invert_color_force; /// Whether to blur window background. bool blur_background; + /// Background state on last paint. + bool blur_background_last; #ifdef CONFIG_VSYNC_OPENGL_GLSL /// Textures and FBO background blur use. diff --git a/compton.c b/compton.c index f58661b7d..483ddd27c 100644 --- a/compton.c +++ b/compton.c @@ -1119,6 +1119,14 @@ paint_preprocess(session_t *ps, win *list) { free_region(ps, &w->reg_ignore); } + // Restore flags from last paint if the window is being faded out + if (IsUnmapped == w->a.map_state) { + win_set_shadow(ps, w, w->shadow_last); + w->fade = w->fade_last; + win_set_invert_color(ps, w, w->invert_color_last); + win_set_blur_background(ps, w, w->blur_background_last); + } + // Update window opacity target and dim state if asked if (WFLAG_OPCT_CHANGE & w->flags) { calc_opacity(ps, w); @@ -1255,8 +1263,17 @@ paint_preprocess(session_t *ps, win *list) { check_fade_fin(ps, w); } - if (!destroyed) + if (!destroyed) { w->to_paint = to_paint; + + if (w->to_paint) { + // Save flags + w->shadow_last = w->shadow; + w->fade_last = w->fade; + w->invert_color_last = w->invert_color; + w->blur_background_last = w->blur_background; + } + } } @@ -2456,39 +2473,55 @@ win_update_prop_shadow(session_t *ps, win *w) { win_determine_shadow(ps, w); } +static void +win_set_shadow(session_t *ps, win *w, bool shadow_new) { + if (w->shadow == shadow_new) return; + + w->shadow = shadow_new; + + // Window extents need update on shadow state change + // Shadow geometry currently doesn't change on shadow state change + // calc_shadow_geometry(ps, w); + if (w->extents) { + // Mark the old extents as damaged if the shadow is removed + if (!w->shadow) + add_damage(ps, w->extents); + else + free_region(ps, &w->extents); + w->extents = win_extents(ps, w); + // Mark the new extents as damaged if the shadow is added + if (w->shadow) + add_damage_win(ps, w); + } +} + /** * Determine if a window should have shadow, and update things depending * on shadow state. */ static void win_determine_shadow(session_t *ps, win *w) { - bool shadow_old = w->shadow; + bool shadow_new = w->shadow; if (UNSET != w->shadow_force) - w->shadow = w->shadow_force; + shadow_new = w->shadow_force; else if (IsViewable == w->a.map_state) - w->shadow = (ps->o.wintype_shadow[w->window_type] + shadow_new = (ps->o.wintype_shadow[w->window_type] && !win_match(ps, w, ps->o.shadow_blacklist, &w->cache_sblst) && !(ps->o.shadow_ignore_shaped && w->bounding_shaped && !w->rounded_corners) && !(ps->o.respect_prop_shadow && 0 == w->prop_shadow)); - // Window extents need update on shadow state change - if (w->shadow != shadow_old) { - // Shadow geometry currently doesn't change on shadow state change - // calc_shadow_geometry(ps, w); - if (w->extents) { - // Mark the old extents as damaged if the shadow is removed - if (!w->shadow) - add_damage(ps, w->extents); - else - free_region(ps, &w->extents); - w->extents = win_extents(ps, w); - // Mark the new extents as damaged if the shadow is added - if (w->shadow) - add_damage_win(ps, w); - } - } + win_set_shadow(ps, w, shadow_new); +} + +static void +win_set_invert_color(session_t *ps, win *w, bool invert_color_new) { + if (w->invert_color == invert_color_new) return; + + w->invert_color = invert_color_new; + + add_damage_win(ps, w); } /** @@ -2496,15 +2529,27 @@ win_determine_shadow(session_t *ps, win *w) { */ static void win_determine_invert_color(session_t *ps, win *w) { - bool invert_color_old = w->invert_color; + bool invert_color_new = w->invert_color; if (UNSET != w->invert_color_force) - w->invert_color = w->invert_color_force; + invert_color_new = w->invert_color_force; else if (IsViewable == w->a.map_state) - w->invert_color = win_match(ps, w, ps->o.invert_color_list, + invert_color_new = win_match(ps, w, ps->o.invert_color_list, &w->cache_ivclst); - if (w->invert_color != invert_color_old) + win_set_invert_color(ps, w, invert_color_new); +} + +static void +win_set_blur_background(session_t *ps, win *w, bool blur_background_new) { + if (w->blur_background == blur_background_new) return; + + w->blur_background = blur_background_new; + + // Only consider window damaged if it's previously painted with background + // blurred + if ((WMODE_SOLID != w->mode + || (ps->o.blur_background_frame && w->frame_opacity))) add_damage_win(ps, w); } @@ -2516,16 +2561,10 @@ win_determine_blur_background(session_t *ps, win *w) { if (IsViewable != w->a.map_state) return; - bool blur_background_old = w->blur_background; - - w->blur_background = ps->o.blur_background + bool blur_background_new = ps->o.blur_background && !win_match(ps, w, ps->o.blur_background_blacklist, &w->cache_bbblst); - // Only consider window damaged if it's previously painted with background - // blurred - if (w->blur_background != blur_background_old && (WMODE_SOLID != w->mode - || (ps->o.blur_background_frame && w->frame_opacity))) - add_damage_win(ps, w); + win_set_blur_background(ps, w, blur_background_new); } /** diff --git a/compton.h b/compton.h index a4e838efb..7eff0a390 100644 --- a/compton.h +++ b/compton.h @@ -831,12 +831,21 @@ win_update_prop_shadow_raw(session_t *ps, win *w); static void win_update_prop_shadow(session_t *ps, win *w); +static void +win_set_shadow(session_t *ps, win *w, bool shadow_new); + static void win_determine_shadow(session_t *ps, win *w); +static void +win_set_invert_color(session_t *ps, win *w, bool invert_color_new); + static void win_determine_invert_color(session_t *ps, win *w); +static void +win_set_blur_background(session_t *ps, win *w, bool blur_background_new); + static void win_determine_blur_background(session_t *ps, win *w); From 6a5738080a69b6978c81a257c4784ab67ce8de92 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Mon, 21 Apr 2014 07:49:29 +0800 Subject: [PATCH 09/25] Misc: Fix a possible assert() failure --- compton.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compton.c b/compton.c index 483ddd27c..14d7d0739 100644 --- a/compton.c +++ b/compton.c @@ -3401,7 +3401,7 @@ win_update_focused(session_t *ps, win *w) { || (ps->o.mark_wmwin_focused && w->wmwin) || (ps->o.mark_ovredir_focused && w->id == w->client_win && !w->wmwin) - || win_match(ps, w, ps->o.focus_blacklist, &w->cache_fcblst)) + || (IsViewable == w->a.map_state && win_match(ps, w, ps->o.focus_blacklist, &w->cache_fcblst))) w->focused = true; // If window grouping detection is enabled, mark the window active if From 5df42e8eb18437bdaa431b8fd5c422c66d67b75f Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Mon, 21 Apr 2014 22:45:27 +0800 Subject: [PATCH 10/25] Bug fix #191: Add rounded-corners detection to --unredir-if-possible Add `bounding_shape` and `rounded_corners` as condition match target. Deprecate --shadow-ignore-shaped. Add rounded-corners detection to win_is_fullscreen(). Slightly modify win_rounded_corners() logic. Thanks to tdryer for reporting. (#191) --- c2.c | 3 +++ c2.h | 4 ++++ common.h | 2 +- compton.c | 15 ++++++++++----- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/c2.c b/c2.c index f70dcb5a7..6baf1337e 100644 --- a/c2.c +++ b/c2.c @@ -764,6 +764,7 @@ c2_l_postprocess(session_t *ps, c2_l_t *pleaf) { if (pleaf->predef) { switch (pleaf->predef) { case C2_L_PFOCUSED: ps->o.track_focus = true; break; + // case C2_L_PROUNDED: ps->o.detect_rounded_corners = true; break; case C2_L_PNAME: case C2_L_PCLASSG: case C2_L_PCLASSI: @@ -1057,6 +1058,8 @@ c2_match_once_leaf(session_t *ps, win *w, const c2_l_t *pleaf, case C2_L_PARGB: tgt = (WMODE_ARGB == w->mode); break; case C2_L_PFOCUSED: tgt = win_is_focused_real(ps, w); break; case C2_L_PWMWIN: tgt = w->wmwin; break; + case C2_L_PBSHAPED: tgt = w->bounding_shaped; break; + case C2_L_PROUNDED: tgt = w->rounded_corners; break; case C2_L_PCLIENT: tgt = w->client_win; break; case C2_L_PLEADER: tgt = w->leader; break; default: *perr = true; assert(0); break; diff --git a/c2.h b/c2.h index 3c56e2fa7..e9aaf8b59 100644 --- a/c2.h +++ b/c2.h @@ -113,6 +113,8 @@ struct _c2_l { C2_L_PARGB, C2_L_PFOCUSED, C2_L_PWMWIN, + C2_L_PBSHAPED, + C2_L_PROUNDED, C2_L_PCLIENT, C2_L_PWINDOWTYPE, C2_L_PLEADER, @@ -201,6 +203,8 @@ const static c2_predef_t C2_PREDEFS[] = { [C2_L_PARGB ] = { "argb" , C2_L_TCARDINAL , 0 }, [C2_L_PFOCUSED ] = { "focused" , C2_L_TCARDINAL , 0 }, [C2_L_PWMWIN ] = { "wmwin" , C2_L_TCARDINAL , 0 }, + [C2_L_PBSHAPED ] = { "bounding_shaped" , C2_L_TCARDINAL , 0 }, + [C2_L_PROUNDED ] = { "rounded_corners" , C2_L_TCARDINAL , 0 }, [C2_L_PCLIENT ] = { "client" , C2_L_TWINDOW , 0 }, [C2_L_PWINDOWTYPE ] = { "window_type" , C2_L_TSTRING , 0 }, [C2_L_PLEADER ] = { "leader" , C2_L_TWINDOW , 0 }, diff --git a/common.h b/common.h index d563c3764..bbbfeae8e 100644 --- a/common.h +++ b/common.h @@ -1962,7 +1962,7 @@ rect_is_fullscreen(session_t *ps, int x, int y, unsigned wid, unsigned hei) { static inline bool win_is_fullscreen(session_t *ps, const win *w) { return rect_is_fullscreen(ps, w->a.x, w->a.y, w->widthb, w->heightb) - && !w->bounding_shaped; + && (!w->bounding_shaped || w->rounded_corners); } /** diff --git a/compton.c b/compton.c index 14d7d0739..00464ffc0 100644 --- a/compton.c +++ b/compton.c @@ -647,6 +647,8 @@ wid_get_prop_adv(const session_t *ps, Window w, Atom atom, long offset, */ static void win_rounded_corners(session_t *ps, win *w) { + w->rounded_corners = false; + if (!w->bounding_shaped) return; @@ -676,11 +678,9 @@ win_rounded_corners(session_t *ps, win *w) { for (i = 0; i < nrects; ++i) if (rects[i].width >= minwidth && rects[i].height >= minheight) { w->rounded_corners = true; - cxfree(rects); - return; + break; } - w->rounded_corners = false; cxfree(rects); } @@ -4483,10 +4483,15 @@ usage(int ret) { "--no-fading-openclose\n" " Do not fade on window open/close.\n" "--shadow-ignore-shaped\n" - " Do not paint shadows on shaped windows.\n" + " Do not paint shadows on shaped windows. (Deprecated, use\n" + " --shadow-exclude \'bounding_shaped\' or\n" + " --shadow-exclude \'bounding_shaped && !rounded_corners\' instead.)\n" "--detect-rounded-corners\n" " Try to detect windows with rounded corners and don't consider\n" - " them shaped windows.\n" + " them shaped windows. Affects --shadow-ignore-shaped,\n" + " --unredir-if-possible, and possibly others. You need to turn this\n" + " on manually if you want to match against rounded_corners in\n" + " conditions.\n" "--detect-client-opacity\n" " Detect _NET_WM_OPACITY on client windows, useful for window\n" " managers not passing _NET_WM_OPACITY of client windows to frame\n" From 6449cc2049b073e27ac49099ce2c1430ec964199 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Mon, 28 Apr 2014 21:21:16 +0800 Subject: [PATCH 11/25] Bug fix #194: Fix assertion failure in some cases Fix assertion failure when evaluating --unredir-if-possible-exclude or --paint-exclude on unmapped windows. Thanks to ppuryear for reporting. (#194) --- compton.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compton.c b/compton.c index 00464ffc0..4c432baed 100644 --- a/compton.c +++ b/compton.c @@ -2623,10 +2623,10 @@ win_on_factor_change(session_t *ps, win *w) { win_determine_blur_background(ps, w); if (ps->o.opacity_rules) win_update_opacity_rule(ps, w); - if (ps->o.paint_blacklist) + if (IsViewable == w->a.map_state && ps->o.paint_blacklist) w->paint_excluded = win_match(ps, w, ps->o.paint_blacklist, &w->cache_pblst); - if (ps->o.unredir_if_possible_blacklist) + if (IsViewable == w->a.map_state && ps->o.unredir_if_possible_blacklist) w->unredir_if_possible_excluded = win_match(ps, w, ps->o.unredir_if_possible_blacklist, &w->cache_uipblst); } From 12225bcad2ad98b011a809ec882452b9d0242caa Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sat, 10 May 2014 12:21:23 +0800 Subject: [PATCH 12/25] Bug fix #195: Pointer incompatibility with libconfig-1.3* Fix pointer incompatibility with libconfig-1.3*. Thanks to sstewartgallus for reporting. (#195) --- compton.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compton.c b/compton.c index 4c432baed..eea986539 100644 --- a/compton.c +++ b/compton.c @@ -5458,7 +5458,7 @@ parse_config(session_t *ps, struct options_tmp *pcfgtmp) { && !parse_conv_kern_lst(ps, sval, ps->o.blur_kerns, MAX_BLUR_PASS)) exit(1); // --resize-damage - config_lookup_int(&cfg, "resize-damage", &ps->o.resize_damage); + lcfg_lookup_int(&cfg, "resize-damage", &ps->o.resize_damage); // --glx-no-stencil lcfg_lookup_bool(&cfg, "glx-no-stencil", &ps->o.glx_no_stencil); // --glx-copy-from-front From 0fa155f566d7d1d290e4b922859410b5f76f9dfa Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Fri, 16 May 2014 15:18:17 +0800 Subject: [PATCH 13/25] Feature: #183 custom window shader & #193 --no-fading-destroyed-argb - Add --glx-fshader-win, which specifies a custom fragment shader for painting windows. compton-default-fshader-win.glsl is the shader with default behavior, and compton-fake-transparency-fshader-win.glsl provides a template of fake transparency. (#183) - Add --force-win-blend to force all windows to be painted with blending. - Add --no-fading-destroyed-argb, as a workaround of bugs in some WMs. (#193) --- common.h | 76 +++++++++++++-- compton.c | 73 ++++++++++++--- compton.h | 27 +++++- opengl.c | 275 +++++++++++++++++++++++++++++++++++++++--------------- 4 files changed, 352 insertions(+), 99 deletions(-) diff --git a/common.h b/common.h index bbbfeae8e..0f8cc39ac 100644 --- a/common.h +++ b/common.h @@ -469,6 +469,25 @@ typedef struct { /// Height of the textures. int height; } glx_blur_cache_t; + +typedef struct { + /// GLSL program. + GLuint prog; + /// Location of uniform "opacity" in window GLSL program. + GLint unifm_opacity; + /// Location of uniform "invert_color" in blur GLSL program. + GLint unifm_invert_color; + /// Location of uniform "tex" in window GLSL program. + GLint unifm_tex; +} glx_prog_main_t; + +#define GLX_PROG_MAIN_INIT { \ + .prog = 0, \ + .unifm_opacity = -1, \ + .unifm_invert_color = -1, \ + .unifm_tex = -1, \ +} + #endif typedef struct { @@ -536,10 +555,12 @@ typedef struct _options_t { int glx_swap_method; /// Whether to use GL_EXT_gpu_shader4 to (hopefully) accelerates blurring. bool glx_use_gpushader4; - /// Whether to try to detect WM windows and mark them as focused. - bool mark_wmwin_focused; - /// Whether to mark override-redirect windows as focused. - bool mark_ovredir_focused; + /// Custom fragment shader for painting windows, as a string. + char *glx_fshader_win_str; +#ifdef CONFIG_VSYNC_OPENGL_GLSL + /// Custom GLX program used for painting window. + glx_prog_main_t glx_prog_win; +#endif /// Whether to fork to background. bool fork_after_register; /// Whether to detect rounded corners. @@ -547,6 +568,8 @@ typedef struct _options_t { /// Whether to paint on X Composite overlay window instead of root /// window. bool paint_on_overlay; + /// Force painting of window content with blending. + bool force_win_blend; /// Resize damage for a specific number of pixels. int resize_damage; /// Whether to unredirect all windows if a full-screen opaque window @@ -622,6 +645,8 @@ typedef struct _options_t { time_ms_t fade_delta; /// Whether to disable fading on window open/close. bool no_fading_openclose; + /// Whether to disable fading on ARGB managed destroyed windows. + bool no_fading_destroyed_argb; /// Fading blacklist. A linked list of conditions. c2_lptr_t *fade_blacklist; @@ -672,6 +697,10 @@ typedef struct _options_t { // === Focus related === /// Consider windows of specific types to be always focused. bool wintype_focus[NUM_WINTYPES]; + /// Whether to try to detect WM windows and mark them as focused. + bool mark_wmwin_focused; + /// Whether to mark override-redirect windows as focused. + bool mark_ovredir_focused; /// Whether to use EWMH _NET_ACTIVE_WINDOW to find active window. bool use_ewmh_active_win; /// A list of windows always to be considered focused. @@ -1965,6 +1994,14 @@ win_is_fullscreen(session_t *ps, const win *w) { && (!w->bounding_shaped || w->rounded_corners); } +/** + * Check if a window will be painted solid. + */ +static inline bool +win_is_solid(session_t *ps, const win *w) { + return WMODE_SOLID == w->mode && !ps->o.force_win_blend; +} + /** * Determine if a window has a specific property. * @@ -2073,6 +2110,13 @@ glx_on_root_change(session_t *ps); bool glx_init_blur(session_t *ps); +#ifdef CONFIG_VSYNC_OPENGL_GLSL +bool +glx_load_prog_main(session_t *ps, + const char *vshader_str, const char *fshader_str, + glx_prog_main_t *pprogram); +#endif + bool glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, unsigned width, unsigned height, unsigned depth); @@ -2108,10 +2152,24 @@ glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, float z, GLfloat factor, XserverRegion reg_tgt, const reg_data_t *pcache_reg); bool -glx_render(session_t *ps, const glx_texture_t *ptex, +glx_render_(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx, int dy, int width, int height, int z, - double opacity, bool neg, - XserverRegion reg_tgt, const reg_data_t *pcache_reg); + double opacity, bool argb, bool neg, + XserverRegion reg_tgt, const reg_data_t *pcache_reg +#ifdef CONFIG_VSYNC_OPENGL_GLSL + , const glx_prog_main_t *pprogram +#endif + ); + +#ifdef CONFIG_VSYNC_OPENGL_GLSL +#define \ + glx_render(ps, ptex, x, y, dx, dy, width, height, z, opacity, argb, neg, reg_tgt, pcache_reg, pprogram) \ + glx_render_(ps, ptex, x, y, dx, dy, width, height, z, opacity, argb, neg, reg_tgt, pcache_reg, pprogram) +#else +#define \ + glx_render(ps, ptex, x, y, dx, dy, width, height, z, opacity, argb, neg, reg_tgt, pcache_reg, pprogram) \ + glx_render_(ps, ptex, x, y, dx, dy, width, height, z, opacity, argb, neg, reg_tgt, pcache_reg) +#endif void glx_swap_copysubbuffermesa(session_t *ps, XserverRegion reg); @@ -2122,6 +2180,10 @@ glx_create_shader(GLenum shader_type, const char *shader_str); GLuint glx_create_program(const GLuint * const shaders, int nshaders); + +GLuint +glx_create_program_from_str(const char *vert_shader_str, + const char *frag_shader_str); #endif /** diff --git a/compton.c b/compton.c index eea986539..dce4d0963 100644 --- a/compton.c +++ b/compton.c @@ -1206,7 +1206,7 @@ paint_preprocess(session_t *ps, win *list) { // If the window is solid, we add the window region to the // ignored region - if (WMODE_SOLID == w->mode) { + if (win_is_solid(ps, w)) { if (!w->frame_opacity) { if (w->border_size) w->reg_ignore = copy_region(ps, w->border_size); @@ -1240,7 +1240,7 @@ paint_preprocess(session_t *ps, win *list) { // is not correctly set. if (ps->o.unredir_if_possible && is_highest && to_paint) { is_highest = false; - if (WMODE_SOLID == w->mode + if (win_is_solid(ps, w) && (!w->frame_opacity || !win_has_frame(w)) && win_is_fullscreen(ps, w) && !w->unredir_if_possible_excluded) @@ -1316,7 +1316,7 @@ win_paint_shadow(session_t *ps, win *w, render(ps, 0, 0, w->a.x + w->shadow_dx, w->a.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, pcache_reg); + w->shadow_paint.pict, w->shadow_paint.ptex, reg_paint, pcache_reg, NULL); } /** @@ -1472,7 +1472,7 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer, // Minimize the region we try to blur, if the window itself is not // opaque, only the frame is. XserverRegion reg_noframe = None; - if (WMODE_SOLID == w->mode) { + if (win_is_solid(ps, w)) { XserverRegion reg_all = border_size(ps, w, false); reg_noframe = win_get_region_noframe(ps, w, false); XFixesSubtractRegion(ps->dpy, reg_noframe, reg_all, reg_noframe); @@ -1496,10 +1496,14 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer, } static void -render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, +render_(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, double opacity, bool argb, bool neg, Picture pict, glx_texture_t *ptex, - XserverRegion reg_paint, const reg_data_t *pcache_reg) { + XserverRegion reg_paint, const reg_data_t *pcache_reg +#ifdef CONFIG_VSYNC_OPENGL_GLSL + , const glx_prog_main_t *pprogram +#endif + ) { switch (ps->o.backend) { case BKEND_XRENDER: case BKEND_XR_GLX_HYBRID: @@ -1515,7 +1519,7 @@ render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, #ifdef CONFIG_VSYNC_OPENGL case BKEND_GLX: glx_render(ps, ptex, x, y, dx, dy, wid, hei, - ps->glx_z, opacity, neg, reg_paint, pcache_reg); + ps->glx_z, opacity, argb, neg, reg_paint, pcache_reg, pprogram); ps->glx_z += 1; break; #endif @@ -1911,7 +1915,7 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t if (!is_region_empty(ps, reg_paint, &cache_reg)) { set_tgt_clip(ps, reg_paint, &cache_reg); // Blur window background - if (w->blur_background && (WMODE_SOLID != w->mode + if (w->blur_background && (!win_is_solid(ps, w) || (ps->o.blur_background_frame && w->frame_opacity))) { win_blur_background(ps, w, ps->tgt_buffer.pict, reg_paint, &cache_reg); } @@ -1992,7 +1996,8 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t glFlush(); glXWaitX(); glx_render(ps, ps->tgt_buffer.ptex, 0, 0, 0, 0, - ps->root_width, ps->root_height, 0, 1.0, false, region_real, NULL); + ps->root_width, ps->root_height, 0, 1.0, false, false, + region_real, NULL, NULL); // No break here! case BKEND_GLX: if (ps->o.glx_use_copysubbuffermesa) @@ -2396,6 +2401,12 @@ win_determine_fade(session_t *ps, win *w) { w->fade = w->fade_force; else if (ps->o.no_fading_openclose && w->in_openclose) w->fade = false; + else if (ps->o.no_fading_destroyed_argb && w->destroyed + && WMODE_ARGB == w->mode && w->client_win && w->client_win != w->id) { + w->fade = false; + // Prevent it from being overwritten by last-paint value + w->fade_last = false; + } // Ignore other possible causes of fading state changes after window // gets unmapped else if (IsViewable != w->a.map_state) { @@ -2548,8 +2559,8 @@ win_set_blur_background(session_t *ps, win *w, bool blur_background_new) { // Only consider window damaged if it's previously painted with background // blurred - if ((WMODE_SOLID != w->mode - || (ps->o.blur_background_frame && w->frame_opacity))) + if (!win_is_solid(ps, w) + || (ps->o.blur_background_frame && w->frame_opacity)) add_damage_win(ps, w); } @@ -3206,6 +3217,9 @@ destroy_win(session_t *ps, Window id) { w->destroyed = true; + if (ps->o.no_fading_destroyed_argb) + win_determine_fade(ps, w); + // Set fading callback set_fade_callback(ps, w, destroy_callback, false); @@ -4482,6 +4496,9 @@ usage(int ret) { " Mark windows that have no WM frame as active.\n" "--no-fading-openclose\n" " Do not fade on window open/close.\n" + "--no-fading-destroyed-argb\n" + " Do not fade destroyed ARGB windows with WM frame. Workaround of bugs\n" + " in Openbox, Fluxbox, etc.\n" "--shadow-ignore-shaped\n" " Do not paint shadows on shaped windows. (Deprecated, use\n" " --shadow-exclude \'bounding_shaped\' or\n" @@ -4674,6 +4691,12 @@ usage(int ret) { #else #define WARNING #endif + "--glx-fshader-win shader\n" + " GLX backend: Use specified GLSL fragment shader for rendering window\n" + " contents.\n" + "--force-win-blend\n" + " Force all windows to be painted with blending. Useful if you have a\n" + " --glx-fshader-win that could turn opaque pixels transparent.\n" "--dbus\n" " Enable remote control via D-Bus. See the D-BUS API section in the\n" " man page for more details." WARNING "\n" @@ -5367,6 +5390,9 @@ parse_config(session_t *ps, struct options_tmp *pcfgtmp) { wintype_arr_enable(ps->o.wintype_fade); // --no-fading-open-close lcfg_lookup_bool(&cfg, "no-fading-openclose", &ps->o.no_fading_openclose); + // --no-fading-destroyed-argb + lcfg_lookup_bool(&cfg, "no-fading-destroyed-argb", + &ps->o.no_fading_destroyed_argb); // --shadow-red config_lookup_float(&cfg, "shadow-red", &ps->o.shadow_red); // --shadow-green @@ -5583,6 +5609,9 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "xrender-sync", no_argument, NULL, 312 }, { "xrender-sync-fence", no_argument, NULL, 313 }, { "show-all-xerrors", no_argument, NULL, 314 }, + { "no-fading-destroyed-argb", no_argument, NULL, 315 }, + { "force-win-blend", no_argument, NULL, 316 }, + { "glx-fshader-win", required_argument, NULL, 317 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -5838,6 +5867,11 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { P_CASEBOOL(311, vsync_use_glfinish); P_CASEBOOL(312, xrender_sync); P_CASEBOOL(313, xrender_sync_fence); + P_CASEBOOL(315, no_fading_destroyed_argb); + P_CASEBOOL(316, force_win_blend); + case 317: + ps->o.glx_fshader_win_str = mstrcpy(optarg); + break; default: usage(1); break; @@ -6776,6 +6810,9 @@ session_init(session_t *ps_old, int argc, char **argv) { .backend = BKEND_XRENDER, .glx_no_stencil = false, .glx_copy_from_front = false, +#ifdef CONFIG_VSYNC_OPENGL_GLSL + .glx_prog_win = GLX_PROG_MAIN_INIT, +#endif .mark_wmwin_focused = false, .mark_ovredir_focused = false, .fork_after_register = false, @@ -6818,6 +6855,7 @@ session_init(session_t *ps_old, int argc, char **argv) { .fade_out_step = 0.03 * OPAQUE, .fade_delta = 10, .no_fading_openclose = false, + .no_fading_destroyed_argb = false, .fade_blacklist = NULL, .wintype_opacity = { 0.0 }, @@ -7146,6 +7184,17 @@ session_init(session_t *ps_old, int argc, char **argv) { #endif } + // Initialize window GL shader + if (BKEND_GLX == ps->o.backend && ps->o.glx_fshader_win_str) { +#ifdef CONFIG_VSYNC_OPENGL_GLSL + if (!glx_load_prog_main(ps, NULL, ps->o.glx_fshader_win_str, &ps->o.glx_prog_win)) + exit(1); +#else + printf_errf("(): GLSL supported not compiled in, can't load shader."); + exit(1); +#endif + } + // Initialize software optimization if (ps->o.sw_opti) ps->o.sw_opti = swopti_init(ps); @@ -7227,7 +7276,6 @@ session_init(session_t *ps_old, int argc, char **argv) { cxfree(children); } - if (ps->o.track_focus) { recheck_focus(ps); } @@ -7395,6 +7443,7 @@ session_destroy(session_t *ps) { free(ps->pfds_read); free(ps->pfds_write); free(ps->pfds_except); + free(ps->o.glx_fshader_win_str); free_xinerama_info(ps); #ifdef CONFIG_VSYNC_OPENGL diff --git a/compton.h b/compton.h index 7eff0a390..524e81ed9 100644 --- a/compton.h +++ b/compton.h @@ -675,20 +675,37 @@ static win * paint_preprocess(session_t *ps, win *list); static void -render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, +render_(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, double opacity, bool argb, bool neg, Picture pict, glx_texture_t *ptex, - XserverRegion reg_paint, const reg_data_t *pcache_reg); + XserverRegion reg_paint, const reg_data_t *pcache_reg +#ifdef CONFIG_VSYNC_OPENGL_GLSL + , const glx_prog_main_t *pprogram +#endif + ); + +#ifdef CONFIG_VSYNC_OPENGL_GLSL +#define \ + render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, pict, ptex, reg_paint, pcache_reg, pprogram) \ + render_(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, pict, ptex, reg_paint, pcache_reg, pprogram) +#else +#define \ + render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, pict, ptex, reg_paint, pcache_reg, pprogram) \ + render_(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, pict, ptex, reg_paint, pcache_reg) +#endif static inline void -win_render(session_t *ps, win *w, int x, int y, int wid, int hei, double opacity, XserverRegion reg_paint, const reg_data_t *pcache_reg, Picture pict) { +win_render(session_t *ps, win *w, int x, int y, int wid, int hei, + double opacity, XserverRegion reg_paint, const reg_data_t *pcache_reg, + Picture pict) { const int dx = (w ? w->a.x: 0) + x; const int dy = (w ? w->a.y: 0) + y; - const bool argb = (w && w->mode == WMODE_ARGB); + const bool argb = (w && (WMODE_ARGB == w->mode || 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, pcache_reg); + pict, (w ? w->paint.ptex: ps->root_tile_paint.ptex), + reg_paint, pcache_reg, (w ? &ps->o.glx_prog_win: NULL)); } static inline void diff --git a/opengl.c b/opengl.c index 951d8e50a..424407f9f 100644 --- a/opengl.c +++ b/opengl.c @@ -259,6 +259,23 @@ glx_init_end: return success; } +#ifdef CONFIG_VSYNC_OPENGL_GLSL + +static void +glx_free_prog_main(session_t *ps, glx_prog_main_t *pprogram) { + if (!pprogram) + return; + if (pprogram->prog) { + glDeleteProgram(pprogram->prog); + pprogram->prog = 0; + } + pprogram->unifm_opacity = -1; + pprogram->unifm_invert_color = -1; + pprogram->unifm_tex = -1; +} + +#endif + /** * Destroy GLX related resources. */ @@ -273,6 +290,10 @@ glx_destroy(session_t *ps) { if (ppass->prog) glDeleteProgram(ppass->prog); } + + glx_free_prog_main(ps, &ps->o.glx_prog_win); + + glx_check_err(ps); #endif // Free FBConfigs @@ -439,6 +460,7 @@ glx_init_blur(session_t *ps) { P_GET_UNIFM_LOC("offset_x", unifm_offset_x); P_GET_UNIFM_LOC("offset_y", unifm_offset_y); } + #undef P_GET_UNIFM_LOC } free(extension); @@ -458,6 +480,43 @@ glx_init_blur(session_t *ps) { #endif } +#ifdef CONFIG_VSYNC_OPENGL_GLSL + +/** + * Load a GLSL main program from shader strings. + */ +bool +glx_load_prog_main(session_t *ps, + const char *vshader_str, const char *fshader_str, + glx_prog_main_t *pprogram) { + assert(pprogram); + + // Build program + pprogram->prog = glx_create_program_from_str(vshader_str, fshader_str); + if (!pprogram->prog) { + printf_errf("(): Failed to create GLSL program."); + return false; + } + + // Get uniform addresses +#define P_GET_UNIFM_LOC(name, target) { \ + pprogram->target = glGetUniformLocation(pprogram->prog, name); \ + if (pprogram->target < 0) { \ + printf_errf("(): Failed to get location of uniform '" name "'. Might be troublesome."); \ + } \ + } + P_GET_UNIFM_LOC("opacity", unifm_opacity); + P_GET_UNIFM_LOC("invert_color", unifm_invert_color); + P_GET_UNIFM_LOC("tex", unifm_tex); +#undef P_GET_UNIFM_LOC + + glx_check_err(ps); + + return true; +} + +#endif + /** * @brief Update the FBConfig of given depth. */ @@ -1314,10 +1373,14 @@ glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, float z, * @brief Render a region with texture data. */ bool -glx_render(session_t *ps, const glx_texture_t *ptex, +glx_render_(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx, int dy, int width, int height, int z, - double opacity, bool neg, - XserverRegion reg_tgt, const reg_data_t *pcache_reg) { + double opacity, bool argb, bool neg, + XserverRegion reg_tgt, const reg_data_t *pcache_reg +#ifdef CONFIG_VSYNC_OPENGL_GLSL + , const glx_prog_main_t *pprogram +#endif + ) { if (!ptex || !ptex->texture) { printf_errf("(): Missing texture."); return false; @@ -1328,8 +1391,11 @@ glx_render(session_t *ps, const glx_texture_t *ptex, return true; #endif - const bool argb = (GLX_TEXTURE_FORMAT_RGBA_EXT == - ps->glx_fbconfigs[ptex->depth]->texture_fmt); + argb = argb || (GLX_TEXTURE_FORMAT_RGBA_EXT == + ps->glx_fbconfigs[ptex->depth]->texture_fmt); +#ifdef CONFIG_VSYNC_OPENGL_GLSL + const bool has_prog = pprogram && pprogram->prog; +#endif bool dual_texture = false; // It's required by legacy versions of OpenGL to enable texture target @@ -1350,77 +1416,96 @@ glx_render(session_t *ps, const glx_texture_t *ptex, glColor4f(opacity, opacity, opacity, opacity); } - // Color negation - if (neg) { - // Simple color negation - if (!glIsEnabled(GL_BLEND)) { - glEnable(GL_COLOR_LOGIC_OP); - glLogicOp(GL_COPY_INVERTED); - } - // ARGB texture color negation - else if (argb) { - dual_texture = true; - - // Use two texture stages because the calculation is too complicated, - // thanks to madsy for providing code - // Texture stage 0 - glActiveTexture(GL_TEXTURE0); - - // Negation for premultiplied color: color = A - C - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_SUBTRACT); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_ALPHA); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); - - // Pass texture alpha through - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); - - // Texture stage 1 - glActiveTexture(GL_TEXTURE1); - glEnable(ptex->target); - glBindTexture(ptex->target, ptex->texture); - - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - - // Modulation with constant factor - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_ALPHA); - - // Modulation with constant factor - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); - - glActiveTexture(GL_TEXTURE0); - } - // RGB blend color negation - else { - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - - // Modulation with constant factor - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); - - // Modulation with constant factor - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); +#ifdef CONFIG_VSYNC_OPENGL_GLSL + if (!has_prog) +#endif + { + // The default, fixed-function path + // Color negation + if (neg) { + // Simple color negation + if (!glIsEnabled(GL_BLEND)) { + glEnable(GL_COLOR_LOGIC_OP); + glLogicOp(GL_COPY_INVERTED); + } + // ARGB texture color negation + else if (argb) { + dual_texture = true; + + // Use two texture stages because the calculation is too complicated, + // thanks to madsy for providing code + // Texture stage 0 + glActiveTexture(GL_TEXTURE0); + + // Negation for premultiplied color: color = A - C + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_SUBTRACT); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_ALPHA); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + + // Pass texture alpha through + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + + // Texture stage 1 + glActiveTexture(GL_TEXTURE1); + glEnable(ptex->target); + glBindTexture(ptex->target, ptex->texture); + + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + + // Modulation with constant factor + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_ALPHA); + + // Modulation with constant factor + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); + + glActiveTexture(GL_TEXTURE0); + } + // RGB blend color negation + else { + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + + // Modulation with constant factor + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + + // Modulation with constant factor + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); + } } } +#ifdef CONFIG_VSYNC_OPENGL_GLSL + else { + // Programmable path + assert(pprogram->prog); + glUseProgram(pprogram->prog); + if (pprogram->unifm_opacity >= 0) + glUniform1f(pprogram->unifm_opacity, opacity); + if (pprogram->unifm_invert_color >= 0) + glUniform1i(pprogram->unifm_invert_color, neg); + if (pprogram->unifm_tex >= 0) + glUniform1i(pprogram->unifm_tex, 0); + } +#endif #ifdef DEBUG_GLX printf_dbgf("(): Draw: %d, %d, %d, %d -> %d, %d (%d, %d) z %d\n", x, y, width, height, dx, dy, ptex->width, ptex->height, z); @@ -1503,6 +1588,11 @@ glx_render(session_t *ps, const glx_texture_t *ptex, glActiveTexture(GL_TEXTURE0); } +#ifdef CONFIG_VSYNC_OPENGL_GLSL + if (has_prog) + glUseProgram(0); +#endif + glx_check_err(ps); return true; @@ -1625,7 +1715,7 @@ glx_create_shader(GLenum shader_type, const char *shader_str) { bool success = false; GLuint shader = glCreateShader(shader_type); if (!shader) { - printf_errf("(): Failed to create shader with type %d.", shader_type); + printf_errf("(): Failed to create shader with type %#x.", shader_type); goto glx_create_shader_end; } glShaderSource(shader, 1, &shader_str, NULL); @@ -1701,5 +1791,40 @@ glx_create_program_end: return program; } + +/** + * @brief Create a program from vertex and fragment shader strings. + */ +GLuint +glx_create_program_from_str(const char *vert_shader_str, + const char *frag_shader_str) { + GLuint vert_shader = 0; + GLuint frag_shader = 0; + GLuint prog = 0; + + if (vert_shader_str) + vert_shader = glx_create_shader(GL_VERTEX_SHADER, vert_shader_str); + if (frag_shader_str) + frag_shader = glx_create_shader(GL_FRAGMENT_SHADER, frag_shader_str); + + { + GLuint shaders[2]; + int count = 0; + if (vert_shader) + shaders[count++] = vert_shader; + if (frag_shader) + shaders[count++] = frag_shader; + assert(count <= sizeof(shaders) / sizeof(shaders[0])); + if (count) + prog = glx_create_program(shaders, count); + } + + if (vert_shader) + glDeleteShader(vert_shader); + if (frag_shader) + glDeleteShader(frag_shader); + + return prog; +} #endif From 8df5b1d5be863bbe0fe3f88602d51bc80c79f3a3 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sun, 15 Jun 2014 11:51:59 +0800 Subject: [PATCH 14/25] Misc #205: Add 2 long options & Update documentation - Add two long variants of short options. - Update documentation. --- compton.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compton.c b/compton.c index dce4d0963..973261c46 100644 --- a/compton.c +++ b/compton.c @@ -5543,6 +5543,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "shadow-offset-y", required_argument, NULL, 't' }, { "fade-in-step", required_argument, NULL, 'I' }, { "fade-out-step", required_argument, NULL, 'O' }, + { "fade-delta", required_argument, NULL, 'D' }, { "menu-opacity", required_argument, NULL, 'm' }, { "shadow", no_argument, NULL, 'c' }, { "no-dock-shadow", no_argument, NULL, 'C' }, @@ -5550,6 +5551,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "fading", no_argument, NULL, 'f' }, { "inactive-opacity", required_argument, NULL, 'i' }, { "frame-opacity", required_argument, NULL, 'e' }, + { "daemon", no_argument, NULL, 'b' }, { "no-dnd-shadow", no_argument, NULL, 'G' }, { "shadow-red", required_argument, NULL, 257 }, { "shadow-green", required_argument, NULL, 258 }, From 1df3c8650b559daa7035f643f2db236b81abc662 Mon Sep 17 00:00:00 2001 From: Michael Reed Date: Fri, 4 Jul 2014 06:27:13 -0400 Subject: [PATCH 15/25] whitespace cleanup --- opengl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opengl.h b/opengl.h index 8628e36d3..35ccf6878 100644 --- a/opengl.h +++ b/opengl.h @@ -44,7 +44,7 @@ glx_check_err_(session_t *ps, const char *func, int line) { if (!ps->glx_context) return; GLenum err = GL_NO_ERROR; - + while (GL_NO_ERROR != (err = glGetError())) { print_timestamp(ps); printf("%s():%d: GLX error ", func, line); From 90c3a42d5539e12c26684ad43fdda2209ecd39fe Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sun, 13 Jul 2014 09:34:38 +0800 Subject: [PATCH 16/25] Misc: Add --version & --no-x-selection - Add --version. (#206) - Add --no-x-selection for debugging. (#207) --- common.h | 2 ++ compton.c | 11 ++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/common.h b/common.h index 0f8cc39ac..9f72badc8 100644 --- a/common.h +++ b/common.h @@ -598,6 +598,8 @@ typedef struct _options_t { bool synchronize; /// Whether to show all X errors. bool show_all_xerrors; + /// Whether to avoid acquiring X Selection. + bool no_x_selection; // === VSync & software optimization === /// User-specified refresh rate. diff --git a/compton.c b/compton.c index 973261c46..fb1d0b5cd 100644 --- a/compton.c +++ b/compton.c @@ -4759,7 +4759,8 @@ register_cm(session_t *ps) { printf_errf("(): Failed to set COMPTON_VERSION."); } - { + // Acquire X Selection _NET_WM_CM_S? + if (!ps->o.no_x_selection) { unsigned len = strlen(REGISTER_PROP) + 2; int s = ps->scr; @@ -5614,6 +5615,8 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "no-fading-destroyed-argb", no_argument, NULL, 315 }, { "force-win-blend", no_argument, NULL, 316 }, { "glx-fshader-win", required_argument, NULL, 317 }, + { "version", no_argument, NULL, 318 }, + { "no-x-selection", no_argument, NULL, 319 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -5636,6 +5639,10 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { ps->o.synchronize = true; else if (314 == o) ps->o.show_all_xerrors = true; + else if (318 == o) { + printf("%s\n", COMPTON_VERSION); + exit(0); + } else if ('?' == o || ':' == o) usage(1); } @@ -5690,6 +5697,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { case 'd': case 'S': case 314: + case 318: break; P_CASELONG('D', fade_delta); case 'I': @@ -5874,6 +5882,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { case 317: ps->o.glx_fshader_win_str = mstrcpy(optarg); break; + P_CASEBOOL(319, no_x_selection); default: usage(1); break; From 7e268e7b449a3bb8cf92161e58193c874c4ca589 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Mon, 28 Jul 2014 12:50:15 +0800 Subject: [PATCH 17/25] Improvement: Separate GLX parts from session_t & Attempt to fix #217 - Separate GLX parts from session_t into glx_session_t. - Add --rererdir-on-root-change and --glx-reinit-on-root-change, as possible solutions for #217. Thanks to jlindgren90 for reporting. --- common.h | 171 ++++++++++++++++++++++++++++++++++-------------------- compton.c | 126 ++++++++++++++++++++++------------------ compton.h | 12 ++-- opengl.c | 164 +++++++++++++++++++++++++++++++++------------------ opengl.h | 2 +- 5 files changed, 290 insertions(+), 185 deletions(-) diff --git a/common.h b/common.h index 9f72badc8..c1bae4223 100644 --- a/common.h +++ b/common.h @@ -443,7 +443,6 @@ struct _glx_texture { unsigned depth; bool y_inverted; }; -#endif #ifdef CONFIG_VSYNC_OPENGL_GLSL typedef struct { @@ -488,6 +487,7 @@ typedef struct { .unifm_tex = -1, \ } +#endif #endif typedef struct { @@ -584,6 +584,10 @@ typedef struct _options_t { switch_t redirected_force; /// Whether to stop painting. Controlled through D-Bus. switch_t stoppaint_force; + /// Whether to re-redirect screen on root size change. + bool reredir_on_root_change; + /// Whether to reinitialize GLX on root size change. + bool glx_reinit_on_root_change; /// Whether to enable D-Bus support. bool dbus; /// Path to log file. @@ -721,6 +725,65 @@ typedef struct _options_t { bool track_leader; } options_t; +#ifdef CONFIG_VSYNC_OPENGL +/// Structure containing GLX-dependent data for a compton session. +typedef struct { + // === OpenGL related === + /// GLX context. + GLXContext context; + /// Whether we have GL_ARB_texture_non_power_of_two. + bool has_texture_non_power_of_two; + /// Pointer to glXGetVideoSyncSGI function. + f_GetVideoSync glXGetVideoSyncSGI; + /// Pointer to glXWaitVideoSyncSGI function. + f_WaitVideoSync glXWaitVideoSyncSGI; + /// Pointer to glXGetSyncValuesOML function. + f_GetSyncValuesOML glXGetSyncValuesOML; + /// Pointer to glXWaitForMscOML function. + f_WaitForMscOML glXWaitForMscOML; + /// Pointer to glXSwapIntervalSGI function. + f_SwapIntervalSGI glXSwapIntervalProc; + /// Pointer to glXSwapIntervalMESA function. + f_SwapIntervalMESA glXSwapIntervalMESAProc; + /// Pointer to glXBindTexImageEXT function. + f_BindTexImageEXT glXBindTexImageProc; + /// Pointer to glXReleaseTexImageEXT function. + f_ReleaseTexImageEXT glXReleaseTexImageProc; + /// Pointer to glXCopySubBufferMESA function. + f_CopySubBuffer glXCopySubBufferProc; +#ifdef CONFIG_GLX_SYNC + /// Pointer to the glFenceSync() function. + f_FenceSync glFenceSyncProc; + /// Pointer to the glIsSync() function. + f_IsSync glIsSyncProc; + /// Pointer to the glDeleteSync() function. + f_DeleteSync glDeleteSyncProc; + /// Pointer to the glClientWaitSync() function. + f_ClientWaitSync glClientWaitSyncProc; + /// Pointer to the glWaitSync() function. + f_WaitSync glWaitSyncProc; + /// Pointer to the glImportSyncEXT() function. + f_ImportSyncEXT glImportSyncEXT; +#endif +#ifdef DEBUG_GLX_MARK + /// Pointer to StringMarkerGREMEDY function. + f_StringMarkerGREMEDY glStringMarkerGREMEDY; + /// Pointer to FrameTerminatorGREMEDY function. + f_FrameTerminatorGREMEDY glFrameTerminatorGREMEDY; +#endif + /// Current GLX Z value. + int z; + /// FBConfig-s for GLX pixmap of different depths. + glx_fbconfig_t *fbconfigs[OPENGL_MAX_DEPTH + 1]; +#ifdef CONFIG_VSYNC_OPENGL_GLSL + glx_blur_pass_t blur_passes[MAX_BLUR_PASS]; +#endif +} glx_session_t; + +#define CGLX_SESSION_INIT { .context = NULL } + +#endif + /// Structure containing all necessary data for a compton session. typedef struct _session_t { // === Display related === @@ -762,6 +825,10 @@ typedef struct _session_t { XdbeBackBuffer root_dbe; /// Window ID of the window we register as a symbol. Window reg_win; +#ifdef CONFIG_VSYNC_OPENGL + /// Pointer to GLX data. + glx_session_t *psglx; +#endif // === Operation related === /// Program options. @@ -804,10 +871,6 @@ typedef struct _session_t { /// Pointer to the next member of tail element of the error /// ignore linked list. ignore_t **ignore_tail; -#ifdef CONFIG_VSYNC_OPENGL - /// Current GLX Z value. - int glx_z; -#endif // Cached blur convolution kernels. XFixed *blur_kerns_cache[MAX_BLUR_PASS]; /// Reset program after next paint. @@ -867,57 +930,6 @@ typedef struct _session_t { int drm_fd; #endif -#ifdef CONFIG_VSYNC_OPENGL - // === OpenGL related === - /// GLX context. - GLXContext glx_context; - /// Whether we have GL_ARB_texture_non_power_of_two. - bool glx_has_texture_non_power_of_two; - /// Pointer to glXGetVideoSyncSGI function. - f_GetVideoSync glXGetVideoSyncSGI; - /// Pointer to glXWaitVideoSyncSGI function. - f_WaitVideoSync glXWaitVideoSyncSGI; - /// Pointer to glXGetSyncValuesOML function. - f_GetSyncValuesOML glXGetSyncValuesOML; - /// Pointer to glXWaitForMscOML function. - f_WaitForMscOML glXWaitForMscOML; - /// Pointer to glXSwapIntervalSGI function. - f_SwapIntervalSGI glXSwapIntervalProc; - /// Pointer to glXSwapIntervalMESA function. - f_SwapIntervalMESA glXSwapIntervalMESAProc; - /// Pointer to glXBindTexImageEXT function. - f_BindTexImageEXT glXBindTexImageProc; - /// Pointer to glXReleaseTexImageEXT function. - f_ReleaseTexImageEXT glXReleaseTexImageProc; - /// Pointer to glXCopySubBufferMESA function. - f_CopySubBuffer glXCopySubBufferProc; -#ifdef CONFIG_GLX_SYNC - /// Pointer to the glFenceSync() function. - f_FenceSync glFenceSyncProc; - /// Pointer to the glIsSync() function. - f_IsSync glIsSyncProc; - /// Pointer to the glDeleteSync() function. - f_DeleteSync glDeleteSyncProc; - /// Pointer to the glClientWaitSync() function. - f_ClientWaitSync glClientWaitSyncProc; - /// Pointer to the glWaitSync() function. - f_WaitSync glWaitSyncProc; - /// Pointer to the glImportSyncEXT() function. - f_ImportSyncEXT glImportSyncEXT; -#endif -#ifdef DEBUG_GLX_MARK - /// Pointer to StringMarkerGREMEDY function. - f_StringMarkerGREMEDY glStringMarkerGREMEDY; - /// Pointer to FrameTerminatorGREMEDY function. - f_FrameTerminatorGREMEDY glFrameTerminatorGREMEDY; -#endif - /// FBConfig-s for GLX pixmap of different depths. - glx_fbconfig_t *glx_fbconfigs[OPENGL_MAX_DEPTH + 1]; -#ifdef CONFIG_VSYNC_OPENGL_GLSL - glx_blur_pass_t glx_blur_passes[MAX_BLUR_PASS]; -#endif -#endif - // === X extension related === /// Event base number for X Fixes extension. int xfixes_event; @@ -1891,6 +1903,18 @@ 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_VSYNC_OPENGL + return ps->psglx && ps->psglx->context; +#else + return false; +#endif +} + /** * Check if a window is really focused. */ @@ -2106,6 +2130,9 @@ glx_init(session_t *ps, bool need_render); void glx_destroy(session_t *ps); +bool +glx_reinit(session_t *ps, bool need_render); + void glx_on_root_change(session_t *ps); @@ -2185,7 +2212,7 @@ glx_create_program(const GLuint * const shaders, int nshaders); GLuint glx_create_program_from_str(const char *vert_shader_str, - const char *frag_shader_str); + const char *frag_shader_str); #endif /** @@ -2194,7 +2221,7 @@ glx_create_program_from_str(const char *vert_shader_str, static inline void free_texture_r(session_t *ps, GLuint *ptexture) { if (*ptexture) { - assert(ps->glx_context); + assert(glx_has_context(ps)); glDeleteTextures(1, ptexture); *ptexture = 0; } @@ -2260,20 +2287,40 @@ free_texture(session_t *ps, glx_texture_t **pptex) { assert(!*pptex); } +/** + * Free GLX part of paint_t. + */ +static inline void +free_paint_glx(session_t *ps, paint_t *ppaint) { + free_texture(ps, &ppaint->ptex); +} + +/** + * Free GLX part of win. + */ +static inline void +free_win_res_glx(session_t *ps, win *w) { + free_paint_glx(ps, &w->paint); + free_paint_glx(ps, &w->shadow_paint); +#ifdef CONFIG_VSYNC_OPENGL_GLSL + free_glx_bc(ps, &w->glx_blur_cache); +#endif +} + /** * Add a OpenGL debugging marker. */ static inline void glx_mark_(session_t *ps, const char *func, XID xid, bool start) { #ifdef DEBUG_GLX_MARK - if (bkend_use_glx(ps) && ps->glStringMarkerGREMEDY) { + if (glx_has_context(ps) && ps->psglx->glStringMarkerGREMEDY) { if (!func) func = "(unknown)"; const char *postfix = (start ? " (start)": " (end)"); char *str = malloc((strlen(func) + 12 + 2 + strlen(postfix) + 5) * sizeof(char)); strcpy(str, func); sprintf(str + strlen(str), "(%#010lx)%s", xid, postfix); - ps->glStringMarkerGREMEDY(strlen(str), str); + ps->psglx->glStringMarkerGREMEDY(strlen(str), str); free(str); } #endif @@ -2287,8 +2334,8 @@ glx_mark_(session_t *ps, const char *func, XID xid, bool start) { static inline void glx_mark_frame(session_t *ps) { #ifdef DEBUG_GLX_MARK - if (bkend_use_glx(ps) && ps->glFrameTerminatorGREMEDY) - ps->glFrameTerminatorGREMEDY(); + if (glx_has_context(ps) && ps->psglx->glFrameTerminatorGREMEDY) + ps->psglx->glFrameTerminatorGREMEDY(); #endif } diff --git a/compton.c b/compton.c index fb1d0b5cd..f4f7244a5 100644 --- a/compton.c +++ b/compton.c @@ -492,14 +492,12 @@ win_build_shadow(session_t *ps, win *w, double opacity) { // Sync it once and only once xr_sync(ps, w->shadow_paint.pixmap, NULL); - bool success = paint_bind_tex(ps, &w->shadow_paint, shadow_image->width, shadow_image->height, 32, true); - XFreeGC(ps->dpy, gc); XDestroyImage(shadow_image); XFreePixmap(ps->dpy, shadow_pixmap); XRenderFreePicture(ps->dpy, shadow_picture); - return success; + return true; shadow_picture_err: if (shadow_image) @@ -1309,6 +1307,9 @@ paint_preprocess(session_t *ps, win *list) { static inline void win_paint_shadow(session_t *ps, win *w, XserverRegion reg_paint, const reg_data_t *pcache_reg) { + // 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 painting data. This is a bad sign.", w->id); return; @@ -1486,7 +1487,7 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer, #ifdef CONFIG_VSYNC_OPENGL_GLSL case BKEND_GLX: // TODO: Handle frame opacity - glx_blur_dst(ps, x, y, wid, hei, ps->glx_z - 0.5, factor_center, + glx_blur_dst(ps, x, y, wid, hei, ps->psglx->z - 0.5, factor_center, reg_paint, pcache_reg, &w->glx_blur_cache); break; #endif @@ -1519,8 +1520,8 @@ render_(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, #ifdef CONFIG_VSYNC_OPENGL case BKEND_GLX: glx_render(ps, ptex, x, y, dx, dy, wid, hei, - ps->glx_z, opacity, argb, neg, reg_paint, pcache_reg, pprogram); - ps->glx_z += 1; + ps->psglx->z, opacity, argb, neg, reg_paint, pcache_reg, pprogram); + ps->psglx->z += 1; break; #endif default: @@ -1701,7 +1702,7 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint, break; #ifdef CONFIG_VSYNC_OPENGL case BKEND_GLX: - glx_dim_dst(ps, x, y, wid, hei, ps->glx_z - 0.7, dim_opacity, + glx_dim_dst(ps, x, y, wid, hei, ps->psglx->z - 0.7, dim_opacity, reg_paint, pcache_reg); break; #endif @@ -1940,7 +1941,7 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t // effect XSync(ps->dpy, False); #ifdef CONFIG_VSYNC_OPENGL - if (ps->glx_context) { + if (glx_has_context(ps)) { if (ps->o.vsync_use_glfinish) glFinish(); else @@ -2017,7 +2018,7 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t XFlush(ps->dpy); #ifdef CONFIG_VSYNC_OPENGL - if (ps->glx_context) { + if (glx_has_context(ps)) { glFlush(); glXWaitX(); } @@ -3050,6 +3051,9 @@ restack_win(session_t *ps, win *w, Window new_above) { } } +static bool +init_filters(session_t *ps); + static void configure_win(session_t *ps, XConfigureEvent *ce) { // On root window changes @@ -3063,11 +3067,28 @@ configure_win(session_t *ps, XConfigureEvent *ce) { rebuild_shadow_exclude_reg(ps); free_all_damage_last(ps); + // Re-redirect screen if required + if (ps->o.reredir_on_root_change && ps->redirected) { + redir_stop(ps); + redir_start(ps); + } + #ifdef CONFIG_VSYNC_OPENGL + // Reinitialize GLX on root change + if (ps->o.glx_reinit_on_root_change && ps->psglx) { + if (!glx_reinit(ps, bkend_use_glx(ps))) + printf_errf("(): Failed to reinitialize GLX, troubles ahead."); + if (BKEND_GLX == ps->o.backend && !init_filters(ps)) + printf_errf("(): Failed to initialize filters."); + } + + // GLX root change callback if (BKEND_GLX == ps->o.backend) glx_on_root_change(ps); #endif + force_repaint(ps); + return; } @@ -4807,7 +4828,7 @@ fork_after(session_t *ps) { #ifdef CONFIG_VSYNC_OPENGL // GLX context must be released and reattached on fork - if (ps->glx_context && !glXMakeCurrent(ps->dpy, None, NULL)) { + if (glx_has_context(ps) && !glXMakeCurrent(ps->dpy, None, NULL)) { printf_errf("(): Failed to detach GLx context."); return false; } @@ -4825,8 +4846,8 @@ fork_after(session_t *ps) { setsid(); #ifdef CONFIG_VSYNC_OPENGL - if (ps->glx_context - && !glXMakeCurrent(ps->dpy, get_tgt_window(ps), ps->glx_context)) { + if (glx_has_context(ps) + && !glXMakeCurrent(ps->dpy, get_tgt_window(ps), ps->psglx->context)) { printf_errf("(): Failed to make GLX context current."); return false; } @@ -5617,6 +5638,8 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "glx-fshader-win", required_argument, NULL, 317 }, { "version", no_argument, NULL, 318 }, { "no-x-selection", no_argument, NULL, 319 }, + { "reredir-on-root-change", no_argument, NULL, 731 }, + { "glx-reinit-on-root-change", no_argument, NULL, 732 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -5883,6 +5906,8 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { ps->o.glx_fshader_win_str = mstrcpy(optarg); break; P_CASEBOOL(319, no_x_selection); + P_CASEBOOL(731, reredir_on_root_change); + P_CASEBOOL(732, glx_reinit_on_root_change); default: usage(1); break; @@ -6166,13 +6191,13 @@ vsync_opengl_init(session_t *ps) { return false; // Get video sync functions - if (!ps->glXGetVideoSyncSGI) - ps->glXGetVideoSyncSGI = (f_GetVideoSync) + if (!ps->psglx->glXGetVideoSyncSGI) + ps->psglx->glXGetVideoSyncSGI = (f_GetVideoSync) glXGetProcAddress((const GLubyte *) "glXGetVideoSyncSGI"); - if (!ps->glXWaitVideoSyncSGI) - ps->glXWaitVideoSyncSGI = (f_WaitVideoSync) + if (!ps->psglx->glXWaitVideoSyncSGI) + ps->psglx->glXWaitVideoSyncSGI = (f_WaitVideoSync) glXGetProcAddress((const GLubyte *) "glXWaitVideoSyncSGI"); - if (!ps->glXWaitVideoSyncSGI || !ps->glXGetVideoSyncSGI) { + if (!ps->psglx->glXWaitVideoSyncSGI || !ps->psglx->glXGetVideoSyncSGI) { printf_errf("(): Failed to get glXWait/GetVideoSyncSGI function."); return false; } @@ -6191,13 +6216,13 @@ vsync_opengl_oml_init(session_t *ps) { return false; // Get video sync functions - if (!ps->glXGetSyncValuesOML) - ps->glXGetSyncValuesOML = (f_GetSyncValuesOML) + if (!ps->psglx->glXGetSyncValuesOML) + ps->psglx->glXGetSyncValuesOML = (f_GetSyncValuesOML) glXGetProcAddress ((const GLubyte *) "glXGetSyncValuesOML"); - if (!ps->glXWaitForMscOML) - ps->glXWaitForMscOML = (f_WaitForMscOML) + if (!ps->psglx->glXWaitForMscOML) + ps->psglx->glXWaitForMscOML = (f_WaitForMscOML) glXGetProcAddress ((const GLubyte *) "glXWaitForMscOML"); - if (!ps->glXGetSyncValuesOML || !ps->glXWaitForMscOML) { + if (!ps->psglx->glXGetSyncValuesOML || !ps->psglx->glXWaitForMscOML) { printf_errf("(): Failed to get OML_sync_control functions."); return false; } @@ -6221,14 +6246,14 @@ vsync_opengl_swc_init(session_t *ps) { } // Get video sync functions - if (!ps->glXSwapIntervalProc) - ps->glXSwapIntervalProc = (f_SwapIntervalSGI) + if (!ps->psglx->glXSwapIntervalProc) + ps->psglx->glXSwapIntervalProc = (f_SwapIntervalSGI) glXGetProcAddress ((const GLubyte *) "glXSwapIntervalSGI"); - if (!ps->glXSwapIntervalProc) { + if (!ps->psglx->glXSwapIntervalProc) { printf_errf("(): Failed to get SGI_swap_control function."); return false; } - ps->glXSwapIntervalProc(1); + ps->psglx->glXSwapIntervalProc(1); return true; #else @@ -6249,14 +6274,14 @@ vsync_opengl_mswc_init(session_t *ps) { } // Get video sync functions - if (!ps->glXSwapIntervalMESAProc) - ps->glXSwapIntervalMESAProc = (f_SwapIntervalMESA) + if (!ps->psglx->glXSwapIntervalMESAProc) + ps->psglx->glXSwapIntervalMESAProc = (f_SwapIntervalMESA) glXGetProcAddress ((const GLubyte *) "glXSwapIntervalMESA"); - if (!ps->glXSwapIntervalMESAProc) { + if (!ps->psglx->glXSwapIntervalMESAProc) { printf_errf("(): Failed to get MESA_swap_control function."); return false; } - ps->glXSwapIntervalMESAProc(1); + ps->psglx->glXSwapIntervalMESAProc(1); return true; #else @@ -6273,8 +6298,8 @@ static int vsync_opengl_wait(session_t *ps) { unsigned vblank_count = 0; - ps->glXGetVideoSyncSGI(&vblank_count); - ps->glXWaitVideoSyncSGI(2, (vblank_count + 1) % 2, &vblank_count); + 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; @@ -6289,8 +6314,8 @@ static int vsync_opengl_oml_wait(session_t *ps) { int64_t ust = 0, msc = 0, sbc = 0; - ps->glXGetSyncValuesOML(ps->dpy, ps->reg_win, &ust, &msc, &sbc); - ps->glXWaitForMscOML(ps->dpy, ps->reg_win, 0, 2, (msc + 1) % 2, + 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; @@ -6299,14 +6324,14 @@ vsync_opengl_oml_wait(session_t *ps) { static void vsync_opengl_swc_deinit(session_t *ps) { // The standard says it doesn't accept 0, but in fact it probably does - if (ps->glx_context && ps->glXSwapIntervalProc) - ps->glXSwapIntervalProc(0); + if (glx_has_context(ps) && ps->psglx->glXSwapIntervalProc) + ps->psglx->glXSwapIntervalProc(0); } static void vsync_opengl_mswc_deinit(session_t *ps) { - if (ps->glx_context && ps->glXSwapIntervalMESAProc) - ps->glXSwapIntervalMESAProc(0); + if (glx_has_context(ps) && ps->psglx->glXSwapIntervalMESAProc) + ps->psglx->glXSwapIntervalMESAProc(0); } #endif @@ -6343,7 +6368,6 @@ void vsync_deinit(session_t *ps) { if (ps->o.vsync && VSYNC_FUNCS_DEINIT[ps->o.vsync]) VSYNC_FUNCS_DEINIT[ps->o.vsync](ps); - ps->o.vsync = VSYNC_NONE; } /** @@ -6383,7 +6407,7 @@ init_dbe(session_t *ps) { /** * Initialize X composite overlay window. */ -static void +static bool init_overlay(session_t *ps) { ps->overlay = XCompositeGetOverlayWindow(ps->dpy, ps->root); if (ps->overlay) { @@ -6411,6 +6435,11 @@ init_overlay(session_t *ps) { "back to painting on root window.\n"); ps->o.paint_on_overlay = false; } +#ifdef DEBUG_REDIR + printf_dbgf("(): overlay = %#010lx\n", ps->overlay); +#endif + + return ps->overlay; } /** @@ -6940,15 +6969,6 @@ session_init(session_t *ps_old, int argc, char **argv) { .drm_fd = -1, #endif -#ifdef CONFIG_VSYNC_OPENGL - .glx_context = None, - .glx_has_texture_non_power_of_two = false, - .glXGetVideoSyncSGI = NULL, - .glXWaitVideoSyncSGI = NULL, - .glXGetSyncValuesOML = NULL, - .glXWaitForMscOML = NULL, -#endif - .xfixes_event = 0, .xfixes_error = 0, .damage_event = 0, @@ -6996,14 +7016,6 @@ session_init(session_t *ps_old, int argc, char **argv) { // Allocate a session and copy default values into it session_t *ps = malloc(sizeof(session_t)); memcpy(ps, &s_def, sizeof(session_t)); -#ifdef CONFIG_VSYNC_OPENGL_GLSL - for (int i = 0; i < MAX_BLUR_PASS; ++i) { - glx_blur_pass_t *ppass = &ps->glx_blur_passes[i]; - ppass->unifm_factor_center = -1; - ppass->unifm_offset_x = -1; - ppass->unifm_offset_y = -1; - } -#endif ps_g = ps; ps->ignore_tail = &ps->ignore_head; gettimeofday(&ps->time_start, NULL); diff --git a/compton.h b/compton.h index 524e81ed9..637d13f34 100644 --- a/compton.h +++ b/compton.h @@ -270,7 +270,7 @@ free_reg_data(reg_data_t *pregd) { */ static inline void free_paint(session_t *ps, paint_t *ppaint) { - free_texture(ps, &ppaint->ptex); + free_paint_glx(ps, ppaint); free_picture(ps, &ppaint->pict); free_pixmap(ps, &ppaint->pixmap); } @@ -289,6 +289,7 @@ free_wpaint(session_t *ps, win *w) { */ static inline void free_win_res(session_t *ps, win *w) { + free_win_res_glx(ps, w); free_region(ps, &w->extents); free_paint(ps, &w->paint); free_region(ps, &w->border_size); @@ -299,9 +300,6 @@ free_win_res(session_t *ps, win *w) { free(w->class_instance); free(w->class_general); free(w->role); -#ifdef CONFIG_VSYNC_OPENGL_GLSL - free_glx_bc(ps, &w->glx_blur_cache); -#endif } /** @@ -1224,10 +1222,10 @@ swopti_handle_timeout(session_t *ps, struct timeval *ptv); static inline bool ensure_glx_context(session_t *ps) { // Create GLX context - if (!ps->glx_context) + if (!glx_has_context(ps)) glx_init(ps, false); - return ps->glx_context; + return ps->psglx->context; } #endif @@ -1274,7 +1272,7 @@ init_alpha_picts(session_t *ps); static bool init_dbe(session_t *ps); -static void +static bool init_overlay(session_t *ps); static void diff --git a/opengl.c b/opengl.c index 424407f9f..965356593 100644 --- a/opengl.c +++ b/opengl.c @@ -14,15 +14,15 @@ void xr_glx_sync(session_t *ps, Drawable d, XSyncFence *pfence) { if (*pfence) { - // GLsync sync = ps->glFenceSyncProc(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - GLsync sync = ps->glImportSyncEXT(GL_SYNC_X11_FENCE_EXT, *pfence, 0); - /* GLenum ret = ps->glClientWaitSyncProc(sync, GL_SYNC_FLUSH_COMMANDS_BIT, + // GLsync sync = ps->psglx->glFenceSyncProc(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + GLsync sync = ps->psglx->glImportSyncEXT(GL_SYNC_X11_FENCE_EXT, *pfence, 0); + /* GLenum ret = ps->psglx->glClientWaitSyncProc(sync, GL_SYNC_FLUSH_COMMANDS_BIT, 1000); assert(GL_CONDITION_SATISFIED == ret); */ XSyncTriggerFence(ps->dpy, *pfence); XFlush(ps->dpy); - ps->glWaitSyncProc(sync, 0, GL_TIMEOUT_IGNORED); - // ps->glDeleteSyncProc(sync); + ps->psglx->glWaitSyncProc(sync, 0, GL_TIMEOUT_IGNORED); + // ps->psglx->glDeleteSyncProc(sync); // XSyncResetFence(ps->dpy, *pfence); } glx_check_err(ps); @@ -98,10 +98,26 @@ glx_init(session_t *ps, bool need_render) { if (need_render && !glx_hasglxext(ps, "GLX_EXT_texture_from_pixmap")) goto glx_init_end; - if (!ps->glx_context) { + // Initialize GLX data structure + if (!ps->psglx) { + static const glx_session_t CGLX_SESSION_DEF = CGLX_SESSION_INIT; + ps->psglx = cmalloc(1, glx_session_t); + memcpy(ps->psglx, &CGLX_SESSION_DEF, sizeof(glx_session_t)); + + for (int i = 0; i < MAX_BLUR_PASS; ++i) { + glx_blur_pass_t *ppass = &ps->psglx->blur_passes[i]; + ppass->unifm_factor_center = -1; + ppass->unifm_offset_x = -1; + ppass->unifm_offset_y = -1; + } + } + + glx_session_t *psglx = ps->psglx; + + if (!psglx->context) { // Get GLX context #ifndef DEBUG_GLX_DEBUG_CONTEXT - ps->glx_context = glXCreateContext(ps->dpy, pvis, None, GL_TRUE); + psglx->context = glXCreateContext(ps->dpy, pvis, None, GL_TRUE); #else { GLXFBConfig fbconfig = get_fbconfig_from_visualinfo(ps, pvis); @@ -123,18 +139,18 @@ glx_init(session_t *ps, bool need_render) { GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB, None }; - ps->glx_context = p_glXCreateContextAttribsARB(ps->dpy, fbconfig, NULL, + psglx->context = p_glXCreateContextAttribsARB(ps->dpy, fbconfig, NULL, GL_TRUE, attrib_list); } #endif - if (!ps->glx_context) { + if (!psglx->context) { printf_errf("(): Failed to get GLX context."); goto glx_init_end; } // Attach GLX context - if (!glXMakeCurrent(ps->dpy, get_tgt_window(ps), ps->glx_context)) { + if (!glXMakeCurrent(ps->dpy, get_tgt_window(ps), psglx->context)) { printf_errf("(): Failed to attach GLX context."); goto glx_init_end; } @@ -169,52 +185,52 @@ glx_init(session_t *ps, bool need_render) { // Check GL_ARB_texture_non_power_of_two, requires a GLX context and // must precede FBConfig fetching if (need_render) - ps->glx_has_texture_non_power_of_two = glx_hasglext(ps, + psglx->has_texture_non_power_of_two = glx_hasglext(ps, "GL_ARB_texture_non_power_of_two"); // Acquire function addresses if (need_render) { #ifdef DEBUG_GLX_MARK - ps->glStringMarkerGREMEDY = (f_StringMarkerGREMEDY) + psglx->glStringMarkerGREMEDY = (f_StringMarkerGREMEDY) glXGetProcAddress((const GLubyte *) "glStringMarkerGREMEDY"); - ps->glFrameTerminatorGREMEDY = (f_FrameTerminatorGREMEDY) + psglx->glFrameTerminatorGREMEDY = (f_FrameTerminatorGREMEDY) glXGetProcAddress((const GLubyte *) "glFrameTerminatorGREMEDY"); #endif - ps->glXBindTexImageProc = (f_BindTexImageEXT) + psglx->glXBindTexImageProc = (f_BindTexImageEXT) glXGetProcAddress((const GLubyte *) "glXBindTexImageEXT"); - ps->glXReleaseTexImageProc = (f_ReleaseTexImageEXT) + psglx->glXReleaseTexImageProc = (f_ReleaseTexImageEXT) glXGetProcAddress((const GLubyte *) "glXReleaseTexImageEXT"); - if (!ps->glXBindTexImageProc || !ps->glXReleaseTexImageProc) { + if (!psglx->glXBindTexImageProc || !psglx->glXReleaseTexImageProc) { printf_errf("(): Failed to acquire glXBindTexImageEXT() / glXReleaseTexImageEXT()."); goto glx_init_end; } if (ps->o.glx_use_copysubbuffermesa) { - ps->glXCopySubBufferProc = (f_CopySubBuffer) + psglx->glXCopySubBufferProc = (f_CopySubBuffer) glXGetProcAddress((const GLubyte *) "glXCopySubBufferMESA"); - if (!ps->glXCopySubBufferProc) { + if (!psglx->glXCopySubBufferProc) { printf_errf("(): Failed to acquire glXCopySubBufferMESA()."); goto glx_init_end; } } #ifdef CONFIG_GLX_SYNC - ps->glFenceSyncProc = (f_FenceSync) + psglx->glFenceSyncProc = (f_FenceSync) glXGetProcAddress((const GLubyte *) "glFenceSync"); - ps->glIsSyncProc = (f_IsSync) + psglx->glIsSyncProc = (f_IsSync) glXGetProcAddress((const GLubyte *) "glIsSync"); - ps->glDeleteSyncProc = (f_DeleteSync) + psglx->glDeleteSyncProc = (f_DeleteSync) glXGetProcAddress((const GLubyte *) "glDeleteSync"); - ps->glClientWaitSyncProc = (f_ClientWaitSync) + psglx->glClientWaitSyncProc = (f_ClientWaitSync) glXGetProcAddress((const GLubyte *) "glClientWaitSync"); - ps->glWaitSyncProc = (f_WaitSync) + psglx->glWaitSyncProc = (f_WaitSync) glXGetProcAddress((const GLubyte *) "glWaitSync"); - ps->glImportSyncEXT = (f_ImportSyncEXT) + psglx->glImportSyncEXT = (f_ImportSyncEXT) glXGetProcAddress((const GLubyte *) "glImportSyncEXT"); - if (!ps->glFenceSyncProc || !ps->glIsSyncProc || !ps->glDeleteSyncProc - || !ps->glClientWaitSyncProc || !ps->glWaitSyncProc - || !ps->glImportSyncEXT) { + if (!psglx->glFenceSyncProc || !psglx->glIsSyncProc || !psglx->glDeleteSyncProc + || !psglx->glClientWaitSyncProc || !psglx->glWaitSyncProc + || !psglx->glImportSyncEXT) { printf_errf("(): Failed to acquire GLX sync functions."); goto glx_init_end; } @@ -281,10 +297,17 @@ glx_free_prog_main(session_t *ps, glx_prog_main_t *pprogram) { */ void glx_destroy(session_t *ps) { + if (!ps->psglx) + return; + + // Free all GLX resources of windows + for (win *w = ps->list; w; w = w->next) + free_win_res_glx(ps, w); + #ifdef CONFIG_VSYNC_OPENGL_GLSL // Free GLSL shaders/programs for (int i = 0; i < MAX_BLUR_PASS; ++i) { - glx_blur_pass_t *ppass = &ps->glx_blur_passes[i]; + glx_blur_pass_t *ppass = &ps->psglx->blur_passes[i]; if (ppass->frag_shader) glDeleteShader(ppass->frag_shader); if (ppass->prog) @@ -298,15 +321,40 @@ glx_destroy(session_t *ps) { // Free FBConfigs for (int i = 0; i <= OPENGL_MAX_DEPTH; ++i) { - free(ps->glx_fbconfigs[i]); - ps->glx_fbconfigs[i] = NULL; + free(ps->psglx->fbconfigs[i]); + ps->psglx->fbconfigs[i] = NULL; } // Destroy GLX context - if (ps->glx_context) { - glXDestroyContext(ps->dpy, ps->glx_context); - ps->glx_context = NULL; + if (ps->psglx->context) { + glXDestroyContext(ps->dpy, ps->psglx->context); + ps->psglx->context = NULL; } + + free(ps->psglx); + ps->psglx = NULL; +} + +/** + * Reinitialize GLX. + */ +bool +glx_reinit(session_t *ps, bool need_render) { + // Reinitialize VSync as well + vsync_deinit(ps); + + glx_destroy(ps); + if (!glx_init(ps, need_render)) { + printf_errf("(): Failed to initialize GLX."); + return false; + } + + if (!vsync_init(ps)) { + printf_errf("(): Failed to initialize VSync."); + return false; + } + + return true; } /** @@ -376,7 +424,7 @@ glx_init_blur(session_t *ps) { " gl_FragColor = sum / (factor_center + float(%.7g));\n" "}\n"; - const bool use_texture_rect = !ps->glx_has_texture_non_power_of_two; + const bool use_texture_rect = !ps->psglx->has_texture_non_power_of_two; const char *sampler_type = (use_texture_rect ? "sampler2DRect": "sampler2D"); const char *texture_func = (use_texture_rect ? @@ -395,7 +443,7 @@ glx_init_blur(session_t *ps) { if (!kern) break; - glx_blur_pass_t *ppass = &ps->glx_blur_passes[i]; + glx_blur_pass_t *ppass = &ps->psglx->blur_passes[i]; // Build shader { @@ -527,15 +575,15 @@ glx_update_fbconfig_bydepth(session_t *ps, int depth, glx_fbconfig_t *pfbcfg) { return; // Compare new FBConfig with current one - if (glx_cmp_fbconfig(ps, ps->glx_fbconfigs[depth], pfbcfg) < 0) { + if (glx_cmp_fbconfig(ps, ps->psglx->fbconfigs[depth], pfbcfg) < 0) { #ifdef DEBUG_GLX - printf_dbgf("(%d): %#x overrides %#x, target %#x.\n", depth, (unsigned) pfbcfg->cfg, (ps->glx_fbconfigs[depth] ? (unsigned) ps->glx_fbconfigs[depth]->cfg: 0), pfbcfg->texture_tgts); + printf_dbgf("(%d): %#x overrides %#x, target %#x.\n", depth, (unsigned) pfbcfg->cfg, (ps->psglx->fbconfigs[depth] ? (unsigned) ps->psglx->fbconfigs[depth]->cfg: 0), pfbcfg->texture_tgts); #endif - if (!ps->glx_fbconfigs[depth]) { - ps->glx_fbconfigs[depth] = malloc(sizeof(glx_fbconfig_t)); - allocchk(ps->glx_fbconfigs[depth]); + if (!ps->psglx->fbconfigs[depth]) { + ps->psglx->fbconfigs[depth] = malloc(sizeof(glx_fbconfig_t)); + allocchk(ps->psglx->fbconfigs[depth]); } - (*ps->glx_fbconfigs[depth]) = *pfbcfg; + (*ps->psglx->fbconfigs[depth]) = *pfbcfg; } } @@ -617,19 +665,19 @@ glx_update_fbconfig(session_t *ps) { cxfree(pfbcfgs); // Sanity checks - if (!ps->glx_fbconfigs[ps->depth]) { + if (!ps->psglx->fbconfigs[ps->depth]) { printf_errf("(): No FBConfig found for default depth %d.", ps->depth); return false; } - if (!ps->glx_fbconfigs[32]) { + if (!ps->psglx->fbconfigs[32]) { printf_errf("(): No FBConfig found for depth 32. Expect crazy things."); } #ifdef DEBUG_GLX printf_dbgf("(): %d-bit: %#3x, 32-bit: %#3x\n", - ps->depth, (int) ps->glx_fbconfigs[ps->depth]->cfg, - (int) ps->glx_fbconfigs[32]->cfg); + ps->depth, (int) ps->psglx->fbconfigs[ps->depth]->cfg, + (int) ps->psglx->fbconfigs[32]->cfg); #endif return true; @@ -733,7 +781,7 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, } } - const glx_fbconfig_t *pcfg = ps->glx_fbconfigs[depth]; + const glx_fbconfig_t *pcfg = ps->psglx->fbconfigs[depth]; if (!pcfg) { printf_errf("(%d): Couldn't find FBConfig with requested depth.", depth); return false; @@ -744,7 +792,7 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, // pixmap-specific parameters, and this may change in the future GLenum tex_tgt = 0; if (GLX_TEXTURE_2D_BIT_EXT & pcfg->texture_tgts - && ps->glx_has_texture_non_power_of_two) + && ps->psglx->has_texture_non_power_of_two) tex_tgt = GLX_TEXTURE_2D_EXT; else if (GLX_TEXTURE_RECTANGLE_BIT_EXT & pcfg->texture_tgts) tex_tgt = GLX_TEXTURE_RECTANGLE_EXT; @@ -809,9 +857,9 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, // The specification requires rebinding whenever the content changes... // We can't follow this, too slow. if (need_release) - ps->glXReleaseTexImageProc(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT); + ps->psglx->glXReleaseTexImageProc(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT); - ps->glXBindTexImageProc(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT, NULL); + ps->psglx->glXBindTexImageProc(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT, NULL); // Cleanup glBindTexture(ptex->target, 0); @@ -830,7 +878,7 @@ glx_release_pixmap(session_t *ps, glx_texture_t *ptex) { // Release binding if (ptex->glpixmap && ptex->texture) { glBindTexture(ptex->target, ptex->texture); - ps->glXReleaseTexImageProc(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT); + ps->psglx->glXReleaseTexImageProc(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT); glBindTexture(ptex->target, 0); } @@ -848,7 +896,7 @@ glx_release_pixmap(session_t *ps, glx_texture_t *ptex) { */ void glx_paint_pre(session_t *ps, XserverRegion *preg) { - ps->glx_z = 0.0; + ps->psglx->z = 0.0; // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Get buffer age @@ -1116,8 +1164,8 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, GLfloat factor_center, XserverRegion reg_tgt, const reg_data_t *pcache_reg, glx_blur_cache_t *pbc) { - assert(ps->glx_blur_passes[0].prog); - const bool more_passes = ps->glx_blur_passes[1].prog; + assert(ps->psglx->blur_passes[0].prog); + const bool more_passes = ps->psglx->blur_passes[1].prog; const bool have_scissors = glIsEnabled(GL_SCISSOR_TEST); const bool have_stencil = glIsEnabled(GL_STENCIL_TEST); bool ret = false; @@ -1154,7 +1202,7 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, */ GLenum tex_tgt = GL_TEXTURE_RECTANGLE; - if (ps->glx_has_texture_non_power_of_two) + if (ps->psglx->has_texture_non_power_of_two) tex_tgt = GL_TEXTURE_2D; // Free textures if size inconsistency discovered @@ -1217,9 +1265,9 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, bool last_pass = false; for (int i = 0; !last_pass; ++i) { - last_pass = !ps->glx_blur_passes[i + 1].prog; + last_pass = !ps->psglx->blur_passes[i + 1].prog; assert(i < MAX_BLUR_PASS - 1); - const glx_blur_pass_t *ppass = &ps->glx_blur_passes[i]; + const glx_blur_pass_t *ppass = &ps->psglx->blur_passes[i]; assert(ppass->prog); assert(tex_scr); @@ -1392,7 +1440,7 @@ glx_render_(session_t *ps, const glx_texture_t *ptex, #endif argb = argb || (GLX_TEXTURE_FORMAT_RGBA_EXT == - ps->glx_fbconfigs[ptex->depth]->texture_fmt); + ps->psglx->fbconfigs[ptex->depth]->texture_fmt); #ifdef CONFIG_VSYNC_OPENGL_GLSL const bool has_prog = pprogram && pprogram->prog; #endif @@ -1695,7 +1743,7 @@ glx_swap_copysubbuffermesa(session_t *ps, XserverRegion reg) { #ifdef DEBUG_GLX printf_dbgf("(): %d, %d, %d, %d\n", x, y, wid, hei); #endif - ps->glXCopySubBufferProc(ps->dpy, get_tgt_window(ps), x, y, wid, hei); + ps->psglx->glXCopySubBufferProc(ps->dpy, get_tgt_window(ps), x, y, wid, hei); } } diff --git a/opengl.h b/opengl.h index 35ccf6878..b4e04440a 100644 --- a/opengl.h +++ b/opengl.h @@ -41,7 +41,7 @@ glx_dump_err_str(GLenum err) { */ static inline void glx_check_err_(session_t *ps, const char *func, int line) { - if (!ps->glx_context) return; + if (!ps->psglx->context) return; GLenum err = GL_NO_ERROR; From d8f0aba7b5e0896ae2cba51a728b33e515682563 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Mon, 28 Jul 2014 16:54:02 +0800 Subject: [PATCH 18/25] Misc #218: Warn about using --glx-no-rebind-pixmap on intel driver Warn about using --glx-no-rebind-pixmap on xf86-video-intel. Thanks to aktau for the info. (#218) --- compton.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compton.c b/compton.c index f4f7244a5..65d9fa752 100644 --- a/compton.c +++ b/compton.c @@ -4682,7 +4682,8 @@ usage(int ret) { "--glx-no-rebind-pixmap\n" " GLX backend: Avoid rebinding pixmap on window damage. Probably\n" " could improve performance on rapid window content changes, but is\n" - " known to break things on some drivers.\n" + " known to break things on some drivers (LLVMpipe, xf86-video-intel,\n" + " etc.).\n" "--glx-swap-method undefined/copy/exchange/3/4/5/6/buffer-age\n" " GLX backend: GLX buffer swap method we assume. Could be\n" " undefined (0), copy (1), exchange (2), 3-6, or buffer-age (-1).\n" From 66b420aa62d28af6f6e7b064f58db446829b4f6a Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Tue, 29 Jul 2014 09:29:26 +0800 Subject: [PATCH 19/25] Misc: Fix compilation with NO_VSYNC_OPENGL_GLSL / NO_C2 Fix compilation with NO_VSYNC_OPENGL_GLSL / NO_C2. --- compton.c | 7 +++++++ opengl.c | 2 ++ 2 files changed, 9 insertions(+) diff --git a/compton.c b/compton.c index 65d9fa752..f249b1881 100644 --- a/compton.c +++ b/compton.c @@ -2587,6 +2587,7 @@ win_update_opacity_rule(session_t *ps, win *w) { if (IsViewable != w->a.map_state) return; +#ifdef CONFIG_C2 // If long is 32-bit, unfortunately there's no way could we express "unset", // so we just entirely don't distinguish "unset" and OPAQUE opacity_t opacity = OPAQUE; @@ -2602,6 +2603,7 @@ win_update_opacity_rule(session_t *ps, win *w) { else if (OPAQUE != w->opacity_set) wid_rm_opacity_prop(ps, w->id); w->opacity_set = opacity; +#endif } /** @@ -5162,6 +5164,7 @@ parse_geometry_end: */ static inline bool parse_rule_opacity(session_t *ps, const char *src) { +#ifdef CONFIG_C2 // Find opacity value char *endptr = NULL; long val = strtol(src, &endptr, 0); @@ -5186,6 +5189,10 @@ parse_rule_opacity(session_t *ps, const char *src) { // Parse pattern // I hope 1-100 is acceptable for (void *) return c2_parsed(ps, &ps->o.opacity_rules, endptr, (void *) val); +#else + printf_errf("(\"%s\"): Condition support not compiled in.", src); + return false; +#endif } #ifdef CONFIG_LIBCONFIG diff --git a/opengl.c b/opengl.c index 965356593..800ebd585 100644 --- a/opengl.c +++ b/opengl.c @@ -104,12 +104,14 @@ glx_init(session_t *ps, bool need_render) { ps->psglx = cmalloc(1, glx_session_t); memcpy(ps->psglx, &CGLX_SESSION_DEF, sizeof(glx_session_t)); +#ifdef CONFIG_VSYNC_OPENGL_GLSL for (int i = 0; i < MAX_BLUR_PASS; ++i) { glx_blur_pass_t *ppass = &ps->psglx->blur_passes[i]; ppass->unifm_factor_center = -1; ppass->unifm_offset_x = -1; ppass->unifm_offset_y = -1; } +#endif } glx_session_t *psglx = ps->psglx; From ebab3dc5061e58da34eed227e8f98965bdd1bc30 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sun, 3 Aug 2014 19:40:40 +0800 Subject: [PATCH 20/25] Bug fix: Fix X pixmap leakage in shadow_paint - Fix X pixmap leakage in shadow_paint. - Add the skeleton of a X resource leakage checker. --- common.h | 8 ++++-- compton.c | 9 ++++++- xrescheck.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++ xrescheck.h | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 149 insertions(+), 3 deletions(-) create mode 100644 xrescheck.c create mode 100644 xrescheck.h diff --git a/common.h b/common.h index c1bae4223..066284e77 100644 --- a/common.h +++ b/common.h @@ -145,8 +145,6 @@ #define GLX_BACK_BUFFER_AGE_EXT 0x20F4 #endif -#endif - // === Macros === #define MSTR_(s) #s @@ -186,6 +184,11 @@ /// Macro used for shortening some debugging code. #define CASESTRRET(s) case s: return #s +// X resource checker +#ifdef DEBUG_XRC +#include "xrescheck.h" +#endif + // === Constants === #if !(COMPOSITE_MAJOR > 0 || COMPOSITE_MINOR >= 2) #error libXcomposite version unsupported @@ -2514,3 +2517,4 @@ hexdump(const char *data, int len) { fflush(stdout); } +#endif diff --git a/compton.c b/compton.c index f249b1881..f545b0dfb 100644 --- a/compton.c +++ b/compton.c @@ -486,7 +486,9 @@ win_build_shadow(session_t *ps, win *w, double opacity) { 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 @@ -1826,7 +1828,7 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t // Painting shadow if (w->shadow) { // Lazy shadow building - if (!paint_isvalid(ps, &w->shadow_paint)) + if (!w->shadow_paint.pixmap) win_build_shadow(ps, w, 1); // Shadow is to be painted based on the ignore region of current @@ -7510,6 +7512,11 @@ session_destroy(session_t *ps) { // Flush all events XSync(ps->dpy, True); +#ifdef DEBUG_XRC + // Report about resource leakage + xrc_report_xid(); +#endif + // Free timeouts ps->tmout_unredir = NULL; timeout_clear(ps); diff --git a/xrescheck.c b/xrescheck.c new file mode 100644 index 000000000..6a63bed36 --- /dev/null +++ b/xrescheck.c @@ -0,0 +1,65 @@ +#include "xrescheck.h" + +static xrc_xid_record_t *gs_xid_records = NULL; + +#define HASH_ADD_XID(head, xidfield, add) \ + HASH_ADD(hh, head, xidfield, sizeof(xid), add) + +#define HASH_FIND_XID(head, findxid, out) \ + HASH_FIND(hh, head, findxid, sizeof(xid), out) + +#define M_CPY_POS_DATA(prec) \ + prec->file = file; \ + prec->func = func; \ + prec->line = line; \ + +/** + * @brief Add a record of given XID to the allocation table. + */ +void +xrc_add_xid_(XID xid, const char *type, M_POS_DATA_PARAMS) { + xrc_xid_record_t *prec = cmalloc(1, xrc_xid_record_t); + prec->xid = xid; + prec->type = type; + M_CPY_POS_DATA(prec); + + HASH_ADD_XID(gs_xid_records, xid, prec); +} + +/** + * @brief Delete a record of given XID in the allocation table. + */ +void +xrc_delete_xid_(XID xid, M_POS_DATA_PARAMS) { + xrc_xid_record_t *prec = NULL; + HASH_FIND_XID(gs_xid_records, &xid, prec); + if (!prec) { + printf_err("XRC: %s:%d %s(): Can't find XID %#010lx we want to delete.", + file, line, func, xid); + return; + } + HASH_DEL(gs_xid_records, prec); + free(prec); +} + +/** + * @brief Report about issues found in the XID allocation table. + */ +void +xrc_report_xid(void) { + for (xrc_xid_record_t *prec = gs_xid_records; prec; prec = prec->hh.next) + printf_dbg("XRC: %s:%d %s(): %#010lx (%s) not freed.\n", + prec->file, prec->line, prec->func, prec->xid, prec->type); +} + +/** + * @brief Clear the XID allocation table. + */ +void +xrc_clear_xid(void) { + xrc_xid_record_t *prec = NULL, *ptmp = NULL; + HASH_ITER(hh, gs_xid_records, prec, ptmp) { + HASH_DEL(gs_xid_records, prec); + free(prec); + } +} diff --git a/xrescheck.h b/xrescheck.h new file mode 100644 index 000000000..48f254b20 --- /dev/null +++ b/xrescheck.h @@ -0,0 +1,70 @@ +#ifndef COMPTON_XRESCHECK_H +#define COMPTON_XRESCHECK_H + +#include "common.h" +#include + +typedef struct { + XID xid; + const char *type; + const char *file; + const char *func; + int line; + UT_hash_handle hh; +} xrc_xid_record_t; + +#define M_POS_DATA_PARAMS const char *file, int line, const char *func +#define M_POS_DATA_PASSTHROUGH file, line, func +#define M_POS_DATA __FILE__, __LINE__, __func__ + +void +xrc_add_xid_(XID xid, const char *type, M_POS_DATA_PARAMS); + +#define xrc_add_xid(xid, type) xrc_add_xid_(xid, type, M_POS_DATA) + +void +xrc_delete_xid_(XID xid, M_POS_DATA_PARAMS); + +#define xrc_delete_xid(xid) xrc_delete_xid_(xid, M_POS_DATA) + +void +xrc_report_xid(void); + +void +xrc_clear_xid(void); + +// Pixmap + +static inline Pixmap +XCreatePixmap_(Display *dpy, Drawable drawable, + unsigned int width, unsigned int height, unsigned int depth, + M_POS_DATA_PARAMS) { + Pixmap ret = XCreatePixmap(dpy, drawable, width, height, depth); + if (ret) + xrc_add_xid_(ret, "Pixmap", M_POS_DATA_PASSTHROUGH); + return ret; +} + +#define XCreatePixmap(dpy, drawable, width, height, depth) \ + XCreatePixmap_(dpy, drawable, width, height, depth, M_POS_DATA) + +static inline Pixmap +XCompositeNameWindowPixmap_(Display *dpy, Window window, M_POS_DATA_PARAMS) { + Pixmap ret = XCompositeNameWindowPixmap(dpy, window); + if (ret) + xrc_add_xid_(ret, "PixmapC", M_POS_DATA_PASSTHROUGH); + return ret; +} + +#define XCompositeNameWindowPixmap(dpy, window) \ + XCompositeNameWindowPixmap_(dpy, window, M_POS_DATA) + +static inline void +XFreePixmap_(Display *dpy, Pixmap pixmap, M_POS_DATA_PARAMS) { + XFreePixmap(dpy, pixmap); + xrc_delete_xid_(pixmap, M_POS_DATA_PASSTHROUGH); +} + +#define XFreePixmap(dpy, pixmap) XFreePixmap_(dpy, pixmap, M_POS_DATA); + +#endif From 42448622a0c442789f949f4bedf5b3623655bb3b Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Wed, 6 Aug 2014 07:32:18 +0800 Subject: [PATCH 21/25] Misc: Fix spelling mistakes Still, "Guassian" -> "Gaussian". (#221) --- common.h | 2 +- compton.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common.h b/common.h index 066284e77..9c352acba 100644 --- a/common.h +++ b/common.h @@ -216,7 +216,7 @@ #define MS_PER_SEC 1000 #define XRFILTER_CONVOLUTION "convolution" -#define XRFILTER_GUASSIAN "gaussian" +#define XRFILTER_GAUSSIAN "gaussian" #define XRFILTER_BINOMIAL "binomial" /// @brief Maximum OpenGL FBConfig depth. diff --git a/compton.c b/compton.c index f545b0dfb..a29fd8724 100644 --- a/compton.c +++ b/compton.c @@ -4628,7 +4628,7 @@ usage(int ret) { " The element in the center must not be included, it will be forever\n" " 1.0 or changing based on opacity, depending on whether you have\n" " --blur-background-fixed.\n" - " A 7x7 Guassian blur kernel looks like:\n" + " A 7x7 Gaussian blur kernel looks like:\n" " --blur-kern '7,7,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003,0.000102,0.003494,0.029143,0.059106,0.029143,0.003494,0.000102,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.001723,0.059106,0.493069,0.493069,0.059106,0.001723,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.000102,0.003494,0.029143,0.059106,0.029143,0.003494,0.000102,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003'\n" " Up to 4 blur kernels may be specified, separated with semicolon, for\n" " multi-pass blur.\n" From 69eb07bb0a2ac0755261fe75ef88258d3eb5919c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?m=C3=A6p?= Date: Wed, 6 Aug 2014 20:42:57 +0200 Subject: [PATCH 22/25] fix crash caused by free of uninitialized pointer --- compton.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compton.h b/compton.h index 637d13f34..4844c9c42 100644 --- a/compton.h +++ b/compton.h @@ -351,7 +351,7 @@ isdamagenotify(session_t *ps, const XEvent *ev) { */ static inline XTextProperty * make_text_prop(session_t *ps, char *str) { - XTextProperty *pprop = cmalloc(1, XTextProperty); + XTextProperty *pprop = ccalloc(1, XTextProperty); if (XmbTextListToTextProperty(ps->dpy, &str, 1, XStringStyle, pprop)) { cxfree(pprop->value); From 882d38739957f4a8f1b2571880262082fa431e93 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sun, 7 Sep 2014 16:05:14 +0800 Subject: [PATCH 23/25] Misc: Add --no-name-pixmap Add --no-name-pixmap to disable the usage of XCompositeNameWindowPixmap(), for debugging. --- common.h | 2 ++ compton.c | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/common.h b/common.h index 9c352acba..f9b5d9453 100644 --- a/common.h +++ b/common.h @@ -601,6 +601,8 @@ typedef struct _options_t { Window benchmark_wid; /// A list of conditions of windows not to paint. c2_lptr_t *paint_blacklist; + /// Whether to avoid using XCompositeNameWindowPixmap(), for debugging. + bool no_name_pixmap; /// Whether to work under synchronized mode for debugging. bool synchronize; /// Whether to show all X errors. diff --git a/compton.c b/compton.c index a29fd8724..330155891 100644 --- a/compton.c +++ b/compton.c @@ -5648,6 +5648,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "glx-fshader-win", required_argument, NULL, 317 }, { "version", no_argument, NULL, 318 }, { "no-x-selection", no_argument, NULL, 319 }, + { "no-name-pixmap", no_argument, NULL, 320 }, { "reredir-on-root-change", no_argument, NULL, 731 }, { "glx-reinit-on-root-change", no_argument, NULL, 732 }, // Must terminate with a NULL entry @@ -5676,6 +5677,8 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { printf("%s\n", COMPTON_VERSION); exit(0); } + else if (320 == o) + ps->o.no_name_pixmap = true; else if ('?' == o || ':' == o) usage(1); } @@ -5731,6 +5734,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { case 'S': case 314: case 318: + case 320: break; P_CASELONG('D', fade_delta); case 'I': @@ -7090,7 +7094,8 @@ session_init(session_t *ps_old, int argc, char **argv) { XCompositeQueryVersion(ps->dpy, &composite_major, &composite_minor); - if (composite_major > 0 || composite_minor >= 2) { + if (!ps->o.no_name_pixmap + && (composite_major > 0 || composite_minor >= 2)) { ps->has_name_pixmap = true; } } From e3717f4f7bf3a0a0a015337454f1e381a6e0278f Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sun, 7 Sep 2014 18:58:09 +0800 Subject: [PATCH 24/25] Misc #204: Add glx_take_screenshot() & others - Add glx_take_screenshot() for taking a screenshot. With ImageMagick the output data could be viewed in this way: display -size [SCREEN-WIDTH]x[SCREEN-HEIGHT] -depth 8 -flip rgb:[PATH] (#204) - Add D-Bus command `opts_get string:paint_on_overlay_id` to get X Composite overlay window ID. (#204) - Code cleanup. --- common.h | 25 +++++++++++++++++ compton.c | 24 ++++++++++++++++ dbus.c | 5 ++++ opengl.c | 84 ++++++++++++++++++++++++++++++++++++------------------- 4 files changed, 109 insertions(+), 29 deletions(-) diff --git a/common.h b/common.h index f9b5d9453..0aa4f18b8 100644 --- a/common.h +++ b/common.h @@ -2208,6 +2208,9 @@ glx_render_(session_t *ps, const glx_texture_t *ptex, void glx_swap_copysubbuffermesa(session_t *ps, XserverRegion reg); +unsigned char * +glx_take_screenshot(session_t *ps, int *out_length); + #ifdef CONFIG_VSYNC_OPENGL_GLSL GLuint glx_create_shader(GLenum shader_type, const char *shader_str); @@ -2484,6 +2487,28 @@ c2_matchd(session_t *ps, win *w, const c2_lptr_t *condlst, #endif +/** + * @brief Dump the given data to a file. + */ +static inline bool +write_binary_data(const char *path, const unsigned char *data, int length) { + if (!data) + return false; + FILE *f = fopen(path, "wb"); + if (!f) { + printf_errf("(\"%s\"): Failed to open file for writing.", path); + return false; + } + int wrote_len = fwrite(data, sizeof(unsigned char), length, f); + fclose(f); + if (wrote_len != length) { + printf_errf("(\"%s\"): Failed to write all blocks: %d / %d", path, + wrote_len, length); + return false; + } + return true; +} + /** * @brief Dump raw bytes in HEX format. * diff --git a/compton.c b/compton.c index 330155891..b967372ee 100644 --- a/compton.c +++ b/compton.c @@ -1411,6 +1411,20 @@ xr_blur_dst(session_t *ps, Picture tgt_buffer, return true; } +/* + * WORK-IN-PROGRESS! +static void +xr_take_screenshot(session_t *ps) { + XImage *img = XGetImage(ps->dpy, get_tgt_window(ps), 0, 0, + ps->root_width, ps->root_height, AllPlanes, XYPixmap); + if (!img) { + printf_errf("(): Failed to get XImage."); + return NULL; + } + assert(0 == img->xoffset); +} +*/ + /** * Blur the background of a window. */ @@ -7530,6 +7544,16 @@ session_destroy(session_t *ps) { ps_g = NULL; } +/* +static inline void +dump_img(session_t *ps) { + int len = 0; + unsigned char *d = glx_take_screenshot(ps, &len); + write_binary_data("/tmp/dump.raw", d, len); + free(d); +} +*/ + /** * Do the actual work. * diff --git a/dbus.c b/dbus.c index 8aec9ea82..874f565ca 100644 --- a/dbus.c +++ b/dbus.c @@ -901,6 +901,11 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg) { cdbus_m_opts_get_do(fork_after_register, cdbus_reply_bool); cdbus_m_opts_get_do(detect_rounded_corners, cdbus_reply_bool); cdbus_m_opts_get_do(paint_on_overlay, cdbus_reply_bool); + // paint_on_overlay_id: Get ID of the X composite overlay window + if (!strcmp("paint_on_overlay_id", target)) { + cdbus_reply_uint32(ps, msg, ps->overlay); + return true; + } cdbus_m_opts_get_do(unredir_if_possible, cdbus_reply_bool); cdbus_m_opts_get_do(unredir_if_possible_delay, cdbus_reply_int32); cdbus_m_opts_get_do(redirected_force, cdbus_reply_enum); diff --git a/opengl.c b/opengl.c index 800ebd585..5a98f4e0a 100644 --- a/opengl.c +++ b/opengl.c @@ -1442,7 +1442,7 @@ glx_render_(session_t *ps, const glx_texture_t *ptex, #endif argb = argb || (GLX_TEXTURE_FORMAT_RGBA_EXT == - ps->psglx->fbconfigs[ptex->depth]->texture_fmt); + ps->psglx->fbconfigs[ptex->depth]->texture_fmt); #ifdef CONFIG_VSYNC_OPENGL_GLSL const bool has_prog = pprogram && pprogram->prog; #endif @@ -1754,6 +1754,32 @@ glx_swap_copysubbuffermesa(session_t *ps, XserverRegion reg) { cxfree(rects); } +/** + * @brief Get tightly packed RGB888 data from GL front buffer. + * + * Don't expect any sort of decent performance. + * + * @returns tightly packed RGB888 data of the size of the screen, + * to be freed with `free()` + */ +unsigned char * +glx_take_screenshot(session_t *ps, int *out_length) { + int length = 3 * ps->root_width * ps->root_height; + GLint unpack_align_old = 0; + glGetIntegerv(GL_UNPACK_ALIGNMENT, &unpack_align_old); + assert(unpack_align_old > 0); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + unsigned char *buf = cmalloc(length, unsigned char); + glReadBuffer(GL_FRONT); + glReadPixels(0, 0, ps->root_width, ps->root_height, GL_RGB, + GL_UNSIGNED_BYTE, buf); + glReadBuffer(GL_BACK); + glPixelStorei(GL_UNPACK_ALIGNMENT, unpack_align_old); + if (out_length) + *out_length = sizeof(unsigned char) * length; + return buf; +} + #ifdef CONFIG_VSYNC_OPENGL_GLSL GLuint glx_create_shader(GLenum shader_type, const char *shader_str) { @@ -1847,34 +1873,34 @@ glx_create_program_end: */ GLuint glx_create_program_from_str(const char *vert_shader_str, - const char *frag_shader_str) { - GLuint vert_shader = 0; - GLuint frag_shader = 0; - GLuint prog = 0; - - if (vert_shader_str) - vert_shader = glx_create_shader(GL_VERTEX_SHADER, vert_shader_str); - if (frag_shader_str) - frag_shader = glx_create_shader(GL_FRAGMENT_SHADER, frag_shader_str); - - { - GLuint shaders[2]; - int count = 0; - if (vert_shader) - shaders[count++] = vert_shader; - if (frag_shader) - shaders[count++] = frag_shader; - assert(count <= sizeof(shaders) / sizeof(shaders[0])); - if (count) - prog = glx_create_program(shaders, count); - } - - if (vert_shader) - glDeleteShader(vert_shader); - if (frag_shader) - glDeleteShader(frag_shader); - - return prog; + const char *frag_shader_str) { + GLuint vert_shader = 0; + GLuint frag_shader = 0; + GLuint prog = 0; + + if (vert_shader_str) + vert_shader = glx_create_shader(GL_VERTEX_SHADER, vert_shader_str); + if (frag_shader_str) + frag_shader = glx_create_shader(GL_FRAGMENT_SHADER, frag_shader_str); + + { + GLuint shaders[2]; + int count = 0; + if (vert_shader) + shaders[count++] = vert_shader; + if (frag_shader) + shaders[count++] = frag_shader; + assert(count <= sizeof(shaders) / sizeof(shaders[0])); + if (count) + prog = glx_create_program(shaders, count); + } + + if (vert_shader) + glDeleteShader(vert_shader); + if (frag_shader) + glDeleteShader(frag_shader); + + return prog; } #endif From 325fb4c2983cb3211138078bff9cbec749be21c8 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Mon, 6 Oct 2014 11:36:47 +0800 Subject: [PATCH 25/25] Bug fix #224: Flush after XUngrabServer() Fix #224 by XFlush() after XUngrabServer(). Thanks to rathsky and smlx for reporting. --- compton.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compton.c b/compton.c index b967372ee..aec56cacd 100644 --- a/compton.c +++ b/compton.c @@ -7333,6 +7333,8 @@ session_init(session_t *ps_old, int argc, char **argv) { } XUngrabServer(ps->dpy); + // ALWAYS flush after XUngrabServer()! + XFlush(ps->dpy); // Initialize DBus if (ps->o.dbus) {