Bug fix #68: Possible fix for failure in client window detection
- Note I'm not in the best state today (bad cold & sleep-deprived). This commit is likely to introduce bugs. - Attempt to fix client window detection failures happening when compton searches for client window before it's ready. - Fix build failure with <libpcre-8.20. Thanks to @pvanek for reporting in #51. - Move client window detection to a new function win_recheck_client(). - Add win_unmark_client(), which unmarks a client window. - Rename a few functions. - Split fetching of values of type-Window properties to a new function wid_get_prop_window(). - Add extra safety checks and assert calls to various functions, to expose potential bugs. - Fix a memory leak that w->role is not freed on window destruction.
This commit is contained in:
parent
a7189263e1
commit
812ba93516
205
src/compton.c
205
src/compton.c
@ -1143,7 +1143,7 @@ border_size(session_t *ps, win *w) {
|
|||||||
*/
|
*/
|
||||||
static Window
|
static Window
|
||||||
find_client_win(session_t *ps, Window w) {
|
find_client_win(session_t *ps, Window w) {
|
||||||
if (wid_has_attr(ps, w, ps->atom_client)) {
|
if (wid_has_prop(ps, w, ps->atom_client)) {
|
||||||
return w;
|
return w;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1799,15 +1799,23 @@ map_win(session_t *ps, Window id) {
|
|||||||
win *w = find_win(ps, id);
|
win *w = find_win(ps, id);
|
||||||
|
|
||||||
// Don't care about window mapping if it's an InputOnly window
|
// Don't care about window mapping if it's an InputOnly window
|
||||||
if (!w || InputOnly == w->a.class) return;
|
// Try avoiding mapping a window twice
|
||||||
|
if (!w || InputOnly == w->a.class
|
||||||
|
|| IsViewable == w->a.map_state)
|
||||||
|
return;
|
||||||
|
|
||||||
w->focused_real = false;
|
assert(!w->focused_real);
|
||||||
if (ps->o.track_focus && ps->o.use_ewmh_active_win
|
|
||||||
&& w == ps->active_win)
|
|
||||||
w->focused_real = true;
|
|
||||||
|
|
||||||
w->a.map_state = IsViewable;
|
w->a.map_state = IsViewable;
|
||||||
|
|
||||||
|
// 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));
|
||||||
@ -1823,31 +1831,11 @@ map_win(session_t *ps, Window id) {
|
|||||||
// Detect client window here instead of in add_win() as the client
|
// Detect client window here instead of in add_win() as the client
|
||||||
// window should have been prepared at this point
|
// window should have been prepared at this point
|
||||||
if (!w->client_win) {
|
if (!w->client_win) {
|
||||||
w->wmwin = false;
|
win_recheck_client(ps, w);
|
||||||
|
|
||||||
Window cw = None;
|
|
||||||
// Always recursively look for a window with WM_STATE, as Fluxbox
|
|
||||||
// sets override-redirect flags on all frame windows.
|
|
||||||
cw = find_client_win(ps, w->id);
|
|
||||||
#ifdef DEBUG_CLIENTWIN
|
|
||||||
if (cw)
|
|
||||||
printf("find_client_win(%#010lx): client %#010lx\n", w->id, cw);
|
|
||||||
#endif
|
|
||||||
// Set a window's client window to itself if we couldn't find a
|
|
||||||
// client window
|
|
||||||
if (!cw) {
|
|
||||||
cw = w->id;
|
|
||||||
w->wmwin = !w->a.override_redirect;
|
|
||||||
#ifdef DEBUG_CLIENTWIN
|
|
||||||
printf("find_client_win(%#010lx): client self (%s)\n", w->id,
|
|
||||||
(w->wmwin ? "wmwin": "override-redirected"));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
mark_client_win(ps, w, cw);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Re-mark client window here
|
// Re-mark client window here
|
||||||
mark_client_win(ps, w, w->client_win);
|
win_mark_client(ps, w, w->client_win);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(w->client_win);
|
assert(w->client_win);
|
||||||
@ -1947,11 +1935,11 @@ unmap_win(session_t *ps, Window id) {
|
|||||||
|
|
||||||
if (!w || IsUnmapped == w->a.map_state) return;
|
if (!w || IsUnmapped == w->a.map_state) return;
|
||||||
|
|
||||||
w->a.map_state = IsUnmapped;
|
|
||||||
|
|
||||||
// Set focus out
|
// Set focus out
|
||||||
win_set_focused(ps, w, false);
|
win_set_focused(ps, w, false);
|
||||||
|
|
||||||
|
w->a.map_state = IsUnmapped;
|
||||||
|
|
||||||
// Fading out
|
// Fading out
|
||||||
w->flags |= WFLAG_OPCT_CHANGE;
|
w->flags |= WFLAG_OPCT_CHANGE;
|
||||||
set_fade_callback(ps, w, unmap_callback, false);
|
set_fade_callback(ps, w, unmap_callback, false);
|
||||||
@ -2207,18 +2195,20 @@ calc_shadow_geometry(session_t *ps, win *w) {
|
|||||||
/**
|
/**
|
||||||
* Mark a window as the client window of another.
|
* Mark a window as the client window of another.
|
||||||
*
|
*
|
||||||
* @param dpy display to use
|
* @param ps current session
|
||||||
* @param w struct _win of the parent window
|
* @param w struct _win of the parent window
|
||||||
* @param client window ID of the client window
|
* @param client window ID of the client window
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
mark_client_win(session_t *ps, win *w, Window client) {
|
win_mark_client(session_t *ps, win *w, Window client) {
|
||||||
w->client_win = client;
|
w->client_win = client;
|
||||||
|
|
||||||
XSelectInput(ps->dpy, client, determine_evmask(ps, client, WIN_EVMODE_CLIENT));
|
XSelectInput(ps->dpy, client, determine_evmask(ps, client, WIN_EVMODE_CLIENT));
|
||||||
|
|
||||||
// Get the frame width and monitor further frame width changes on client
|
// Make sure the XSelectInput() requests are sent
|
||||||
// window if necessary
|
XSync(ps->dpy, False);
|
||||||
|
|
||||||
|
// Get frame widths if needed
|
||||||
if (ps->o.frame_opacity) {
|
if (ps->o.frame_opacity) {
|
||||||
get_frame_extents(ps, w, client);
|
get_frame_extents(ps, w, client);
|
||||||
}
|
}
|
||||||
@ -2232,11 +2222,69 @@ mark_client_win(session_t *ps, win *w, Window client) {
|
|||||||
// _NET_WM_WINDOW_TYPE_NORMAL, otherwise as _NET_WM_WINDOW_TYPE_DIALOG.
|
// _NET_WM_WINDOW_TYPE_NORMAL, otherwise as _NET_WM_WINDOW_TYPE_DIALOG.
|
||||||
if (WINTYPE_UNKNOWN == w->window_type) {
|
if (WINTYPE_UNKNOWN == w->window_type) {
|
||||||
if (w->a.override_redirect
|
if (w->a.override_redirect
|
||||||
|| !wid_has_attr(ps, client, ps->atom_transient))
|
|| !wid_has_prop(ps, client, ps->atom_transient))
|
||||||
w->window_type = WINTYPE_NORMAL;
|
w->window_type = WINTYPE_NORMAL;
|
||||||
else
|
else
|
||||||
w->window_type = WINTYPE_DIALOG;
|
w->window_type = WINTYPE_DIALOG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
win_update_focused(ps, w);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unmark current client window of a window.
|
||||||
|
*
|
||||||
|
* @param ps current session
|
||||||
|
* @param w struct _win of the parent window
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
win_unmark_client(session_t *ps, win *w) {
|
||||||
|
Window client = w->client_win;
|
||||||
|
|
||||||
|
w->client_win = None;
|
||||||
|
|
||||||
|
// Recheck event mask
|
||||||
|
XSelectInput(ps->dpy, client,
|
||||||
|
determine_evmask(ps, client, WIN_EVMODE_UNKNOWN));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recheck client window of a window.
|
||||||
|
*
|
||||||
|
* @param ps current session
|
||||||
|
* @param w struct _win of the parent window
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
win_recheck_client(session_t *ps, win *w) {
|
||||||
|
// Initialize wmwin to false
|
||||||
|
w->wmwin = false;
|
||||||
|
|
||||||
|
// Look for the client window
|
||||||
|
|
||||||
|
// Always recursively look for a window with WM_STATE, as Fluxbox
|
||||||
|
// sets override-redirect flags on all frame windows.
|
||||||
|
Window cw = find_client_win(ps, w->id);
|
||||||
|
#ifdef DEBUG_CLIENTWIN
|
||||||
|
if (cw)
|
||||||
|
printf_dbgf("(%#010lx): client %#010lx\n", w->id, cw);
|
||||||
|
#endif
|
||||||
|
// Set a window's client window to itself if we couldn't find a
|
||||||
|
// client window
|
||||||
|
if (!cw) {
|
||||||
|
cw = w->id;
|
||||||
|
w->wmwin = !w->a.override_redirect;
|
||||||
|
#ifdef DEBUG_CLIENTWIN
|
||||||
|
printf_dbgf("(%#010lx): client self (%s)\n", w->id,
|
||||||
|
(w->wmwin ? "wmwin": "override-redirected"));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmark the old one
|
||||||
|
if (w->client_win && w->client_win != cw)
|
||||||
|
win_unmark_client(ps, w);
|
||||||
|
|
||||||
|
// Mark the new one
|
||||||
|
win_mark_client(ps, w, cw);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -2268,6 +2316,11 @@ add_win(session_t *ps, Window id, Window prev) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delay window mapping
|
||||||
|
int map_state = new->a.map_state;
|
||||||
|
assert(IsViewable == map_state || IsUnmapped == map_state);
|
||||||
|
new->a.map_state = IsUnmapped;
|
||||||
|
|
||||||
new->damaged = 0;
|
new->damaged = 0;
|
||||||
new->to_paint = false;
|
new->to_paint = false;
|
||||||
new->pixmap = None;
|
new->pixmap = None;
|
||||||
@ -2342,7 +2395,7 @@ add_win(session_t *ps, Window id, Window prev) {
|
|||||||
new->next = *p;
|
new->next = *p;
|
||||||
*p = new;
|
*p = new;
|
||||||
|
|
||||||
if (new->a.map_state == IsViewable) {
|
if (map_state == IsViewable) {
|
||||||
map_win(ps, id);
|
map_win(ps, id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2655,6 +2708,27 @@ expose_root(session_t *ps, XRectangle *rects, int nrects) {
|
|||||||
add_damage(ps, region);
|
add_damage(ps, region);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value of a type-<code>Window</code> property of a window.
|
||||||
|
*
|
||||||
|
* @return the value if successful, 0 otherwise
|
||||||
|
*/
|
||||||
|
static Window
|
||||||
|
wid_get_prop_window(session_t *ps, Window wid, Atom aprop) {
|
||||||
|
// Get the attribute
|
||||||
|
Window p = None;
|
||||||
|
winprop_t prop = wid_get_prop(ps, wid, aprop, 1L, XA_WINDOW, 32);
|
||||||
|
|
||||||
|
// Return it
|
||||||
|
if (prop.nitems) {
|
||||||
|
p = *prop.data.p32;
|
||||||
|
}
|
||||||
|
|
||||||
|
free_winprop(&prop);
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the value of a text property of a window.
|
* Get the value of a text property of a window.
|
||||||
*/
|
*/
|
||||||
@ -3006,15 +3080,32 @@ ev_reparent_notify(session_t *ps, XReparentEvent *ev) {
|
|||||||
add_win(ps, ev->window, 0);
|
add_win(ps, ev->window, 0);
|
||||||
} else {
|
} else {
|
||||||
destroy_win(ps, ev->window);
|
destroy_win(ps, ev->window);
|
||||||
|
|
||||||
// Reset event mask in case something wrong happens
|
// Reset event mask in case something wrong happens
|
||||||
XSelectInput(ps->dpy, ev->window,
|
XSelectInput(ps->dpy, ev->window,
|
||||||
determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN));
|
determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN));
|
||||||
/*
|
|
||||||
// Check if the window is a client window of another
|
// Check if the window is an undetected client window
|
||||||
win *w_top = find_toplevel2(ps, ev->window);
|
// Firstly, check if it's a known client window
|
||||||
if (w_top && !(w_top->client_win)) {
|
if (!find_toplevel(ps, ev->window)) {
|
||||||
mark_client_win(ps, w_top, ev->window);
|
// If not, look for its frame window
|
||||||
} */
|
win *w_top = find_toplevel2(ps, ev->parent);
|
||||||
|
// If found, and its frame may not have a correct client, continue
|
||||||
|
if (w_top && w_top->client_win == w_top->id) {
|
||||||
|
// If it has WM_STATE, mark it the client window
|
||||||
|
if (wid_has_prop(ps, ev->window, ps->atom_client)) {
|
||||||
|
w_top->wmwin = false;
|
||||||
|
win_unmark_client(ps, w_top);
|
||||||
|
win_mark_client(ps, w_top, ev->window);
|
||||||
|
}
|
||||||
|
// Otherwise, watch for WM_STATE on it
|
||||||
|
else {
|
||||||
|
XSelectInput(ps->dpy, ev->window,
|
||||||
|
determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN)
|
||||||
|
| PropertyChangeMask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3059,18 +3150,10 @@ 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) {
|
||||||
// Get the attribute firstly
|
|
||||||
winprop_t prop = wid_get_prop(ps, ps->root, ps->atom_ewmh_active_win,
|
|
||||||
1L, XA_WINDOW, 32);
|
|
||||||
if (!prop.nitems) {
|
|
||||||
free_winprop(&prop);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search for the window
|
// Search for the window
|
||||||
Window wid = *prop.data.p32;
|
Window wid =
|
||||||
|
wid_get_prop_window(ps, ps->root, ps->atom_ewmh_active_win);
|
||||||
win *w = NULL;
|
win *w = NULL;
|
||||||
free_winprop(&prop);
|
|
||||||
|
|
||||||
if (wid && !(w = find_toplevel(ps, wid)))
|
if (wid && !(w = find_toplevel(ps, wid)))
|
||||||
if (!(w = find_win(ps, wid)))
|
if (!(w = find_win(ps, wid)))
|
||||||
@ -3107,6 +3190,24 @@ ev_property_notify(session_t *ps, XPropertyEvent *ev) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If WM_STATE changes
|
||||||
|
if (ev->atom == ps->atom_client) {
|
||||||
|
// Check whether it could be a client window
|
||||||
|
if (!find_toplevel(ps, ev->window)) {
|
||||||
|
// Reset event mask anyway
|
||||||
|
XSelectInput(ps->dpy, ev->window,
|
||||||
|
determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN));
|
||||||
|
|
||||||
|
win *w_top = find_toplevel2(ps, ev->window);
|
||||||
|
if (w_top && w_top->client_win == w_top->id
|
||||||
|
&& wid_has_prop(ps, ev->window, ps->atom_client)) {
|
||||||
|
w_top->wmwin = false;
|
||||||
|
win_unmark_client(ps, w_top);
|
||||||
|
win_mark_client(ps, w_top, ev->window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If _NET_WM_OPACITY changes
|
// If _NET_WM_OPACITY changes
|
||||||
if (ev->atom == ps->atom_opacity) {
|
if (ev->atom == ps->atom_opacity) {
|
||||||
win *w = NULL;
|
win *w = NULL;
|
||||||
|
@ -58,7 +58,10 @@
|
|||||||
|
|
||||||
// For compatiblity with <libpcre-8.20
|
// For compatiblity with <libpcre-8.20
|
||||||
#ifndef PCRE_STUDY_JIT_COMPILE
|
#ifndef PCRE_STUDY_JIT_COMPILE
|
||||||
#define PCRE_STUDY_JIT_COMPILE 0
|
#define PCRE_STUDY_JIT_COMPILE 0
|
||||||
|
#define LPCRE_FREE_STUDY(extra) pcre_free(extra)
|
||||||
|
#else
|
||||||
|
#define LPCRE_FREE_STUDY(extra) pcre_free_study(extra)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@ -1039,7 +1042,7 @@ free_wincond(wincond_t *cond) {
|
|||||||
free(cond->pattern);
|
free(cond->pattern);
|
||||||
#ifdef CONFIG_REGEX_PCRE
|
#ifdef CONFIG_REGEX_PCRE
|
||||||
if (cond->regex_pcre_extra)
|
if (cond->regex_pcre_extra)
|
||||||
pcre_free_study(cond->regex_pcre_extra);
|
LPCRE_FREE_STUDY(cond->regex_pcre_extra);
|
||||||
if (cond->regex_pcre)
|
if (cond->regex_pcre)
|
||||||
pcre_free(cond->regex_pcre);
|
pcre_free(cond->regex_pcre);
|
||||||
#endif
|
#endif
|
||||||
@ -1077,6 +1080,7 @@ free_win_res(session_t *ps, win *w) {
|
|||||||
free(w->name);
|
free(w->name);
|
||||||
free(w->class_instance);
|
free(w->class_instance);
|
||||||
free(w->class_general);
|
free(w->class_general);
|
||||||
|
free(w->role);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1149,15 +1153,15 @@ static inline bool is_normal_win(const win *w) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if a window has a specific attribute.
|
* Determine if a window has a specific property.
|
||||||
*
|
*
|
||||||
* @param session_t current session
|
* @param session_t current session
|
||||||
* @param w window to check
|
* @param w window to check
|
||||||
* @param atom atom of attribute to check
|
* @param atom atom of property to check
|
||||||
* @return 1 if it has the attribute, 0 otherwise
|
* @return 1 if it has the attribute, 0 otherwise
|
||||||
*/
|
*/
|
||||||
static inline bool
|
static inline bool
|
||||||
wid_has_attr(const session_t *ps, Window w, Atom atom) {
|
wid_has_prop(const session_t *ps, Window w, Atom atom) {
|
||||||
Atom type = None;
|
Atom type = None;
|
||||||
int format;
|
int format;
|
||||||
unsigned long nitems, after;
|
unsigned long nitems, after;
|
||||||
@ -1451,6 +1455,10 @@ win_update_focused(session_t *ps, win *w) {
|
|||||||
*/
|
*/
|
||||||
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) {
|
||||||
|
// Unmapped windows will have their focused state reset on map
|
||||||
|
if (IsUnmapped == w->a.map_state)
|
||||||
|
return;
|
||||||
|
|
||||||
w->focused_real = focused;
|
w->focused_real = focused;
|
||||||
win_update_focused(ps, w);
|
win_update_focused(ps, w);
|
||||||
}
|
}
|
||||||
@ -1480,7 +1488,13 @@ static void
|
|||||||
calc_shadow_geometry(session_t *ps, win *w);
|
calc_shadow_geometry(session_t *ps, win *w);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
mark_client_win(session_t *ps, win *w, Window client);
|
win_mark_client(session_t *ps, win *w, Window client);
|
||||||
|
|
||||||
|
static void
|
||||||
|
win_unmark_client(session_t *ps, win *w);
|
||||||
|
|
||||||
|
static void
|
||||||
|
win_recheck_client(session_t *ps, win *w);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
add_win(session_t *ps, Window id, Window prev);
|
add_win(session_t *ps, Window id, Window prev);
|
||||||
@ -1516,6 +1530,9 @@ static bool
|
|||||||
wid_get_text_prop(session_t *ps, Window wid, Atom prop,
|
wid_get_text_prop(session_t *ps, Window wid, Atom prop,
|
||||||
char ***pstrlst, int *pnstr);
|
char ***pstrlst, int *pnstr);
|
||||||
|
|
||||||
|
static Window
|
||||||
|
wid_get_prop_window(session_t *ps, Window wid, Atom aprop);
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
wid_get_name(session_t *ps, Window w, char **name);
|
wid_get_name(session_t *ps, Window w, char **name);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user