Merge branch 'i3bar-systray' into next
This commit is contained in:
commit
1f028a72b8
|
@ -9,8 +9,9 @@
|
||||||
#ifndef COMMON_H_
|
#ifndef COMMON_H_
|
||||||
#define COMMON_H_
|
#define COMMON_H_
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
typedef struct rect_t rect;
|
typedef struct rect_t rect;
|
||||||
typedef int bool;
|
|
||||||
|
|
||||||
struct ev_loop* main_loop;
|
struct ev_loop* main_loop;
|
||||||
char *statusline;
|
char *statusline;
|
||||||
|
@ -29,6 +30,7 @@ struct rect_t {
|
||||||
#include "outputs.h"
|
#include "outputs.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "workspaces.h"
|
#include "workspaces.h"
|
||||||
|
#include "trayclients.h"
|
||||||
#include "xcb.h"
|
#include "xcb.h"
|
||||||
#include "ucs2_to_utf8.h"
|
#include "ucs2_to_utf8.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
|
@ -47,6 +47,7 @@ struct i3_output {
|
||||||
xcb_gcontext_t bargc; /* The graphical context of the bar */
|
xcb_gcontext_t bargc; /* The graphical context of the bar */
|
||||||
|
|
||||||
struct ws_head *workspaces; /* The workspaces on this output */
|
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 */
|
SLIST_ENTRY(i3_output) slist; /* Pointer for the SLIST-Macro */
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* 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 */
|
||||||
|
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 */
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -12,6 +12,18 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
//#include "outputs.h"
|
//#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
|
||||||
|
#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 {
|
struct xcb_color_strings_t {
|
||||||
char *bar_fg;
|
char *bar_fg;
|
||||||
char *bar_bg;
|
char *bar_bg;
|
||||||
|
|
|
@ -2,4 +2,10 @@ ATOM_DO(_NET_WM_WINDOW_TYPE)
|
||||||
ATOM_DO(_NET_WM_WINDOW_TYPE_DOCK)
|
ATOM_DO(_NET_WM_WINDOW_TYPE_DOCK)
|
||||||
ATOM_DO(_NET_WM_STRUT_PARTIAL)
|
ATOM_DO(_NET_WM_STRUT_PARTIAL)
|
||||||
ATOM_DO(I3_SOCKET_PATH)
|
ATOM_DO(I3_SOCKET_PATH)
|
||||||
|
ATOM_DO(MANAGER)
|
||||||
|
ATOM_DO(_NET_SYSTEM_TRAY_ORIENTATION)
|
||||||
|
ATOM_DO(_NET_SYSTEM_TRAY_VISUAL)
|
||||||
|
ATOM_DO(_NET_SYSTEM_TRAY_OPCODE)
|
||||||
|
ATOM_DO(_XEMBED_INFO)
|
||||||
|
ATOM_DO(_XEMBED)
|
||||||
#undef ATOM_DO
|
#undef ATOM_DO
|
||||||
|
|
|
@ -43,7 +43,7 @@ static int outputs_null_cb(void *params_) {
|
||||||
* Parse a boolean value (active)
|
* 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_;
|
struct outputs_json_params *params = (struct outputs_json_params*) params_;
|
||||||
|
|
||||||
if (strcmp(params->cur_key, "active")) {
|
if (strcmp(params->cur_key, "active")) {
|
||||||
|
@ -161,6 +161,9 @@ static int outputs_start_map_cb(void *params_) {
|
||||||
new_output->workspaces = malloc(sizeof(struct ws_head));
|
new_output->workspaces = malloc(sizeof(struct ws_head));
|
||||||
TAILQ_INIT(new_output->workspaces);
|
TAILQ_INIT(new_output->workspaces);
|
||||||
|
|
||||||
|
new_output->trayclients = malloc(sizeof(struct tc_head));
|
||||||
|
TAILQ_INIT(new_output->trayclients);
|
||||||
|
|
||||||
params->outputs_walk = new_output;
|
params->outputs_walk = new_output;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
@ -29,7 +29,7 @@ struct workspaces_json_params {
|
||||||
* Parse a boolean value (visible, focused, urgent)
|
* 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_;
|
struct workspaces_json_params *params = (struct workspaces_json_params*) params_;
|
||||||
|
|
||||||
if (!strcmp(params->cur_key, "visible")) {
|
if (!strcmp(params->cur_key, "visible")) {
|
||||||
|
|
383
i3bar/src/xcb.c
383
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 */
|
/* Variables, that are the same for all functions at all times */
|
||||||
xcb_connection_t *xcb_connection;
|
xcb_connection_t *xcb_connection;
|
||||||
|
int screen;
|
||||||
xcb_screen_t *xcb_screen;
|
xcb_screen_t *xcb_screen;
|
||||||
xcb_window_t xcb_root;
|
xcb_window_t xcb_root;
|
||||||
xcb_font_t xcb_font;
|
xcb_font_t xcb_font;
|
||||||
|
@ -383,6 +384,252 @@ void handle_button(xcb_button_press_event_t *event) {
|
||||||
i3_send_msg(I3_IPC_MESSAGE_TYPE_COMMAND, buffer);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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");
|
||||||
|
/* event->data.data32[0] is the timestamp */
|
||||||
|
uint32_t op = event->data.data32[1];
|
||||||
|
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;
|
||||||
|
int xe_version = 1;
|
||||||
|
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);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
DLOG("X window %08x requested docking\n", client);
|
||||||
|
i3_output *walk, *output;
|
||||||
|
SLIST_FOREACH(walk, outputs, slist) {
|
||||||
|
if (!walk->active)
|
||||||
|
continue;
|
||||||
|
DLOG("using output %s\n", walk->name);
|
||||||
|
output = walk;
|
||||||
|
}
|
||||||
|
xcb_reparent_window(xcb_connection,
|
||||||
|
client,
|
||||||
|
output->bar,
|
||||||
|
output->rect.w - font_height - 2,
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
} else {
|
||||||
|
DLOG("Not mapping dock client yet\n");
|
||||||
|
}
|
||||||
|
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
|
||||||
|
* position */
|
||||||
|
configure_trayclients();
|
||||||
|
draw_bars();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
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 */
|
||||||
|
configure_trayclients();
|
||||||
|
draw_bars();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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] &&
|
||||||
|
event->state == XCB_PROPERTY_NEW_VALUE) {
|
||||||
|
DLOG("xembed_info updated\n");
|
||||||
|
trayclient *trayclient = NULL, *walk;
|
||||||
|
i3_output *o_walk;
|
||||||
|
SLIST_FOREACH(o_walk, outputs, slist) {
|
||||||
|
if (!o_walk->active)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
TAILQ_FOREACH(walk, o_walk->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
|
* This function is called immediately before the main loop locks. We flush xcb
|
||||||
* then (and only then)
|
* then (and only then)
|
||||||
|
@ -413,6 +660,19 @@ 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 */
|
/* Button-press-events are mouse-buttons clicked on one of our bars */
|
||||||
handle_button((xcb_button_press_event_t*) event);
|
handle_button((xcb_button_press_event_t*) event);
|
||||||
break;
|
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;
|
||||||
|
case XCB_UNMAP_NOTIFY:
|
||||||
|
/* 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);
|
FREE(event);
|
||||||
}
|
}
|
||||||
|
@ -470,7 +730,7 @@ void xkb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
|
||||||
*/
|
*/
|
||||||
char *init_xcb(char *fontname) {
|
char *init_xcb(char *fontname) {
|
||||||
/* FIXME: xcb_connect leaks Memory */
|
/* FIXME: xcb_connect leaks Memory */
|
||||||
xcb_connection = xcb_connect(NULL, NULL);
|
xcb_connection = xcb_connect(NULL, &screen);
|
||||||
if (xcb_connection_has_error(xcb_connection)) {
|
if (xcb_connection_has_error(xcb_connection)) {
|
||||||
ELOG("Cannot open display\n");
|
ELOG("Cannot open display\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
|
@ -634,6 +894,95 @@ char *init_xcb(char *fontname) {
|
||||||
return path;
|
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];
|
||||||
|
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_window_t selwin = xcb_generate_id(xcb_connection);
|
||||||
|
uint32_t selmask = XCB_CW_OVERRIDE_REDIRECT;
|
||||||
|
uint32_t selval[] = { 1 };
|
||||||
|
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 */
|
||||||
|
xcb_change_property(xcb_connection,
|
||||||
|
XCB_PROP_MODE_REPLACE,
|
||||||
|
selwin,
|
||||||
|
atoms[_NET_SYSTEM_TRAY_ORIENTATION],
|
||||||
|
XCB_ATOM_CARDINAL,
|
||||||
|
32,
|
||||||
|
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,
|
||||||
|
tray_reply->atom,
|
||||||
|
XCB_CURRENT_TIME);
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
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,
|
||||||
|
XCB_EVENT_MASK_STRUCTURE_NOTIFY,
|
||||||
|
(char*)ev);
|
||||||
|
free(event);
|
||||||
|
free(tray_reply);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Cleanup the xcb-stuff.
|
* Cleanup the xcb-stuff.
|
||||||
* Called once, before the program terminates.
|
* Called once, before the program terminates.
|
||||||
|
@ -641,15 +990,27 @@ char *init_xcb(char *fontname) {
|
||||||
*/
|
*/
|
||||||
void clean_xcb() {
|
void clean_xcb() {
|
||||||
i3_output *o_walk;
|
i3_output *o_walk;
|
||||||
|
trayclient *trayclient;
|
||||||
free_workspaces();
|
free_workspaces();
|
||||||
SLIST_FOREACH(o_walk, outputs, slist) {
|
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);
|
destroy_window(o_walk);
|
||||||
|
FREE(o_walk->trayclients);
|
||||||
FREE(o_walk->workspaces);
|
FREE(o_walk->workspaces);
|
||||||
FREE(o_walk->name);
|
FREE(o_walk->name);
|
||||||
}
|
}
|
||||||
FREE_SLIST(outputs, i3_output);
|
FREE_SLIST(outputs, i3_output);
|
||||||
FREE(outputs);
|
FREE(outputs);
|
||||||
|
|
||||||
|
xcb_flush(xcb_connection);
|
||||||
xcb_disconnect(xcb_connection);
|
xcb_disconnect(xcb_connection);
|
||||||
|
|
||||||
ev_check_stop(main_loop, xcb_chk);
|
ev_check_stop(main_loop, xcb_chk);
|
||||||
|
@ -757,6 +1118,9 @@ void reconfig_windows() {
|
||||||
if (walk->bar == XCB_NONE) {
|
if (walk->bar == XCB_NONE) {
|
||||||
DLOG("Creating Window for output %s\n", walk->name);
|
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->bar = xcb_generate_id(xcb_connection);
|
||||||
walk->buffer = 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;
|
mask = XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK;
|
||||||
|
@ -946,13 +1310,26 @@ void draw_bars() {
|
||||||
/* Luckily we already prepared a seperate pixmap containing the rendered
|
/* Luckily we already prepared a seperate pixmap containing the rendered
|
||||||
* statusline, we just have to copy the relevant parts to the relevant
|
* statusline, we just have to copy the relevant parts to the relevant
|
||||||
* position */
|
* position */
|
||||||
|
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. */
|
||||||
|
traypx += font_height;
|
||||||
|
}
|
||||||
|
/* Add 2px of padding if there are any tray icons */
|
||||||
|
if (traypx > 0)
|
||||||
|
traypx += 2;
|
||||||
xcb_copy_area(xcb_connection,
|
xcb_copy_area(xcb_connection,
|
||||||
statusline_pm,
|
statusline_pm,
|
||||||
outputs_walk->buffer,
|
outputs_walk->buffer,
|
||||||
outputs_walk->bargc,
|
outputs_walk->bargc,
|
||||||
MAX(0, (int16_t)(statusline_width - outputs_walk->rect.w + 4)), 0,
|
MAX(0, (int16_t)(statusline_width - outputs_walk->rect.w + 4)), 0,
|
||||||
MAX(0, (int16_t)(outputs_walk->rect.w - statusline_width - 4)), 3,
|
MAX(0, (int16_t)(outputs_walk->rect.w - statusline_width - traypx - 4)), 3,
|
||||||
MIN(outputs_walk->rect.w - 4, statusline_width), font_height);
|
MIN(outputs_walk->rect.w - traypx - 4, statusline_width), font_height);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.disable_ws) {
|
if (config.disable_ws) {
|
||||||
|
|
Loading…
Reference in New Issue