From 025dd68f628e35b865db9eb734e72cd43e980317 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Fri, 12 Aug 2011 19:14:27 +0200 Subject: [PATCH 01/10] i3bar: quick & dirty systray implementation Works correctly only with exactly one dock client on exactly one output. Maybe not even then. You have been warned. Proof-of-concept code ;). --- i3bar/include/outputs.h | 2 + i3bar/include/xcb_atoms.def | 6 ++ i3bar/src/outputs.c | 1 + i3bar/src/xcb.c | 111 +++++++++++++++++++++++++++++++++++- 4 files changed, 118 insertions(+), 2 deletions(-) diff --git a/i3bar/include/outputs.h b/i3bar/include/outputs.h index 41712ac3..680261b1 100644 --- a/i3bar/include/outputs.h +++ b/i3bar/include/outputs.h @@ -46,6 +46,8 @@ struct i3_output { xcb_pixmap_t buffer; /* An extra pixmap for double-buffering */ xcb_gcontext_t bargc; /* The graphical context of the bar */ + int traypx; /* Amount of pixels reserved for tray icons */ + struct ws_head *workspaces; /* The workspaces on this output */ SLIST_ENTRY(i3_output) slist; /* Pointer for the SLIST-Macro */ diff --git a/i3bar/include/xcb_atoms.def b/i3bar/include/xcb_atoms.def index 5d168873..792ef9a9 100644 --- a/i3bar/include/xcb_atoms.def +++ b/i3bar/include/xcb_atoms.def @@ -2,4 +2,10 @@ ATOM_DO(_NET_WM_WINDOW_TYPE) ATOM_DO(_NET_WM_WINDOW_TYPE_DOCK) ATOM_DO(_NET_WM_STRUT_PARTIAL) ATOM_DO(I3_SOCKET_PATH) +ATOM_DO(_NET_SYSTEM_TRAY_S0) +ATOM_DO(MANAGER) +ATOM_DO(_NET_SYSTEM_TRAY_ORIENTATION) +ATOM_DO(_NET_SYSTEM_TRAY_VISUAL) +ATOM_DO(CARDINAL) +ATOM_DO(_NET_SYSTEM_TRAY_OPCODE) #undef ATOM_DO diff --git a/i3bar/src/outputs.c b/i3bar/src/outputs.c index 9daf328d..39bad192 100644 --- a/i3bar/src/outputs.c +++ b/i3bar/src/outputs.c @@ -157,6 +157,7 @@ static int outputs_start_map_cb(void *params_) { new_output->ws = 0, memset(&new_output->rect, 0, sizeof(rect)); new_output->bar = XCB_NONE; + new_output->traypx = 0; new_output->workspaces = malloc(sizeof(struct ws_head)); TAILQ_INIT(new_output->workspaces); diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index 28ef3ea2..35d90f77 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -383,6 +383,45 @@ void handle_button(xcb_button_press_event_t *event) { i3_send_msg(I3_IPC_MESSAGE_TYPE_COMMAND, buffer); } +void handle_client_message(xcb_client_message_event_t* event) { + printf("got a client message, yay\n"); + if (event->type == atoms[_NET_SYSTEM_TRAY_OPCODE] && + event->format == 32) { + printf("system tray message\n"); + /* event->data.data32[0] is the timestamp */ + uint32_t op = event->data.data32[1]; +#define SYSTEM_TRAY_REQUEST_DOCK 0 +#define SYSTEM_TRAY_BEGIN_MESSAGE 1 +#define SYSTEM_TRAY_CANCEL_MESSAGE 2 + if (op == SYSTEM_TRAY_REQUEST_DOCK) { + printf("docking requested of x window id %d\n", event->data.data32[2]); + /* TODO: correctly handle multiple dock clients */ + xcb_window_t client = event->data.data32[2]; + i3_output *walk, *output; + SLIST_FOREACH(walk, outputs, slist) { + if (!walk->active) + continue; + printf("using output %s\n", walk->name); + output = walk; + } + xcb_reparent_window(xcb_connection, + client, + output->bar, + output->rect.w - font_height - 2, /* TODO: why -2? */ + 0); + uint32_t mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; + uint32_t values[] = { font_height, font_height }; + xcb_configure_window(xcb_connection, + client, + mask, + values); + xcb_map_window(xcb_connection, client); + /* XXX: We assume that icons are quadratic. Is that so? */ + output->traypx += font_height; + } + } +} + /* * This function is called immediately before the main loop locks. We flush xcb * then (and only then) @@ -413,6 +452,11 @@ void xcb_chk_cb(struct ev_loop *loop, ev_check *watcher, int revents) { /* Button-press-events are mouse-buttons clicked on one of our bars */ handle_button((xcb_button_press_event_t*) event); break; + case XCB_CLIENT_MESSAGE: + /* Client messages are used for client-to-client communication, for + * example system tray widgets talk to us directly via client messages. */ + handle_client_message((xcb_client_message_event_t*) event); + break; } FREE(event); } @@ -634,6 +678,62 @@ char *init_xcb(char *fontname) { return path; } +void init_tray() { +/* tray support: we need a window to own the selection */ + xcb_void_cookie_t selwin_cookie; + xcb_window_t selwin = xcb_generate_id(xcb_connection); + uint32_t selmask = XCB_CW_OVERRIDE_REDIRECT; + uint32_t selval[] = { 1 }; + selwin_cookie = xcb_create_window_checked(xcb_connection, + xcb_screen->root_depth, + selwin, + xcb_root, + -1, -1, + 1, 1, + 1, + XCB_WINDOW_CLASS_INPUT_OUTPUT, + xcb_screen->root_visual, + selmask, + selval); + +#define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0 +#define _NET_SYSTEM_TRAY_ORIENTATION_VERT 1 + uint32_t orientation = _NET_SYSTEM_TRAY_ORIENTATION_HORZ; + /* set the atoms */ + xcb_change_property(xcb_connection, + XCB_PROP_MODE_REPLACE, + selwin, + atoms[_NET_SYSTEM_TRAY_ORIENTATION], + atoms[CARDINAL], + 32, + 1, + &orientation); + + + xcb_set_selection_owner(xcb_connection, + selwin, + /* TODO: request this atom separately */ + atoms[_NET_SYSTEM_TRAY_S0], + XCB_CURRENT_TIME); + /* FIXME: don't use XCB_CURRENT_TIME */ + + /* TODO: check if we got the selection */ + void *event = calloc(32, 1); + 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] = atoms[_NET_SYSTEM_TRAY_S0]; + ev->data.data32[2] = selwin; + xcb_send_event(xcb_connection, + 0, + xcb_root, + XCB_EVENT_MASK_STRUCTURE_NOTIFY, + (char*)ev); +} + /* * Cleanup the xcb-stuff. * Called once, before the program terminates. @@ -757,6 +857,9 @@ void reconfig_windows() { if (walk->bar == XCB_NONE) { DLOG("Creating Window for output %s\n", walk->name); + /* TODO: only call init_tray() if the tray is configured for this output */ + init_tray(); + walk->bar = xcb_generate_id(xcb_connection); walk->buffer = xcb_generate_id(xcb_connection); mask = XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK; @@ -946,13 +1049,17 @@ void draw_bars() { /* Luckily we already prepared a seperate pixmap containing the rendered * statusline, we just have to copy the relevant parts to the relevant * position */ + int traypx = outputs_walk->traypx; + /* Add 2px of padding if there are any tray icons */ + if (traypx > 0) + traypx += 2; xcb_copy_area(xcb_connection, statusline_pm, outputs_walk->buffer, outputs_walk->bargc, MAX(0, (int16_t)(statusline_width - outputs_walk->rect.w + 4)), 0, - MAX(0, (int16_t)(outputs_walk->rect.w - statusline_width - 4)), 3, - MIN(outputs_walk->rect.w - 4, statusline_width), font_height); + MAX(0, (int16_t)(outputs_walk->rect.w - statusline_width - traypx - 4)), 3, + MIN(outputs_walk->rect.w - outputs_walk->traypx - 4, statusline_width), font_height); } if (config.disable_ws) { From 6efa7a754d5120a207b2dc8af0efdafdff64c450 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 15 Aug 2011 15:12:01 +0200 Subject: [PATCH 02/10] i3bar: trigger an update after docking a new client --- i3bar/src/xcb.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index 35d90f77..29b31aef 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -418,6 +418,10 @@ void handle_client_message(xcb_client_message_event_t* event) { xcb_map_window(xcb_connection, client); /* XXX: We assume that icons are quadratic. Is that so? */ output->traypx += font_height; + + /* Trigger an update to copy the statusline text to the appropriate + * position */ + draw_bars(); } } } From 2046e4112f551db819e471b9553dee22f957eb20 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 15 Aug 2011 15:57:52 +0200 Subject: [PATCH 03/10] i3bar: Correctly handle removal of tray clients --- i3bar/include/common.h | 1 + i3bar/include/outputs.h | 3 +- i3bar/include/trayclients.h | 24 +++++++++++++++ i3bar/src/outputs.c | 4 ++- i3bar/src/xcb.c | 60 +++++++++++++++++++++++++++++++++---- 5 files changed, 84 insertions(+), 8 deletions(-) create mode 100644 i3bar/include/trayclients.h diff --git a/i3bar/include/common.h b/i3bar/include/common.h index 644b777c..9737d22f 100644 --- a/i3bar/include/common.h +++ b/i3bar/include/common.h @@ -29,6 +29,7 @@ struct rect_t { #include "outputs.h" #include "util.h" #include "workspaces.h" +#include "trayclients.h" #include "xcb.h" #include "ucs2_to_utf8.h" #include "config.h" diff --git a/i3bar/include/outputs.h b/i3bar/include/outputs.h index 680261b1..c6402a5b 100644 --- a/i3bar/include/outputs.h +++ b/i3bar/include/outputs.h @@ -46,9 +46,8 @@ struct i3_output { xcb_pixmap_t buffer; /* An extra pixmap for double-buffering */ xcb_gcontext_t bargc; /* The graphical context of the bar */ - int traypx; /* Amount of pixels reserved for tray icons */ - struct ws_head *workspaces; /* The workspaces on this output */ + struct tc_head *trayclients; /* The tray clients on this output */ SLIST_ENTRY(i3_output) slist; /* Pointer for the SLIST-Macro */ }; diff --git a/i3bar/include/trayclients.h b/i3bar/include/trayclients.h new file mode 100644 index 00000000..3218578a --- /dev/null +++ b/i3bar/include/trayclients.h @@ -0,0 +1,24 @@ +/* + * i3bar - an xcb-based status- and ws-bar for i3 + * + * © 2010-2011 Axel Wagner and contributors + * + * See file LICNSE for license information + * + */ +#ifndef TRAYCLIENT_H_ +#define TRAYCLIENT_H_ + +#include "common.h" + +typedef struct trayclient trayclient; + +TAILQ_HEAD(tc_head, trayclient); + +struct trayclient { + xcb_window_t win; /* The window ID of the tray client */ + + TAILQ_ENTRY(trayclient) tailq; /* Pointer for the TAILQ-Macro */ +}; + +#endif diff --git a/i3bar/src/outputs.c b/i3bar/src/outputs.c index 39bad192..53544d17 100644 --- a/i3bar/src/outputs.c +++ b/i3bar/src/outputs.c @@ -157,11 +157,13 @@ static int outputs_start_map_cb(void *params_) { new_output->ws = 0, memset(&new_output->rect, 0, sizeof(rect)); new_output->bar = XCB_NONE; - new_output->traypx = 0; new_output->workspaces = malloc(sizeof(struct ws_head)); TAILQ_INIT(new_output->workspaces); + new_output->trayclients = malloc(sizeof(struct tc_head)); + TAILQ_INIT(new_output->trayclients); + params->outputs_walk = new_output; return 1; diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index 29b31aef..3da23a81 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -408,16 +408,32 @@ void handle_client_message(xcb_client_message_event_t* event) { client, output->bar, output->rect.w - font_height - 2, /* TODO: why -2? */ - 0); + 2); + /* We reconfigure the window to use a reasonable size. The systray + * specification explicitly says: + * Tray icons may be assigned any size by the system tray, and + * should do their best to cope with any size effectively + */ uint32_t mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; uint32_t values[] = { font_height, font_height }; xcb_configure_window(xcb_connection, client, mask, values); + + /* Listen for PropertyNotify events to get the most recent value of + * the XEMBED_MAPPED atom, also listen for UnmapNotify events */ + mask = XCB_CW_EVENT_MASK; + values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE | + XCB_EVENT_MASK_STRUCTURE_NOTIFY; + xcb_change_window_attributes(xcb_connection, + client, + mask, + values); xcb_map_window(xcb_connection, client); - /* XXX: We assume that icons are quadratic. Is that so? */ - output->traypx += font_height; + trayclient *tc = malloc(sizeof(trayclient)); + tc->win = client; + TAILQ_INSERT_TAIL(output->trayclients, tc, tailq); /* Trigger an update to copy the statusline text to the appropriate * position */ @@ -426,6 +442,29 @@ void handle_client_message(xcb_client_message_event_t* event) { } } +void handle_unmap_notify(xcb_unmap_notify_event_t* event) { + DLOG("UnmapNotify for window = %08x, event = %08x\n", event->window, event->event); + + i3_output *walk; + SLIST_FOREACH(walk, outputs, slist) { + if (!walk->active) + continue; + DLOG("checking output %s\n", walk->name); + trayclient *trayclient; + TAILQ_FOREACH(trayclient, walk->trayclients, tailq) { + if (trayclient->win != event->window) + continue; + + DLOG("Removing tray client with window ID %08x\n", event->window); + TAILQ_REMOVE(walk->trayclients, trayclient, tailq); + + /* Trigger an update, we now have more space for the statusline */ + draw_bars(); + return; + } + } +} + /* * This function is called immediately before the main loop locks. We flush xcb * then (and only then) @@ -461,6 +500,10 @@ void xcb_chk_cb(struct ev_loop *loop, ev_check *watcher, int revents) { * example system tray widgets talk to us directly via client messages. */ handle_client_message((xcb_client_message_event_t*) event); break; + case XCB_UNMAP_NOTIFY: + /* UnmapNotifies are received when a tray window unmaps itself */ + handle_unmap_notify((xcb_unmap_notify_event_t*) event); + break; } FREE(event); } @@ -1053,7 +1096,14 @@ void draw_bars() { /* Luckily we already prepared a seperate pixmap containing the rendered * statusline, we just have to copy the relevant parts to the relevant * position */ - int traypx = outputs_walk->traypx; + trayclient *trayclient; + int traypx = 0; + TAILQ_FOREACH(trayclient, outputs_walk->trayclients, tailq) { + /* We assume the tray icons are quadratic (we use the font + * *height* as *width* of the icons) because we configured them + * like this. */ + traypx += font_height; + } /* Add 2px of padding if there are any tray icons */ if (traypx > 0) traypx += 2; @@ -1063,7 +1113,7 @@ void draw_bars() { outputs_walk->bargc, MAX(0, (int16_t)(statusline_width - outputs_walk->rect.w + 4)), 0, MAX(0, (int16_t)(outputs_walk->rect.w - statusline_width - traypx - 4)), 3, - MIN(outputs_walk->rect.w - outputs_walk->traypx - 4, statusline_width), font_height); + MIN(outputs_walk->rect.w - traypx - 4, statusline_width), font_height); } if (config.disable_ws) { From 7df43989c930a5b7c0f138d00f925836ec3b4453 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Tue, 16 Aug 2011 22:44:42 +0200 Subject: [PATCH 04/10] i3bar: correctly handle multiple tray clients --- i3bar/src/xcb.c | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index 3da23a81..eb1ae0ab 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -383,25 +383,50 @@ void handle_button(xcb_button_press_event_t *event) { i3_send_msg(I3_IPC_MESSAGE_TYPE_COMMAND, buffer); } +/* + * Configures the x coordinate of all trayclients. To be called after adding a + * new tray client or removing an old one. + * + */ +static void configure_trayclients() { + trayclient *trayclient; + i3_output *output; + SLIST_FOREACH(output, outputs, slist) { + if (!output->active) + continue; + + int clients = 0; + TAILQ_FOREACH_REVERSE(trayclient, output->trayclients, tc_head, tailq) { + clients++; + + DLOG("Configuring tray window %08x to x=%d\n", + trayclient->win, output->rect.w - (clients * (font_height + 2))); + uint32_t x = output->rect.w - (clients * (font_height + 2)); + xcb_configure_window(xcb_connection, + trayclient->win, + XCB_CONFIG_WINDOW_X, + &x); + } + } +} + void handle_client_message(xcb_client_message_event_t* event) { - printf("got a client message, yay\n"); if (event->type == atoms[_NET_SYSTEM_TRAY_OPCODE] && event->format == 32) { - printf("system tray message\n"); + DLOG("_NET_SYSTEM_TRAY_OPCODE received\n"); /* event->data.data32[0] is the timestamp */ uint32_t op = event->data.data32[1]; #define SYSTEM_TRAY_REQUEST_DOCK 0 #define SYSTEM_TRAY_BEGIN_MESSAGE 1 #define SYSTEM_TRAY_CANCEL_MESSAGE 2 if (op == SYSTEM_TRAY_REQUEST_DOCK) { - printf("docking requested of x window id %d\n", event->data.data32[2]); - /* TODO: correctly handle multiple dock clients */ xcb_window_t client = event->data.data32[2]; + DLOG("X window %08x requested docking\n", client); i3_output *walk, *output; SLIST_FOREACH(walk, outputs, slist) { if (!walk->active) continue; - printf("using output %s\n", walk->name); + DLOG("using output %s\n", walk->name); output = walk; } xcb_reparent_window(xcb_connection, @@ -437,6 +462,7 @@ void handle_client_message(xcb_client_message_event_t* event) { /* Trigger an update to copy the statusline text to the appropriate * position */ + configure_trayclients(); draw_bars(); } } @@ -459,6 +485,7 @@ void handle_unmap_notify(xcb_unmap_notify_event_t* event) { TAILQ_REMOVE(walk->trayclients, trayclient, tailq); /* Trigger an update, we now have more space for the statusline */ + configure_trayclients(); draw_bars(); return; } From 737cd10bdf17e66c71b037dff2b71396a14c5a5b Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 17 Aug 2011 00:05:05 +0200 Subject: [PATCH 05/10] i3bar: properly handle the _XEMBED_INFO property --- i3bar/include/common.h | 3 +- i3bar/include/trayclients.h | 1 + i3bar/include/xcb.h | 5 ++ i3bar/include/xcb_atoms.def | 1 + i3bar/src/outputs.c | 2 +- i3bar/src/workspaces.c | 2 +- i3bar/src/xcb.c | 133 ++++++++++++++++++++++++++++++++---- 7 files changed, 129 insertions(+), 18 deletions(-) diff --git a/i3bar/include/common.h b/i3bar/include/common.h index 9737d22f..74bd2152 100644 --- a/i3bar/include/common.h +++ b/i3bar/include/common.h @@ -9,8 +9,9 @@ #ifndef COMMON_H_ #define COMMON_H_ +#include + typedef struct rect_t rect; -typedef int bool; struct ev_loop* main_loop; char *statusline; diff --git a/i3bar/include/trayclients.h b/i3bar/include/trayclients.h index 3218578a..277cd1cb 100644 --- a/i3bar/include/trayclients.h +++ b/i3bar/include/trayclients.h @@ -17,6 +17,7 @@ TAILQ_HEAD(tc_head, trayclient); struct trayclient { xcb_window_t win; /* The window ID of the tray client */ + bool mapped; /* Whether this window is mapped */ TAILQ_ENTRY(trayclient) tailq; /* Pointer for the TAILQ-Macro */ }; diff --git a/i3bar/include/xcb.h b/i3bar/include/xcb.h index 531fdfe9..3fadc4c5 100644 --- a/i3bar/include/xcb.h +++ b/i3bar/include/xcb.h @@ -12,6 +12,11 @@ #include //#include "outputs.h" +#define SYSTEM_TRAY_REQUEST_DOCK 0 +#define SYSTEM_TRAY_BEGIN_MESSAGE 1 +#define SYSTEM_TRAY_CANCEL_MESSAGE 2 +#define XEMBED_MAPPED (1 << 0) + struct xcb_color_strings_t { char *bar_fg; char *bar_bg; diff --git a/i3bar/include/xcb_atoms.def b/i3bar/include/xcb_atoms.def index 792ef9a9..3f2b9775 100644 --- a/i3bar/include/xcb_atoms.def +++ b/i3bar/include/xcb_atoms.def @@ -8,4 +8,5 @@ ATOM_DO(_NET_SYSTEM_TRAY_ORIENTATION) ATOM_DO(_NET_SYSTEM_TRAY_VISUAL) ATOM_DO(CARDINAL) ATOM_DO(_NET_SYSTEM_TRAY_OPCODE) +ATOM_DO(_XEMBED_INFO) #undef ATOM_DO diff --git a/i3bar/src/outputs.c b/i3bar/src/outputs.c index 53544d17..464f24a0 100644 --- a/i3bar/src/outputs.c +++ b/i3bar/src/outputs.c @@ -43,7 +43,7 @@ static int outputs_null_cb(void *params_) { * Parse a boolean value (active) * */ -static int outputs_boolean_cb(void *params_, bool val) { +static int outputs_boolean_cb(void *params_, int val) { struct outputs_json_params *params = (struct outputs_json_params*) params_; if (strcmp(params->cur_key, "active")) { diff --git a/i3bar/src/workspaces.c b/i3bar/src/workspaces.c index eeb9ca34..a84e152b 100644 --- a/i3bar/src/workspaces.c +++ b/i3bar/src/workspaces.c @@ -29,7 +29,7 @@ struct workspaces_json_params { * Parse a boolean value (visible, focused, urgent) * */ -static int workspaces_boolean_cb(void *params_, bool val) { +static int workspaces_boolean_cb(void *params_, int val) { struct workspaces_json_params *params = (struct workspaces_json_params*) params_; if (!strcmp(params->cur_key, "visible")) { diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index eb1ae0ab..c7ddfe88 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -416,11 +416,48 @@ void handle_client_message(xcb_client_message_event_t* event) { DLOG("_NET_SYSTEM_TRAY_OPCODE received\n"); /* event->data.data32[0] is the timestamp */ uint32_t op = event->data.data32[1]; -#define SYSTEM_TRAY_REQUEST_DOCK 0 -#define SYSTEM_TRAY_BEGIN_MESSAGE 1 -#define SYSTEM_TRAY_CANCEL_MESSAGE 2 + uint32_t mask; + uint32_t values[2]; if (op == SYSTEM_TRAY_REQUEST_DOCK) { xcb_window_t client = event->data.data32[2]; + + /* Listen for PropertyNotify events to get the most recent value of + * the XEMBED_MAPPED atom, also listen for UnmapNotify events */ + mask = XCB_CW_EVENT_MASK; + values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE | + XCB_EVENT_MASK_STRUCTURE_NOTIFY; + xcb_change_window_attributes(xcb_connection, + client, + mask, + values); + + /* Request the _XEMBED_INFO property. The XEMBED specification + * (which is referred by the tray specification) says this *has* to + * be set, but VLC does not set it… */ + bool map_it = true; + xcb_get_property_cookie_t xembedc; + xembedc = xcb_get_property_unchecked(xcb_connection, + 0, + client, + atoms[_XEMBED_INFO], + XCB_GET_PROPERTY_TYPE_ANY, + 0, + 2 * 32); + + xcb_get_property_reply_t *xembedr = xcb_get_property_reply(xcb_connection, + xembedc, + NULL); + if (xembedr != NULL && xembedr->length != 0) { + DLOG("xembed format = %d, len = %d\n", xembedr->format, xembedr->length); + uint32_t *xembed = xcb_get_property_value(xembedr); + DLOG("xembed version = %d\n", xembed[0]); + DLOG("xembed flags = %d\n", xembed[1]); + map_it = ((xembed[1] & XEMBED_MAPPED) == XEMBED_MAPPED); + free(xembedr); + } else { + ELOG("Window %08x violates the XEMBED protocol, _XEMBED_INFO not set\n", client); + } + DLOG("X window %08x requested docking\n", client); i3_output *walk, *output; SLIST_FOREACH(walk, outputs, slist) { @@ -439,25 +476,23 @@ void handle_client_message(xcb_client_message_event_t* event) { * Tray icons may be assigned any size by the system tray, and * should do their best to cope with any size effectively */ - uint32_t mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; - uint32_t values[] = { font_height, font_height }; + mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; + values[0] = font_height; + values[1] = font_height; xcb_configure_window(xcb_connection, client, mask, values); - /* Listen for PropertyNotify events to get the most recent value of - * the XEMBED_MAPPED atom, also listen for UnmapNotify events */ - mask = XCB_CW_EVENT_MASK; - values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE | - XCB_EVENT_MASK_STRUCTURE_NOTIFY; - xcb_change_window_attributes(xcb_connection, - client, - mask, - values); - xcb_map_window(xcb_connection, client); + if (map_it) { + DLOG("Mapping dock client\n"); + xcb_map_window(xcb_connection, client); + } else { + DLOG("Not mapping dock client yet\n"); + } trayclient *tc = malloc(sizeof(trayclient)); tc->win = client; + tc->mapped = map_it; TAILQ_INSERT_TAIL(output->trayclients, tc, tailq); /* Trigger an update to copy the statusline text to the appropriate @@ -492,6 +527,68 @@ void handle_unmap_notify(xcb_unmap_notify_event_t* event) { } } +static void handle_property_notify(xcb_property_notify_event_t *event) { + DLOG("PropertyNotify\n"); + if (event->atom == atoms[_XEMBED_INFO] && + event->state == XCB_PROPERTY_NEW_VALUE) { + DLOG("xembed_info updated\n"); + trayclient *trayclient = NULL, *walk; + i3_output *output; + SLIST_FOREACH(output, outputs, slist) { + if (!output->active) + continue; + + TAILQ_FOREACH(walk, output->trayclients, tailq) { + if (walk->win != event->window) + continue; + trayclient = walk; + break; + } + + if (trayclient) + break; + } + if (!trayclient) { + ELOG("PropertyNotify received for unknown window %08x\n", + event->window); + return; + } + xcb_get_property_cookie_t xembedc; + xembedc = xcb_get_property_unchecked(xcb_connection, + 0, + trayclient->win, + atoms[_XEMBED_INFO], + XCB_GET_PROPERTY_TYPE_ANY, + 0, + 2 * 32); + + xcb_get_property_reply_t *xembedr = xcb_get_property_reply(xcb_connection, + xembedc, + NULL); + if (xembedr == NULL || xembedr->length == 0) + return; + + DLOG("xembed format = %d, len = %d\n", xembedr->format, xembedr->length); + uint32_t *xembed = xcb_get_property_value(xembedr); + DLOG("xembed version = %d\n", xembed[0]); + DLOG("xembed flags = %d\n", xembed[1]); + bool map_it = ((xembed[1] & XEMBED_MAPPED) == XEMBED_MAPPED); + DLOG("map-state now %d\n", map_it); + if (trayclient->mapped && !map_it) { + /* need to unmap the window */ + xcb_unmap_window(xcb_connection, trayclient->win); + trayclient->mapped = map_it; + draw_bars(); + } else if (!trayclient->mapped && map_it) { + /* need to map the window */ + xcb_map_window(xcb_connection, trayclient->win); + trayclient->mapped = map_it; + draw_bars(); + } + free(xembedr); + } +} + /* * This function is called immediately before the main loop locks. We flush xcb * then (and only then) @@ -531,6 +628,10 @@ void xcb_chk_cb(struct ev_loop *loop, ev_check *watcher, int revents) { /* UnmapNotifies are received when a tray window unmaps itself */ handle_unmap_notify((xcb_unmap_notify_event_t*) event); break; + case XCB_PROPERTY_NOTIFY: + /* PropertyNotify */ + handle_property_notify((xcb_property_notify_event_t*) event); + break; } FREE(event); } @@ -1126,6 +1227,8 @@ void draw_bars() { trayclient *trayclient; int traypx = 0; TAILQ_FOREACH(trayclient, outputs_walk->trayclients, tailq) { + if (!trayclient->mapped) + continue; /* We assume the tray icons are quadratic (we use the font * *height* as *width* of the icons) because we configured them * like this. */ From 893878cbcc7306a6553cda51b3423c73c25e9e99 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 17 Aug 2011 00:44:03 +0200 Subject: [PATCH 06/10] i3bar: send XEMBED_EMBEDDED_NOTIFY after reparenting/mapping tray clients --- i3bar/include/trayclients.h | 1 + i3bar/include/xcb.h | 1 + i3bar/include/xcb_atoms.def | 1 + i3bar/src/xcb.c | 32 ++++++++++++++++++++++++++++---- 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/i3bar/include/trayclients.h b/i3bar/include/trayclients.h index 277cd1cb..1113daeb 100644 --- a/i3bar/include/trayclients.h +++ b/i3bar/include/trayclients.h @@ -18,6 +18,7 @@ TAILQ_HEAD(tc_head, trayclient); struct trayclient { xcb_window_t win; /* The window ID of the tray client */ bool mapped; /* Whether this window is mapped */ + int xe_version; /* The XEMBED version supported by the client */ TAILQ_ENTRY(trayclient) tailq; /* Pointer for the TAILQ-Macro */ }; diff --git a/i3bar/include/xcb.h b/i3bar/include/xcb.h index 3fadc4c5..b70d29ef 100644 --- a/i3bar/include/xcb.h +++ b/i3bar/include/xcb.h @@ -16,6 +16,7 @@ #define SYSTEM_TRAY_BEGIN_MESSAGE 1 #define SYSTEM_TRAY_CANCEL_MESSAGE 2 #define XEMBED_MAPPED (1 << 0) +#define XEMBED_EMBEDDED_NOTIFY 0 struct xcb_color_strings_t { char *bar_fg; diff --git a/i3bar/include/xcb_atoms.def b/i3bar/include/xcb_atoms.def index 3f2b9775..625fc8a4 100644 --- a/i3bar/include/xcb_atoms.def +++ b/i3bar/include/xcb_atoms.def @@ -9,4 +9,5 @@ ATOM_DO(_NET_SYSTEM_TRAY_VISUAL) ATOM_DO(CARDINAL) ATOM_DO(_NET_SYSTEM_TRAY_OPCODE) ATOM_DO(_XEMBED_INFO) +ATOM_DO(_XEMBED) #undef ATOM_DO diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index c7ddfe88..81c00ac4 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -435,6 +435,7 @@ void handle_client_message(xcb_client_message_event_t* event) { * (which is referred by the tray specification) says this *has* to * be set, but VLC does not set it… */ bool map_it = true; + int xe_version = 1; xcb_get_property_cookie_t xembedc; xembedc = xcb_get_property_unchecked(xcb_connection, 0, @@ -453,6 +454,9 @@ void handle_client_message(xcb_client_message_event_t* event) { DLOG("xembed version = %d\n", xembed[0]); DLOG("xembed flags = %d\n", xembed[1]); map_it = ((xembed[1] & XEMBED_MAPPED) == XEMBED_MAPPED); + xe_version = xembed[0]; + if (xe_version > 1) + xe_version = 1; free(xembedr); } else { ELOG("Window %08x violates the XEMBED protocol, _XEMBED_INFO not set\n", client); @@ -484,6 +488,24 @@ void handle_client_message(xcb_client_message_event_t* event) { mask, values); + /* send the XEMBED_EMBEDDED_NOTIFY message */ + void *event = calloc(32, 1); + xcb_client_message_event_t *ev = event; + ev->response_type = XCB_CLIENT_MESSAGE; + ev->window = client; + ev->type = atoms[_XEMBED]; + ev->format = 32; + ev->data.data32[0] = XCB_CURRENT_TIME; + ev->data.data32[1] = atoms[XEMBED_EMBEDDED_NOTIFY]; + ev->data.data32[2] = output->bar; + ev->data.data32[3] = xe_version; + xcb_send_event(xcb_connection, + 0, + client, + XCB_EVENT_MASK_NO_EVENT, + (char*)ev); + free(event); + if (map_it) { DLOG("Mapping dock client\n"); xcb_map_window(xcb_connection, client); @@ -493,6 +515,7 @@ void handle_client_message(xcb_client_message_event_t* event) { trayclient *tc = malloc(sizeof(trayclient)); tc->win = client; tc->mapped = map_it; + tc->xe_version = xe_version; TAILQ_INSERT_TAIL(output->trayclients, tc, tailq); /* Trigger an update to copy the statusline text to the appropriate @@ -533,12 +556,12 @@ static void handle_property_notify(xcb_property_notify_event_t *event) { event->state == XCB_PROPERTY_NEW_VALUE) { DLOG("xembed_info updated\n"); trayclient *trayclient = NULL, *walk; - i3_output *output; - SLIST_FOREACH(output, outputs, slist) { - if (!output->active) + i3_output *o_walk; + SLIST_FOREACH(o_walk, outputs, slist) { + if (!o_walk->active) continue; - TAILQ_FOREACH(walk, output->trayclients, tailq) { + TAILQ_FOREACH(walk, o_walk->trayclients, tailq) { if (walk->win != event->window) continue; trayclient = walk; @@ -907,6 +930,7 @@ void init_tray() { xcb_root, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (char*)ev); + free(event); } /* From 55e503c17b000cc2d6ff90a04d8085956f3ecdab Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 17 Aug 2011 00:58:00 +0200 Subject: [PATCH 07/10] i3bar: request the appropriate _NET_SYSTEM_TRAY atom for the display we are running on --- i3bar/include/xcb.h | 2 ++ i3bar/include/xcb_atoms.def | 1 - i3bar/src/xcb.c | 25 +++++++++++++++++-------- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/i3bar/include/xcb.h b/i3bar/include/xcb.h index b70d29ef..0276d3c7 100644 --- a/i3bar/include/xcb.h +++ b/i3bar/include/xcb.h @@ -12,6 +12,8 @@ #include //#include "outputs.h" +#define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0 +#define _NET_SYSTEM_TRAY_ORIENTATION_VERT 1 #define SYSTEM_TRAY_REQUEST_DOCK 0 #define SYSTEM_TRAY_BEGIN_MESSAGE 1 #define SYSTEM_TRAY_CANCEL_MESSAGE 2 diff --git a/i3bar/include/xcb_atoms.def b/i3bar/include/xcb_atoms.def index 625fc8a4..0c0b207e 100644 --- a/i3bar/include/xcb_atoms.def +++ b/i3bar/include/xcb_atoms.def @@ -2,7 +2,6 @@ ATOM_DO(_NET_WM_WINDOW_TYPE) ATOM_DO(_NET_WM_WINDOW_TYPE_DOCK) ATOM_DO(_NET_WM_STRUT_PARTIAL) ATOM_DO(I3_SOCKET_PATH) -ATOM_DO(_NET_SYSTEM_TRAY_S0) ATOM_DO(MANAGER) ATOM_DO(_NET_SYSTEM_TRAY_ORIENTATION) ATOM_DO(_NET_SYSTEM_TRAY_VISUAL) diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index 81c00ac4..0cb8bcc8 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -63,6 +63,7 @@ xcb_atom_t atoms[NUM_ATOMS]; /* Variables, that are the same for all functions at all times */ xcb_connection_t *xcb_connection; +int screen; xcb_screen_t *xcb_screen; xcb_window_t xcb_root; xcb_font_t xcb_font; @@ -712,7 +713,7 @@ void xkb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) { */ char *init_xcb(char *fontname) { /* FIXME: xcb_connect leaks Memory */ - xcb_connection = xcb_connect(NULL, NULL); + xcb_connection = xcb_connect(NULL, &screen); if (xcb_connection_has_error(xcb_connection)) { ELOG("Cannot open display\n"); exit(EXIT_FAILURE); @@ -877,7 +878,14 @@ char *init_xcb(char *fontname) { } void init_tray() { -/* tray support: we need a window to own the selection */ + /* request the tray manager atom for the X11 display we are running on */ + 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); + + /* tray support: we need a window to own the selection */ xcb_void_cookie_t selwin_cookie; xcb_window_t selwin = xcb_generate_id(xcb_connection); uint32_t selmask = XCB_CW_OVERRIDE_REDIRECT; @@ -894,8 +902,6 @@ void init_tray() { selmask, selval); -#define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0 -#define _NET_SYSTEM_TRAY_ORIENTATION_VERT 1 uint32_t orientation = _NET_SYSTEM_TRAY_ORIENTATION_HORZ; /* set the atoms */ xcb_change_property(xcb_connection, @@ -907,13 +913,15 @@ void init_tray() { 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); + } xcb_set_selection_owner(xcb_connection, selwin, - /* TODO: request this atom separately */ - atoms[_NET_SYSTEM_TRAY_S0], + tray_reply->atom, XCB_CURRENT_TIME); - /* FIXME: don't use XCB_CURRENT_TIME */ /* TODO: check if we got the selection */ void *event = calloc(32, 1); @@ -923,7 +931,7 @@ void init_tray() { ev->type = atoms[MANAGER]; ev->format = 32; ev->data.data32[0] = XCB_CURRENT_TIME; - ev->data.data32[1] = atoms[_NET_SYSTEM_TRAY_S0]; + ev->data.data32[1] = tray_reply->atom; ev->data.data32[2] = selwin; xcb_send_event(xcb_connection, 0, @@ -931,6 +939,7 @@ void init_tray() { XCB_EVENT_MASK_STRUCTURE_NOTIFY, (char*)ev); free(event); + free(tray_reply); } /* From bd2a3363c03dbea9bd10e754402bcd24a0d0e44d Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 17 Aug 2011 01:12:42 +0200 Subject: [PATCH 08/10] i3bar: tray: little cleanups, more comments --- i3bar/src/xcb.c | 73 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 57 insertions(+), 16 deletions(-) diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index 0cb8bcc8..e35c4355 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -411,7 +411,14 @@ static void configure_trayclients() { } } -void handle_client_message(xcb_client_message_event_t* event) { +/* + * Handles ClientMessages (messages sent from another client directly to us). + * + * At the moment, only the tray window will receive client messages. All + * supported client messages currently are _NET_SYSTEM_TRAY_OPCODE. + * + */ +static void handle_client_message(xcb_client_message_event_t* event) { if (event->type == atoms[_NET_SYSTEM_TRAY_OPCODE] && event->format == 32) { DLOG("_NET_SYSTEM_TRAY_OPCODE received\n"); @@ -474,7 +481,7 @@ void handle_client_message(xcb_client_message_event_t* event) { xcb_reparent_window(xcb_connection, client, output->bar, - output->rect.w - font_height - 2, /* TODO: why -2? */ + output->rect.w - font_height - 2, 2); /* We reconfigure the window to use a reasonable size. The systray * specification explicitly says: @@ -527,7 +534,12 @@ void handle_client_message(xcb_client_message_event_t* event) { } } -void handle_unmap_notify(xcb_unmap_notify_event_t* event) { +/* + * Handles UnmapNotify events. These events happen when a tray window unmaps + * itself. We then update our data structure + * + */ +static void handle_unmap_notify(xcb_unmap_notify_event_t* event) { DLOG("UnmapNotify for window = %08x, event = %08x\n", event->window, event->event); i3_output *walk; @@ -551,6 +563,11 @@ void handle_unmap_notify(xcb_unmap_notify_event_t* event) { } } +/* + * Handle PropertyNotify messages. Currently only the _XEMBED_INFO property is + * handled, which tells us whether a dock client should be mapped or unmapped. + * + */ static void handle_property_notify(xcb_property_notify_event_t *event) { DLOG("PropertyNotify\n"); if (event->atom == atoms[_XEMBED_INFO] && @@ -877,6 +894,12 @@ char *init_xcb(char *fontname) { return path; } +/* + * 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 + * atom. Afterwards, tray clients will send ClientMessages to our window. + * + */ void init_tray() { /* request the tray manager atom for the X11 display we are running on */ char atomname[strlen("_NET_SYSTEM_TRAY_S") + 11]; @@ -886,21 +909,20 @@ void init_tray() { tray_cookie = xcb_intern_atom(xcb_connection, 0, strlen(atomname), atomname); /* tray support: we need a window to own the selection */ - xcb_void_cookie_t selwin_cookie; xcb_window_t selwin = xcb_generate_id(xcb_connection); uint32_t selmask = XCB_CW_OVERRIDE_REDIRECT; uint32_t selval[] = { 1 }; - selwin_cookie = xcb_create_window_checked(xcb_connection, - xcb_screen->root_depth, - selwin, - xcb_root, - -1, -1, - 1, 1, - 1, - XCB_WINDOW_CLASS_INPUT_OUTPUT, - xcb_screen->root_visual, - selmask, - selval); + xcb_create_window(xcb_connection, + xcb_screen->root_depth, + selwin, + xcb_root, + -1, -1, + 1, 1, + 1, + XCB_WINDOW_CLASS_INPUT_OUTPUT, + xcb_screen->root_visual, + selmask, + selval); uint32_t orientation = _NET_SYSTEM_TRAY_ORIENTATION_HORZ; /* set the atoms */ @@ -923,7 +945,26 @@ void init_tray() { tray_reply->atom, XCB_CURRENT_TIME); - /* TODO: check if we got the selection */ + /* Verify that we have the selection */ + xcb_get_selection_owner_cookie_t selcookie; + xcb_get_selection_owner_reply_t *selreply; + + selcookie = xcb_get_selection_owner(xcb_connection, tray_reply->atom); + if (!(selreply = xcb_get_selection_owner_reply(xcb_connection, selcookie, NULL))) { + ELOG("Could not get selection owner for %s\n", atomname); + exit(EXIT_FAILURE); + } + + if (selreply->owner != selwin) { + ELOG("Could not set the %s selection. " \ + "Maybe another tray is already running?\n", atomname); + /* NOTE that this error is not fatal. We just can’t provide tray + * functionality */ + free(selreply); + return; + } + + /* Inform clients waiting for a new _NET_SYSTEM_TRAY that we are here */ void *event = calloc(32, 1); xcb_client_message_event_t *ev = event; ev->response_type = XCB_CLIENT_MESSAGE; From 1c2c22d11756ad7eeae0a628f0bfb2ef749c9bea Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 17 Aug 2011 01:19:38 +0200 Subject: [PATCH 09/10] i3bar: properly end the XEMBED protocol by reparenting the dock clients to root, flush connection before disconnecting --- i3bar/src/xcb.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index e35c4355..00d25272 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -990,15 +990,27 @@ void init_tray() { */ void clean_xcb() { i3_output *o_walk; + trayclient *trayclient; free_workspaces(); SLIST_FOREACH(o_walk, outputs, slist) { + TAILQ_FOREACH(trayclient, o_walk->trayclients, tailq) { + /* Unmap, then reparent (to root) the tray client windows */ + xcb_unmap_window(xcb_connection, trayclient->win); + xcb_reparent_window(xcb_connection, + trayclient->win, + xcb_root, + 0, + 0); + } destroy_window(o_walk); + FREE(o_walk->trayclients); FREE(o_walk->workspaces); FREE(o_walk->name); } FREE_SLIST(outputs, i3_output); FREE(outputs); + xcb_flush(xcb_connection); xcb_disconnect(xcb_connection); ev_check_stop(main_loop, xcb_chk); From 06ba1c0e65a8bbf5b06a14c4bc0045e23f83619d Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 17 Aug 2011 01:32:29 +0200 Subject: [PATCH 10/10] Fix compilation with xcb 0.3.6 --- i3bar/include/xcb.h | 4 ++++ i3bar/include/xcb_atoms.def | 1 - i3bar/src/xcb.c | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/i3bar/include/xcb.h b/i3bar/include/xcb.h index 0276d3c7..c1b7cc14 100644 --- a/i3bar/include/xcb.h +++ b/i3bar/include/xcb.h @@ -12,6 +12,10 @@ #include //#include "outputs.h" +#ifdef XCB_COMPAT +#define XCB_ATOM_CARDINAL CARDINAL +#endif + #define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0 #define _NET_SYSTEM_TRAY_ORIENTATION_VERT 1 #define SYSTEM_TRAY_REQUEST_DOCK 0 diff --git a/i3bar/include/xcb_atoms.def b/i3bar/include/xcb_atoms.def index 0c0b207e..b75ceabd 100644 --- a/i3bar/include/xcb_atoms.def +++ b/i3bar/include/xcb_atoms.def @@ -5,7 +5,6 @@ ATOM_DO(I3_SOCKET_PATH) ATOM_DO(MANAGER) ATOM_DO(_NET_SYSTEM_TRAY_ORIENTATION) ATOM_DO(_NET_SYSTEM_TRAY_VISUAL) -ATOM_DO(CARDINAL) ATOM_DO(_NET_SYSTEM_TRAY_OPCODE) ATOM_DO(_XEMBED_INFO) ATOM_DO(_XEMBED) diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index 00d25272..e25bc959 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -930,7 +930,7 @@ void init_tray() { XCB_PROP_MODE_REPLACE, selwin, atoms[_NET_SYSTEM_TRAY_ORIENTATION], - atoms[CARDINAL], + XCB_ATOM_CARDINAL, 32, 1, &orientation);