diff --git a/compton.c b/compton.c index 045c536c7..4026a583b 100644 --- a/compton.c +++ b/compton.c @@ -948,7 +948,7 @@ recheck_focus(session_t *ps) { // And we set the focus state and opacity here if (w) { - set_focused(ps, w, true); + win_set_focused(ps, w, true); return w; } @@ -1241,6 +1241,12 @@ paint_preprocess(session_t *ps, win *list) { if (ps->reg_ignore_expire) free_region(ps, &w->reg_ignore); + // Update window opacity target and dim state if asked + if (WFLAG_OPCT_CHANGE & w->flags) { + calc_opacity(ps, w); + calc_dim(ps, w); + } + // Run fading run_fade(ps, w, steps); @@ -1754,13 +1760,13 @@ repair_win(session_t *ps, win *w) { static wintype_t wid_get_prop_wintype(session_t *ps, Window wid) { - int i, j; + int i; set_ignore_next(ps); winprop_t prop = wid_get_prop(ps, wid, ps->atom_win_type, 32L, XA_ATOM, 32); for (i = 0; i < prop.nitems; ++i) { - for (j = 1; j < NUM_WINTYPES; ++j) { + for (wintype_t j = 1; j < NUM_WINTYPES; ++j) { if (ps->atoms_wintypes[j] == (Atom) prop.data.p32[i]) { free_winprop(&prop); return j; @@ -1780,9 +1786,10 @@ map_win(session_t *ps, Window id) { // Don't care about window mapping if it's an InputOnly window if (!w || InputOnly == w->a.class) return; - w->focused = false; - if (ps->o.use_ewmh_active_win && w == ps->active_win) - w->focused = true; + w->focused_real = false; + if (ps->o.track_focus && ps->o.use_ewmh_active_win + && w == ps->active_win) + w->focused_real = true; w->a.map_state = IsViewable; @@ -1841,23 +1848,22 @@ map_win(session_t *ps, Window id) { win_get_class(ps, w); } - if (ps->o.track_focus) { - // Occasionally compton does not seem able to get a FocusIn event from - // a window just mapped. I suspect it's a timing issue again when the - // XSelectInput() is called too late. We have to recheck the focused - // window here. It makes no sense if we are using EWMH - // _NET_ACTIVE_WINDOW. - if (!ps->o.use_ewmh_active_win) - recheck_focus(ps); - - // Consider a window without client window a WM window and mark it - // focused if mark_wmwin_focused is on, or it's over-redirected and - // mark_ovredir_focused is on - if ((ps->o.mark_wmwin_focused && !w->client_win) - || (ps->o.mark_ovredir_focused && w->id == w->client_win)) - w->focused = true; + // Occasionally compton does not seem able to get a FocusIn event from + // a window just mapped. I suspect it's a timing issue again when the + // XSelectInput() is called too late. We have to recheck the focused + // window here. It makes no sense if we are using EWMH + // _NET_ACTIVE_WINDOW. + if (ps->o.track_focus && !ps->o.use_ewmh_active_win) { + recheck_focus(ps); } + // Update window focus state + win_update_focused(ps, w); + + // Update opacity and dim state + win_update_opacity_prop(ps, w); + w->flags |= WFLAG_OPCT_CHANGE; + // Check for _COMPTON_SHADOW if (ps->o.respect_prop_shadow) win_update_attr_shadow_raw(ps, w); @@ -1865,9 +1871,6 @@ map_win(session_t *ps, Window id) { // Many things above could affect shadow determine_shadow(ps, w); - // Fading in - calc_opacity(ps, w, true); - // Set fading state if (ps->o.no_fading_openclose) { set_fade_callback(ps, w, finish_map_win, true); @@ -1880,8 +1883,6 @@ map_win(session_t *ps, Window id) { determine_fade(ps, w); } - calc_dim(ps, w); - w->damaged = 1; @@ -1931,7 +1932,7 @@ unmap_win(session_t *ps, Window id) { w->a.map_state = IsUnmapped; // Fading out - w->opacity_tgt = 0; + w->flags |= WFLAG_OPCT_CHANGE; set_fade_callback(ps, w, unmap_callback, false); if (ps->o.no_fading_openclose) w->fade = false; @@ -2006,35 +2007,23 @@ determine_mode(session_t *ps, win *w) { * refetched */ static void -calc_opacity(session_t *ps, win *w, bool refetch_prop) { - opacity_t opacity; - - // Do nothing for unmapped window, calc_opacity() will be called - // when it's mapped - // I suppose I need not to check for IsUnviewable here? - if (IsViewable != w->a.map_state) return; - - // Do not refetch the opacity window attribute unless necessary, this - // is probably an expensive operation in some cases - if (refetch_prop) { - w->opacity_prop = wid_get_opacity_prop(ps, w->id, OPAQUE); - if (!ps->o.detect_client_opacity || !w->client_win - || w->id == w->client_win) - w->opacity_prop_client = OPAQUE; - else - w->opacity_prop_client = wid_get_opacity_prop(ps, w->client_win, - OPAQUE); - } +calc_opacity(session_t *ps, win *w) { + opacity_t opacity = OPAQUE; - if (OPAQUE == (opacity = w->opacity_prop) - && OPAQUE == (opacity = w->opacity_prop_client)) { - opacity = ps->o.wintype_opacity[w->window_type] * OPAQUE; - } + if (w->destroyed || IsViewable != w->a.map_state) + opacity = 0; + else { + // Try obeying opacity property and window type opacity firstly + if (OPAQUE == (opacity = w->opacity_prop) + && OPAQUE == (opacity = w->opacity_prop_client)) { + opacity = ps->o.wintype_opacity[w->window_type] * OPAQUE; + } - // Respect inactive_opacity in some cases - if (ps->o.inactive_opacity && is_normal_win(w) && false == w->focused - && (OPAQUE == opacity || ps->o.inactive_opacity_override)) { - opacity = ps->o.inactive_opacity; + // Respect inactive_opacity in some cases + if (ps->o.inactive_opacity && is_normal_win(w) && false == w->focused + && (OPAQUE == opacity || ps->o.inactive_opacity_override)) { + opacity = ps->o.inactive_opacity; + } } w->opacity_tgt = opacity; @@ -2047,6 +2036,10 @@ static void calc_dim(session_t *ps, win *w) { bool dim; + // Make sure we do nothing if the window is unmapped / destroyed + if (w->destroyed || IsViewable != w->a.map_state) + return; + if (ps->o.inactive_dim && is_normal_win(w) && !(w->focused)) { dim = true; } else { @@ -2273,6 +2266,7 @@ add_win(session_t *ps, Window id, Window prev) { new->class_general = NULL; new->cache_sblst = NULL; new->cache_fblst = NULL; + new->cache_fcblst = NULL; new->bounding_shaped = false; new->rounded_corners = false; @@ -2298,6 +2292,7 @@ add_win(session_t *ps, Window id, Window prev) { new->frame_alpha_pict = None; new->dim = false; new->focused = false; + new->focused_real = false; new->destroyed = false; new->need_configure = false; new->window_type = WINTYPE_UNKNOWN; @@ -2517,7 +2512,7 @@ destroy_win(session_t *ps, Window id) { w->destroyed = true; // Fading out the window - w->opacity_tgt = 0; + w->flags |= WFLAG_OPCT_CHANGE; set_fade_callback(ps, w, destroy_callback, false); } } @@ -2909,7 +2904,7 @@ ev_focus_in(session_t *ps, XFocusChangeEvent *ev) { // To deal with events sent from windows just destroyed if (!w) return; - set_focused(ps, w, true); + win_set_focused(ps, w, true); } inline static void @@ -2926,7 +2921,7 @@ ev_focus_out(session_t *ps, XFocusChangeEvent *ev) { // To deal with events sent from windows just destroyed if (!w) return; - set_focused(ps, w, false); + win_set_focused(ps, w, false); } inline static void @@ -3039,10 +3034,9 @@ update_ewmh_active_win(session_t *ps) { // Mark the window focused if (w) { - if (!w->focused) - set_focused(ps, w, true); + win_set_focused(ps, w, true); if (ps->active_win && w != ps->active_win) - set_focused(ps, ps->active_win, false); + win_set_focused(ps, ps->active_win, false); ps->active_win = w; } } @@ -3079,7 +3073,7 @@ ev_property_notify(session_t *ps, XPropertyEvent *ev) { w->opacity_prop_client = wid_get_opacity_prop(ps, w->client_win, OPAQUE); if (w) { - calc_opacity(ps, w, false); + w->flags |= WFLAG_OPCT_CHANGE; } } @@ -3097,8 +3091,10 @@ ev_property_notify(session_t *ps, XPropertyEvent *ev) { if (ps->o.track_wdata && (ps->atom_name == ev->atom || ps->atom_name_ewmh == ev->atom)) { win *w = find_toplevel(ps, ev->window); - if (w && 1 == win_get_name(ps, w)) + if (w && 1 == win_get_name(ps, w)) { determine_shadow(ps, w); + win_update_focused(ps, w); + } } // If class changes @@ -3107,6 +3103,7 @@ ev_property_notify(session_t *ps, XPropertyEvent *ev) { if (w) { win_get_class(ps, w); determine_shadow(ps, w); + win_update_focused(ps, w); } } @@ -3354,7 +3351,6 @@ usage(void) { " Detect _NET_WM_OPACITY on client windows, useful for window\n" " managers not passing _NET_WM_OPACITY of client windows to frame\n" " windows.\n" - "\n" "--refresh-rate val\n" " Specify refresh rate of the screen. If not specified or 0, compton\n" " will try detecting this with X RandR extension.\n" @@ -3390,6 +3386,9 @@ usage(void) { " Unredirect all windows if a full-screen opaque window is\n" " detected, to maximize performance for full-screen windows.\n" " Experimental.\n" + "--focus-exclude condition\n" + " Specify a list of conditions of windows that should always be\n" + " considered focused.\n" "\n" "Format of a condition:\n" "\n" @@ -3632,6 +3631,28 @@ parse_vsync(session_t *ps, const char *optarg) { } } +/** + * Parse a condition list in configuration file. + */ +static void +parse_cfg_condlst(const config_t *pcfg, wincond_t **pcondlst, + const char *name) { + config_setting_t *setting = config_lookup(pcfg, name); + if (setting) { + // Parse an array of options + if (config_setting_is_array(setting)) { + int i = config_setting_length(setting); + while (i--) { + condlst_add(pcondlst, config_setting_get_string_elem(setting, i)); + } + } + // Treat it as a single pattern if it's a string + else if (CONFIG_TYPE_STRING == config_setting_type(setting)) { + condlst_add(pcondlst, config_setting_get_string(setting)); + } + } +} + /** * Parse a configuration file from default location. */ @@ -3763,25 +3784,9 @@ parse_config(session_t *ps, char *cpath, struct options_tmp *pcfgtmp) { lcfg_lookup_bool(&cfg, "unredir-if-possible", &ps->o.unredir_if_possible); // --shadow-exclude - { - config_setting_t *setting = - config_lookup(&cfg, "shadow-exclude"); - if (setting) { - // Parse an array of shadow-exclude - if (config_setting_is_array(setting)) { - int i = config_setting_length(setting); - while (i--) { - condlst_add(&ps->o.shadow_blacklist, - config_setting_get_string_elem(setting, i)); - } - } - // Treat it as a single pattern if it's a string - else if (CONFIG_TYPE_STRING == config_setting_type(setting)) { - condlst_add(&ps->o.shadow_blacklist, - config_setting_get_string(setting)); - } - } - } + parse_cfg_condlst(&cfg, &ps->o.shadow_blacklist, "shadow-exclude"); + // --focus-exclude + parse_cfg_condlst(&cfg, &ps->o.focus_blacklist, "focus-exclude"); // Wintype settings { wintype_t i; @@ -3835,6 +3840,7 @@ get_cfg(session_t *ps, int argc, char *const *argv) { { "use-ewmh-active-win", no_argument, NULL, 276 }, { "respect-prop-shadow", no_argument, NULL, 277 }, { "unredir-if-possible", no_argument, NULL, 278 }, + { "focus-exclude", required_argument, NULL, 279 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -4036,6 +4042,10 @@ get_cfg(session_t *ps, int argc, char *const *argv) { // --unredir-if-possible ps->o.unredir_if_possible = true; break; + case 279: + // --focus-exclude + condlst_add(&ps->o.focus_blacklist, optarg); + break; default: usage(); } @@ -4082,7 +4092,8 @@ get_cfg(session_t *ps, int argc, char *const *argv) { } // Determine whether we need to track window name and class - if (ps->o.shadow_blacklist || ps->o.fade_blacklist) + if (ps->o.shadow_blacklist || ps->o.fade_blacklist + || ps->o.focus_blacklist) ps->o.track_wdata = true; } @@ -4564,6 +4575,7 @@ session_init(session_t *ps_old, int argc, char **argv) { .inactive_dim = 0.0, .alpha_step = 0.03, .use_ewmh_active_win = false, + .focus_blacklist = NULL, .track_focus = false, .track_wdata = false, diff --git a/compton.h b/compton.h index de4d84e5e..00f6349e1 100644 --- a/compton.h +++ b/compton.h @@ -116,6 +116,8 @@ #define WFLAG_SIZE_CHANGE 0x0001 // Window size/position is changed #define WFLAG_POS_CHANGE 0x0002 +// Window opacity / dim state changed +#define WFLAG_OPCT_CHANGE 0x0004 /** * Types @@ -290,8 +292,12 @@ typedef struct { double inactive_dim; /// Step for pregenerating alpha pictures. 0.01 - 1.0. double alpha_step; + + // === Focus related === /// 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. + wincond_t *focus_blacklist; // === Calculated === /// Whether compton needs to track focus changes. @@ -512,8 +518,10 @@ typedef struct _win { XserverRegion extents; // Type of the window. wintype_t window_type; - /// Whether the window is focused. + /// Whether the window is to be considered focused. bool focused; + /// Whether the window is actually focused. + bool focused_real; /// Whether the window has been destroyed. bool destroyed; /// Cached width/height of the window including border. @@ -531,6 +539,7 @@ typedef struct _win { char *class_general; wincond_t *cache_sblst; wincond_t *cache_fblst; + wincond_t *cache_fcblst; // Opacity-related members /// Current window opacity. @@ -1354,6 +1363,20 @@ unmap_win(session_t *ps, Window id); static opacity_t wid_get_opacity_prop(session_t *ps, Window wid, opacity_t def); +/** + * Reread opacity property of a window. + */ +static inline void +win_update_opacity_prop(session_t *ps, win *w) { + w->opacity_prop = wid_get_opacity_prop(ps, w->id, OPAQUE); + if (!ps->o.detect_client_opacity || !w->client_win + || w->id == w->client_win) + w->opacity_prop_client = OPAQUE; + else + w->opacity_prop_client = wid_get_opacity_prop(ps, w->client_win, + OPAQUE); +} + static double get_opacity_percent(win *w); @@ -1361,16 +1384,39 @@ static void determine_mode(session_t *ps, win *w); static void -calc_opacity(session_t *ps, win *w, bool refetch_prop); +calc_opacity(session_t *ps, win *w); static void calc_dim(session_t *ps, win *w); +/** + * Update focused state of a window. + */ +static inline void +win_update_focused(session_t *ps, win *w) { + bool focused_old = w->focused; + + w->focused = w->focused_real; + + // Consider a window without client window a WM window and mark it + // focused if mark_wmwin_focused is on, or it's over-redirected and + // mark_ovredir_focused is on + if ((ps->o.mark_wmwin_focused && !w->client_win) + || (ps->o.mark_ovredir_focused && w->id == w->client_win) + || win_match(w, ps->o.focus_blacklist, &w->cache_fcblst)) + w->focused = true; + + if (w->focused != focused_old) + w->flags |= WFLAG_OPCT_CHANGE; +} + +/** + * Set real focused state of a window. + */ static inline void -set_focused(session_t *ps, win *w, bool focused) { - w->focused = focused; - calc_opacity(ps, w, false); - calc_dim(ps, w); +win_set_focused(session_t *ps, win *w, bool focused) { + w->focused_real = focused; + win_update_focused(ps, w); } static void @@ -1628,6 +1674,10 @@ lcfg_lookup_int(const config_t *config, const char *path, int *value) { static FILE * open_config_file(char *cpath, char **path); +static void +parse_cfg_condlst(const config_t *pcfg, wincond_t **pcondlst, + const char *name); + static void parse_config(session_t *ps, char *cpath, struct options_tmp *pcfgtmp); #endif