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 ;).
This commit is contained in:
parent
642a745004
commit
025dd68f62
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
111
i3bar/src/xcb.c
111
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) {
|
||||
|
Loading…
Reference in New Issue
Block a user