From 025dd68f628e35b865db9eb734e72cd43e980317 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Fri, 12 Aug 2011 19:14:27 +0200 Subject: [PATCH] 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) {