Bugfix: Only set ENTER_WINDOW event mask for mapped windows (fixes focus problems)
Fixes focus problems when switching to empty workspaces or when going in/out of fullscreen.
This commit is contained in:
parent
23d4917e43
commit
71741d7620
|
@ -439,9 +439,10 @@ static int handle_screen_change(xcb_generic_event_t *e) {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
static int handle_unmap_notify_event(xcb_unmap_notify_event_t *event) {
|
static int handle_unmap_notify_event(xcb_unmap_notify_event_t *event) {
|
||||||
|
// XXX: this is commented out because in src/x.c we disable EnterNotify events
|
||||||
/* we need to ignore EnterNotify events which will be generated because a
|
/* we need to ignore EnterNotify events which will be generated because a
|
||||||
* different window is visible now */
|
* different window is visible now */
|
||||||
add_ignore_event(event->sequence, XCB_ENTER_NOTIFY);
|
//add_ignore_event(event->sequence, XCB_ENTER_NOTIFY);
|
||||||
|
|
||||||
DLOG("UnmapNotify for 0x%08x (received from 0x%08x), serial %d\n", event->window, event->event, event->sequence);
|
DLOG("UnmapNotify for 0x%08x (received from 0x%08x), serial %d\n", event->window, event->event, event->sequence);
|
||||||
Con *con = con_by_window_id(event->window);
|
Con *con = con_by_window_id(event->window);
|
||||||
|
|
|
@ -117,16 +117,14 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t mask = 0;
|
|
||||||
uint32_t values[1];
|
uint32_t values[1];
|
||||||
|
|
||||||
/* Set a temporary event mask for the new window, consisting only of
|
/* Set a temporary event mask for the new window, consisting only of
|
||||||
* PropertyChange. We need to be notified of PropertyChanges because the
|
* PropertyChange. We need to be notified of PropertyChanges because the
|
||||||
* client can change its properties *after* we requested them but *before*
|
* client can change its properties *after* we requested them but *before*
|
||||||
* we actually reparented it and have set our final event mask. */
|
* we actually reparented it and have set our final event mask. */
|
||||||
mask = XCB_CW_EVENT_MASK;
|
|
||||||
values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE;
|
values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE;
|
||||||
xcb_change_window_attributes(conn, window, mask, values);
|
xcb_change_window_attributes(conn, window, XCB_CW_EVENT_MASK, values);
|
||||||
|
|
||||||
#define GET_PROPERTY(atom, len) xcb_get_property_unchecked(conn, false, window, atom, XCB_GET_PROPERTY_TYPE_ANY, 0, len)
|
#define GET_PROPERTY(atom, len) xcb_get_property_unchecked(conn, false, window, atom, XCB_GET_PROPERTY_TYPE_ANY, 0, len)
|
||||||
|
|
||||||
|
@ -326,9 +324,8 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
mask = XCB_CW_EVENT_MASK;
|
values[0] = CHILD_EVENT_MASK & ~XCB_EVENT_MASK_ENTER_WINDOW;
|
||||||
values[0] = CHILD_EVENT_MASK;
|
xcb_change_window_attributes(conn, window, XCB_CW_EVENT_MASK, values);
|
||||||
xcb_change_window_attributes(conn, window, mask, values);
|
|
||||||
xcb_flush(conn);
|
xcb_flush(conn);
|
||||||
|
|
||||||
reply = xcb_get_property_reply(conn, state_cookie, NULL);
|
reply = xcb_get_property_reply(conn, state_cookie, NULL);
|
||||||
|
|
47
src/x.c
47
src/x.c
|
@ -18,6 +18,7 @@ xcb_window_t focused_id = XCB_NONE;
|
||||||
typedef struct con_state {
|
typedef struct con_state {
|
||||||
xcb_window_t id;
|
xcb_window_t id;
|
||||||
bool mapped;
|
bool mapped;
|
||||||
|
bool unmap_now;
|
||||||
bool child_mapped;
|
bool child_mapped;
|
||||||
|
|
||||||
/* For reparenting, we have a flag (need_reparent) and the X ID of the old
|
/* For reparenting, we have a flag (need_reparent) and the X ID of the old
|
||||||
|
@ -78,9 +79,9 @@ void x_con_init(Con *con) {
|
||||||
mask |= XCB_CW_OVERRIDE_REDIRECT;
|
mask |= XCB_CW_OVERRIDE_REDIRECT;
|
||||||
values[0] = 1;
|
values[0] = 1;
|
||||||
|
|
||||||
/* We want to know when… */
|
/* see include/xcb.h for the FRAME_EVENT_MASK */
|
||||||
mask |= XCB_CW_EVENT_MASK;
|
mask |= XCB_CW_EVENT_MASK;
|
||||||
values[1] = FRAME_EVENT_MASK;
|
values[1] = FRAME_EVENT_MASK & ~XCB_EVENT_MASK_ENTER_WINDOW;
|
||||||
|
|
||||||
Rect dims = { -15, -15, 10, 10 };
|
Rect dims = { -15, -15, 10, 10 };
|
||||||
con->frame = create_window(conn, dims, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCURSOR_CURSOR_POINTER, false, mask, values);
|
con->frame = create_window(conn, dims, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCURSOR_CURSOR_POINTER, false, mask, values);
|
||||||
|
@ -612,21 +613,29 @@ void x_push_node(Con *con) {
|
||||||
A_WM_STATE, A_WM_STATE, 32, 2, data);
|
A_WM_STATE, A_WM_STATE, 32, 2, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t values[1];
|
||||||
if (!state->child_mapped && con->window != NULL) {
|
if (!state->child_mapped && con->window != NULL) {
|
||||||
cookie = xcb_map_window(conn, con->window->id);
|
cookie = xcb_map_window(conn, con->window->id);
|
||||||
|
|
||||||
|
/* We are interested in EnterNotifys as soon as the window is
|
||||||
|
* mapped */
|
||||||
|
values[0] = CHILD_EVENT_MASK;
|
||||||
|
xcb_change_window_attributes(conn, con->window->id, XCB_CW_EVENT_MASK, values);
|
||||||
DLOG("mapping child window (serial %d)\n", cookie.sequence);
|
DLOG("mapping child window (serial %d)\n", cookie.sequence);
|
||||||
/* Ignore enter_notifies which are generated when mapping */
|
|
||||||
add_ignore_event(cookie.sequence, 0);
|
|
||||||
state->child_mapped = true;
|
state->child_mapped = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
cookie = xcb_map_window(conn, con->frame);
|
cookie = xcb_map_window(conn, con->frame);
|
||||||
DLOG("mapping container (serial %d)\n", cookie.sequence);
|
|
||||||
/* Ignore enter_notifies which are generated when mapping */
|
values[0] = FRAME_EVENT_MASK;
|
||||||
add_ignore_event(cookie.sequence, 0);
|
xcb_change_window_attributes(conn, con->frame, XCB_CW_EVENT_MASK, values);
|
||||||
|
|
||||||
|
DLOG("mapping container %08x (serial %d)\n", con->frame, cookie.sequence);
|
||||||
state->mapped = con->mapped;
|
state->mapped = con->mapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state->unmap_now = (state->mapped != con->mapped) && !con->mapped;
|
||||||
|
|
||||||
if (fake_notify) {
|
if (fake_notify) {
|
||||||
DLOG("Sending fake configure notify\n");
|
DLOG("Sending fake configure notify\n");
|
||||||
fake_absolute_configure_notify(con);
|
fake_absolute_configure_notify(con);
|
||||||
|
@ -658,8 +667,7 @@ static void x_push_node_unmaps(Con *con) {
|
||||||
/* map/unmap if map state changed, also ensure that the child window
|
/* map/unmap 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) */
|
||||||
if ((state->mapped != con->mapped || (con->mapped && state->initial)) &&
|
if (state->unmap_now) {
|
||||||
!con->mapped) {
|
|
||||||
xcb_void_cookie_t cookie;
|
xcb_void_cookie_t cookie;
|
||||||
if (con->window != NULL) {
|
if (con->window != NULL) {
|
||||||
/* Set WM_STATE_WITHDRAWN, it seems like Java apps need it */
|
/* Set WM_STATE_WITHDRAWN, it seems like Java apps need it */
|
||||||
|
@ -677,8 +685,6 @@ static void x_push_node_unmaps(Con *con) {
|
||||||
con->ignore_unmap++;
|
con->ignore_unmap++;
|
||||||
DLOG("ignore_unmap for con %p (frame 0x%08x) now %d\n", con, con->frame, 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, 0);
|
|
||||||
state->mapped = con->mapped;
|
state->mapped = con->mapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -731,8 +737,10 @@ void x_push_changes(Con *con) {
|
||||||
state->initial = false;
|
state->initial = false;
|
||||||
}
|
}
|
||||||
//DLOG("Re-enabling EnterNotify\n");
|
//DLOG("Re-enabling EnterNotify\n");
|
||||||
values[0] = FRAME_EVENT_MASK;
|
|
||||||
CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) {
|
CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) {
|
||||||
|
values[0] = FRAME_EVENT_MASK;
|
||||||
|
if (!state->mapped)
|
||||||
|
values[0] &= ~XCB_EVENT_MASK_ENTER_WINDOW;
|
||||||
xcb_change_window_attributes(conn, state->id, XCB_CW_EVENT_MASK, values);
|
xcb_change_window_attributes(conn, state->id, XCB_CW_EVENT_MASK, values);
|
||||||
}
|
}
|
||||||
//DLOG("Done, EnterNotify re-enabled\n");
|
//DLOG("Done, EnterNotify re-enabled\n");
|
||||||
|
@ -785,6 +793,21 @@ void x_push_changes(Con *con) {
|
||||||
xcb_flush(conn);
|
xcb_flush(conn);
|
||||||
DLOG("\n\n ENDING CHANGES\n\n");
|
DLOG("\n\n ENDING CHANGES\n\n");
|
||||||
|
|
||||||
|
/* Disable EnterWindow events for windows which will be unmapped in
|
||||||
|
* x_push_node_unmaps() now. Unmapping windows happens when switching
|
||||||
|
* workspaces. We want to avoid getting EnterNotifies during that phase
|
||||||
|
* because they would screw up our focus. One of these cases is having a
|
||||||
|
* stack with two windows. If the first window is focused and gets
|
||||||
|
* unmapped, the second one appears under the cursor and therefore gets an
|
||||||
|
* EnterNotify event. */
|
||||||
|
values[0] = FRAME_EVENT_MASK & ~XCB_EVENT_MASK_ENTER_WINDOW;
|
||||||
|
CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) {
|
||||||
|
if (!state->unmap_now)
|
||||||
|
continue;
|
||||||
|
xcb_change_window_attributes(conn, state->id, XCB_CW_EVENT_MASK, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Push all pending unmaps */
|
||||||
x_push_node_unmaps(con);
|
x_push_node_unmaps(con);
|
||||||
|
|
||||||
/* save the current stack as old stack */
|
/* save the current stack as old stack */
|
||||||
|
|
Loading…
Reference in New Issue