Feature #65: --focus-exclude

- Add --focus-exclude, which should be used with a list of conditions to
  set certain windows to always be considered focused.

- Change focus determination process, with a newly added w->focused_real
  that reflects whether the window is actually focused, while w->focused
  represents whether compton considers it focused. The primary
  difference is now when a window considered focused because of
  --mark-wmwin-focused or --mark-ovredir-focused receives a FocusOut
  event, it's still considered focused by compton.

- Change opacity target and dim state calculation so that it's done at
  most once every paint.

- Split window opacity property fetching from calc_opacity() to a new
  function win_update_opacity_prop().

- Silence a warning in wid_get_prop_wintype().

- Rename a few functions. Code clean-up.

- My time is very limited currently, few tests are done, so this commit
  may very well introduce bugs.

- Known issue: Dim picture opacity does not change based on window
  opacity, causing somehow annoying effects when a window fades off.
This commit is contained in:
Richard Grenville 2012-11-28 11:44:00 +08:00
parent 0f851b17a2
commit 72c18b6219
3 changed files with 152 additions and 89 deletions

View File

@ -40,6 +40,7 @@ dbe = false;
paint-on-overlay = false;
sw-opti = false;
unredir-if-possible = false;
focus-exclude = [ ];
# Window type settings
wintypes:

View File

@ -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;
calc_opacity(session_t *ps, win *w) {
opacity_t opacity = OPAQUE;
// 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;
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;
}
// 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);
}
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,

View File

@ -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
set_focused(session_t *ps, win *w, bool focused) {
w->focused = focused;
calc_opacity(ps, w, false);
calc_dim(ps, w);
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
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