From d41c7507250be2349080fcea44b5f33f84dd65aa Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Wed, 4 Sep 2013 22:00:51 +0800 Subject: Improvement: --unredir-if-possible-exclude & --unredir-if-possible-delay - Add --unredir-if-possible-exclude, to exclude certain windows when evaluating --unredir-if-possible. (#140) - Add --unredir-if-possible-delay, to add some delay before unredirecting screen. (#138, #140) - Code clean-up. --- common.h | 15 +++++ compton.c | 189 +++++++++++++++++++++++++++++++++++++++++++------------------- compton.h | 11 ++++ dbus.c | 1 + 4 files changed, 158 insertions(+), 58 deletions(-) diff --git a/common.h b/common.h index f90cba714..ea95235ae 100644 --- a/common.h +++ b/common.h @@ -464,6 +464,11 @@ typedef struct { /// Whether to unredirect all windows if a full-screen opaque window /// is detected. bool unredir_if_possible; + /// List of conditions of windows to ignore as a full-screen window + /// when determining if a window could be unredirected. + c2_lptr_t *unredir_if_possible_blacklist; + /// Delay before unredirecting screen. + time_ms_t unredir_if_possible_delay; /// Forced redirection setting through D-Bus. switch_t redirected_force; /// Whether to stop painting. Controlled through D-Bus. @@ -644,6 +649,10 @@ typedef struct { int nfds_max; /// Linked list of all timeouts. struct _timeout_t *tmout_lst; + /// Timeout for delayed unredirection. + struct _timeout_t *tmout_unredir; + /// Whether we have hit unredirection timeout. + bool tmout_unredir_hit; /// Whether we have received an event in this cycle. bool ev_received; /// Whether the program is idling. I.e. no fading, no potential window @@ -921,6 +930,8 @@ typedef struct _win { bool to_paint; /// Whether the window is painting excluded. bool paint_excluded; + /// Whether the window is unredirect-if-possible excluded. + bool unredir_if_possible_excluded; /// Whether this window is in open/close state. bool in_openclose; @@ -962,6 +973,7 @@ typedef struct _win { const c2_lptr_t *cache_bbblst; const c2_lptr_t *cache_oparule; const c2_lptr_t *cache_pblst; + const c2_lptr_t *cache_uipblst; // Opacity-related members /// Current window opacity. @@ -1527,6 +1539,9 @@ timeout_invoke(session_t *ps, timeout_t *ptmout); bool timeout_drop(session_t *ps, timeout_t *prm); +void +timeout_reset(session_t *ps, timeout_t *ptmout); + /** * Add a file descriptor to a select() fd_set. */ diff --git a/compton.c b/compton.c index ae39e2417..b8f1fc6d5 100644 --- a/compton.c +++ b/compton.c @@ -1075,13 +1075,7 @@ get_alpha_pict_o(session_t *ps, opacity_t o) { static win * paint_preprocess(session_t *ps, win *list) { - // Initialize unredir_possible - bool unredir_possible = false; - - win *w; win *t = NULL, *next = NULL; - // Trace whether it's the highest window to paint - bool is_highest = true; // Fading step calculation time_ms_t steps = 0L; @@ -1097,7 +1091,10 @@ paint_preprocess(session_t *ps, win *list) { XserverRegion last_reg_ignore = None; - for (w = list; w; w = next) { + bool unredir_possible = false; + // Trace whether it's the highest window to paint + bool is_highest = true; + for (win *w = list; w; w = next) { bool to_paint = true; const winmode_t mode_old = w->mode; @@ -1229,12 +1226,17 @@ paint_preprocess(session_t *ps, win *list) { last_reg_ignore = w->reg_ignore; + // (Un)redirect screen + // We could definitely unredirect the screen when there's no window to + // paint, but this is typically unnecessary, may cause flickering when + // fading is enabled, and could create inconsistency when the wallpaper + // is not correctly set. if (ps->o.unredir_if_possible && is_highest && to_paint) { is_highest = false; - // Disable unredirection for multi-screen setups if (WMODE_SOLID == w->mode && (!w->frame_opacity || !win_has_frame(w)) - && win_is_fullscreen(ps, w)) + && win_is_fullscreen(ps, w) + && !w->unredir_if_possible_excluded) unredir_possible = true; } @@ -1242,7 +1244,6 @@ paint_preprocess(session_t *ps, win *list) { w->flags = 0; } - // Avoid setting w->to_paint if w is to be freed bool destroyed = (w->opacity_tgt == w->opacity && w->destroyed); @@ -1258,14 +1259,25 @@ paint_preprocess(session_t *ps, win *list) { w->to_paint = to_paint; } + // If possible, unredirect all windows and stop painting if (UNSET != ps->o.redirected_force) unredir_possible = !ps->o.redirected_force; - if (unredir_possible) - redir_stop(ps); - else + if (unredir_possible) { + if (ps->redirected) { + if (!ps->o.unredir_if_possible_delay || ps->tmout_unredir_hit) + redir_stop(ps); + else if (!ps->tmout_unredir->enabled) { + timeout_reset(ps, ps->tmout_unredir); + ps->tmout_unredir->enabled = true; + } + } + } + else { + ps->tmout_unredir->enabled = false; redir_start(ps); + } return t; } @@ -1691,7 +1703,6 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t #ifdef DEBUG_REPAINT static struct timespec last_paint = { 0 }; #endif - win *w = NULL; XserverRegion reg_paint = None, reg_tmp = None, reg_tmp2 = None; #ifdef CONFIG_VSYNC_OPENGL @@ -1771,7 +1782,7 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t reg_tmp = XFixesCreateRegion(ps->dpy, NULL, 0); reg_tmp2 = XFixesCreateRegion(ps->dpy, NULL, 0); - for (w = t; w; w = w->prev_trans) { + for (win *w = t; w; w = w->prev_trans) { // Painting shadow if (w->shadow) { // Shadow is to be painted based on the ignore region of current @@ -1953,7 +1964,7 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t printf("[ %5ld:%09ld ] ", diff.tv_sec, diff.tv_nsec); last_paint = now; printf("paint:"); - for (w = t; w; w = w->prev_trans) + for (win *w = t; w; w = w->prev_trans) printf(" %#010lx", w->id); putchar('\n'); fflush(stdout); @@ -1962,7 +1973,7 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t // Check if fading is finished on all painted windows { win *pprev = NULL; - for (w = t; w; w = pprev) { + for (win *w = t; w; w = pprev) { pprev = w->prev_trans; check_fade_fin(ps, w); } @@ -1971,6 +1982,11 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t static void add_damage(session_t *ps, XserverRegion damage) { + // Ignore damage when screen isn't redirected + if (!ps->redirected) + free_region(ps, &damage); + + if (!damage) return; if (ps->all_damage) { XFixesUnionRegion(ps->dpy, ps->all_damage, ps->all_damage, damage); XFixesDestroyRegion(ps->dpy, damage); @@ -1999,13 +2015,18 @@ repair_win(session_t *ps, win *w) { w->a.y + w->a.border_width); } + w->damaged = true; + w->pixmap_damaged = true; + + // Why care about damage when screen is unredirected? + // We will force full-screen repaint on redirection. + if (!ps->redirected) return; + // Remove the part in the damage area that could be ignored if (!ps->reg_ignore_expire && w->prev_trans && w->prev_trans->reg_ignore) XFixesSubtractRegion(ps->dpy, parts, parts, w->prev_trans->reg_ignore); add_damage(ps, parts); - w->damaged = true; - w->pixmap_damaged = true; } static wintype_t @@ -2507,6 +2528,9 @@ win_on_factor_change(session_t *ps, win *w) { if (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) + w->unredir_if_possible_excluded = win_match(ps, w, + ps->o.unredir_if_possible_blacklist, &w->cache_uipblst); } /** @@ -3677,9 +3701,8 @@ ev_name(session_t *ps, XEvent *ev) { CASESTRRET(PropertyNotify); CASESTRRET(ClientMessage); default: - if (ev->type == ps->damage_event + XDamageNotify) { + if (isdamagenotify(ps, ev)) return "Damage"; - } if (ps->shape_exists && ev->type == ps->shape_event) { return "ShapeNotify"; @@ -3718,7 +3741,7 @@ ev_window(session_t *ps, XEvent *ev) { case ClientMessage: return ev->xclient.window; default: - if (ev->type == ps->damage_event + XDamageNotify) { + if (isdamagenotify(ps, ev)) { return ((XDamageNotifyEvent *)ev)->drawable; } @@ -4166,7 +4189,7 @@ ev_handle(session_t *ps, XEvent *ev) { } #ifdef DEBUG_EVENTS - if (ev->type != ps->damage_event + XDamageNotify) { + if (!isdamagenotify(ps, ev)) { Window wid = ev_window(ps, ev); char *window_name = NULL; bool to_free = false; @@ -4228,10 +4251,10 @@ ev_handle(session_t *ps, XEvent *ev) { ev_screen_change_notify(ps, (XRRScreenChangeNotifyEvent *) ev); break; } - if (ev->type == ps->damage_event + XDamageNotify) { - ev_damage_notify(ps, (XDamageNotifyEvent *)ev); + if (isdamagenotify(ps, ev)) { + ev_damage_notify(ps, (XDamageNotifyEvent *) ev); + break; } - break; } } @@ -4376,6 +4399,12 @@ usage(int ret) { "--unredir-if-possible\n" " Unredirect all windows if a full-screen opaque window is\n" " detected, to maximize performance for full-screen windows.\n" + "--unredir-if-possible-delay ms\n" + " Delay before unredirecting the window, in milliseconds.\n" + " Defaults to 0.\n" + "--unredir-if-possible-exclude condition\n" + " Conditions of windows that shouldn't be considered full-screen\n" + " for unredirecting screen.\n" "--focus-exclude condition\n" " Specify a list of conditions of windows that should always be\n" " considered focused.\n" @@ -4602,6 +4631,27 @@ fork_after(session_t *ps) { return success; } +/** + * Parse a long number. + */ +static inline bool +parse_long(const char *s, long *dest) { + const char *endptr = NULL; + long val = strtol(s, (char **) &endptr, 0); + if (!endptr || endptr == s) { + printf_errf("(\"%s\"): Invalid number.", s); + return false; + } + while (isspace(*endptr)) + ++endptr; + if (*endptr) { + printf_errf("(\"%s\"): Trailing characters.", s); + return false; + } + *dest = val; + return true; +} + /** * Parse a floating-point number in matrix. */ @@ -5288,6 +5338,8 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "shadow-exclude-reg", required_argument, NULL, 305 }, { "paint-exclude", required_argument, NULL, 306 }, { "xinerama-shadow-crop", no_argument, NULL, 307 }, + { "unredir-if-possible-exclude", required_argument, NULL, 308 }, + { "unredir-if-possible-delay", required_argument, NULL, 309 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -5344,17 +5396,22 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { optind = 1; while (-1 != (o = getopt_long(argc, argv, shortopts, longopts, &longopt_idx))) { + long val = 0; switch (o) { #define P_CASEBOOL(idx, option) case idx: ps->o.option = true; break +#define P_CASELONG(idx, option) \ + case idx: \ + if (!parse_long(optarg, &val)) exit(1); \ + ps->o.option = val; \ + break + // Short options case 'h': usage(0); break; case 'd': break; - case 'D': - ps->o.fade_delta = atoi(optarg); - break; + P_CASELONG('D', fade_delta); case 'I': ps->o.fade_in_step = normalize_d(atof(optarg)) * OPAQUE; break; @@ -5378,18 +5435,12 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { fading_enable = true; break; P_CASEBOOL('S', synchronize); - case 'r': - ps->o.shadow_radius = atoi(optarg); - break; + P_CASELONG('r', shadow_radius); case 'o': ps->o.shadow_opacity = atof(optarg); break; - case 'l': - ps->o.shadow_offset_x = atoi(optarg); - break; - case 't': - ps->o.shadow_offset_y = atoi(optarg); - break; + P_CASELONG('l', shadow_offset_x); + P_CASELONG('t', shadow_offset_y); case 'i': ps->o.inactive_opacity = (normalize_d(atof(optarg)) * OPAQUE); break; @@ -5434,10 +5485,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { P_CASEBOOL(266, shadow_ignore_shaped); P_CASEBOOL(267, detect_rounded_corners); P_CASEBOOL(268, detect_client_opacity); - case 269: - // --refresh-rate - ps->o.refresh_rate = atoi(optarg); - break; + P_CASELONG(269, refresh_rate); case 270: // --vsync if (!parse_vsync(ps, optarg)) @@ -5484,10 +5532,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { break; P_CASEBOOL(291, glx_no_stencil); P_CASEBOOL(292, glx_copy_from_front); - case 293: - // --benchmark - ps->o.benchmark = atoi(optarg); - break; + P_CASELONG(293, benchmark); case 294: // --benchmark-wid ps->o.benchmark_wid = strtol(optarg, NULL, 0); @@ -5516,10 +5561,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { if (!parse_conv_kern_lst(ps, optarg, ps->o.blur_kerns, MAX_BLUR_PASS)) exit(1); break; - case 302: - // --resize-damage - ps->o.resize_damage = atoi(optarg); - break; + P_CASELONG(302, resize_damage); P_CASEBOOL(303, glx_use_gpushader4); case 304: // --opacity-rule @@ -5536,6 +5578,11 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { condlst_add(ps, &ps->o.paint_blacklist, optarg); break; P_CASEBOOL(307, xinerama_shadow_crop); + case 308: + // --unredir-if-possible-exclude + condlst_add(ps, &ps->o.unredir_if_possible_blacklist, optarg); + break; + P_CASELONG(309, unredir_if_possible_delay); default: usage(1); break; @@ -5583,10 +5630,6 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { if (ps->o.blur_background_frame) ps->o.blur_background = true; - // Free background blur blacklist if background blur is not actually enabled - if (!ps->o.blur_background) - free_wincondlst(&ps->o.blur_background_blacklist); - // Other variables determined by options // Determine whether we need to track focus changes @@ -6110,7 +6153,8 @@ static void redir_start(session_t *ps) { if (!ps->redirected) { #ifdef DEBUG_REDIR - printf("redir_start(): Screen redirected.\n"); + print_timestamp(ps); + printf_dbgf("(): Screen redirected.\n"); #endif // Map overlay window. Done firstly according to this: @@ -6133,6 +6177,9 @@ redir_start(session_t *ps) { XSync(ps->dpy, False); ps->redirected = true; + + // Repaint the whole screen + force_repaint(ps); } } @@ -6272,6 +6319,14 @@ timeout_invoke(session_t *ps, timeout_t *ptmout) { ptmout->callback(ps, ptmout); } +/** + * Reset a timeout to initial state. + */ +void +timeout_reset(session_t *ps, timeout_t *ptmout) { + ptmout->firstrun = ptmout->lastrun = get_time_ms(); +} + /** * Unredirect all windows. */ @@ -6279,7 +6334,8 @@ static void redir_stop(session_t *ps) { if (ps->redirected) { #ifdef DEBUG_REDIR - printf("redir_stop(): Screen unredirected.\n"); + print_timestamp(ps); + printf_dbgf("(): Screen unredirected.\n"); #endif // Destroy all Pictures as they expire once windows are unredirected // If we don't destroy them here, looks like the resources are just @@ -6299,6 +6355,17 @@ redir_stop(session_t *ps) { } } +/** + * Unredirection timeout callback. + */ +static bool +tmout_unredir_callback(session_t *ps, timeout_t *tmout) { + ps->tmout_unredir_hit = true; + tmout->enabled = false; + + return true; +} + /** * Main loop. */ @@ -6452,6 +6519,8 @@ session_init(session_t *ps_old, int argc, char **argv) { .paint_on_overlay = false, .resize_damage = 0, .unredir_if_possible = false, + .unredir_if_possible_blacklist = NULL, + .unredir_if_possible_delay = 0, .redirected_force = UNSET, .stoppaint_force = UNSET, .dbus = false, @@ -6829,11 +6898,12 @@ session_init(session_t *ps_old, int argc, char **argv) { } fds_insert(ps, ConnectionNumber(ps->dpy), POLLIN); + ps->tmout_unredir = timeout_insert(ps, ps->o.unredir_if_possible_delay, + tmout_unredir_callback, NULL); + ps->tmout_unredir->enabled = false; XGrabServer(ps->dpy); - redir_start(ps); - { Window root_return, parent_return; Window *children; @@ -6942,6 +7012,7 @@ session_destroy(session_t *ps) { free_wincondlst(&ps->o.blur_background_blacklist); free_wincondlst(&ps->o.opacity_rules); free_wincondlst(&ps->o.paint_blacklist); + free_wincondlst(&ps->o.unredir_if_possible_blacklist); #endif // Free tracked atom list @@ -7047,6 +7118,7 @@ session_destroy(session_t *ps) { XSync(ps->dpy, True); // Free timeouts + ps->tmout_unredir = NULL; timeout_clear(ps); if (ps == ps_g) @@ -7101,6 +7173,7 @@ session_run(session_t *ps) { ps->idling = true; t = paint_preprocess(ps, ps->list); + ps->tmout_unredir_hit = false; // If the screen is unredirected, free all_damage to stop painting if (!ps->redirected || ON == ps->o.stoppaint_force) diff --git a/compton.h b/compton.h index c66739dd4..d44544633 100644 --- a/compton.h +++ b/compton.h @@ -324,6 +324,14 @@ ms_to_tv(int timeout) { }; } +/** + * Whether an event is DamageNotify. + */ +static inline bool +isdamagenotify(session_t *ps, const XEvent *ev) { + return ps->damage_event + XDamageNotify == ev->type; +} + /** * Create a XTextProperty of a single string. */ @@ -1205,6 +1213,9 @@ timeout_get_poll_time(session_t *ps); static void timeout_clear(session_t *ps); +static bool +tmout_unredir_callback(session_t *ps, timeout_t *tmout); + static bool mainloop(session_t *ps); diff --git a/dbus.c b/dbus.c index 8f53eeefa..299c5ac0b 100644 --- a/dbus.c +++ b/dbus.c @@ -898,6 +898,7 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg) { cdbus_m_opts_get_do(detect_rounded_corners, cdbus_reply_bool); cdbus_m_opts_get_do(paint_on_overlay, cdbus_reply_bool); 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); cdbus_m_opts_get_do(stoppaint_force, cdbus_reply_enum); cdbus_m_opts_get_do(logpath, cdbus_reply_string); -- cgit v1.2.1