dwm/patch/systray.c
bakkeby f58c699dbb Fix for dwm randomly crashing when the first systray application starts
The crash error was:

dwm: fatal error: request code=12, error code=11
X Error of failed request:  BadAlloc (insufficient resources for operation)
  Major opcode of failed request:  12 (X_ConfigureWindow)
  Serial number of failed request:  3333
  Current serial number in output stream:  3338

This was backtracked to function updatesystray where XConfigureWindow was
called with large negative X positions (e.g. -836585). The large number
coming from the width of the systray icon.

w += i->w;

The systray icon width is set through the updatesystrayicongeom function
and this is called by resizerequest, clientmessage and propertynotify.

The height and width would appear to come from the X window attributes and
sometimes have random values (e.g. height 32708 and width 1369918222).

updatesystrayicongeom restricts the height of the icon to that of the bar,
but did not have any restictions on the icon width. This fix limits the
icon width to that of the bar height if it is greater than twice the bar
height, leaving room for systray icons that are slightly wider than they
are tall.
2020-07-05 13:20:28 +02:00

206 lines
5.4 KiB
C

static Systray *systray = NULL;
static unsigned long systrayorientation = _NET_SYSTEM_TRAY_ORIENTATION_HORZ;
unsigned int
getsystraywidth()
{
unsigned int w = 0;
Client *i;
if (showsystray)
for (i = systray->icons; i; w += i->w + systrayspacing, i = i->next);
return w ? w + systrayspacing : 0;
}
void
removesystrayicon(Client *i)
{
Client **ii;
if (!showsystray || !i)
return;
for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next);
if (ii)
*ii = i->next;
free(i);
}
void
resizerequest(XEvent *e)
{
XResizeRequestEvent *ev = &e->xresizerequest;
Client *i;
if ((i = wintosystrayicon(ev->window))) {
updatesystrayicongeom(i, ev->width, ev->height);
updatesystray();
}
}
Monitor *
systraytomon(Monitor *m)
{
Monitor *t;
int i, n;
if (!systraypinning) {
if (!m)
return selmon;
return m == selmon ? m : NULL;
}
for (n = 1, t = mons; t && t->next; n++, t = t->next);
for (i = 1, t = mons; t && t->next && i < systraypinning; i++, t = t->next);
if (systraypinningfailfirst && n < systraypinning)
return mons;
return t;
}
void
updatesystray(void)
{
XSetWindowAttributes wa;
XWindowChanges wc;
Client *i;
Monitor *m = systraytomon(NULL);
unsigned int x = m->mx + m->mw;
unsigned int w = 1, xpad = 0, ypad = 0;
#if BARPADDING_PATCH
xpad = sp;
ypad = vp;
#endif // BARPADDING_PATCH
if (!showsystray)
return;
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.background_pixel = scheme[SchemeNorm][ColBg].pixel;
wa.border_pixel = 0;
#if ALPHA_PATCH
wa.colormap = cmap;
systray->win = XCreateWindow(dpy, root, x - xpad, m->by + ypad, w, bh, 0, depth,
InputOutput, visual,
CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wa);
#else
systray->win = XCreateSimpleWindow(dpy, root, x - xpad, m->by + ypad, w, bh, 0, 0, scheme[SchemeNorm][ColBg].pixel);
XChangeWindowAttributes(dpy, systray->win, CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWEventMask, &wa);
#endif // ALPHA_PATCH
XSelectInput(dpy, systray->win, SubstructureNotifyMask);
XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32,
PropModeReplace, (unsigned char *)&systrayorientation, 1);
#if ALPHA_PATCH
XChangeProperty(dpy, systray->win, netatom[NetSystemTrayVisual], XA_VISUALID, 32,
PropModeReplace, (unsigned char *)&visual->visualid, 1);
#endif // ALPHA_PATCH
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, "dwm: unable to obtain system tray.\n");
free(systray);
systray = NULL;
return;
}
}
drw_setscheme(drw, scheme[SchemeNorm]);
for (w = 0, i = systray->icons; i; i = i->next) {
/* make sure the background color stays the same */
wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa);
XMapRaised(dpy, i->win);
w += systrayspacing;
i->x = w;
XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h);
w += i->w;
if (i->mon != m)
i->mon = m;
}
w = w ? w + systrayspacing : 1;
x -= w;
XMoveResizeWindow(dpy, systray->win, x - xpad, m->by + ypad, w, bh);
wc.x = x - xpad;
wc.y = m->by + ypad;
wc.width = w;
wc.height = bh;
wc.stack_mode = Above; wc.sibling = m->barwin;
XConfigureWindow(dpy, systray->win, CWX|CWY|CWWidth|CWHeight|CWSibling|CWStackMode, &wc);
XMapWindow(dpy, systray->win);
XMapSubwindows(dpy, systray->win);
/* redraw background */
XSetForeground(dpy, drw->gc, scheme[SchemeNorm][ColBg].pixel);
XFillRectangle(dpy, systray->win, drw->gc, 0, 0, w, bh);
XSync(dpy, False);
}
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 (!showsystray || !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)
{
Client *i = NULL;
if (!showsystray || !w)
return i;
for (i = systray->icons; i && i->win != w; i = i->next);
return i;
}