Bugfix: unmap windows in a separate step to avoid focus problems with fullscreen windows

This fixes an ugly bug with Adobe Flash in fullscreen mode, for example on
YouTube. See comments in the diff for some explanation.
This commit is contained in:
Michael Stapelberg 2010-11-27 17:45:23 +01:00
parent d47a1edf22
commit 2c3e5dbc65

114
src/x.c
View File

@ -454,51 +454,33 @@ static void x_push_node(Con *con) {
fake_notify = true; fake_notify = true;
} }
/* map/unmap if map state changed, also ensure that the child window /* Map if map state changed, also ensure that the child window
* is changed if we are mapped *and* in initial state (meaning the * is changed if we are mapped *and* in initial state (meaning the
* container was empty before, but now got a child) */ * container was empty before, but now got a child). Unmaps are handled in
if (state->mapped != con->mapped || (con->mapped && state->initial)) { * x_push_node_unmaps(). */
if (!con->mapped) { if ((state->mapped != con->mapped || (con->mapped && state->initial)) &&
xcb_void_cookie_t cookie; con->mapped) {
if (con->window != NULL) { xcb_void_cookie_t cookie;
/* Set WM_STATE_WITHDRAWN, it seems like Java apps need it */
long data[] = { XCB_WM_STATE_WITHDRAWN, XCB_NONE };
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, con->window->id,
atoms[WM_STATE], atoms[WM_STATE], 32, 2, data);
}
cookie = xcb_unmap_window(conn, con->frame); if (con->window != NULL) {
LOG("unmapping container (serial %d)\n", cookie.sequence); /* Set WM_STATE_NORMAL because GTK applications dont want to
/* we need to increase ignore_unmap for this container (if it contains a window) and for every window "under" this one which contains a window */ * drag & drop if we dont. Also, xprop(1) needs it. */
if (con->window != NULL) { long data[] = { XCB_WM_STATE_NORMAL, XCB_NONE };
con->ignore_unmap++; xcb_change_property(conn, XCB_PROP_MODE_REPLACE, con->window->id,
DLOG("ignore_unmap for con %p (frame 0x%08x) now %d\n", con, con->frame, con->ignore_unmap); atoms[WM_STATE], atoms[WM_STATE], 32, 2, data);
} }
/* Ignore enter_notifies which are generated when unmapping */
add_ignore_event(cookie.sequence);
} else {
xcb_void_cookie_t cookie;
if (con->window != NULL) { if (state->initial && con->window != NULL) {
/* Set WM_STATE_NORMAL because GTK applications dont want to cookie = xcb_map_window(conn, con->window->id);
* drag & drop if we dont. Also, xprop(1) needs it. */ LOG("mapping child window (serial %d)\n", cookie.sequence);
long data[] = { XCB_WM_STATE_NORMAL, XCB_NONE };
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, con->window->id,
atoms[WM_STATE], atoms[WM_STATE], 32, 2, data);
}
if (state->initial && con->window != NULL) {
cookie = xcb_map_window(conn, con->window->id);
LOG("mapping child window (serial %d)\n", cookie.sequence);
/* Ignore enter_notifies which are generated when mapping */
add_ignore_event(cookie.sequence);
}
cookie = xcb_map_window(conn, con->frame);
LOG("mapping container (serial %d)\n", cookie.sequence);
/* Ignore enter_notifies which are generated when mapping */ /* Ignore enter_notifies which are generated when mapping */
add_ignore_event(cookie.sequence); add_ignore_event(cookie.sequence);
} }
cookie = xcb_map_window(conn, con->frame);
LOG("mapping container (serial %d)\n", cookie.sequence);
/* Ignore enter_notifies which are generated when mapping */
add_ignore_event(cookie.sequence);
state->mapped = con->mapped; state->mapped = con->mapped;
} }
@ -518,6 +500,57 @@ static void x_push_node(Con *con) {
x_draw_decoration(con); x_draw_decoration(con);
} }
/*
* Same idea as in x_push_node(), but this function only unmaps windows. It is
* necessary to split this up to handle new fullscreen clients properly: The
* new window needs to be mapped and focus needs to be set *before* the
* underlying windows are unmapped. Otherwise, focus will revert to the
* PointerRoot and will then be set to the new window, generating unnecessary
* FocusIn/FocusOut events.
*
*/
static void x_push_node_unmaps(Con *con) {
Con *current;
con_state *state;
LOG("Pushing changes (with unmaps) for node %p / %s\n", con, con->name);
state = state_for_frame(con->frame);
/* map/unmap if map state changed, also ensure that the child window
* is changed if we are mapped *and* in initial state (meaning the
* container was empty before, but now got a child) */
if ((state->mapped != con->mapped || (con->mapped && state->initial)) &&
!con->mapped) {
xcb_void_cookie_t cookie;
if (con->window != NULL) {
/* Set WM_STATE_WITHDRAWN, it seems like Java apps need it */
long data[] = { XCB_WM_STATE_WITHDRAWN, XCB_NONE };
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, con->window->id,
atoms[WM_STATE], atoms[WM_STATE], 32, 2, data);
}
cookie = xcb_unmap_window(conn, con->frame);
LOG("unmapping container (serial %d)\n", cookie.sequence);
/* we need to increase ignore_unmap for this container (if it
* contains a window) and for every window "under" this one which
* contains a window */
if (con->window != NULL) {
con->ignore_unmap++;
DLOG("ignore_unmap for con %p (frame 0x%08x) now %d\n", con, con->frame, con->ignore_unmap);
}
/* Ignore enter_notifies which are generated when unmapping */
add_ignore_event(cookie.sequence);
state->mapped = con->mapped;
}
/* handle all children and floating windows of this node */
TAILQ_FOREACH(current, &(con->nodes_head), nodes)
x_push_node_unmaps(current);
TAILQ_FOREACH(current, &(con->floating_head), floating_windows)
x_push_node_unmaps(current);
}
/* /*
* Pushes all changes (state of each node, see x_push_node() and the window * Pushes all changes (state of each node, see x_push_node() and the window
* stack) to X11. * stack) to X11.
@ -557,11 +590,14 @@ void x_push_changes(Con *con) {
if (focused_id != to_focus) { if (focused_id != to_focus) {
LOG("Updating focus (focused: %p / %s)\n", focused, focused->name); LOG("Updating focus (focused: %p / %s)\n", focused, focused->name);
xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, to_focus, XCB_CURRENT_TIME); xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, to_focus, XCB_CURRENT_TIME);
focused_id = to_focus;
} }
xcb_flush(conn); xcb_flush(conn);
LOG("\n\n ENDING CHANGES\n\n"); LOG("\n\n ENDING CHANGES\n\n");
x_push_node_unmaps(con);
/* save the current stack as old stack */ /* save the current stack as old stack */
CIRCLEQ_FOREACH(state, &state_head, state) { CIRCLEQ_FOREACH(state, &state_head, state) {
CIRCLEQ_REMOVE(&old_state_head, state, old_state); CIRCLEQ_REMOVE(&old_state_head, state, old_state);