From 2004e5dddfa7bdbdf36587dd57a6c9d61db0d8c6 Mon Sep 17 00:00:00 2001 From: speedie Date: Sat, 3 Dec 2022 17:31:43 +0100 Subject: [PATCH] Add systray --- bar.h | 5 +- bar/items.c | 5 +- bar/items.h | 5 +- bar/systray.c | 178 ++++++++++++++++++++++++++++ bar/systray.h | 41 +++++++ docs/example.Xresources | 2 +- docs/example.signal | 2 +- keybinds.h | 5 - options.h | 2 +- signal.h | 3 - speedwm.c | 254 ++++++++++++++++++++++++++++++++++++++-- toggle.h | 2 +- 12 files changed, 476 insertions(+), 28 deletions(-) create mode 100644 bar/systray.c create mode 100644 bar/systray.h diff --git a/bar.h b/bar.h index d1d4cb4..de1efe2 100644 --- a/bar.h +++ b/bar.h @@ -12,8 +12,9 @@ */ static const BarRule barrules[] = { /* monitor bar alignment widthfunc drawfunc clickfunc name */ - { -1, 0, BAR_ALIGN_RIGHT, width_tags, draw_tags, click_tags, "tags" }, + { -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, "tags" }, { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, "layout" }, - { 'A', 0, BAR_ALIGN_LEFT, width_status, draw_status, click_status, "status" }, + //{ 'A', 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_status, "status" }, + { 'A', 0, BAR_ALIGN_RIGHT, width_systray, draw_systray, click_systray, "systray" }, { -1, 0, BAR_ALIGN_NONE, width_wintitle, draw_wintitle, click_wintitle, "wintitle" }, }; diff --git a/bar/items.c b/bar/items.c index 55d2597..304f47b 100644 --- a/bar/items.c +++ b/bar/items.c @@ -1,5 +1,8 @@ -/* Todo: fill this with shit */ +#include "../toggle.h" #include "tags.c" #include "layoutindicator.c" #include "statusbar.c" #include "title.c" +#if USESYSTRAY +#include "systray.c" +#endif diff --git a/bar/items.h b/bar/items.h index 1eb2443..218aee5 100644 --- a/bar/items.h +++ b/bar/items.h @@ -1,5 +1,8 @@ -/* Todo: fill this with shit */ +#include "../toggle.h" #include "tags.h" #include "layoutindicator.h" #include "statusbar.h" #include "title.h" +#if USESYSTRAY +#include "systray.h" +#endif diff --git a/bar/systray.c b/bar/systray.c new file mode 100644 index 0000000..acc8ba2 --- /dev/null +++ b/bar/systray.c @@ -0,0 +1,178 @@ +static Systray *systray = NULL; +static unsigned long systrayorientation = _NET_SYSTEM_TRAY_ORIENTATION_HORZ; + +int +width_systray(Bar *bar, BarWidthArg *a) +{ + unsigned int w = 0; + Client *i; + if (!systray) + return 1; + if (!hidesystray) + for (i = systray->icons; i; w += i->w + systrayspacing, i = i->next); + return w ? w + lrpad - systrayspacing : 0; +} + +int +draw_systray(Bar *bar, BarDrawArg *a) +{ + if (hidesystray) { + if (systray) + XMoveWindow(dpy, systray->win, -500, bar->by); + return a->x; + } + + XSetWindowAttributes wa; + Client *i; + unsigned int w; + + if (!systray) { + /* init systray */ + if (!(systray = (Systray *)calloc(1, sizeof(Systray)))) + die("fatal: could not malloc() %u bytes\n", sizeof(Systray)); + + wa.override_redirect = True; + wa.event_mask = ButtonPressMask|ExposureMask; + wa.border_pixel = 0; + wa.background_pixel = 0; + wa.colormap = cmap; + systray->win = XCreateWindow(dpy, root, bar->bx + a->x + lrpad / 2, bar->by, MAX(a->w + 40, 1), bar->bh, 0, depth, + InputOutput, visual, + CWOverrideRedirect|CWBorderPixel|CWBackPixel|CWColormap|CWEventMask, &wa); // CWBackPixmap + + XSelectInput(dpy, systray->win, SubstructureNotifyMask); + XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32, + PropModeReplace, (unsigned char *)&systrayorientation, 1); + XChangeProperty(dpy, systray->win, netatom[NetSystemTrayVisual], XA_VISUALID, 32, + PropModeReplace, (unsigned char *)&visual->visualid, 1); + XChangeProperty(dpy, systray->win, netatom[NetWMWindowType], XA_ATOM, 32, + PropModeReplace, (unsigned char *)&netatom[NetWMWindowTypeDock], 1); + XMapRaised(dpy, systray->win); + XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime); + if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) { + sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0); + XSync(dpy, False); + } else { + fprintf(stderr, "speedwm: unable to obtain system tray.\n"); + free(systray); + systray = NULL; + return a->x; + } + } + + systray->bar = bar; + + drw_setscheme(drw, scheme[SchemeSystray]); + for (w = 0, i = systray->icons; i; i = i->next) { + wa.background_pixel = 0; + XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa); + XMapRaised(dpy, i->win); + i->x = w; + XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h); + w += i->w; + if (i->next) + w += systrayspacing; + if (i->mon != bar->mon) + i->mon = bar->mon; + } + + XMoveResizeWindow(dpy, systray->win, bar->bx + a->x + lrpad / 2, (w ? bar->by : -bar->by), MAX(w, 1), bar->bh); + return a->x + a->w; +} + +int +click_systray(Bar *bar, Arg *arg, BarClickArg *a) +{ + return -1; +} + +void +removesystrayicon(Client *i) +{ + Client **ii; + + if (hidesystray || !i) + return; + for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next); + if (ii) + *ii = i->next; + free(i); + drawbarwin(systray->bar); +} + +void +resizerequest(XEvent *e) +{ + XResizeRequestEvent *ev = &e->xresizerequest; + Client *i; + + if ((i = wintosystrayicon(ev->window))) { + updatesystrayicongeom(i, ev->width, ev->height); + drawbarwin(systray->bar); + } +} + +void +updatesystrayicongeom(Client *i, int w, int h) +{ + if (i) { + i->h = bh; + if (w == h) + i->w = bh; + else if (h == bh) + i->w = w; + else + i->w = (int) ((float)bh * ((float)w / (float)h)); + applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False); + /* force icons into the systray dimensions if they don't want to */ + if (i->h > bh) { + if (i->w == i->h) + i->w = bh; + else + i->w = (int) ((float)bh * ((float)i->w / (float)i->h)); + i->h = bh; + } + if (i->w > 2*bh) + i->w = bh; + } +} + +void +updatesystrayiconstate(Client *i, XPropertyEvent *ev) +{ + long flags; + int code = 0; + + if (hidesystray || !systray || !i || ev->atom != xatom[XembedInfo] || + !(flags = getatomprop(i, xatom[XembedInfo]))) + return; + + if (flags & XEMBED_MAPPED && !i->tags) { + i->tags = 1; + code = XEMBED_WINDOW_ACTIVATE; + XMapRaised(dpy, i->win); + setclientstate(i, NormalState); + } + else if (!(flags & XEMBED_MAPPED) && i->tags) { + i->tags = 0; + code = XEMBED_WINDOW_DEACTIVATE; + XUnmapWindow(dpy, i->win); + setclientstate(i, WithdrawnState); + } + else + return; + sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0, + systray->win, XEMBED_EMBEDDED_VERSION); +} + +Client * +wintosystrayicon(Window w) +{ + if (!systray) + return NULL; + Client *i = NULL; + if (hidesystray || !w) + return i; + for (i = systray->icons; i && i->win != w; i = i->next); + return i; +} diff --git a/bar/systray.h b/bar/systray.h new file mode 100644 index 0000000..6dd0be6 --- /dev/null +++ b/bar/systray.h @@ -0,0 +1,41 @@ +#define SYSTEM_TRAY_REQUEST_DOCK 0 +#define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0 + +/* XEMBED messages */ +#define XEMBED_EMBEDDED_NOTIFY 0 +#define XEMBED_WINDOW_ACTIVATE 1 +#define XEMBED_FOCUS_IN 4 +#define XEMBED_MODALITY_ON 10 + +#define XEMBED_MAPPED (1 << 0) +#define XEMBED_WINDOW_ACTIVATE 1 +#define XEMBED_WINDOW_DEACTIVATE 2 + +#define VERSION_MAJOR 0 +#define VERSION_MINOR 0 +#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR + +/* enums */ +enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */ + +typedef struct Systray Systray; +struct Systray { + Window win; + Client *icons; + Bar *bar; +}; + +/* bar integration */ +static int width_systray(Bar *bar, BarWidthArg *a); +static int draw_systray(Bar *bar, BarDrawArg *a); +static int click_systray(Bar *bar, Arg *arg, BarClickArg *a); + +/* function declarations */ +static Atom getatomprop(Client *c, Atom prop); +static void removesystrayicon(Client *i); +static void resizerequest(XEvent *e); +static void updatesystrayicongeom(Client *i, int w, int h); +static void updatesystrayiconstate(Client *i, XPropertyEvent *ev); +static void togglesystray(); +static Client *wintosystrayicon(Window w); + diff --git a/docs/example.Xresources b/docs/example.Xresources index c0a4508..b794520 100644 --- a/docs/example.Xresources +++ b/docs/example.Xresources @@ -22,7 +22,7 @@ speedwm.bar.hide.floating: 0 ! Hide the floating window indicator (0/ speedwm.bar.hide.layout: 0 ! Hide the layout indicator (0/1) speedwm.bar.hide.sticky: 0 ! Hide the sticky indicator (0/1) speedwm.bar.hide.status: 0 ! Hide the status bar (0/1) -speedwm.bar.hide.systray: 1 ! Hide the systray (0/1) +speedwm.bar.hide.systray: 0 ! Hide the systray (0/1) speedwm.bar.hide.title: 0 ! Hide the window title (0/1) speedwm.bar.hide.unseltitle: 0 ! Hide the unselected window title (0/1) speedwm.bar.hide.icon: 0 ! Hide the window icon (0/1) diff --git a/docs/example.signal b/docs/example.signal index e37e68f..55ce954 100644 --- a/docs/example.signal +++ b/docs/example.signal @@ -58,7 +58,7 @@ - 58 - Remove the scratchpad - 59 - Reset layout/mfact - 60 - Reset mastercount -- 61 - Show/Hide systray +- 61 - Unused - 62 - Hide all windows - 63 - Show all windows - 64 - Reset mfact diff --git a/keybinds.h b/keybinds.h index 4d5254c..7e75196 100644 --- a/keybinds.h +++ b/keybinds.h @@ -78,11 +78,6 @@ static Key keys[] = { { MODIFIER1|CONTROL|SHIFT, -1, XK_n, spawn, cmd( "speedwm-netctrl" ) }, { MODIFIER1|CONTROL|SHIFT, -1, XK_b, spawn, cmd( "speedwm-btctrl" ) }, { MODIFIER1|CONTROL|SHIFT, -1, XK_r, spawn, cmd( "libspeedwm --perform core_wm_restart; speedwm -s 'Loading'" ) }, - - /* System tray */ - #if USESYSTRAY - { MODIFIER1, -1, XK_s, togglesystray, {0} }, - #endif /* Switcher */ #if USESWITCHER diff --git a/options.h b/options.h index d6795c9..424320c 100644 --- a/options.h +++ b/options.h @@ -143,7 +143,7 @@ static int hideunselectedtitle = 0; /* Hide unselected title (1) static int hidestatus = 0; /* Hide status bar (1) or show (0) */ static int hideicon = 0; /* Hide icon (1) or show (0) */ static int hidetags = 0; /* Hide status bar (1) or show (0) */ -static int hidesystray = 1; /* Hide systray by default (1) or show (0) */ +static int hidesystray = 0; /* Hide systray by default (1) or show (0) */ static int hideemptytags = 1; /* Hide empty tags (1) or show (0) */ static int hidetagpowerline = 0; /* Hide tag powerline (1) or show (0) */ static int hidetitlepowerline = 0; /* Hide title powerline (1) or show (0) */ diff --git a/signal.h b/signal.h index 3de5d9a..b4bcc9b 100644 --- a/signal.h +++ b/signal.h @@ -67,9 +67,6 @@ static Signal signals[] = { { 58, scratchpad_remove, {0} }, { 59, reset_layout, {0} }, { 60, resetmastercount, {0} }, -#if USESYSTRAY - { 61, togglesystray, {0} }, -#endif { 62, hideall, {0} }, { 63, showall, {0} }, { 64, reset_mfact, {0} }, diff --git a/speedwm.c b/speedwm.c index 3a7cc46..881fd99 100644 --- a/speedwm.c +++ b/speedwm.c @@ -104,6 +104,10 @@ enum { NetSupported, NetWMName, NetWMWindowTypeDialog, NetClientList, NetDesktopNames, NetDesktopViewport, NetNumberOfDesktops, NetCurrentDesktop, #if USEFADE NetWMWindowsOpacity, +#endif +#if USESYSTRAY + NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, + NetSystemTrayVisual, NetWMWindowTypeDock, NetSystemTrayOrientationHorz, #endif NetClientListStacking, NetClientInfo, NetLast }; /* EWMH atoms */ @@ -460,10 +464,13 @@ static Atom getatomprop(Client *c, Atom prop); static int getrootptr(int *x, int *y); static long getstate(Window w); static unsigned int getsystraywidth(); -//static void resizebarwin(Monitor *m); -/* systray */ +#if USESYSTRAY +static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4); +#else static int sendevent(Client *c, Atom proto); +#endif + static int gettextprop(Window w, Atom atom, char *text, unsigned int size); static void grabbuttons(Client *c, int focused); @@ -781,9 +788,17 @@ static void (*handler[LASTEvent]) (XEvent *) = { [MapRequest] = maprequest, [MotionNotify] = motionnotify, [PropertyNotify] = propertynotify, + #if USESYSTRAY + [ResizeRequest] = resizerequest, + #endif [UnmapNotify] = unmapnotify }; +#if USESYSTRAY +static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast], motifatom; +#else static Atom wmatom[WMLast], netatom[NetLast], motifatom; +#endif + #if USEIPC static int epoll_fd; static int dpy_fd; @@ -1409,6 +1424,17 @@ cleanup(void) XUngrabKey(dpy, AnyKey, AnyModifier, root); while (mons) cleanupmon(mons); + #if USESYSTRAY + if (!hidesystray && systray) { + while (systray->icons) + removesystrayicon(systray->icons); + if (systray->win) { + XUnmapWindow(dpy, systray->win); + XDestroyWindow(dpy, systray->win); + } + free(systray); + } + #endif for (i = 0; i < CurLast; i++) drw_cur_free(drw, cursor[i]); @@ -1454,9 +1480,50 @@ clientmessage(XEvent *e) { XClientMessageEvent *cme = &e->xclient; Client *c = wintoclient(cme->window); - unsigned int i; + #if USESYSTRAY + XWindowAttributes wa; + XSetWindowAttributes swa; + + if (!hidesystray && systray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) { + /* add systray icons */ + if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) { + if (!(c = (Client *)calloc(1, sizeof(Client)))) + die("fatal: could not malloc() %u bytes\n", sizeof(Client)); + if (!(c->win = cme->data.l[2])) { + free(c); + return; + } + + c->mon = selmon; + c->next = systray->icons; + systray->icons = c; + XGetWindowAttributes(dpy, c->win, &wa); + c->x = c->oldx = c->y = c->oldy = 0; + c->w = c->oldw = wa.width; + c->h = c->oldh = wa.height; + c->oldbw = wa.border_width; + c->bw = 0; + c->isfloating = True; + /* reuse tags field as mapped status */ + c->tags = 1; + updatesizehints(c); + updatesystrayicongeom(c, wa.width, wa.height); + XAddToSaveSet(dpy, c->win); + XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask); + XReparentWindow(dpy, c->win, systray->win, 0, 0); + /* use parents background color */ + swa.background_pixel = scheme[SchemeSystray][ColBg].pixel; /* might be a good idea to change this to SchemeBar */ + XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa); + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION); + XSync(dpy, False); + setclientstate(c, NormalState); + } + return; + } + #endif + if (!c) return; if (cme->message_type == netatom[NetWMState]) { @@ -1779,11 +1846,18 @@ destroynotify(XEvent *e) Monitor *m; XDestroyWindowEvent *ev = &e->xdestroywindow; - if ((c = wintoclient(ev->window))) + if ((c = wintoclient(ev->window))) { unmanage(c, 1); + + #if USESYSTRAY + } else if (!hidesystray && (c = wintosystrayicon(ev->window))) { + removesystrayicon(c); + drawbarwin(systray->bar); + #endif - else if ((c = swallowingclient(ev->window))) + } else if ((c = swallowingclient(ev->window))) { unmanage(c->swallowing, 1); + } } /* run ltmenu */ @@ -2500,10 +2574,25 @@ getatomprop(Client *c, Atom prop) unsigned char *p = NULL; Atom da, atom = None; + #if USESYSTRAY + Atom req = XA_ATOM; + if (prop == xatom[XembedInfo]) + req = xatom[XembedInfo]; + #endif + + #if USESYSTRAY + if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req, + #else if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, + #endif &da, &di, &dl, &dl, &p) == Success && p) { atom = *(Atom *)p; + #if USESYSTRAY + if (da == xatom[XembedInfo] && dl == 2) + atom = ((Atom *)p)[1]; + #endif + XFree(p); } return atom; @@ -2642,8 +2731,6 @@ hidewin(Client *c) { XSelectInput(dpy, root, ra.your_event_mask); XSelectInput(dpy, w, ca.your_event_mask); XUngrabServer(dpy); - - c->ishidden = 1; } #if USEIPC @@ -2900,8 +2987,6 @@ showwin(Client *c) if (!c || !HIDDEN(c)) return; - c->ishidden = 0; - XMapWindow(dpy, c->win); setclientstate(c, NormalState); arrange(c->mon); @@ -3166,7 +3251,11 @@ killclient(const Arg *arg) { if (!selmon->sel || selmon->sel->ispermanent) return; + #if USESYSTRAY + if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0, 0, 0)) { + #else if (!sendevent(selmon->sel, wmatom[WMDelete])) { + #endif XGrabServer(dpy); XSetErrorHandler(xerrordummy); XSetCloseDownMode(dpy, DestroyAll); @@ -3187,7 +3276,11 @@ killunsel(const Arg *arg) for (i = selmon->clients; i; i = i->next) { if (ISVISIBLE(i) && i != selmon->sel) { + #if USESYSTRAY + if (!sendevent(i->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0, 0, 0)) { + #else if (!sendevent(selmon->sel, wmatom[WMDelete])) { + #endif XGrabServer(dpy); XSetErrorHandler(xerrordummy); XSetCloseDownMode(dpy, DestroyAll); @@ -3388,6 +3481,14 @@ maprequest(XEvent *e) static XWindowAttributes wa; XMapRequestEvent *ev = &e->xmaprequest; + #if USESYSTRAY + Client *i; + + if (!hidesystray && systray && (i = wintosystrayicon(ev->window))) { + sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION); + drawbarwin(systray->bar); + } + #endif if (!XGetWindowAttributes(dpy, ev->window, &wa) || wa.override_redirect) return; @@ -3440,8 +3541,8 @@ window_map(Display *dpy, Client *c, int deiconify) Window win = c->win; /* fix: hidden windows immediately get mapped */ - if (c->ishidden) - return; + //if (c->ishidden) + // return; if (deiconify) window_set_state(dpy, win, NormalState); @@ -4232,6 +4333,18 @@ propertynotify(XEvent *e) Window trans; XPropertyEvent *ev = &e->xproperty; + #if USESYSTRAY + if (!hidesystray && (c = wintosystrayicon(ev->window))) { + if (ev->atom == XA_WM_NORMAL_HINTS) { + updatesizehints(c); + updatesystrayicongeom(c, c->w, c->h); + } + else + updatesystrayiconstate(c, ev); + drawbarwin(systray->bar); + } + #endif + if ((ev->window == root) && (ev->atom == XA_WM_NAME)) { if (!getsignal()) updatestatus(); @@ -4798,27 +4911,121 @@ setdesktopnames(void) XSetTextProperty(dpy, root, &text, netatom[NetDesktopNames]); } +/* int +#if USESYSTRAY +sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4) +#else sendevent(Client *c, Atom proto) +#endif { int n; Atom *protocols; int exists = 0; XEvent ev; + #if USESYSTRAY + Atom mt; + #endif + + #if USESYSTRAY + if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) { + mt = wmatom[WMProtocols]; + if (XGetWMProtocols(dpy, w, &protocols, &n)) { + while (!exists && n--) + exists = protocols[n] == proto; + XFree(protocols); + } + } else { + exists = True; + mt = proto; + #else if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { while (!exists && n--) exists = protocols[n] == proto; XFree(protocols); } + #endif if (exists) { - ev.type = ClientMessage; + ev.type = ClientMessage; ev.xclient.format = 32; + #if USESYSTRAY + ev.xclient.window = w; + ev.xclient.message_type = mt; + ev.xclient.data.l[0] = d0; + ev.xclient.data.l[1] = d1; + ev.xclient.data.l[2] = d2; + ev.xclient.data.l[3] = d3; + ev.xclient.data.l[4] = d4; + XSendEvent(dpy, w, False, mask, &ev); + #else ev.xclient.window = c->win; ev.xclient.message_type = wmatom[WMProtocols]; ev.xclient.data.l[0] = proto; ev.xclient.data.l[1] = CurrentTime; XSendEvent(dpy, c->win, False, NoEventMask, &ev); + #endif + } + return exists; + } +} +*/ + +int +#if USESYSTRAY +sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4) +#else +sendevent(Client *c, Atom proto) +#endif +{ + int n; +#if USESYSTRAY + Atom *protocols, mt; +#else + Atom *protocols; +#endif + int exists = 0; + XEvent ev; + +#if USESYSTRAY + if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) { + mt = wmatom[WMProtocols]; +#endif + +#if USESYSTRAY + if (XGetWMProtocols(dpy, w, &protocols, &n)) { +#else + if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { +#endif + while (!exists && n--) + exists = protocols[n] == proto; + XFree(protocols); + } +#if USESYSTRAY + } else { + exists = True; + mt = proto; + } +#endif + if (exists) { + ev.type = ClientMessage; + ev.xclient.format = 32; +#if USESYSTRAY + ev.xclient.window = w; + ev.xclient.message_type = mt; + ev.xclient.data.l[0] = d0; + ev.xclient.data.l[1] = d1; + ev.xclient.data.l[2] = d2; + ev.xclient.data.l[3] = d3; + ev.xclient.data.l[4] = d4; + XSendEvent(dpy, w, False, mask, &ev); +#else + ev.xclient.window = c->win; + ev.xclient.message_type = wmatom[WMProtocols]; + ev.xclient.data.l[0] = proto; + ev.xclient.data.l[1] = CurrentTime; + XSendEvent(dpy, c->win, False, NoEventMask, &ev); +#endif } return exists; } @@ -4842,7 +5049,11 @@ setfocus(Client *c) XA_WINDOW, 32, PropModeReplace, (unsigned char *) &(c->win), 1); } + #if USESYSTRAY + sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0); + #else sendevent(c, wmatom[WMTakeFocus]); + #endif } void @@ -5023,6 +5234,18 @@ setup(void) netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); + #if USESYSTRAY + netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False); + netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False); + netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False); + netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False); + netatom[NetSystemTrayVisual] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_VISUAL", False); + netatom[NetWMWindowTypeDock] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False); + xatom[Manager] = XInternAtom(dpy, "MANAGER", False); + xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False); + xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False); + #endif + if (fullscreenhidebar) netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); @@ -5683,6 +5906,13 @@ unmapnotify(XEvent *e) setclientstate(c, WithdrawnState); else unmanage(c, 0); + #if USESYSTRAY + } else if (!hidesystray && (c = wintosystrayicon(ev->window))) { + /* KLUDGE! sometimes icons occasionally unmap their windows, but do + * _not_ destroy them. We map those windows back */ + XMapRaised(dpy, c->win); + drawbarwin(systray->bar); + #endif } } diff --git a/toggle.h b/toggle.h index 66f4ebb..4202760 100644 --- a/toggle.h +++ b/toggle.h @@ -19,7 +19,7 @@ Not compatible with BSDs so for those, set this to 0. */ /* Miscellanious */ #define USESWITCHER 1 /* Whether or not to include the switcher */ -#define USESYSTRAY 0 /* Whether or not to include the systray */ +#define USESYSTRAY 1 /* Whether or not to include the systray */ #define USEROUNDCORNERS 1 /* Whether or not to include rounded corners */ #define USEMEDIA 1 /* Whether or not to include media keys */ #define USEMOUSE 1 /* Whether or not to include mouse binds */