11 Commits

Author SHA1 Message Date
Pijus Kamandulis 7db835c560 Apply customizations 2025-09-28 19:27:57 +03:00
Pijus Kamandulis 82bb5a18b3 Apply uselessgap patch 2025-09-28 19:17:45 +03:00
Pijus Kamandulis 4cb3175832 Apply systray patch 2025-09-28 19:02:56 +03:00
Hiltjo Posthuma 693d94d350 bump version to 6.6 2025-08-09 14:34:03 +02:00
Raymond Cole cfb8627a80 Avoid unsigned integer underflow in drw_text() 2024-10-30 13:02:17 +01:00
Hiltjo Posthuma fcb2476b69 util.c: output function might override errno and thus affect perror()
Original patch by Raymond Cole with some modifications, thanks!
2024-10-27 20:10:07 +01:00
Hiltjo Posthuma 8933ebcf50 sync drw.{c,h} from dmenu
- drw: minor improvement to the nomatches cache
- overhaul utf8decoding and render invalid utf8 sequences as U+FFFD.

Thanks NRK for these improvements!
2024-10-05 13:06:08 +02:00
Pontus Stenetorp 5687f46964 Add missing void to updateclientlist definition
Caught by -pedantic implying -Wstrict-prototypes for OpenBSD's 16.0.6 Clang.
2024-06-08 18:21:00 +02:00
Hiltjo Posthuma 061e9fe9a7 bump version to 6.5 2024-03-19 12:13:16 +01:00
Hiltjo Posthuma 9f8855343c Makefile: remove the options target
The Makefile used to suppress output (by using @), so this target made sense at
the time.

But the Makefile should be simple and make debugging with less abstractions or
fancy printing.  The Makefile was made verbose and doesn't hide the build
output, so remove this target.

Prompted by a question on the mailing list about the options target.
2023-09-22 15:13:29 +02:00
Hiltjo Posthuma e81f17d4c1 restore SIGCHLD sighandler to default before spawning a program
From sigaction(2):
A child created via fork(2) inherits a copy of its parent's signal dispositions.
During an execve(2), the dispositions of handled signals are reset to the default;
the dispositions of ignored signals are left unchanged.

This refused to start directly some programs from configuring in config.h:

static Key keys[] = {
	MODKEY,                       XK_o,      spawn,          {.v = cmd } },
};

Some reported programs that didn't start were: mpv, anki, dmenu_extended.

Reported by pfx.
Initial patch suggestion by Storkman.
2023-04-09 12:37:14 +02:00
8 changed files with 629 additions and 110 deletions
+2 -8
View File
@@ -6,13 +6,7 @@ include config.mk
SRC = drw.c dwm.c util.c SRC = drw.c dwm.c util.c
OBJ = ${SRC:.c=.o} OBJ = ${SRC:.c=.o}
all: options dwm all: dwm
options:
@echo dwm build options:
@echo "CFLAGS = ${CFLAGS}"
@echo "LDFLAGS = ${LDFLAGS}"
@echo "CC = ${CC}"
.c.o: .c.o:
${CC} -c ${CFLAGS} $< ${CC} -c ${CFLAGS} $<
@@ -48,4 +42,4 @@ uninstall:
rm -f ${DESTDIR}${PREFIX}/bin/dwm\ rm -f ${DESTDIR}${PREFIX}/bin/dwm\
${DESTDIR}${MANPREFIX}/man1/dwm.1 ${DESTDIR}${MANPREFIX}/man1/dwm.1
.PHONY: all options clean dist install uninstall .PHONY: all clean dist install uninstall
+7 -1
View File
@@ -2,7 +2,13 @@
/* appearance */ /* appearance */
static const unsigned int borderpx = 1; /* border pixel of windows */ static const unsigned int borderpx = 1; /* border pixel of windows */
static const unsigned int gappx = 6; /* gaps between windows */
static const unsigned int snap = 32; /* snap pixel */ static const unsigned int snap = 32; /* snap pixel */
static const unsigned int systraypinning = 0; /* 0: sloppy systray follows selected monitor, >0: pin systray to monitor X */
static const unsigned int systrayonleft = 0; /* 0: systray in the right corner, >0: systray on left of status text */
static const unsigned int systrayspacing = 2; /* systray spacing */
static const int systraypinningfailfirst = 1; /* 1: if pinning fails, display systray on the first monitor, False: display systray on the last monitor*/
static const int showsystray = 1; /* 0 means no systray */
static const int showbar = 1; /* 0 means no bar */ static const int showbar = 1; /* 0 means no bar */
static const int topbar = 1; /* 0 means bottom bar */ static const int topbar = 1; /* 0 means bottom bar */
static const char *fonts[] = { "monospace:size=10" }; static const char *fonts[] = { "monospace:size=10" };
@@ -34,7 +40,7 @@ static const Rule rules[] = {
/* layout(s) */ /* layout(s) */
static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */
static const int nmaster = 1; /* number of clients in master area */ static const int nmaster = 1; /* number of clients in master area */
static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ static const int resizehints = 0; /* 1 means respect size hints in tiled resizals */
static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */ static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */
static const Layout layouts[] = { static const Layout layouts[] = {
+130
View File
@@ -0,0 +1,130 @@
/* See LICENSE file for copyright and license details. */
/* appearance */
static const unsigned int borderpx = 2; /* border pixel of windows */
static const unsigned int gappx = 2; /* gaps between windows */
static const unsigned int snap = 32; /* snap pixel */
static const unsigned int systraypinning = 0; /* 0: sloppy systray follows selected monitor, >0: pin systray to monitor X */
static const unsigned int systrayonleft = 0; /* 0: systray in the right corner, >0: systray on left of status text */
static const unsigned int systrayspacing = 2; /* systray spacing */
static const int systraypinningfailfirst = 1; /* 1: if pinning fails, display systray on the first monitor, False: display systray on the last monitor*/
static const int showsystray = 1; /* 0 means no systray */
static const int showbar = 1; /* 0 means no bar */
static const int topbar = 1; /* 0 means bottom bar */
static const char *fonts[] = { "monospace:size=12" };
static const char dmenufont[] = "monospace:size=12";
static const char col_gray1[] = "#2C2C2C";
static const char col_gray2[] = "#444444";
static const char col_gray3[] = "#DCDCDC";
static const char col_gray4[] = "#DCDCDC";
static const char col_cyan[] = "#60B48A";
static const char *colors[][3] = {
/* fg bg border */
[SchemeNorm] = { col_gray3, col_gray1, col_gray2 },
[SchemeSel] = { col_gray4, col_cyan, col_cyan },
};
/* tagging */
static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
static const Rule rules[] = {
/* xprop(1):
* WM_CLASS(STRING) = instance, class
* WM_NAME(STRING) = title
*/
/* class instance title tags mask isfloating monitor */
{ "Gimp", NULL, NULL, 0, 1, -1 },
{ "Firefox", NULL, NULL, 1 << 8, 0, -1 },
};
/* layout(s) */
static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */
static const int nmaster = 1; /* number of clients in master area */
static const int resizehints = 0; /* 1 means respect size hints in tiled resizals */
static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */
static const Layout layouts[] = {
/* symbol arrange function */
{ "[]=", tile }, /* first entry is default */
{ "><>", NULL }, /* no layout function means floating behavior */
{ "[M]", monocle },
};
/* key definitions */
#define MODKEY Mod1Mask
#define SUPERKEY Mod4Mask
#define TAGKEYS(KEY,TAG) \
{ MODKEY, KEY, view, {.ui = 1 << TAG} }, \
{ MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \
{ MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \
{ MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} },
/* helper for spawning shell commands in the pre dwm-5.0 fashion */
#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
/* commands */
static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */
static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL };
static const char *termcmd[] = { "st", NULL };
static const char *slockcmd[] = { "slock", NULL };
/* shell commands */
static const char maimselcmd[] = "maim -s ~/Pictures/screen-sel-$(date '+%y%m%d-%H%M-%S').png";
static const char maimfullcmd[] = "maim ~/Pictures/screen-full-$(date '+%y%m%d-%H%M-%S').png";
static const Key keys[] = {
/* modifier key function argument */
{ MODKEY, XK_p, spawn, {.v = dmenucmd } },
{ MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } },
{ SUPERKEY, XK_l, spawn, {.v = slockcmd } },
{ SUPERKEY|ShiftMask, XK_s, spawn, SHCMD(maimselcmd) },
{ 0, XK_Print, spawn, SHCMD(maimfullcmd) },
{ MODKEY, XK_b, togglebar, {0} },
{ MODKEY, XK_j, focusstack, {.i = +1 } },
{ MODKEY, XK_k, focusstack, {.i = -1 } },
{ MODKEY, XK_i, incnmaster, {.i = +1 } },
{ MODKEY, XK_d, incnmaster, {.i = -1 } },
{ MODKEY, XK_h, setmfact, {.f = -0.05} },
{ MODKEY, XK_l, setmfact, {.f = +0.05} },
{ MODKEY, XK_Return, zoom, {0} },
{ MODKEY, XK_Tab, view, {0} },
{ MODKEY|ShiftMask, XK_c, killclient, {0} },
{ MODKEY, XK_t, setlayout, {.v = &layouts[0]} },
{ MODKEY, XK_f, setlayout, {.v = &layouts[1]} },
{ MODKEY, XK_m, setlayout, {.v = &layouts[2]} },
{ MODKEY, XK_space, setlayout, {0} },
{ MODKEY|ShiftMask, XK_space, togglefloating, {0} },
{ MODKEY, XK_0, view, {.ui = ~0 } },
{ MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } },
{ MODKEY, XK_comma, focusmon, {.i = -1 } },
{ MODKEY, XK_period, focusmon, {.i = +1 } },
{ MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } },
{ MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } },
TAGKEYS( XK_1, 0)
TAGKEYS( XK_2, 1)
TAGKEYS( XK_3, 2)
TAGKEYS( XK_4, 3)
TAGKEYS( XK_5, 4)
TAGKEYS( XK_6, 5)
TAGKEYS( XK_7, 6)
TAGKEYS( XK_8, 7)
TAGKEYS( XK_9, 8)
{ MODKEY|ShiftMask, XK_q, quit, {0} },
};
/* button definitions */
/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */
static const Button buttons[] = {
/* click event mask button function argument */
{ ClkLtSymbol, 0, Button1, setlayout, {0} },
{ ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} },
{ ClkWinTitle, 0, Button2, zoom, {0} },
{ ClkStatusText, 0, Button2, spawn, {.v = termcmd } },
{ ClkClientWin, MODKEY, Button1, movemouse, {0} },
{ ClkClientWin, MODKEY, Button2, togglefloating, {0} },
{ ClkClientWin, MODKEY, Button3, resizemouse, {0} },
{ ClkTagBar, 0, Button1, view, {0} },
{ ClkTagBar, 0, Button3, toggleview, {0} },
{ ClkTagBar, MODKEY, Button1, tag, {0} },
{ ClkTagBar, MODKEY, Button3, toggletag, {0} },
};
+1 -1
View File
@@ -1,5 +1,5 @@
# dwm version # dwm version
VERSION = 6.4 VERSION = 6.6
# Customize below to fit your system # Customize below to fit your system
+57 -59
View File
@@ -9,54 +9,40 @@
#include "util.h" #include "util.h"
#define UTF_INVALID 0xFFFD #define UTF_INVALID 0xFFFD
#define UTF_SIZ 4
static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; static int
static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; utf8decode(const char *s_in, long *u, int *err)
static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000};
static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
static long
utf8decodebyte(const char c, size_t *i)
{ {
for (*i = 0; *i < (UTF_SIZ + 1); ++(*i)) static const unsigned char lens[] = {
if (((unsigned char)c & utfmask[*i]) == utfbyte[*i]) /* 0XXXX */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
return (unsigned char)c & ~utfmask[*i]; /* 10XXX */ 0, 0, 0, 0, 0, 0, 0, 0, /* invalid */
return 0; /* 110XX */ 2, 2, 2, 2,
} /* 1110X */ 3, 3,
/* 11110 */ 4,
static size_t /* 11111 */ 0, /* invalid */
utf8validate(long *u, size_t i) };
{ static const unsigned char leading_mask[] = { 0x7F, 0x1F, 0x0F, 0x07 };
if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) static const unsigned int overlong[] = { 0x0, 0x80, 0x0800, 0x10000 };
*u = UTF_INVALID;
for (i = 1; *u > utfmax[i]; ++i)
;
return i;
}
static size_t
utf8decode(const char *c, long *u, size_t clen)
{
size_t i, j, len, type;
long udecoded;
const unsigned char *s = (const unsigned char *)s_in;
int len = lens[*s >> 3];
*u = UTF_INVALID; *u = UTF_INVALID;
if (!clen) *err = 1;
return 0; if (len == 0)
udecoded = utf8decodebyte(c[0], &len);
if (!BETWEEN(len, 1, UTF_SIZ))
return 1; return 1;
for (i = 1, j = 1; i < clen && j < len; ++i, ++j) {
udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type);
if (type)
return j;
}
if (j < len)
return 0;
*u = udecoded;
utf8validate(u, len);
long cp = s[0] & leading_mask[len - 1];
for (int i = 1; i < len; ++i) {
if (s[i] == '\0' || (s[i] & 0xC0) != 0x80)
return i;
cp = (cp << 6) | (s[i] & 0x3F);
}
/* out of range, surrogate, overlong encoding */
if (cp > 0x10FFFF || (cp >> 11) == 0x1B || cp < overlong[len - 1])
return len;
*err = 0;
*u = cp;
return len; return len;
} }
@@ -238,11 +224,11 @@ drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int
int int
drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert)
{ {
int i, ty, ellipsis_x = 0; int ty, ellipsis_x = 0;
unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len; unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len, hash, h0, h1;
XftDraw *d = NULL; XftDraw *d = NULL;
Fnt *usedfont, *curfont, *nextfont; Fnt *usedfont, *curfont, *nextfont;
int utf8strlen, utf8charlen, render = x || y || w || h; int utf8strlen, utf8charlen, utf8err, render = x || y || w || h;
long utf8codepoint = 0; long utf8codepoint = 0;
const char *utf8str; const char *utf8str;
FcCharSet *fccharset; FcCharSet *fccharset;
@@ -251,9 +237,8 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
XftResult result; XftResult result;
int charexists = 0, overflow = 0; int charexists = 0, overflow = 0;
/* keep track of a couple codepoints for which we have no match. */ /* keep track of a couple codepoints for which we have no match. */
enum { nomatches_len = 64 }; static unsigned int nomatches[128], ellipsis_width, invalid_width;
static struct { long codepoint[nomatches_len]; unsigned int idx; } nomatches; static const char invalid[] = "";
static unsigned int ellipsis_width = 0;
if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts) if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts)
return 0; return 0;
@@ -263,6 +248,8 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
} else { } else {
XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
if (w < lpad)
return x + w;
d = XftDrawCreate(drw->dpy, drw->drawable, d = XftDrawCreate(drw->dpy, drw->drawable,
DefaultVisual(drw->dpy, drw->screen), DefaultVisual(drw->dpy, drw->screen),
DefaultColormap(drw->dpy, drw->screen)); DefaultColormap(drw->dpy, drw->screen));
@@ -273,12 +260,14 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
usedfont = drw->fonts; usedfont = drw->fonts;
if (!ellipsis_width && render) if (!ellipsis_width && render)
ellipsis_width = drw_fontset_getwidth(drw, "..."); ellipsis_width = drw_fontset_getwidth(drw, "...");
if (!invalid_width && render)
invalid_width = drw_fontset_getwidth(drw, invalid);
while (1) { while (1) {
ew = ellipsis_len = utf8strlen = 0; ew = ellipsis_len = utf8err = utf8charlen = utf8strlen = 0;
utf8str = text; utf8str = text;
nextfont = NULL; nextfont = NULL;
while (*text) { while (*text) {
utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); utf8charlen = utf8decode(text, &utf8codepoint, &utf8err);
for (curfont = drw->fonts; curfont; curfont = curfont->next) { for (curfont = drw->fonts; curfont; curfont = curfont->next) {
charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint);
if (charexists) { if (charexists) {
@@ -300,9 +289,9 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
else else
utf8strlen = ellipsis_len; utf8strlen = ellipsis_len;
} else if (curfont == usedfont) { } else if (curfont == usedfont) {
utf8strlen += utf8charlen;
text += utf8charlen; text += utf8charlen;
ew += tmpw; utf8strlen += utf8err ? 0 : utf8charlen;
ew += utf8err ? 0 : tmpw;
} else { } else {
nextfont = curfont; nextfont = curfont;
} }
@@ -310,7 +299,7 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
} }
} }
if (overflow || !charexists || nextfont) if (overflow || !charexists || nextfont || utf8err)
break; break;
else else
charexists = 0; charexists = 0;
@@ -325,6 +314,12 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
x += ew; x += ew;
w -= ew; w -= ew;
} }
if (utf8err && (!render || invalid_width < w)) {
if (render)
drw_text(drw, x, y, w, h, 0, invalid, invert);
x += invalid_width;
w -= invalid_width;
}
if (render && overflow) if (render && overflow)
drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert); drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert);
@@ -338,11 +333,14 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
* character must be drawn. */ * character must be drawn. */
charexists = 1; charexists = 1;
for (i = 0; i < nomatches_len; ++i) { hash = (unsigned int)utf8codepoint;
/* avoid calling XftFontMatch if we know we won't find a match */ hash = ((hash >> 16) ^ hash) * 0x21F0AAAD;
if (utf8codepoint == nomatches.codepoint[i]) hash = ((hash >> 15) ^ hash) * 0xD35A2D97;
goto no_match; h0 = ((hash >> 15) ^ hash) % LENGTH(nomatches);
} h1 = (hash >> 17) % LENGTH(nomatches);
/* avoid expensive XftFontMatch call when we know we won't find a match */
if (nomatches[h0] == utf8codepoint || nomatches[h1] == utf8codepoint)
goto no_match;
fccharset = FcCharSetCreate(); fccharset = FcCharSetCreate();
FcCharSetAddChar(fccharset, utf8codepoint); FcCharSetAddChar(fccharset, utf8codepoint);
@@ -371,7 +369,7 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
curfont->next = usedfont; curfont->next = usedfont;
} else { } else {
xfont_free(usedfont); xfont_free(usedfont);
nomatches.codepoint[++nomatches.idx % nomatches_len] = utf8codepoint; nomatches[nomatches[h0] ? h1 : h0] = utf8codepoint;
no_match: no_match:
usedfont = drw->fonts; usedfont = drw->fonts;
} }
+424 -35
View File
@@ -50,19 +50,33 @@
#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ #define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \
* MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy)))
#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) #define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]))
#define LENGTH(X) (sizeof X / sizeof X[0])
#define MOUSEMASK (BUTTONMASK|PointerMotionMask) #define MOUSEMASK (BUTTONMASK|PointerMotionMask)
#define WIDTH(X) ((X)->w + 2 * (X)->bw) #define WIDTH(X) ((X)->w + 2 * (X)->bw + gappx)
#define HEIGHT(X) ((X)->h + 2 * (X)->bw) #define HEIGHT(X) ((X)->h + 2 * (X)->bw + gappx)
#define TAGMASK ((1 << LENGTH(tags)) - 1) #define TAGMASK ((1 << LENGTH(tags)) - 1)
#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
#define SYSTEM_TRAY_REQUEST_DOCK 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 */ /* enums */
enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
enum { SchemeNorm, SchemeSel }; /* color schemes */ enum { SchemeNorm, SchemeSel }; /* color schemes */
enum { NetSupported, NetWMName, NetWMState, NetWMCheck, enum { NetSupported, NetWMName, NetWMState, NetWMCheck,
NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, NetSystemTrayOrientationHorz,
NetWMFullscreen, NetActiveWindow, NetWMWindowType, NetWMFullscreen, NetActiveWindow, NetWMWindowType,
NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */
enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
@@ -141,6 +155,12 @@ typedef struct {
int monitor; int monitor;
} Rule; } Rule;
typedef struct Systray Systray;
struct Systray {
Window win;
Client *icons;
};
/* function declarations */ /* function declarations */
static void applyrules(Client *c); static void applyrules(Client *c);
static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact);
@@ -172,6 +192,7 @@ static void focusstack(const Arg *arg);
static Atom getatomprop(Client *c, Atom prop); static Atom getatomprop(Client *c, Atom prop);
static int getrootptr(int *x, int *y); static int getrootptr(int *x, int *y);
static long getstate(Window w); static long getstate(Window w);
static unsigned int getsystraywidth();
static int gettextprop(Window w, Atom atom, char *text, unsigned int size); static int gettextprop(Window w, Atom atom, char *text, unsigned int size);
static void grabbuttons(Client *c, int focused); static void grabbuttons(Client *c, int focused);
static void grabkeys(void); static void grabkeys(void);
@@ -189,13 +210,16 @@ static void pop(Client *c);
static void propertynotify(XEvent *e); static void propertynotify(XEvent *e);
static void quit(const Arg *arg); static void quit(const Arg *arg);
static Monitor *recttomon(int x, int y, int w, int h); static Monitor *recttomon(int x, int y, int w, int h);
static void removesystrayicon(Client *i);
static void resize(Client *c, int x, int y, int w, int h, int interact); static void resize(Client *c, int x, int y, int w, int h, int interact);
static void resizebarwin(Monitor *m);
static void resizeclient(Client *c, int x, int y, int w, int h); static void resizeclient(Client *c, int x, int y, int w, int h);
static void resizemouse(const Arg *arg); static void resizemouse(const Arg *arg);
static void resizerequest(XEvent *e);
static void restack(Monitor *m); static void restack(Monitor *m);
static void run(void); static void run(void);
static void scan(void); static void scan(void);
static int sendevent(Client *c, Atom proto); static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4);
static void sendmon(Client *c, Monitor *m); static void sendmon(Client *c, Monitor *m);
static void setclientstate(Client *c, long state); static void setclientstate(Client *c, long state);
static void setfocus(Client *c); static void setfocus(Client *c);
@@ -206,6 +230,7 @@ static void setup(void);
static void seturgent(Client *c, int urg); static void seturgent(Client *c, int urg);
static void showhide(Client *c); static void showhide(Client *c);
static void spawn(const Arg *arg); static void spawn(const Arg *arg);
static Monitor *systraytomon(Monitor *m);
static void tag(const Arg *arg); static void tag(const Arg *arg);
static void tagmon(const Arg *arg); static void tagmon(const Arg *arg);
static void tile(Monitor *m); static void tile(Monitor *m);
@@ -223,18 +248,23 @@ static int updategeom(void);
static void updatenumlockmask(void); static void updatenumlockmask(void);
static void updatesizehints(Client *c); static void updatesizehints(Client *c);
static void updatestatus(void); static void updatestatus(void);
static void updatesystray(void);
static void updatesystrayicongeom(Client *i, int w, int h);
static void updatesystrayiconstate(Client *i, XPropertyEvent *ev);
static void updatetitle(Client *c); static void updatetitle(Client *c);
static void updatewindowtype(Client *c); static void updatewindowtype(Client *c);
static void updatewmhints(Client *c); static void updatewmhints(Client *c);
static void view(const Arg *arg); static void view(const Arg *arg);
static Client *wintoclient(Window w); static Client *wintoclient(Window w);
static Monitor *wintomon(Window w); static Monitor *wintomon(Window w);
static Client *wintosystrayicon(Window w);
static int xerror(Display *dpy, XErrorEvent *ee); static int xerror(Display *dpy, XErrorEvent *ee);
static int xerrordummy(Display *dpy, XErrorEvent *ee); static int xerrordummy(Display *dpy, XErrorEvent *ee);
static int xerrorstart(Display *dpy, XErrorEvent *ee); static int xerrorstart(Display *dpy, XErrorEvent *ee);
static void zoom(const Arg *arg); static void zoom(const Arg *arg);
/* variables */ /* variables */
static Systray *systray = NULL;
static const char broken[] = "broken"; static const char broken[] = "broken";
static char stext[256]; static char stext[256];
static int screen; static int screen;
@@ -257,9 +287,10 @@ static void (*handler[LASTEvent]) (XEvent *) = {
[MapRequest] = maprequest, [MapRequest] = maprequest,
[MotionNotify] = motionnotify, [MotionNotify] = motionnotify,
[PropertyNotify] = propertynotify, [PropertyNotify] = propertynotify,
[ResizeRequest] = resizerequest,
[UnmapNotify] = unmapnotify [UnmapNotify] = unmapnotify
}; };
static Atom wmatom[WMLast], netatom[NetLast]; static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast];
static int running = 1; static int running = 1;
static Cur *cursor[CurLast]; static Cur *cursor[CurLast];
static Clr **scheme; static Clr **scheme;
@@ -441,7 +472,7 @@ buttonpress(XEvent *e)
arg.ui = 1 << i; arg.ui = 1 << i;
} else if (ev->x < x + TEXTW(selmon->ltsymbol)) } else if (ev->x < x + TEXTW(selmon->ltsymbol))
click = ClkLtSymbol; click = ClkLtSymbol;
else if (ev->x > selmon->ww - (int)TEXTW(stext)) else if (ev->x > selmon->ww - (int)TEXTW(stext) - getsystraywidth())
click = ClkStatusText; click = ClkStatusText;
else else
click = ClkWinTitle; click = ClkWinTitle;
@@ -484,6 +515,13 @@ cleanup(void)
XUngrabKey(dpy, AnyKey, AnyModifier, root); XUngrabKey(dpy, AnyKey, AnyModifier, root);
while (mons) while (mons)
cleanupmon(mons); cleanupmon(mons);
if (showsystray) {
XUnmapWindow(dpy, systray->win);
XDestroyWindow(dpy, systray->win);
free(systray);
}
for (i = 0; i < CurLast; i++) for (i = 0; i < CurLast; i++)
drw_cur_free(drw, cursor[i]); drw_cur_free(drw, cursor[i]);
for (i = 0; i < LENGTH(colors); i++) for (i = 0; i < LENGTH(colors); i++)
@@ -515,9 +553,58 @@ cleanupmon(Monitor *mon)
void void
clientmessage(XEvent *e) clientmessage(XEvent *e)
{ {
XWindowAttributes wa;
XSetWindowAttributes swa;
XClientMessageEvent *cme = &e->xclient; XClientMessageEvent *cme = &e->xclient;
Client *c = wintoclient(cme->window); Client *c = wintoclient(cme->window);
if (showsystray && 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;
if (!XGetWindowAttributes(dpy, c->win, &wa)) {
/* use sane defaults */
wa.width = bh;
wa.height = bh;
wa.border_width = 0;
}
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[SchemeNorm][ColBg].pixel;
XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa);
sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
/* FIXME not sure if I have to send these events, too */
sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_FOCUS_IN, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_MODALITY_ON, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
XSync(dpy, False);
resizebarwin(selmon);
updatesystray();
setclientstate(c, NormalState);
}
return;
}
if (!c) if (!c)
return; return;
if (cme->message_type == netatom[NetWMState]) { if (cme->message_type == netatom[NetWMState]) {
@@ -570,7 +657,7 @@ configurenotify(XEvent *e)
for (c = m->clients; c; c = c->next) for (c = m->clients; c; c = c->next)
if (c->isfullscreen) if (c->isfullscreen)
resizeclient(c, m->mx, m->my, m->mw, m->mh); resizeclient(c, m->mx, m->my, m->mw, m->mh);
XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); resizebarwin(m);
} }
focus(NULL); focus(NULL);
arrange(NULL); arrange(NULL);
@@ -655,6 +742,11 @@ destroynotify(XEvent *e)
if ((c = wintoclient(ev->window))) if ((c = wintoclient(ev->window)))
unmanage(c, 1); unmanage(c, 1);
else if ((c = wintosystrayicon(ev->window))) {
removesystrayicon(c);
resizebarwin(selmon);
updatesystray();
}
} }
void void
@@ -698,7 +790,7 @@ dirtomon(int dir)
void void
drawbar(Monitor *m) drawbar(Monitor *m)
{ {
int x, w, tw = 0; int x, w, tw = 0, stw = 0;
int boxs = drw->fonts->h / 9; int boxs = drw->fonts->h / 9;
int boxw = drw->fonts->h / 6 + 2; int boxw = drw->fonts->h / 6 + 2;
unsigned int i, occ = 0, urg = 0; unsigned int i, occ = 0, urg = 0;
@@ -707,13 +799,17 @@ drawbar(Monitor *m)
if (!m->showbar) if (!m->showbar)
return; return;
if(showsystray && m == systraytomon(m) && !systrayonleft)
stw = getsystraywidth();
/* draw status first so it can be overdrawn by tags later */ /* draw status first so it can be overdrawn by tags later */
if (m == selmon) { /* status is only drawn on selected monitor */ if (m == selmon) { /* status is only drawn on selected monitor */
drw_setscheme(drw, scheme[SchemeNorm]); drw_setscheme(drw, scheme[SchemeNorm]);
tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ tw = TEXTW(stext) - lrpad / 2 + 2; /* 2px extra right padding */
drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); drw_text(drw, m->ww - tw - stw, 0, tw, bh, lrpad / 2 - 2, stext, 0);
} }
resizebarwin(m);
for (c = m->clients; c; c = c->next) { for (c = m->clients; c; c = c->next) {
occ |= c->tags; occ |= c->tags;
if (c->isurgent) if (c->isurgent)
@@ -734,7 +830,7 @@ drawbar(Monitor *m)
drw_setscheme(drw, scheme[SchemeNorm]); drw_setscheme(drw, scheme[SchemeNorm]);
x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0);
if ((w = m->ww - tw - x) > bh) { if ((w = m->ww - tw - stw - x) > bh) {
if (m->sel) { if (m->sel) {
drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]);
drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0);
@@ -745,7 +841,7 @@ drawbar(Monitor *m)
drw_rect(drw, x, 0, w, bh, 1, 1); drw_rect(drw, x, 0, w, bh, 1, 1);
} }
} }
drw_map(drw, m->barwin, 0, 0, m->ww, bh); drw_map(drw, m->barwin, 0, 0, m->ww - stw, bh);
} }
void void
@@ -782,8 +878,11 @@ expose(XEvent *e)
Monitor *m; Monitor *m;
XExposeEvent *ev = &e->xexpose; XExposeEvent *ev = &e->xexpose;
if (ev->count == 0 && (m = wintomon(ev->window))) if (ev->count == 0 && (m = wintomon(ev->window))) {
drawbar(m); drawbar(m);
if (m == selmon)
updatesystray();
}
} }
void void
@@ -869,14 +968,32 @@ getatomprop(Client *c, Atom prop)
unsigned char *p = NULL; unsigned char *p = NULL;
Atom da, atom = None; Atom da, atom = None;
if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, /* FIXME getatomprop should return the number of items and a pointer to
* the stored data instead of this workaround */
Atom req = XA_ATOM;
if (prop == xatom[XembedInfo])
req = xatom[XembedInfo];
if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req,
&da, &di, &dl, &dl, &p) == Success && p) { &da, &di, &dl, &dl, &p) == Success && p) {
atom = *(Atom *)p; atom = *(Atom *)p;
if (da == xatom[XembedInfo] && dl == 2)
atom = ((Atom *)p)[1];
XFree(p); XFree(p);
} }
return atom; return atom;
} }
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 : 1;
}
int int
getrootptr(int *x, int *y) getrootptr(int *x, int *y)
{ {
@@ -1017,7 +1134,8 @@ killclient(const Arg *arg)
{ {
if (!selmon->sel) if (!selmon->sel)
return; return;
if (!sendevent(selmon->sel, wmatom[WMDelete])) {
if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0 , 0, 0)) {
XGrabServer(dpy); XGrabServer(dpy);
XSetErrorHandler(xerrordummy); XSetErrorHandler(xerrordummy);
XSetCloseDownMode(dpy, DestroyAll); XSetCloseDownMode(dpy, DestroyAll);
@@ -1104,6 +1222,13 @@ maprequest(XEvent *e)
static XWindowAttributes wa; static XWindowAttributes wa;
XMapRequestEvent *ev = &e->xmaprequest; XMapRequestEvent *ev = &e->xmaprequest;
Client *i;
if ((i = wintosystrayicon(ev->window))) {
sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION);
resizebarwin(selmon);
updatesystray();
}
if (!XGetWindowAttributes(dpy, ev->window, &wa) || wa.override_redirect) if (!XGetWindowAttributes(dpy, ev->window, &wa) || wa.override_redirect)
return; return;
if (!wintoclient(ev->window)) if (!wintoclient(ev->window))
@@ -1225,6 +1350,17 @@ propertynotify(XEvent *e)
Window trans; Window trans;
XPropertyEvent *ev = &e->xproperty; XPropertyEvent *ev = &e->xproperty;
if ((c = wintosystrayicon(ev->window))) {
if (ev->atom == XA_WM_NORMAL_HINTS) {
updatesizehints(c);
updatesystrayicongeom(c, c->w, c->h);
}
else
updatesystrayiconstate(c, ev);
resizebarwin(selmon);
updatesystray();
}
if ((ev->window == root) && (ev->atom == XA_WM_NAME)) if ((ev->window == root) && (ev->atom == XA_WM_NAME))
updatestatus(); updatestatus();
else if (ev->state == PropertyDelete) else if (ev->state == PropertyDelete)
@@ -1275,6 +1411,19 @@ recttomon(int x, int y, int w, int h)
return r; return r;
} }
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 void
resize(Client *c, int x, int y, int w, int h, int interact) resize(Client *c, int x, int y, int w, int h, int interact)
{ {
@@ -1282,21 +1431,66 @@ resize(Client *c, int x, int y, int w, int h, int interact)
resizeclient(c, x, y, w, h); resizeclient(c, x, y, w, h);
} }
void
resizebarwin(Monitor *m) {
unsigned int w = m->ww;
if (showsystray && m == systraytomon(m) && !systrayonleft)
w -= getsystraywidth();
XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, w, bh);
}
void void
resizeclient(Client *c, int x, int y, int w, int h) resizeclient(Client *c, int x, int y, int w, int h)
{ {
XWindowChanges wc; XWindowChanges wc;
unsigned int n;
unsigned int gapoffset;
unsigned int gapincr;
Client *nbc;
c->oldx = c->x; c->x = wc.x = x;
c->oldy = c->y; c->y = wc.y = y;
c->oldw = c->w; c->w = wc.width = w;
c->oldh = c->h; c->h = wc.height = h;
wc.border_width = c->bw; wc.border_width = c->bw;
/* Get number of clients for the client's monitor */
for (n = 0, nbc = nexttiled(c->mon->clients); nbc; nbc = nexttiled(nbc->next), n++);
/* Do nothing if layout is floating */
if (c->isfloating || c->mon->lt[c->mon->sellt]->arrange == NULL) {
gapincr = gapoffset = 0;
} else {
/* Remove border and gap if layout is monocle or only one client */
if (c->mon->lt[c->mon->sellt]->arrange == monocle || n == 1) {
gapoffset = 0;
gapincr = -2 * borderpx;
wc.border_width = 0;
} else {
gapoffset = gappx;
gapincr = 2 * gappx;
}
}
c->oldx = c->x; c->x = wc.x = x + gapoffset;
c->oldy = c->y; c->y = wc.y = y + gapoffset;
c->oldw = c->w; c->w = wc.width = w - gapincr;
c->oldh = c->h; c->h = wc.height = h - gapincr;
XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc);
configure(c); configure(c);
XSync(dpy, False); XSync(dpy, False);
} }
void
resizerequest(XEvent *e)
{
XResizeRequestEvent *ev = &e->xresizerequest;
Client *i;
if ((i = wintosystrayicon(ev->window))) {
updatesystrayicongeom(i, ev->width, ev->height);
resizebarwin(selmon);
updatesystray();
}
}
void void
resizemouse(const Arg *arg) resizemouse(const Arg *arg)
{ {
@@ -1443,26 +1637,37 @@ setclientstate(Client *c, long state)
} }
int int
sendevent(Client *c, Atom proto) sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4)
{ {
int n; int n;
Atom *protocols; Atom *protocols, mt;
int exists = 0; int exists = 0;
XEvent ev; XEvent ev;
if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) {
while (!exists && n--) mt = wmatom[WMProtocols];
exists = protocols[n] == proto; if (XGetWMProtocols(dpy, w, &protocols, &n)) {
XFree(protocols); while (!exists && n--)
exists = protocols[n] == proto;
XFree(protocols);
}
} }
else {
exists = True;
mt = proto;
}
if (exists) { if (exists) {
ev.type = ClientMessage; ev.type = ClientMessage;
ev.xclient.window = c->win; ev.xclient.window = w;
ev.xclient.message_type = wmatom[WMProtocols]; ev.xclient.message_type = mt;
ev.xclient.format = 32; ev.xclient.format = 32;
ev.xclient.data.l[0] = proto; ev.xclient.data.l[0] = d0;
ev.xclient.data.l[1] = CurrentTime; ev.xclient.data.l[1] = d1;
XSendEvent(dpy, c->win, False, NoEventMask, &ev); ev.xclient.data.l[2] = d2;
ev.xclient.data.l[3] = d3;
ev.xclient.data.l[4] = d4;
XSendEvent(dpy, w, False, mask, &ev);
} }
return exists; return exists;
} }
@@ -1476,7 +1681,7 @@ setfocus(Client *c)
XA_WINDOW, 32, PropModeReplace, XA_WINDOW, 32, PropModeReplace,
(unsigned char *) &(c->win), 1); (unsigned char *) &(c->win), 1);
} }
sendevent(c, wmatom[WMTakeFocus]); sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0);
} }
void void
@@ -1572,6 +1777,10 @@ setup(void)
wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
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[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False);
netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False);
@@ -1579,6 +1788,9 @@ setup(void)
netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False);
xatom[Manager] = XInternAtom(dpy, "MANAGER", False);
xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False);
xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False);
/* init cursors */ /* init cursors */
cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr);
cursor[CurResize] = drw_cur_create(drw, XC_sizing); cursor[CurResize] = drw_cur_create(drw, XC_sizing);
@@ -1587,6 +1799,8 @@ setup(void)
scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); scheme = ecalloc(LENGTH(colors), sizeof(Clr *));
for (i = 0; i < LENGTH(colors); i++) for (i = 0; i < LENGTH(colors); i++)
scheme[i] = drw_scm_create(drw, colors[i], 3); scheme[i] = drw_scm_create(drw, colors[i], 3);
/* init system tray */
updatesystray();
/* init bars */ /* init bars */
updatebars(); updatebars();
updatestatus(); updatestatus();
@@ -1647,12 +1861,20 @@ showhide(Client *c)
void void
spawn(const Arg *arg) spawn(const Arg *arg)
{ {
struct sigaction sa;
if (arg->v == dmenucmd) if (arg->v == dmenucmd)
dmenumon[0] = '0' + selmon->num; dmenumon[0] = '0' + selmon->num;
if (fork() == 0) { if (fork() == 0) {
if (dpy) if (dpy)
close(ConnectionNumber(dpy)); close(ConnectionNumber(dpy));
setsid(); setsid();
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = SIG_DFL;
sigaction(SIGCHLD, &sa, NULL);
execvp(((char **)arg->v)[0], (char **)arg->v); execvp(((char **)arg->v)[0], (char **)arg->v);
die("dwm: execvp '%s' failed:", ((char **)arg->v)[0]); die("dwm: execvp '%s' failed:", ((char **)arg->v)[0]);
} }
@@ -1693,7 +1915,7 @@ tile(Monitor *m)
for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++)
if (i < m->nmaster) { if (i < m->nmaster) {
h = (m->wh - my) / (MIN(n, m->nmaster) - i); h = (m->wh - my) / (MIN(n, m->nmaster) - i);
resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0); resize(c, m->wx, m->wy + my, mw - (2*c->bw) + (n > 1 ? gappx : 0), h - (2*c->bw), 0);
if (my + HEIGHT(c) < m->wh) if (my + HEIGHT(c) < m->wh)
my += HEIGHT(c); my += HEIGHT(c);
} else { } else {
@@ -1709,7 +1931,18 @@ togglebar(const Arg *arg)
{ {
selmon->showbar = !selmon->showbar; selmon->showbar = !selmon->showbar;
updatebarpos(selmon); updatebarpos(selmon);
XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); resizebarwin(selmon);
if (showsystray) {
XWindowChanges wc;
if (!selmon->showbar)
wc.y = -bh;
else if (selmon->showbar) {
wc.y = 0;
if (!selmon->topbar)
wc.y = selmon->mh - bh;
}
XConfigureWindow(dpy, systray->win, CWY, &wc);
}
arrange(selmon); arrange(selmon);
} }
@@ -1805,11 +2038,18 @@ unmapnotify(XEvent *e)
else else
unmanage(c, 0); unmanage(c, 0);
} }
else if ((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);
updatesystray();
}
} }
void void
updatebars(void) updatebars(void)
{ {
unsigned int w;
Monitor *m; Monitor *m;
XSetWindowAttributes wa = { XSetWindowAttributes wa = {
.override_redirect = True, .override_redirect = True,
@@ -1820,10 +2060,15 @@ updatebars(void)
for (m = mons; m; m = m->next) { for (m = mons; m; m = m->next) {
if (m->barwin) if (m->barwin)
continue; continue;
m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), w = m->ww;
if (showsystray && m == systraytomon(m))
w -= getsystraywidth();
m->barwin = XCreateWindow(dpy, root, m->wx, m->by, w, bh, 0, DefaultDepth(dpy, screen),
CopyFromParent, DefaultVisual(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen),
CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);
if (showsystray && m == systraytomon(m))
XMapRaised(dpy, systray->win);
XMapRaised(dpy, m->barwin); XMapRaised(dpy, m->barwin);
XSetClassHint(dpy, m->barwin, &ch); XSetClassHint(dpy, m->barwin, &ch);
} }
@@ -1843,7 +2088,7 @@ updatebarpos(Monitor *m)
} }
void void
updateclientlist() updateclientlist(void)
{ {
Client *c; Client *c;
Monitor *m; Monitor *m;
@@ -2000,6 +2245,124 @@ updatestatus(void)
if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext)))
strcpy(stext, "dwm-"VERSION); strcpy(stext, "dwm-"VERSION);
drawbar(selmon); drawbar(selmon);
updatesystray();
}
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;
}
}
}
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);
}
void
updatesystray(void)
{
XSetWindowAttributes wa;
XWindowChanges wc;
Client *i;
Monitor *m = systraytomon(NULL);
unsigned int x = m->mx + m->mw;
unsigned int sw = TEXTW(stext) - lrpad + systrayspacing;
unsigned int w = 1;
if (!showsystray)
return;
if (systrayonleft)
x -= sw + lrpad / 2;
if (!systray) {
/* init systray */
if (!(systray = (Systray *)calloc(1, sizeof(Systray))))
die("fatal: could not malloc() %u bytes\n", sizeof(Systray));
systray->win = XCreateSimpleWindow(dpy, root, x, m->by, w, bh, 0, 0, scheme[SchemeSel][ColBg].pixel);
wa.event_mask = ButtonPressMask | ExposureMask;
wa.override_redirect = True;
wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
XSelectInput(dpy, systray->win, SubstructureNotifyMask);
XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32,
PropModeReplace, (unsigned char *)&netatom[NetSystemTrayOrientationHorz], 1);
XChangeWindowAttributes(dpy, systray->win, CWEventMask|CWOverrideRedirect|CWBackPixel, &wa);
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;
}
}
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, m->by, w, bh);
wc.x = x; wc.y = m->by; 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 void
@@ -2067,6 +2430,16 @@ wintoclient(Window w)
return NULL; return NULL;
} }
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;
}
Monitor * Monitor *
wintomon(Window w) wintomon(Window w)
{ {
@@ -2120,6 +2493,22 @@ xerrorstart(Display *dpy, XErrorEvent *ee)
return -1; return -1;
} }
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 void
zoom(const Arg *arg) zoom(const Arg *arg)
{ {
+7 -6
View File
@@ -1,4 +1,5 @@
/* See LICENSE file for copyright and license details. */ /* See LICENSE file for copyright and license details. */
#include <errno.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -10,17 +11,17 @@ void
die(const char *fmt, ...) die(const char *fmt, ...)
{ {
va_list ap; va_list ap;
int saved_errno;
saved_errno = errno;
va_start(ap, fmt); va_start(ap, fmt);
vfprintf(stderr, fmt, ap); vfprintf(stderr, fmt, ap);
va_end(ap); va_end(ap);
if (fmt[0] && fmt[strlen(fmt)-1] == ':') { if (fmt[0] && fmt[strlen(fmt)-1] == ':')
fputc(' ', stderr); fprintf(stderr, " %s", strerror(saved_errno));
perror(NULL); fputc('\n', stderr);
} else {
fputc('\n', stderr);
}
exit(1); exit(1);
} }
+1
View File
@@ -3,6 +3,7 @@
#define MAX(A, B) ((A) > (B) ? (A) : (B)) #define MAX(A, B) ((A) > (B) ? (A) : (B))
#define MIN(A, B) ((A) < (B) ? (A) : (B)) #define MIN(A, B) ((A) < (B) ? (A) : (B))
#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) #define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B))
#define LENGTH(X) (sizeof (X) / sizeof (X)[0])
void die(const char *fmt, ...); void die(const char *fmt, ...);
void *ecalloc(size_t nmemb, size_t size); void *ecalloc(size_t nmemb, size_t size);