Speed up client_ignore_enterleave_events()

There are some situations where we do things that can make the mouse pointer
enter another window. We do not want to react to these "self inflicted" mouse
enter and leave events, because they aren't "real" (= generated by the user).

Before this commit, this is done by going through all windows and toggling the
"please send us enter and leave events"-bit on them. This becomes slower when
many windows are visible and floods the server with requests.

This commit changes this to a constant-time logic. Each event contains the
sequence number of the last request that the X11 server handled. Thus, we just
remember the right sequence numbers and ignore any events that comes in whose
sequence number falls into the ignored range.

In detail, we keep a list of "begin" and "end" sequence numbers and ignore any
enter and leave events that fall in this range. If we get any event with a
sequence number higher than "end", we remove this pair from the list, since it
is no longer needed.

To generate these pairs, we use a GrabServer request in
client_ignore_enterleave_events(). This gives us a sequence number and makes
sure that nothing else besides us can cause events. The server is ours! In
client_restore_enterleave_events(), we first do a NoOperation request to
generate the sequence number for the end of the pair and then do UngrabServer.
Any event that is generated after UngrabServer will have at least the sequence
number of the UngrabServer request and thus no longer fails between begin and
end.

Fixes: https://github.com/awesomeWM/awesome/issues/1107
Signed-off-by: Uli Schlachter <psychon@znc.in>
This commit is contained in:
Uli Schlachter 2016-09-25 07:04:47 +02:00
parent fcf6c863cd
commit 19094de661
3 changed files with 63 additions and 22 deletions

33
event.c
View File

@ -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 */

View File

@ -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;

View File

@ -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.