From 5d83eee16f6038e151e7d51dd613dbadfa19944c Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Sat, 15 Apr 2017 11:46:10 +0200 Subject: [PATCH 1/3] Initialize globalconf.timestamp at startup There are lots of places where ICCCM says that using CurrentTime for a timestamp is wrong. So, instead of having globalconf.timestamp initialized to CurrentTime, initialize it with a proper timestamp. Signed-off-by: Uli Schlachter --- awesome.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/awesome.c b/awesome.c index 36bb84f3..4e092e69 100644 --- a/awesome.c +++ b/awesome.c @@ -294,6 +294,49 @@ acquire_WM_Sn(bool replace) xcb_send_event(globalconf.connection, false, globalconf.screen->root, 0xFFFFFF, (char *) &ev); } +static void +acquire_timestamp(void) +{ + /* Getting a current timestamp is hard. ICCCM recommends a zero-length + * append to a property, so let's do that. + */ + xcb_generic_event_t *event; + xcb_window_t win = globalconf.screen->root; + xcb_atom_t atom = XCB_ATOM_RESOURCE_MANAGER; /* Just something random */ + xcb_atom_t type = XCB_ATOM_STRING; /* Equally random */ + + xcb_grab_server(globalconf.connection); + xcb_change_window_attributes(globalconf.connection, win, + XCB_CW_EVENT_MASK, (uint32_t[]) { XCB_EVENT_MASK_PROPERTY_CHANGE }); + xcb_change_property(globalconf.connection, XCB_PROP_MODE_APPEND, win, + atom, type, 8, 0, ""); + xcb_change_window_attributes(globalconf.connection, win, + XCB_CW_EVENT_MASK, (uint32_t[]) { 0 }); + xcb_ungrab_server(globalconf.connection); + xcb_flush(globalconf.connection); + + /* Now wait for the event */ + while((event = xcb_wait_for_event(globalconf.connection))) + { + /* Is it the event we are waiting for? */ + if(XCB_EVENT_RESPONSE_TYPE(event) == XCB_PROPERTY_NOTIFY) + { + xcb_property_notify_event_t *ev = (void *) event; + globalconf.timestamp = ev->time; + p_delete(&event); + break; + } + + /* Hm, not the right event. */ + if (globalconf.pending_event != NULL) + { + event_handle(globalconf.pending_event); + p_delete(&globalconf.pending_event); + } + globalconf.pending_event = event; + } +} + static xcb_generic_event_t *poll_for_event(void) { if (globalconf.pending_event) { @@ -635,6 +678,9 @@ main(int argc, char **argv) globalconf.visual->visual_id); } + /* Get a recent timestamp */ + acquire_timestamp(); + /* Prefetch all the extensions we might need */ xcb_prefetch_extension_data(globalconf.connection, &xcb_big_requests_id); xcb_prefetch_extension_data(globalconf.connection, &xcb_test_id); From 148dc269a832d2b02cc50c7b1c742ca86c40489b Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Sat, 15 Apr 2017 12:04:57 +0200 Subject: [PATCH 2/3] xembed: Stop using XCB_CURRENT_TIME This changes the xembed code so that the caller passes in a timestamp that should be used instead of XCB_CURRENT_TIME. Signed-off-by: Uli Schlachter --- common/xembed.c | 14 ++++++++------ common/xembed.h | 31 ++++++++++++++++++------------- event.c | 2 +- property.c | 3 ++- systray.c | 5 +++-- 5 files changed, 32 insertions(+), 23 deletions(-) diff --git a/common/xembed.c b/common/xembed.c index 3f4c8673..7e854fee 100644 --- a/common/xembed.c +++ b/common/xembed.c @@ -37,7 +37,7 @@ void luaA_systray_invalidate(void); */ void xembed_message_send(xcb_connection_t *connection, xcb_window_t towin, - long message, long d1, long d2, long d3) + xcb_timestamp_t timestamp, uint32_t message, uint32_t d1, uint32_t d2, uint32_t d3) { xcb_client_message_event_t ev; @@ -45,7 +45,7 @@ xembed_message_send(xcb_connection_t *connection, xcb_window_t towin, ev.response_type = XCB_CLIENT_MESSAGE; ev.window = towin; ev.format = 32; - ev.data.data32[0] = XCB_CURRENT_TIME; + ev.data.data32[0] = timestamp; ev.data.data32[1] = message; ev.data.data32[2] = d1; ev.data.data32[3] = d2; @@ -116,10 +116,12 @@ xembed_getbywin(xembed_window_array_t *list, xcb_window_t win) /** Update embedded window properties. * \param connection The X connection. * \param emwin The embedded window. + * \param timestamp The timestamp. + * \param reply The property reply to handle. */ void xembed_property_update(xcb_connection_t *connection, xembed_window_t *emwin, - xcb_get_property_reply_t *reply) + xcb_timestamp_t timestamp, xcb_get_property_reply_t *reply) { int flags_changed; xembed_info_t info = { 0, 0 }; @@ -136,13 +138,13 @@ xembed_property_update(xcb_connection_t *connection, xembed_window_t *emwin, if(info.flags & XEMBED_MAPPED) { xcb_map_window(connection, emwin->win); - xembed_window_activate(connection, emwin->win); + xembed_window_activate(connection, emwin->win, timestamp); } else { xcb_unmap_window(connection, emwin->win); - xembed_window_deactivate(connection, emwin->win); - xembed_focus_out(connection, emwin->win); + xembed_window_deactivate(connection, emwin->win, timestamp); + xembed_focus_out(connection, emwin->win, timestamp); } luaA_systray_invalidate(); } diff --git a/common/xembed.h b/common/xembed.h index 4a9a1622..484f5eb0 100644 --- a/common/xembed.h +++ b/common/xembed.h @@ -87,9 +87,9 @@ DO_ARRAY(xembed_window_t, xembed_window, DO_NOTHING) #define XEMBED_ACCELERATOR_OVERLOADED (1 << 0) -void xembed_message_send(xcb_connection_t *, xcb_window_t, long, long, long, long); +void xembed_message_send(xcb_connection_t *, xcb_window_t, xcb_timestamp_t, uint32_t, uint32_t, uint32_t, uint32_t); xembed_window_t * xembed_getbywin(xembed_window_array_t *, xcb_window_t); -void xembed_property_update(xcb_connection_t *, xembed_window_t *, xcb_get_property_reply_t *); +void xembed_property_update(xcb_connection_t *, xembed_window_t *, xcb_timestamp_t, xcb_get_property_reply_t *); xcb_get_property_cookie_t xembed_info_get_unchecked(xcb_connection_t *, xcb_window_t); bool xembed_info_get_reply(xcb_connection_t *connection, @@ -100,46 +100,50 @@ bool xembed_info_get_reply(xcb_connection_t *connection, /** Indicate to an embedded window that it has focus. * \param c The X connection. * \param client The client. + * \param timestamp The timestamp. * \param focus_type The type of focus. */ static inline void -xembed_focus_in(xcb_connection_t *c, xcb_window_t client, long focus_type) +xembed_focus_in(xcb_connection_t *c, xcb_window_t client, xcb_timestamp_t timestamp, uint32_t focus_type) { - xembed_message_send(c, client, XEMBED_FOCUS_IN, focus_type, 0, 0); + xembed_message_send(c, client, timestamp, XEMBED_FOCUS_IN, focus_type, 0, 0); } /** Notify a window that it has become active. * \param c The X connection. * \param client The window to notify. + * \param timestamp The timestamp. */ static inline void -xembed_window_activate(xcb_connection_t *c, xcb_window_t client) +xembed_window_activate(xcb_connection_t *c, xcb_window_t client, xcb_timestamp_t timestamp) { - xembed_message_send(c, client, XEMBED_WINDOW_ACTIVATE, 0, 0, 0); + xembed_message_send(c, client, timestamp, XEMBED_WINDOW_ACTIVATE, 0, 0, 0); } /** Notify a window that it has become inactive. * \param c The X connection. * \param client The window to notify. + * \param timestamp The timestamp. */ static inline -void xembed_window_deactivate(xcb_connection_t *c, xcb_window_t client) +void xembed_window_deactivate(xcb_connection_t *c, xcb_window_t client, xcb_timestamp_t timestamp) { - xembed_message_send(c, client, XEMBED_WINDOW_DEACTIVATE, 0, 0, 0); + xembed_message_send(c, client, timestamp, XEMBED_WINDOW_DEACTIVATE, 0, 0, 0); } /** Notify a window that its embed request has been received and accepted. * \param c The X connection. * \param client The client to send message to. + * \param timestamp The timestamp. * \param embedder The embedder window. * \param version The version. */ static inline void xembed_embedded_notify(xcb_connection_t *c, - xcb_window_t client, xcb_window_t embedder, - long version) + xcb_window_t client, xcb_timestamp_t timestamp, + xcb_window_t embedder, uint32_t version) { - xembed_message_send(c, client, XEMBED_EMBEDDED_NOTIFY, 0, embedder, version); + xembed_message_send(c, client, timestamp, XEMBED_EMBEDDED_NOTIFY, 0, embedder, version); } /** Have the embedder end XEMBED protocol communication with a child. @@ -156,11 +160,12 @@ xembed_window_unembed(xcb_connection_t *connection, xcb_window_t child, xcb_wind /** Indicate to an embedded window that it has lost focus. * \param c The X connection. * \param client The client to send message to. + * \param timestamp The timestamp. */ static inline void -xembed_focus_out(xcb_connection_t *c, xcb_window_t client) +xembed_focus_out(xcb_connection_t *c, xcb_window_t client, xcb_timestamp_t timestamp) { - xembed_message_send(c, client, XEMBED_FOCUS_OUT, 0, 0, 0); + xembed_message_send(c, client, timestamp, XEMBED_FOCUS_OUT, 0, 0, 0); } diff --git a/event.c b/event.c index 889e1c09..de60131d 100644 --- a/event.c +++ b/event.c @@ -751,7 +751,7 @@ event_handle_maprequest(xcb_map_request_event_t *ev) if((em = xembed_getbywin(&globalconf.embedded, ev->window))) { xcb_map_window(globalconf.connection, ev->window); - xembed_window_activate(globalconf.connection, ev->window); + xembed_window_activate(globalconf.connection, ev->window, globalconf.timestamp); /* The correct way to set this is via the _XEMBED_INFO property. Neither * of the XEMBED not the systray spec talk about mapping windows. * Apparently, Qt doesn't care and does not set an _XEMBED_INFO diff --git a/property.c b/property.c index e57effb4..9453621d 100644 --- a/property.c +++ b/property.c @@ -341,7 +341,8 @@ property_handle_xembed_info(uint8_t state, XCB_GET_PROPERTY_TYPE_ANY, 0, 3); xcb_get_property_reply_t *propr = xcb_get_property_reply(globalconf.connection, cookie, 0); - xembed_property_update(globalconf.connection, emwin, propr); + xembed_property_update(globalconf.connection, emwin, + globalconf.timestamp, propr); p_delete(&propr); } diff --git a/systray.c b/systray.c index 93182f37..cb1f4bec 100644 --- a/systray.c +++ b/systray.c @@ -166,7 +166,7 @@ systray_request_handle(xcb_window_t embed_win) } xembed_embedded_notify(globalconf.connection, em.win, - globalconf.systray.window, + globalconf.timestamp, globalconf.systray.window, MIN(XEMBED_VERSION, em.info.version)); xembed_window_array_append(&globalconf.embedded, em); @@ -241,7 +241,8 @@ xembed_process_client_message(xcb_client_message_event_t *ev) switch(ev->data.data32[1]) { case XEMBED_REQUEST_FOCUS: - xembed_focus_in(globalconf.connection, ev->window, XEMBED_FOCUS_CURRENT); + xembed_focus_in(globalconf.connection, ev->window, + globalconf.timestamp, XEMBED_FOCUS_CURRENT); break; } return 0; From 29aee713f3c78fc1584c5b9111d1a4e3c5e8ea77 Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Sat, 15 Apr 2017 12:05:42 +0200 Subject: [PATCH 3/3] Remove most uses of XCB_CURRENT_TIME XCB_CURRENT_TIME really should be avoided as much as possible (see ICCCM). However, the screen, mousegrabber and keygrabber code have cases where they really want up-to-date information / want to activate a grab *now*. These cases are fine with XCB_CURRENT_TIME since they are not related to some events. Signed-off-by: Uli Schlachter --- awesome.c | 6 +++--- event.c | 8 ++++++-- root.c | 2 +- systray.c | 6 +++--- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/awesome.c b/awesome.c index 4e092e69..3f5d6fbb 100644 --- a/awesome.c +++ b/awesome.c @@ -107,7 +107,7 @@ awesome_atexit(bool restart) * Work around this by placing the focus where we like it to be. */ xcb_set_input_focus(globalconf.connection, XCB_INPUT_FOCUS_POINTER_ROOT, - XCB_NONE, XCB_CURRENT_TIME); + XCB_NONE, globalconf.timestamp); xcb_aux_sync(globalconf.connection); xkb_free(); @@ -266,7 +266,7 @@ acquire_WM_Sn(bool replace) /* Acquire the selection */ xcb_set_selection_owner(globalconf.connection, globalconf.selection_owner_window, - globalconf.selection_atom, XCB_CURRENT_TIME); + globalconf.selection_atom, globalconf.timestamp); if (get_sel_reply->owner != XCB_NONE) { /* Wait for the old owner to go away */ @@ -286,7 +286,7 @@ acquire_WM_Sn(bool replace) ev.window = globalconf.screen->root; ev.format = 32; ev.type = MANAGER; - ev.data.data32[0] = XCB_CURRENT_TIME; + ev.data.data32[0] = globalconf.timestamp; ev.data.data32[1] = globalconf.selection_atom; ev.data.data32[2] = globalconf.selection_owner_window; ev.data.data32[3] = ev.data.data32[4] = 0; diff --git a/event.c b/event.c index de60131d..23096668 100644 --- a/event.c +++ b/event.c @@ -221,7 +221,7 @@ event_handle_button(xcb_button_press_event_t *ev) if(ev->child == XCB_NONE) xcb_allow_events(globalconf.connection, XCB_ALLOW_ASYNC_POINTER, - XCB_CURRENT_TIME); + ev->time); } else if((c = client_getbyframewin(ev->event)) || (c = client_getbywin(ev->event))) { @@ -263,7 +263,7 @@ event_handle_button(xcb_button_press_event_t *ev) } xcb_allow_events(globalconf.connection, XCB_ALLOW_REPLAY_POINTER, - XCB_CURRENT_TIME); + ev->time); } else if(ev->child == XCB_NONE) if(globalconf.screen->root == ev->event) @@ -837,6 +837,10 @@ event_handle_randr_output_change_notify(xcb_randr_notify_event_t *ev) xcb_randr_get_output_info_reply_t *info = NULL; lua_State *L = globalconf_get_lua_State(); + /* The following explicitly uses XCB_CURRENT_TIME since we want to know + * the final state of the connection. There could be more notification + * events underway and using some "old" timestamp causes problems. + */ info = xcb_randr_get_output_info_reply(globalconf.connection, xcb_randr_get_output_info_unchecked(globalconf.connection, output, diff --git a/root.c b/root.c index 968cbba1..52c149bc 100644 --- a/root.c +++ b/root.c @@ -276,7 +276,7 @@ luaA_root_fake_input(lua_State *L) xcb_test_fake_input(globalconf.connection, type, detail, - XCB_CURRENT_TIME, + 0, /* This is a delay, not a timestamp! */ XCB_NONE, x, y, 0); diff --git a/systray.c b/systray.c index cb1f4bec..aa2b1100 100644 --- a/systray.c +++ b/systray.c @@ -90,7 +90,7 @@ systray_register(void) ev.window = xscreen->root; ev.format = 32; ev.type = MANAGER; - ev.data.data32[0] = XCB_CURRENT_TIME; + ev.data.data32[0] = globalconf.timestamp; ev.data.data32[1] = globalconf.systray.atom; ev.data.data32[2] = globalconf.systray.window; ev.data.data32[3] = ev.data.data32[4] = 0; @@ -98,7 +98,7 @@ systray_register(void) xcb_set_selection_owner(globalconf.connection, globalconf.systray.window, globalconf.systray.atom, - XCB_CURRENT_TIME); + globalconf.timestamp); xcb_send_event(globalconf.connection, false, xscreen->root, 0xFFFFFF, (char *) &ev); } @@ -116,7 +116,7 @@ systray_cleanup(void) xcb_set_selection_owner(globalconf.connection, XCB_NONE, globalconf.systray.atom, - XCB_CURRENT_TIME); + globalconf.timestamp); xcb_unmap_window(globalconf.connection, globalconf.systray.window);