Bug fix #99: Rewrite focus detection logic
- Rewrite focus detection logic. Remove w->focused_real and use ps->active_win to identify focused window uniformly. Use a more expensive way to filter FocusIn/Out events to improve reliability. Only limited tests are done, and bugs are likely to be introduced. (#99) - Known issue: Under fvwm, compton sometimes does not consistently report the window input gets sent to. But there's something wrong in that case: XGetInputFocus() shows the root window is focused but another window is receiving input.
This commit is contained in:
parent
a6b76e954f
commit
4bd3db2bc7
2
src/c2.c
2
src/c2.c
|
@ -1055,7 +1055,7 @@ c2_match_once_leaf(session_t *ps, win *w, const c2_l_t *pleaf,
|
||||||
case C2_L_PFULLSCREEN: tgt = win_is_fullscreen(ps, w); break;
|
case C2_L_PFULLSCREEN: tgt = win_is_fullscreen(ps, w); break;
|
||||||
case C2_L_POVREDIR: tgt = w->a.override_redirect; break;
|
case C2_L_POVREDIR: tgt = w->a.override_redirect; break;
|
||||||
case C2_L_PARGB: tgt = (WMODE_ARGB == w->mode); break;
|
case C2_L_PARGB: tgt = (WMODE_ARGB == w->mode); break;
|
||||||
case C2_L_PFOCUSED: tgt = w->focused_real; break;
|
case C2_L_PFOCUSED: tgt = win_is_focused_real(ps, w); break;
|
||||||
case C2_L_PWMWIN: tgt = w->wmwin; break;
|
case C2_L_PWMWIN: tgt = w->wmwin; break;
|
||||||
case C2_L_PCLIENT: tgt = w->client_win; break;
|
case C2_L_PCLIENT: tgt = w->client_win; break;
|
||||||
case C2_L_PLEADER: tgt = w->leader; break;
|
case C2_L_PLEADER: tgt = w->leader; break;
|
||||||
|
|
22
src/common.h
22
src/common.h
|
@ -954,8 +954,6 @@ typedef struct _win {
|
||||||
bool focused;
|
bool focused;
|
||||||
/// Override value of window focus state. Set by D-Bus method calls.
|
/// Override value of window focus state. Set by D-Bus method calls.
|
||||||
switch_t focused_force;
|
switch_t focused_force;
|
||||||
/// Whether the window is actually focused.
|
|
||||||
bool focused_real;
|
|
||||||
|
|
||||||
// Blacklist related members
|
// Blacklist related members
|
||||||
/// Name of the window.
|
/// Name of the window.
|
||||||
|
@ -1695,6 +1693,14 @@ find_toplevel(session_t *ps, Window id) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a window is really focused.
|
||||||
|
*/
|
||||||
|
static inline bool
|
||||||
|
win_is_focused_real(session_t *ps, const win *w) {
|
||||||
|
return IsViewable == w->a.map_state && ps->active_win == w;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find out the currently focused window.
|
* Find out the currently focused window.
|
||||||
*
|
*
|
||||||
|
@ -1702,19 +1708,15 @@ find_toplevel(session_t *ps, Window id) {
|
||||||
*/
|
*/
|
||||||
static inline win *
|
static inline win *
|
||||||
find_focused(session_t *ps) {
|
find_focused(session_t *ps) {
|
||||||
if (!ps->o.track_focus)
|
if (!ps->o.track_focus) return NULL;
|
||||||
return NULL;
|
|
||||||
|
|
||||||
for (win *w = ps->list; w; w = w->next) {
|
|
||||||
if (w->focused_real && !w->destroyed)
|
|
||||||
return w;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (ps->active_win && win_is_focused_real(ps, ps->active_win))
|
||||||
|
return ps->active_win;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copies a region
|
* Copies a region.
|
||||||
*/
|
*/
|
||||||
static inline XserverRegion
|
static inline XserverRegion
|
||||||
copy_region(const session_t *ps, XserverRegion oldregion) {
|
copy_region(const session_t *ps, XserverRegion oldregion) {
|
||||||
|
|
103
src/compton.c
103
src/compton.c
|
@ -776,19 +776,12 @@ recheck_focus(session_t *ps) {
|
||||||
// opacity on it
|
// opacity on it
|
||||||
Window wid = 0;
|
Window wid = 0;
|
||||||
int revert_to;
|
int revert_to;
|
||||||
win *w = NULL;
|
|
||||||
|
|
||||||
XGetInputFocus(ps->dpy, &wid, &revert_to);
|
XGetInputFocus(ps->dpy, &wid, &revert_to);
|
||||||
|
|
||||||
if (!wid || PointerRoot == wid)
|
win *w = find_win_all(ps, wid);
|
||||||
return NULL;
|
|
||||||
|
|
||||||
// Fallback to the old method if find_toplevel() fails
|
// And we set the focus state here
|
||||||
if (!(w = find_toplevel(ps, wid))) {
|
|
||||||
w = find_toplevel2(ps, wid);
|
|
||||||
}
|
|
||||||
|
|
||||||
// And we set the focus state and opacity here
|
|
||||||
if (w) {
|
if (w) {
|
||||||
win_set_focused(ps, w, true);
|
win_set_focused(ps, w, true);
|
||||||
return w;
|
return w;
|
||||||
|
@ -2066,19 +2059,12 @@ map_win(session_t *ps, Window id) {
|
||||||
|| IsViewable == w->a.map_state)
|
|| IsViewable == w->a.map_state)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
assert(!w->focused_real);
|
assert(!win_is_focused_real(ps, w));
|
||||||
|
|
||||||
w->a.map_state = IsViewable;
|
w->a.map_state = IsViewable;
|
||||||
|
|
||||||
cxinerama_win_upd_scr(ps, w);
|
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
|
|
||||||
&& w == ps->active_win)
|
|
||||||
focused_real = true;
|
|
||||||
win_set_focused(ps, w, focused_real);
|
|
||||||
|
|
||||||
// Call XSelectInput() before reading properties so that no property
|
// Call XSelectInput() before reading properties so that no property
|
||||||
// changes are lost
|
// changes are lost
|
||||||
XSelectInput(ps->dpy, id, determine_evmask(ps, id, WIN_EVMODE_FRAME));
|
XSelectInput(ps->dpy, id, determine_evmask(ps, id, WIN_EVMODE_FRAME));
|
||||||
|
@ -2113,14 +2099,10 @@ map_win(session_t *ps, Window id) {
|
||||||
// Detect if the window is shaped or has rounded corners
|
// Detect if the window is shaped or has rounded corners
|
||||||
win_update_shape_raw(ps, w);
|
win_update_shape_raw(ps, w);
|
||||||
|
|
||||||
// Occasionally compton does not seem able to get a FocusIn event from
|
// FocusIn/Out may be ignored when the window is unmapped, so we must
|
||||||
// a window just mapped. I suspect it's a timing issue again when the
|
// recheck focus here
|
||||||
// XSelectInput() is called too late. We have to recheck the focused
|
if (ps->o.track_focus)
|
||||||
// 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);
|
recheck_focus(ps);
|
||||||
}
|
|
||||||
|
|
||||||
// Update window focus state
|
// Update window focus state
|
||||||
win_update_focused(ps, w);
|
win_update_focused(ps, w);
|
||||||
|
@ -2296,7 +2278,7 @@ calc_opacity(session_t *ps, win *w) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Respect active_opacity only when the window is physically focused
|
// Respect active_opacity only when the window is physically focused
|
||||||
if (OPAQUE == opacity && ps->o.active_opacity && w->focused_real)
|
if (OPAQUE == opacity && ps->o.active_opacity && win_is_focused_real(ps, w))
|
||||||
opacity = ps->o.active_opacity;
|
opacity = ps->o.active_opacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2752,7 +2734,6 @@ add_win(session_t *ps, Window id, Window prev) {
|
||||||
|
|
||||||
.focused = false,
|
.focused = false,
|
||||||
.focused_force = UNSET,
|
.focused_force = UNSET,
|
||||||
.focused_real = false,
|
|
||||||
|
|
||||||
.name = NULL,
|
.name = NULL,
|
||||||
.class_instance = NULL,
|
.class_instance = NULL,
|
||||||
|
@ -3270,7 +3251,7 @@ win_update_focused(session_t *ps, win *w) {
|
||||||
w->focused = w->focused_force;
|
w->focused = w->focused_force;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
w->focused = w->focused_real;
|
w->focused = win_is_focused_real(ps, w);
|
||||||
|
|
||||||
// Use wintype_focus, and treat WM windows and override-redirected
|
// Use wintype_focus, and treat WM windows and override-redirected
|
||||||
// windows specially
|
// windows specially
|
||||||
|
@ -3302,15 +3283,32 @@ win_set_focused(session_t *ps, win *w, bool focused) {
|
||||||
if (IsUnmapped == w->a.map_state)
|
if (IsUnmapped == w->a.map_state)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (w->focused_real != focused) {
|
if (win_is_focused_real(ps, w) == focused) return;
|
||||||
w->focused_real = focused;
|
|
||||||
|
|
||||||
|
if (focused) {
|
||||||
|
if (ps->active_win)
|
||||||
|
win_set_focused(ps, ps->active_win, false);
|
||||||
|
ps->active_win = w;
|
||||||
|
}
|
||||||
|
else if (w == ps->active_win)
|
||||||
|
ps->active_win = NULL;
|
||||||
|
|
||||||
|
assert(win_is_focused_real(ps, w) == focused);
|
||||||
|
|
||||||
|
win_on_focus_change(ps, w);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle window focus change.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
win_on_focus_change(session_t *ps, win *w) {
|
||||||
// If window grouping detection is enabled
|
// If window grouping detection is enabled
|
||||||
if (ps->o.track_leader) {
|
if (ps->o.track_leader) {
|
||||||
Window leader = win_get_leader(ps, w);
|
Window leader = win_get_leader(ps, w);
|
||||||
|
|
||||||
// If the window gets focused, replace the old active_leader
|
// If the window gets focused, replace the old active_leader
|
||||||
if (w->focused_real && leader != ps->active_leader) {
|
if (win_is_focused_real(ps, w) && leader != ps->active_leader) {
|
||||||
Window active_leader_old = ps->active_leader;
|
Window active_leader_old = ps->active_leader;
|
||||||
|
|
||||||
ps->active_leader = leader;
|
ps->active_leader = leader;
|
||||||
|
@ -3319,7 +3317,7 @@ win_set_focused(session_t *ps, win *w, bool focused) {
|
||||||
group_update_focused(ps, leader);
|
group_update_focused(ps, leader);
|
||||||
}
|
}
|
||||||
// If the group get unfocused, remove it from active_leader
|
// If the group get unfocused, remove it from active_leader
|
||||||
else if (!w->focused_real && leader && leader == ps->active_leader
|
else if (!win_is_focused_real(ps, w) && leader && leader == ps->active_leader
|
||||||
&& !group_is_focused(ps, leader)) {
|
&& !group_is_focused(ps, leader)) {
|
||||||
ps->active_leader = None;
|
ps->active_leader = None;
|
||||||
group_update_focused(ps, leader);
|
group_update_focused(ps, leader);
|
||||||
|
@ -3339,14 +3337,14 @@ win_set_focused(session_t *ps, win *w, bool focused) {
|
||||||
#ifdef CONFIG_DBUS
|
#ifdef CONFIG_DBUS
|
||||||
// Send D-Bus signal
|
// Send D-Bus signal
|
||||||
if (ps->o.dbus) {
|
if (ps->o.dbus) {
|
||||||
if (w->focused_real)
|
if (win_is_focused_real(ps, w))
|
||||||
cdbus_ev_win_focusin(ps, w);
|
cdbus_ev_win_focusin(ps, w);
|
||||||
else
|
else
|
||||||
cdbus_ev_win_focusout(ps, w);
|
cdbus_ev_win_focusout(ps, w);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Update leader of a window.
|
* Update leader of a window.
|
||||||
*/
|
*/
|
||||||
|
@ -3386,7 +3384,7 @@ win_set_leader(session_t *ps, win *w, Window nleader) {
|
||||||
// Update the old and new window group and active_leader if the window
|
// Update the old and new window group and active_leader if the window
|
||||||
// could affect their state.
|
// could affect their state.
|
||||||
Window cache_leader = win_get_leader(ps, w);
|
Window cache_leader = win_get_leader(ps, w);
|
||||||
if (w->focused_real && cache_leader_old != cache_leader) {
|
if (win_is_focused_real(ps, w) && cache_leader_old != cache_leader) {
|
||||||
ps->active_leader = cache_leader;
|
ps->active_leader = cache_leader;
|
||||||
|
|
||||||
group_update_focused(ps, cache_leader_old);
|
group_update_focused(ps, cache_leader_old);
|
||||||
|
@ -3808,11 +3806,10 @@ ev_focus_report(XFocusChangeEvent* ev) {
|
||||||
*/
|
*/
|
||||||
inline static bool
|
inline static bool
|
||||||
ev_focus_accept(XFocusChangeEvent *ev) {
|
ev_focus_accept(XFocusChangeEvent *ev) {
|
||||||
return ev->detail == NotifyNonlinear
|
return NotifyNormal == ev->mode || NotifyUngrab == ev->mode;
|
||||||
|| ev->detail == NotifyNonlinearVirtual;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline static void
|
static inline void
|
||||||
ev_focus_in(session_t *ps, XFocusChangeEvent *ev) {
|
ev_focus_in(session_t *ps, XFocusChangeEvent *ev) {
|
||||||
#ifdef DEBUG_EVENTS
|
#ifdef DEBUG_EVENTS
|
||||||
ev_focus_report(ev);
|
ev_focus_report(ev);
|
||||||
|
@ -3821,11 +3818,8 @@ ev_focus_in(session_t *ps, XFocusChangeEvent *ev) {
|
||||||
if (!ev_focus_accept(ev))
|
if (!ev_focus_accept(ev))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
win *w = find_win(ps, ev->window);
|
win *w = find_win_all(ps, ev->window);
|
||||||
|
if (w)
|
||||||
// To deal with events sent from windows just destroyed
|
|
||||||
if (!w) return;
|
|
||||||
|
|
||||||
win_set_focused(ps, w, true);
|
win_set_focused(ps, w, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3838,11 +3832,8 @@ ev_focus_out(session_t *ps, XFocusChangeEvent *ev) {
|
||||||
if (!ev_focus_accept(ev))
|
if (!ev_focus_accept(ev))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
win *w = find_win(ps, ev->window);
|
win *w = find_win_all(ps, ev->window);
|
||||||
|
if (w)
|
||||||
// To deal with events sent from windows just destroyed
|
|
||||||
if (!w) return;
|
|
||||||
|
|
||||||
win_set_focused(ps, w, false);
|
win_set_focused(ps, w, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3965,21 +3956,11 @@ ev_expose(session_t *ps, XExposeEvent *ev) {
|
||||||
static void
|
static void
|
||||||
update_ewmh_active_win(session_t *ps) {
|
update_ewmh_active_win(session_t *ps) {
|
||||||
// Search for the window
|
// Search for the window
|
||||||
Window wid =
|
Window wid = wid_get_prop_window(ps, ps->root, ps->atom_ewmh_active_win);
|
||||||
wid_get_prop_window(ps, ps->root, ps->atom_ewmh_active_win);
|
win *w = find_win_all(ps, wid);
|
||||||
win *w = NULL;
|
|
||||||
|
|
||||||
if (wid && !(w = find_toplevel(ps, wid)))
|
// Mark the window focused. No need to unfocus the previous one.
|
||||||
if (!(w = find_win(ps, wid)))
|
if (w) win_set_focused(ps, w, true);
|
||||||
w = find_toplevel2(ps, wid);
|
|
||||||
|
|
||||||
// Mark the window focused
|
|
||||||
if (w) {
|
|
||||||
if (ps->active_win && w != ps->active_win)
|
|
||||||
win_set_focused(ps, ps->active_win, false);
|
|
||||||
ps->active_win = w;
|
|
||||||
win_set_focused(ps, w, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline static void
|
inline static void
|
||||||
|
|
|
@ -563,6 +563,20 @@ clear_cache_win_leaders(session_t *ps) {
|
||||||
static win *
|
static win *
|
||||||
find_toplevel2(session_t *ps, Window wid);
|
find_toplevel2(session_t *ps, Window wid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find matched window.
|
||||||
|
*/
|
||||||
|
static inline win *
|
||||||
|
find_win_all(session_t *ps, const Window wid) {
|
||||||
|
if (!wid || PointerRoot == wid || wid == ps->root || wid == ps->overlay)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
win *w = find_win(ps, wid);
|
||||||
|
if (!w) w = find_toplevel(ps, wid);
|
||||||
|
if (!w) w = find_toplevel2(ps, wid);
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
static Window
|
static Window
|
||||||
win_get_leader_raw(session_t *ps, win *w, int recursions);
|
win_get_leader_raw(session_t *ps, win *w, int recursions);
|
||||||
|
|
||||||
|
@ -589,7 +603,7 @@ group_is_focused(session_t *ps, Window leader) {
|
||||||
|
|
||||||
for (win *w = ps->list; w; w = w->next) {
|
for (win *w = ps->list; w; w = w->next) {
|
||||||
if (win_get_leader(ps, w) == leader && !w->destroyed
|
if (win_get_leader(ps, w) == leader && !w->destroyed
|
||||||
&& w->focused_real)
|
&& win_is_focused_real(ps, w))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -764,6 +778,9 @@ group_update_focused(session_t *ps, Window leader) {
|
||||||
static inline void
|
static inline void
|
||||||
win_set_focused(session_t *ps, win *w, bool focused);
|
win_set_focused(session_t *ps, win *w, bool focused);
|
||||||
|
|
||||||
|
static void
|
||||||
|
win_on_focus_change(session_t *ps, win *w);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
win_determine_fade(session_t *ps, win *w);
|
win_determine_fade(session_t *ps, win *w);
|
||||||
|
|
||||||
|
|
|
@ -722,7 +722,11 @@ cdbus_process_win_get(session_t *ps, DBusMessage *msg) {
|
||||||
cdbus_m_win_get_do(window_type, cdbus_reply_enum);
|
cdbus_m_win_get_do(window_type, cdbus_reply_enum);
|
||||||
cdbus_m_win_get_do(wmwin, cdbus_reply_bool);
|
cdbus_m_win_get_do(wmwin, cdbus_reply_bool);
|
||||||
cdbus_m_win_get_do(leader, cdbus_reply_wid);
|
cdbus_m_win_get_do(leader, cdbus_reply_wid);
|
||||||
cdbus_m_win_get_do(focused_real, cdbus_reply_bool);
|
// focused_real
|
||||||
|
if (!strcmp("focused_real", target)) {
|
||||||
|
cdbus_reply_bool(ps, msg, win_is_focused_real(ps, w));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
cdbus_m_win_get_do(fade_force, cdbus_reply_enum);
|
cdbus_m_win_get_do(fade_force, cdbus_reply_enum);
|
||||||
cdbus_m_win_get_do(shadow_force, cdbus_reply_enum);
|
cdbus_m_win_get_do(shadow_force, cdbus_reply_enum);
|
||||||
cdbus_m_win_get_do(focused_force, cdbus_reply_enum);
|
cdbus_m_win_get_do(focused_force, cdbus_reply_enum);
|
||||||
|
|
Loading…
Reference in New Issue