From 3e71f8fa2539a6a29911f399e4089b9eecf8f8e9 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Thu, 22 Aug 2013 21:15:04 +0800 Subject: [PATCH] Improvement #137: --xinerama-shadow-crop Add --xinerama-shadow-crop to crop shadow to current Xinerama screen. Thanks to Feltzer for suggestion. --- Makefile | 6 +++++ src/common.h | 23 ++++++++++++++++- src/compton.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/compton.h | 42 +++++++++++++++++++++++++++++++ src/dbus.c | 1 + 5 files changed, 140 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 9acba9d..655c77d 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,12 @@ OBJS = compton.o # === Configuration flags === CFG = -std=c99 +# ==== Xinerama ==== +ifeq "$(NO_XINERAMA)" "" + CFG += -DCONFIG_XINERAMA + PACKAGES += xinerama +endif + # ==== libconfig ==== ifeq "$(NO_LIBCONFIG)" "" CFG += -DCONFIG_LIBCONFIG diff --git a/src/common.h b/src/common.h index bf0b480..f90cba7 100644 --- a/src/common.h +++ b/src/common.h @@ -85,6 +85,10 @@ #include #include +#ifdef CONFIG_XINERAMA +#include +#endif + // Workarounds for missing definitions in very old versions of X headers, // thanks to consolers for reporting #ifndef PictOpDifference @@ -506,6 +510,8 @@ typedef struct { bool shadow_ignore_shaped; /// Whether to respect _COMPTON_SHADOW. bool respect_prop_shadow; + /// Whether to crop shadow to the very Xinerama screen. + bool xinerama_shadow_crop; // === Fading === /// Enable/disable fading for specific window types. @@ -806,6 +812,16 @@ typedef struct { #endif /// Whether X DBE extension exists. bool dbe_exists; +#ifdef CONFIG_XINERAMA + /// Whether X Xinerama extension exists. + bool xinerama_exists; + /// Xinerama screen info. + XineramaScreenInfo *xinerama_scrs; + /// Xinerama screen regions. + XserverRegion *xinerama_scr_regs; + /// Number of Xinerama screens. + int xinerama_nscrs; +#endif /// Whether X Render convolution filter exists. bool xrfilter_convolution_exists; @@ -861,6 +877,10 @@ typedef struct _win { Window id; /// Window attributes. XWindowAttributes a; +#ifdef CONFIG_XINERAMA + /// Xinerama screen this window is on. + int xinerama_scr; +#endif /// Window visual pict format; XRenderPictFormat *pictfmt; /// Window painting mode. @@ -1107,12 +1127,13 @@ XFixesDestroyRegion_(Display *dpy, XserverRegion reg, /** * @brief Quit if the passed-in pointer is empty. */ -static inline void +static inline void * allocchk_(const char *func_name, void *ptr) { if (!ptr) { printf_err("%s(): Failed to allocate memory.", func_name); exit(1); } + return ptr; } /// @brief Wrapper of allocchk_(). diff --git a/src/compton.c b/src/compton.c index 474d2a2..6fe9279 100644 --- a/src/compton.c +++ b/src/compton.c @@ -1817,6 +1817,12 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t if (ps->o.clear_shadow && w->border_size) XFixesSubtractRegion(ps->dpy, reg_paint, reg_paint, w->border_size); +#ifdef CONFIG_XINERAMA + if (ps->o.xinerama_shadow_crop && w->xinerama_scr >= 0) + XFixesIntersectRegion(ps->dpy, reg_paint, reg_paint, + ps->xinerama_scr_regs[w->xinerama_scr]); +#endif + // Detect if the region is empty before painting { reg_data_t cache_reg = REG_DATA_INIT; @@ -2035,6 +2041,8 @@ map_win(session_t *ps, Window id) { w->a.map_state = IsViewable; + cxinerama_win_upd_scr(ps, w); + // Set focused to false bool focused_real = false; if (ps->o.track_focus && ps->o.use_ewmh_active_win @@ -2677,6 +2685,9 @@ add_win(session_t *ps, Window id, Window prev) { .id = None, .a = { }, +#ifdef CONFIG_XINERAMA + .xinerama_scr = -1, +#endif .pictfmt = NULL, .mode = WMODE_TRANS, .damaged = false, @@ -2989,8 +3000,10 @@ configure_win(session_t *ps, XConfigureEvent *ce) { add_damage(ps, damage); } - if (factor_change) + if (factor_change) { + cxinerama_win_upd_scr(ps, w); win_on_factor_change(ps, w); + } } // override_redirect flag cannot be changed after window creation, as far @@ -4417,6 +4430,15 @@ usage(int ret) { " should not be painted in, such as a dock window region.\n" " Use --shadow-exclude-reg \'x10+0-0\', for example, if the 10 pixels\n" " on the bottom of the screen should not have shadows painted on.\n" +#undef WARNING +#ifndef CONFIG_XINERAMA +#define WARNING WARNING_DISABLED +#else +#define WARNING +#endif + "--xinerama-shadow-crop\n" + " Crop shadow of a window fully on a particular Xinerama screen to the\n" + " screen." WARNING "\n" "--backend backend\n" " Choose backend. Possible choices are xrender and glx" WARNING ".\n" "--glx-no-stencil\n" @@ -5245,6 +5267,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "opacity-rule", required_argument, NULL, 304 }, { "shadow-exclude-reg", required_argument, NULL, 305 }, { "paint-exclude", required_argument, NULL, 306 }, + { "xinerama-shadow-crop", no_argument, NULL, 307 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -5492,6 +5515,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { // --paint-exclude condlst_add(ps, &ps->o.paint_blacklist, optarg); break; + P_CASEBOOL(307, xinerama_shadow_crop); default: usage(1); break; @@ -6343,6 +6367,35 @@ mainloop(session_t *ps) { return true; } +static void +cxinerama_upd_scrs(session_t *ps) { +#ifdef CONFIG_XINERAMA + free_xinerama_info(ps); + + if (!ps->o.xinerama_shadow_crop || !ps->xinerama_exists) return; + + if (!XineramaIsActive(ps->dpy)) return; + + ps->xinerama_scrs = XineramaQueryScreens(ps->dpy, &ps->xinerama_nscrs); + + // Just in case the shit hits the fan... + if (!ps->xinerama_nscrs) { + cxfree(ps->xinerama_scrs); + ps->xinerama_scrs = NULL; + return; + } + + ps->xinerama_scr_regs = allocchk(malloc(sizeof(XserverRegion *) + * ps->xinerama_nscrs)); + for (int i = 0; i < ps->xinerama_nscrs; ++i) { + const XineramaScreenInfo * const s = &ps->xinerama_scrs[i]; + XRectangle r = { .x = s->x_org, .y = s->y_org, + .width = s->width, .height = s->height }; + ps->xinerama_scr_regs[i] = XFixesCreateRegion(ps->dpy, &r, 1); + } +#endif +} + /** * Initialize a session. * @@ -6409,6 +6462,7 @@ session_init(session_t *ps_old, int argc, char **argv) { .shadow_blacklist = NULL, .shadow_ignore_shaped = false, .respect_prop_shadow = false, + .xinerama_shadow_crop = false, .wintype_fade = { false }, .fade_in_step = 0.028 * OPAQUE, @@ -6663,6 +6717,17 @@ session_init(session_t *ps_old, int argc, char **argv) { ps->o.dbe = false; } + // Query X Xinerama extension + if (ps->o.xinerama_shadow_crop) { +#ifdef CONFIG_XINERAMA + int xinerama_event = 0, xinerama_error = 0; + if (XineramaQueryExtension(ps->dpy, &xinerama_event, &xinerama_error)) + ps->xinerama_exists = true; +#else + printf_errf("(): Xinerama support not compiled in."); +#endif + } + rebuild_screen_reg(ps); // Overlay must be initialized before double buffer, and before creation @@ -6697,6 +6762,8 @@ session_init(session_t *ps_old, int argc, char **argv) { if (!vsync_init(ps)) exit(1); + cxinerama_upd_scrs(ps); + // Create registration window if (!ps->reg_win && !register_cm(ps)) exit(1); @@ -6923,6 +6990,7 @@ session_destroy(session_t *ps) { free(ps->pfds_read); free(ps->pfds_write); free(ps->pfds_except); + free_xinerama_info(ps); #ifdef CONFIG_VSYNC_OPENGL glx_destroy(ps); diff --git a/src/compton.h b/src/compton.h index c0773db..f4a7ef9 100644 --- a/src/compton.h +++ b/src/compton.h @@ -192,6 +192,23 @@ free_wincondlst(c2_lptr_t **pcondlst) { #endif } +/** + * Free Xinerama screen info. + */ +static inline void +free_xinerama_info(session_t *ps) { +#ifdef CONFIG_XINERAMA + if (ps->xinerama_scr_regs) { + for (int i = 0; i < ps->xinerama_nscrs; ++i) + free_region(ps, &ps->xinerama_scr_regs[i]); + free(ps->xinerama_scr_regs); + } + cxfree(ps->xinerama_scrs); + ps->xinerama_scrs = NULL; + ps->xinerama_nscrs = 0; +#endif +} + /** * Check whether a paint_t contains enough data. */ @@ -1191,6 +1208,31 @@ timeout_clear(session_t *ps); static bool mainloop(session_t *ps); +#ifdef CONFIG_XINERAMA +static void +cxinerama_upd_scrs(session_t *ps); +#endif + +/** + * Get the Xinerama screen a window is on. + * + * Return an index >= 0, or -1 if not found. + */ +static inline void +cxinerama_win_upd_scr(session_t *ps, win *w) { +#ifdef CONFIG_XINERAMA + w->xinerama_scr = -1; + for (XineramaScreenInfo *s = ps->xinerama_scrs; + s < ps->xinerama_scrs + ps->xinerama_nscrs; ++s) + if (s->x_org <= w->a.x && s->y_org <= w->a.y + && s->x_org + s->width >= w->a.x + w->widthb + && s->y_org + s->height >= w->a.y + w->heightb) { + w->xinerama_scr = s - ps->xinerama_scrs; + return; + } +#endif +} + static session_t * session_init(session_t *ps_old, int argc, char **argv); diff --git a/src/dbus.c b/src/dbus.c index 671ef8e..8f53eee 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -926,6 +926,7 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg) { cdbus_m_opts_get_do(shadow_offset_y, cdbus_reply_int32); cdbus_m_opts_get_do(shadow_opacity, cdbus_reply_double); cdbus_m_opts_get_do(clear_shadow, cdbus_reply_bool); + cdbus_m_opts_get_do(xinerama_shadow_crop, cdbus_reply_bool); cdbus_m_opts_get_do(fade_delta, cdbus_reply_int32); cdbus_m_opts_get_do(fade_in_step, cdbus_reply_int32);