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:
parent
fcf6c863cd
commit
19094de661
33
event.c
33
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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue