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