i3bar: fake DestroyNotify and send MANAGER ClientMessages to fix tray restarts

fixes #745
This commit is contained in:
Michael Stapelberg 2013-01-02 22:48:20 +01:00
parent 2043076518
commit e9503a1fa3
1 changed files with 55 additions and 23 deletions

View File

@ -49,6 +49,10 @@ int screen;
xcb_screen_t *root_screen;
xcb_window_t xcb_root;
/* selection window for tray support */
static xcb_window_t selwin = XCB_NONE;
static xcb_intern_atom_reply_t *tray_reply = NULL;
/* This is needed for integration with libi3 */
xcb_connection_t *conn;
@ -991,6 +995,31 @@ void init_xcb_late(char *fontname) {
}
}
/*
* Inform clients waiting for a new _NET_SYSTEM_TRAY that we took the
* selection.
*
*/
static void send_tray_clientmessage(void) {
uint8_t buffer[32] = { 0 };
xcb_client_message_event_t *ev = (xcb_client_message_event_t*)buffer;
ev->response_type = XCB_CLIENT_MESSAGE;
ev->window = xcb_root;
ev->type = atoms[MANAGER];
ev->format = 32;
ev->data.data32[0] = XCB_CURRENT_TIME;
ev->data.data32[1] = tray_reply->atom;
ev->data.data32[2] = selwin;
xcb_send_event(xcb_connection,
0,
xcb_root,
0xFFFFFF,
(char*)buffer);
}
/*
* Initializes tray support by requesting the appropriate _NET_SYSTEM_TRAY atom
* for the X11 display we are running on, then acquiring the selection for this
@ -1003,11 +1032,11 @@ void init_tray(void) {
char atomname[strlen("_NET_SYSTEM_TRAY_S") + 11];
snprintf(atomname, strlen("_NET_SYSTEM_TRAY_S") + 11, "_NET_SYSTEM_TRAY_S%d", screen);
xcb_intern_atom_cookie_t tray_cookie;
xcb_intern_atom_reply_t *tray_reply;
tray_cookie = xcb_intern_atom(xcb_connection, 0, strlen(atomname), atomname);
if (tray_reply == NULL)
tray_cookie = xcb_intern_atom(xcb_connection, 0, strlen(atomname), atomname);
/* tray support: we need a window to own the selection */
xcb_window_t selwin = xcb_generate_id(xcb_connection);
selwin = xcb_generate_id(xcb_connection);
uint32_t selmask = XCB_CW_OVERRIDE_REDIRECT;
uint32_t selval[] = { 1 };
xcb_create_window(xcb_connection,
@ -1033,9 +1062,11 @@ void init_tray(void) {
1,
&orientation);
if (!(tray_reply = xcb_intern_atom_reply(xcb_connection, tray_cookie, NULL))) {
ELOG("Could not get atom %s\n", atomname);
exit(EXIT_FAILURE);
if (tray_reply == NULL) {
if (!(tray_reply = xcb_intern_atom_reply(xcb_connection, tray_cookie, NULL))) {
ELOG("Could not get atom %s\n", atomname);
exit(EXIT_FAILURE);
}
}
xcb_set_selection_owner(xcb_connection,
@ -1062,23 +1093,7 @@ void init_tray(void) {
return;
}
/* Inform clients waiting for a new _NET_SYSTEM_TRAY that we are here */
void *event = scalloc(32);
xcb_client_message_event_t *ev = event;
ev->response_type = XCB_CLIENT_MESSAGE;
ev->window = xcb_root;
ev->type = atoms[MANAGER];
ev->format = 32;
ev->data.data32[0] = XCB_CURRENT_TIME;
ev->data.data32[1] = tray_reply->atom;
ev->data.data32[2] = selwin;
xcb_send_event(xcb_connection,
0,
xcb_root,
0xFFFFFF,
(char*)ev);
free(event);
free(tray_reply);
send_tray_clientmessage();
}
/*
@ -1138,6 +1153,9 @@ void get_atoms(void) {
*
*/
void kick_tray_clients(i3_output *output) {
if (TAILQ_EMPTY(output->trayclients))
return;
trayclient *trayclient;
while (!TAILQ_EMPTY(output->trayclients)) {
trayclient = TAILQ_FIRST(output->trayclients);
@ -1153,6 +1171,20 @@ void kick_tray_clients(i3_output *output) {
* event afterwards, but better safe than sorry. */
TAILQ_REMOVE(output->trayclients, trayclient, tailq);
}
/* Fake a DestroyNotify so that Qt re-adds tray icons.
* We cannot actually destroy the window because then Qt will not restore
* its event mask on the new window. */
uint8_t buffer[32] = { 0 };
xcb_destroy_notify_event_t *event = (xcb_destroy_notify_event_t*)buffer;
event->response_type = XCB_DESTROY_NOTIFY;
event->event = selwin;
event->window = selwin;
xcb_send_event(conn, false, selwin, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (char*)event);
send_tray_clientmessage();
}
/*