Destroy Input Only window properly

Destruction of Input Only windows is erroneously ignored by commit
cea010a, causing future window with the same window id as the destroyed
Input Only window to be treated by an Input Only window even when they
are not. The end result is that some windows don't show up.

Also improved the comments in win.c.

Fixes #119

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui 2019-02-20 00:17:23 +00:00
parent 02ddf96fd5
commit 709065d531
No known key found for this signature in database
GPG Key ID: 37C999F617EA1A47
3 changed files with 163 additions and 171 deletions

View File

@ -747,6 +747,8 @@ restack_win(session_t *ps, win *w, xcb_window_t new_above) {
} else {
old_above = XCB_NONE;
}
log_debug("Restack %#010x (%s), old_above: %#010x, new_above: %#010x",
w->id, w->name, old_above, new_above);
if (old_above != new_above) {
w->reg_ignore_valid = false;
@ -758,16 +760,7 @@ restack_win(session_t *ps, win *w, xcb_window_t new_above) {
win **prev = NULL, **prev_old = NULL;
// unhook
for (prev = &ps->list; *prev; prev = &(*prev)->next) {
if ((*prev) == w) break;
}
prev_old = prev;
bool found = false;
// rehook
for (prev = &ps->list; *prev; prev = &(*prev)->next) {
if ((*prev)->id == new_above && (*prev)->state != WSTATE_DESTROYING) {
found = true;
@ -780,8 +773,13 @@ restack_win(session_t *ps, win *w, xcb_window_t new_above) {
return;
}
*prev_old = w->next;
for (prev_old = &ps->list; *prev_old; prev_old = &(*prev_old)->next) {
if ((*prev_old) == w) {
break;
}
}
*prev_old = w->next;
w->next = *prev;
*prev = w;
@ -789,32 +787,13 @@ restack_win(session_t *ps, win *w, xcb_window_t new_above) {
add_damage_from_win(ps, w);
#ifdef DEBUG_RESTACK
{
const char *desc;
char *window_name = NULL;
bool to_free;
win* c = ps->list;
log_trace("(%#010lx, %#010lx): "
"Window stack modified. Current stack:", w->id, new_above);
for (; c; c = c->next) {
window_name = "(Failed to get title)";
to_free = ev_window_name(ps, c->id, &window_name);
desc = "";
if (c->destroying) desc = "(D) ";
printf("%#010lx \"%s\" %s", c->id, window_name, desc);
if (c->next)
printf("-> ");
if (to_free) {
cxfree(window_name);
window_name = NULL;
}
log_trace("Window stack modified. Current stack:");
for (win *c = ps->list; c; c = c->next) {
const char *desc = "";
if (c->destroying) {
desc = "(D) ";
}
fputs("\n", stdout);
log_trace("%#010x \"%s\" %s", c->id, c->name, desc);
}
#endif
}
@ -865,25 +844,26 @@ configure_win(session_t *ps, xcb_configure_notify_event_t *ce) {
return;
}
// Other window changes
// Non-root window changes
win *w = find_win(ps, ce->window);
region_t damage;
pixman_region32_init(&damage);
if (!w)
if (!w) {
return;
}
if (w->a.map_state == XCB_MAP_STATE_UNMAPPED) {
if (w->state == WSTATE_UNMAPPED) {
/* save the configure event for when the window maps */
w->need_configure = true;
w->queue_configure = *ce;
restack_win(ps, w, ce->above_sibling);
} else {
if (!w->need_configure)
if (!w->need_configure) {
restack_win(ps, w, ce->above_sibling);
}
bool factor_change = false;
w->need_configure = false;
win_extents(w, &damage);
@ -1228,8 +1208,8 @@ ev_create_notify(session_t *ps, xcb_create_notify_event_t *ev) {
inline static void
ev_configure_notify(session_t *ps, xcb_configure_notify_event_t *ev) {
log_trace("{ send_event: %d, above: %#010x, override_redirect: %d }",
ev->event, ev->above_sibling, ev->override_redirect);
log_trace("{ send_event: %d, id: %#010x, above: %#010x, override_redirect: %d }",
ev->event, ev->window, ev->above_sibling, ev->override_redirect);
configure_win(ps, ev);
}
@ -2719,6 +2699,14 @@ session_init(int argc, char **argv, Display *dpy, const char *config_file,
}
free(reply);
log_trace("Initial stack:");
for (win *c = ps->list; c; c = c->next) {
const char *desc = "";
if (c->destroying) {
desc = "(D) ";
}
log_trace("%#010x \"%s\" %s", c->id, c->name, desc);
}
}
if (ps->o.track_focus) {

260
src/win.c
View File

@ -770,95 +770,146 @@ void free_win_res(session_t *ps, win *w) {
}
// TODO: probably split into win_new (in win.c) and add_win (in compton.c)
bool add_win(session_t *ps, xcb_window_t id, xcb_window_t prev) {
void add_win(session_t *ps, xcb_window_t id, xcb_window_t prev) {
static const win win_def = {
.win_data = NULL,
.next = NULL,
.prev_trans = NULL,
.id = XCB_NONE,
.a = {},
.xinerama_scr = -1,
.pictfmt = NULL,
.mode = WMODE_TRANS,
.ever_damaged = false,
.damage = XCB_NONE,
.pixmap_damaged = false,
.paint = PAINT_INIT,
.flags = 0,
.need_configure = false,
.queue_configure = {},
// No need to initialize. (or, you can think that
// they are initialized right here).
// The following ones are updated during paint or paint preprocess
.shadow_opacity = 0.0,
.to_paint = false,
.frame_opacity = 1.0,
.dim = false,
.invert_color = false,
.blur_background = false,
.reg_ignore = NULL,
.reg_ignore_valid = false,
// The following ones are updated for other reasons
.pixmap_damaged = false, // updated by damage events
.state = WSTATE_UNMAPPED, // updated by window state changes
.in_openclose = true, // set to false after first map is done,
// true here because window is just created
.need_configure = false, // set to true when window is configured
// while unmapped.
.queue_configure = {}, // same as above
.reg_ignore_valid = false,// set to true when damaged
.flags = 0, // updated by property/attributes/etc change
// Runtime variables, updated by dbus
.fade_force = UNSET,
.shadow_force = UNSET,
.focused_force = UNSET,
.invert_color_force = UNSET,
// Initialized in this function
.next = NULL,
.id = XCB_NONE,
.a = {0},
.pictfmt = NULL,
.widthb = 0,
.heightb = 0,
.state = WSTATE_UNMAPPED,
.bounding_shaped = false,
.rounded_corners = false,
.to_paint = false,
// true when the window is first created.
// set to false after the first map is done
.in_openclose = true,
.shadow_dx = 0,
.shadow_dy = 0,
.shadow_width = 0,
.shadow_height = 0,
.damage = XCB_NONE,
// Not initialized until mapped, this variables
// have no meaning or have no use until the window
// is mapped
.win_data = NULL,
.prev_trans = NULL,
.shadow = false,
.xinerama_scr = -1,
.mode = WMODE_TRANS,
.ever_damaged = false,
.client_win = XCB_NONE,
.window_type = WINTYPE_UNKNOWN,
.wmwin = false,
.leader = XCB_NONE,
.cache_leader = XCB_NONE,
.window_type = WINTYPE_UNKNOWN,
.wmwin = false,
.focused = false,
.focused_force = UNSET,
.name = NULL,
.class_instance = NULL,
.class_general = NULL,
.role = NULL,
.opacity = 0,
.opacity_tgt = 0,
.has_opacity_prop = false,
.opacity_prop = OPAQUE,
.opacity_is_set = false,
.opacity_set = 1,
.fade_force = UNSET,
.frame_opacity = 1.0,
.frame_extents = MARGIN_INIT,
.shadow = false,
.shadow_force = UNSET,
.shadow_opacity = 0.0,
.shadow_dx = 0,
.shadow_dy = 0,
.shadow_width = 0,
.shadow_height = 0,
.shadow_paint = PAINT_INIT,
.frame_extents = MARGIN_INIT, // in win_mark_client
.bounding_shaped = false,
.bounding_shape = {0},
.rounded_corners = false,
.paint_excluded = false,
.unredir_if_possible_excluded = false,
.prop_shadow = -1,
// following 4 are set in win_mark_client
.name = NULL,
.class_instance = NULL,
.class_general = NULL,
.role = NULL,
.dim = false,
.invert_color = false,
.invert_color_force = UNSET,
.blur_background = false,
// Initialized during paint
.paint = PAINT_INIT,
.shadow_paint = PAINT_INIT,
};
// Reject overlay window and already added windows
if (id == ps->overlay || find_win(ps, id)) {
return false;
if (id == ps->overlay) {
return;
}
auto duplicated_win = find_win(ps, id);
if (duplicated_win) {
log_warn("Window %#010x (recorded name: %s) added multiple times", id,
duplicated_win->name);
return;
}
log_debug("Adding window %#010x, prev %#010x", id, prev);
xcb_get_window_attributes_cookie_t acookie = xcb_get_window_attributes(ps->c, id);
xcb_get_geometry_cookie_t gcookie = xcb_get_geometry(ps->c, id);
xcb_get_window_attributes_reply_t *a = xcb_get_window_attributes_reply(ps->c, acookie, NULL);
xcb_get_geometry_reply_t *g = xcb_get_geometry_reply(ps->c, gcookie, NULL);
if (!a || !g || a->map_state == XCB_MAP_STATE_UNVIEWABLE) {
// Failed to get window attributes or geometry probably means
// the window is gone already. Unviewable means the window is
// already reparented elsewhere.
// BTW, we don't care about Input Only windows, except for stacking
// proposes, so we need to keep track of them still.
free(a);
free(g);
return;
}
// Allocate and initialize the new win structure
auto new = cmalloc(win);
log_trace("(%#010x): %p", id, new);
// Fill structure
// We only need to initialize the part that are not initialized
// by map_win
*new = win_def;
new->id = id;
new->a = *a;
new->g = *g;
pixman_region32_init(&new->bounding_shape);
free(g);
free(a);
// Create Damage for window (if not Input Only)
if (new->a._class != XCB_WINDOW_CLASS_INPUT_ONLY) {
new->damage = xcb_generate_id(ps->c);
xcb_generic_error_t *e = xcb_request_check(ps->c,
xcb_damage_create_checked(ps->c, new->damage, id, XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY));
if (e) {
free(e);
free(new);
return;
}
new->pictfmt = x_get_pictform_for_visual(ps->c, new->a.visual);
}
calc_win_size(ps, new);
// Find window insertion point
win **p = NULL;
if (prev) {
@ -869,58 +920,8 @@ bool add_win(session_t *ps, xcb_window_t id, xcb_window_t prev) {
} else {
p = &ps->list;
}
// Fill structure
new->id = id;
xcb_get_window_attributes_cookie_t acookie = xcb_get_window_attributes(ps->c, id);
xcb_get_geometry_cookie_t gcookie = xcb_get_geometry(ps->c, id);
xcb_get_window_attributes_reply_t *a = xcb_get_window_attributes_reply(ps->c, acookie, NULL);
xcb_get_geometry_reply_t *g = xcb_get_geometry_reply(ps->c, gcookie, NULL);
if (!a || a->map_state == XCB_MAP_STATE_UNVIEWABLE) {
// Failed to get window attributes probably means the window is gone
// already. Unviewable means the window is already reparented
// elsewhere.
free(a);
free(g);
free(new);
return false;
}
new->a = *a;
free(a);
if (!g) {
free(new);
return false;
}
new->g = *g;
free(g);
// Delay window mapping
int map_state = new->a.map_state;
assert(map_state == XCB_MAP_STATE_VIEWABLE || map_state == XCB_MAP_STATE_UNMAPPED);
new->a.map_state = XCB_MAP_STATE_UNMAPPED;
if (new->a._class == XCB_WINDOW_CLASS_INPUT_OUTPUT) {
// Create Damage for window
new->damage = xcb_generate_id(ps->c);
xcb_generic_error_t *e = xcb_request_check(ps->c,
xcb_damage_create_checked(ps->c, new->damage, id, XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY));
if (e) {
free(e);
free(new);
return false;
}
new->pictfmt = x_get_pictform_for_visual(ps->c, new->a.visual);
}
calc_win_size(ps, new);
new->next = *p;
*p = new;
win_update_bounding_shape(ps, new);
#ifdef CONFIG_DBUS
// Send D-Bus signal
@ -929,11 +930,10 @@ bool add_win(session_t *ps, xcb_window_t id, xcb_window_t prev) {
}
#endif
if (map_state == XCB_MAP_STATE_VIEWABLE) {
if (new->a.map_state == XCB_MAP_STATE_VIEWABLE) {
map_win(ps, id);
}
return true;
return;
}
/**
@ -942,8 +942,7 @@ bool add_win(session_t *ps, xcb_window_t id, xcb_window_t prev) {
void win_update_focused(session_t *ps, win *w) {
if (UNSET != w->focused_force) {
w->focused = w->focused_force;
}
else {
} else {
w->focused = win_is_focused_real(ps, w);
// Use wintype_focus, and treat WM windows and override-redirected
@ -1303,18 +1302,14 @@ bool win_is_region_ignore_valid(session_t *ps, win *w) {
* Stop listening for events on a particular window.
*/
void win_ev_stop(session_t *ps, win *w) {
// Will get BadWindow if the window is destroyed
set_ignore_cookie(ps,
xcb_change_window_attributes(ps->c, w->id, XCB_CW_EVENT_MASK, (const uint32_t[]) { 0 }));
xcb_change_window_attributes(ps->c, w->id, XCB_CW_EVENT_MASK, (const uint32_t[]) { 0 });
if (w->client_win) {
set_ignore_cookie(ps,
xcb_change_window_attributes(ps->c, w->client_win, XCB_CW_EVENT_MASK, (const uint32_t[]) { 0 }));
xcb_change_window_attributes(ps->c, w->client_win, XCB_CW_EVENT_MASK, (const uint32_t[]) { 0 });
}
if (ps->shape_exists) {
set_ignore_cookie(ps,
xcb_shape_select_input(ps->c, w->id, 0));
xcb_shape_select_input(ps->c, w->id, 0);
}
}
@ -1384,7 +1379,15 @@ unmap_win(session_t *ps, win **_w, bool destroy) {
winstate_t target_state = destroy ? WSTATE_DESTROYING : WSTATE_UNMAPPING;
if (unlikely(!w) || w->a._class == XCB_WINDOW_CLASS_INPUT_ONLY) {
if (unlikely(!w)) {
return;
}
if (!destroy && w->a._class == XCB_WINDOW_CLASS_INPUT_ONLY) {
// We don't care about mapping / unmapping of Input Only windows.
// But we need to remember to destroy them, so future window with
// the same id won't be handled incorrectly.
// Issue #119 was caused by this.
return;
}
@ -1401,12 +1404,12 @@ unmap_win(session_t *ps, win **_w, bool destroy) {
return;
}
if (unlikely(w->state == WSTATE_UNMAPPED)) {
if (unlikely(w->state == WSTATE_UNMAPPED) || w->a._class == XCB_WINDOW_CLASS_INPUT_ONLY) {
if (unlikely(!destroy)) {
log_warn("Unmapping an already unmapped window %#010x %s twice", w->id, w->name);
return;
}
// Window is already unmapped, just destroy it
// Window is already unmapped, or is an Input Only window, just destroy it
finish_destroy_win(ps, _w);
return;
}
@ -1421,7 +1424,9 @@ unmap_win(session_t *ps, win **_w, bool destroy) {
w->in_openclose = destroy;
// don't care about properties anymore
win_ev_stop(ps, w);
if (!destroy) {
win_ev_stop(ps, w);
}
#ifdef CONFIG_DBUS
// Send D-Bus signal
@ -1506,12 +1511,11 @@ map_win(session_t *ps, xcb_window_t id) {
win *w = find_win(ps, id);
// Don't care about window mapping if it's an InputOnly window
// Also, try avoiding mapping a window twice
// TODO don't even add the input only windows
if (!w || w->a._class == XCB_WINDOW_CLASS_INPUT_ONLY) {
return;
}
log_debug("Mapping (%#010x \"%s\")", id, (w ? w->name: NULL));
log_debug("Mapping (%#010x \"%s\")", id, w->name);
if (w->state != WSTATE_UNMAPPED && w->state != WSTATE_UNMAPPING) {
log_warn("Mapping an already mapped window");

View File

@ -358,7 +358,7 @@ region_t win_get_region_noframe_local_by_val(win *w);
*/
void
win_update_frame_extents(session_t *ps, win *w, xcb_window_t client);
bool add_win(session_t *ps, xcb_window_t id, xcb_window_t prev);
void add_win(session_t *ps, xcb_window_t id, xcb_window_t prev);
/// Unmap or destroy a window
void unmap_win(session_t *ps, win **, bool destroy);