diff --git a/event.c b/event.c index ee1411f1..3bf513c7 100644 --- a/event.c +++ b/event.c @@ -991,10 +991,43 @@ xerror(xcb_generic_error_t *e) return; } +static bool +should_ignore(xcb_generic_event_t *event) +{ + uint8_t response_type = XCB_EVENT_RESPONSE_TYPE(event); + + /* Remove completed sequences */ + uint32_t sequence = event->full_sequence; + while (globalconf.ignore_enter_leave_events.len > 0) { + uint32_t end = globalconf.ignore_enter_leave_events.tab[0].end.sequence; + /* Do if (end >= sequence) break;, but handle wrap-around: The above is + * equivalent to end-sequence > 0 (assuming unlimited precision). With + * int32_t, this would mean that the sign bit is cleared, which means: + */ + if (end - sequence < UINT32_MAX / 2) + break; + sequence_pair_array_take(&globalconf.ignore_enter_leave_events, 0); + } + + /* Check if this event should be ignored */ + if ((response_type == XCB_ENTER_NOTIFY || response_type == XCB_LEAVE_NOTIFY) + && globalconf.ignore_enter_leave_events.len > 0) { + uint32_t begin = globalconf.ignore_enter_leave_events.tab[0].begin.sequence; + uint32_t end = globalconf.ignore_enter_leave_events.tab[0].end.sequence; + if (sequence >= begin && sequence <= end) + return true; + } + + return false; +} + void event_handle(xcb_generic_event_t *event) { uint8_t response_type = XCB_EVENT_RESPONSE_TYPE(event); + if (should_ignore(event)) + return; + if(response_type == 0) { /* This is an error, not a event */ diff --git a/globalconf.h b/globalconf.h index 877be221..0d4057f7 100644 --- a/globalconf.h +++ b/globalconf.h @@ -55,6 +55,11 @@ typedef struct button_t button_t; typedef struct client_t client_t; typedef struct tag tag_t; typedef struct xproperty xproperty_t; +struct sequence_pair { + xcb_void_cookie_t begin; + xcb_void_cookie_t end; +}; +typedef struct sequence_pair sequence_pair_t; ARRAY_TYPE(button_t *, button) ARRAY_TYPE(tag_t *, tag) @@ -62,6 +67,7 @@ ARRAY_TYPE(screen_t *, screen) ARRAY_TYPE(client_t *, client) ARRAY_TYPE(drawin_t *, drawin) ARRAY_TYPE(xproperty_t, xproperty) +DO_ARRAY(sequence_pair_t, sequence_pair, DO_NOTHING) /** Main configuration structure */ typedef struct @@ -180,6 +186,9 @@ typedef struct uint32_t preferred_icon_size; /** Cached wallpaper information */ cairo_surface_t *wallpaper; + /** List of enter/leave events to ignore */ + sequence_pair_array_t ignore_enter_leave_events; + xcb_void_cookie_t pending_enter_leave_begin; } awesome_t; extern awesome_t globalconf; diff --git a/objects/client.c b/objects/client.c index bdb37266..6d28b5aa 100644 --- a/objects/client.c +++ b/objects/client.c @@ -999,37 +999,36 @@ client_ban(client_t *c) /** This is part of The Bob Marley Algorithm: we ignore enter and leave window * in certain cases, like map/unmap or move, so we don't get spurious events. + * The implementation works by noting the range of sequence numbers for which we + * should ignore events. We grab the server to make sure that only we could + * generate events in this range. */ void client_ignore_enterleave_events(void) { - foreach(c, globalconf.clients) - { - xcb_change_window_attributes(globalconf.connection, - (*c)->window, - XCB_CW_EVENT_MASK, - (const uint32_t []) { CLIENT_SELECT_INPUT_EVENT_MASK & ~(XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW) }); - xcb_change_window_attributes(globalconf.connection, - (*c)->frame_window, - XCB_CW_EVENT_MASK, - (const uint32_t []) { FRAME_SELECT_INPUT_EVENT_MASK & ~(XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW) }); - } + assert(globalconf.pending_enter_leave_begin.sequence == 0); + globalconf.pending_enter_leave_begin = xcb_grab_server(globalconf.connection); + /* If the connection is broken, we get a request with sequence number 0 + * which would then trigger an assertion in + * client_restore_enterleave_events(). Handle this nicely. + */ + if(xcb_connection_has_error(globalconf.connection)) + fatal("X server connection broke (error %d)", + xcb_connection_has_error(globalconf.connection)); + assert(globalconf.pending_enter_leave_begin.sequence != 0); } void client_restore_enterleave_events(void) { - foreach(c, globalconf.clients) - { - xcb_change_window_attributes(globalconf.connection, - (*c)->window, - XCB_CW_EVENT_MASK, - (const uint32_t []) { CLIENT_SELECT_INPUT_EVENT_MASK }); - xcb_change_window_attributes(globalconf.connection, - (*c)->frame_window, - XCB_CW_EVENT_MASK, - (const uint32_t []) { FRAME_SELECT_INPUT_EVENT_MASK }); - } + sequence_pair_t pair; + + assert(globalconf.pending_enter_leave_begin.sequence != 0); + pair.begin = globalconf.pending_enter_leave_begin; + pair.end = xcb_no_operation(globalconf.connection); + xcb_ungrab_server(globalconf.connection); + globalconf.pending_enter_leave_begin.sequence = 0; + sequence_pair_array_append(&globalconf.ignore_enter_leave_events, pair); } /** Record that a client got focus.