Compare commits

...

22 Commits

Author SHA1 Message Date
b355743792 WIP my local config changes 2025-10-09 00:07:17 +02:00
explosion-mental
a07421c195 shift-tools - shifttag, moves the current selected client to the adjacent tag - shifttagclients, moves the current selected client to the adjacent tag that has at least one client else acts as shifttag - shiftview, view adjacent tag - shiftviewclients, view the closes tag that has a client. If none acts as shiftview - shiftboth, shifttag and shiftview. Basically moves the window to the next/prev tag and follows it. - shiftswaptags, its a shift implementation on the swaptags function (see 65379c6264/config.h (L48) for more details), which in short 'swaps tags' (swaps all clients with the clients on the adjacent tag). A pretty useful example of this is chosing a tag empty and sending all your clients to that tag. - swapfunction is the 'helper' function for the shiftswaptags. remember that these functions **shift**, which means you can go from tag 1 to 9 or 9 to 1. Also remember that the default argument is 1 and you can change it. 2025-10-08 23:43:48 +02:00
006706b5d0 agree: dwm-goback with dwm-warp 2025-10-08 23:09:15 +02:00
Max Schillinger
0243a59c57 add patch/function 'goback' 2025-10-08 22:57:08 +02:00
c013256eef agree: dwm-autodarkmode with dwm-awesomebar 2025-10-07 00:06:55 +02:00
dimarogiv
431135ecd9 With this patch you can focus on any of the currently open windows on the current tagset with just one key combination 2025-10-07 00:02:23 +02:00
Cyril Cressent
f2837055a1 Port the uselessgap patch to 6.2 2025-10-06 23:58:04 +02:00
Jonas Dujava
5bcc02f2a7 Warp patch
Warps the mouse cursor to the center of the currently focused
window or screen when the mouse cursor is
  (a) on a different screen, or
  (b) on top of a different window.

This version properly handles warping to windows that have not been
mapped yet (before it resulted in a change of the stack order).
See the discussion in (thanks goes to Bakkeby):
    https://github.com/bakkeby/patches/issues/60
2025-10-06 23:58:04 +02:00
9e3e644c21 apply dwm-xinerama-monitors-xorder-6.4.diff 2025-10-06 23:58:04 +02:00
b6d063c786 agree: change dwm-autodarkmode signal to USR1 and leave restarsig signal to HUP 2025-10-06 23:58:04 +02:00
272db9d233 apply patch dwm-autodarkmode-20250224-6.5.diff 2025-10-06 22:33:03 +02:00
e93f93b98c apply patch dwm-awesomebar-20230431-6.4.diff 2025-10-06 22:14:07 +02:00
cc4d5e65d7 apply patch dwm-exitmenu-6.3.diff 2025-10-06 21:31:59 +02:00
Arda Atci
9b49e9ad18 preserve clients on old tags when renewing dwm
By default, when dwm is recompiled-restarted all clients will
lose it's current tag and collapse to first tag. This patch preserves
clients on old tags, however note that layout order is not preserved.
2025-10-06 21:25:59 +02:00
Christopher Drelich
cfe958510f Modifies quit to handle restarts and adds SIGHUP and SIGTERM handlers.
Modified quit() to restart if it receives arg .i = 1
MOD+CTRL+SHIFT+Q was added to confid.def.h to do just that.

Signal handlers were handled for SIGHUP and SIGTERM.
If dwm receives these signals it calls quit() with
arg .i = to 1 or 0, respectively.

To restart dwm:
MOD+CTRL+SHIFT+Q
or
kill -HUP dwmpid

To quit dwm cleanly:
MOD+SHIFT+Q
or
kill -TERM dwmpid
2025-10-06 21:23:18 +02: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
10 changed files with 807 additions and 129 deletions

View File

@@ -6,13 +6,7 @@ include config.mk
SRC = drw.c dwm.c util.c
OBJ = ${SRC:.c=.o}
all: options dwm
options:
@echo dwm build options:
@echo "CFLAGS = ${CFLAGS}"
@echo "LDFLAGS = ${LDFLAGS}"
@echo "CC = ${CC}"
all: dwm
.c.o:
${CC} -c ${CFLAGS} $<
@@ -48,4 +42,4 @@ uninstall:
rm -f ${DESTDIR}${PREFIX}/bin/dwm\
${DESTDIR}${MANPREFIX}/man1/dwm.1
.PHONY: all options clean dist install uninstall
.PHONY: all clean dist install uninstall

View File

@@ -1,7 +1,8 @@
/* See LICENSE file for copyright and license details. */
/* appearance */
static const unsigned int borderpx = 1; /* border pixel of windows */
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 int showbar = 1; /* 0 means no bar */
static const int topbar = 1; /* 0 means bottom bar */
@@ -12,10 +13,19 @@ static const char col_gray2[] = "#444444";
static const char col_gray3[] = "#bbbbbb";
static const char col_gray4[] = "#eeeeee";
static const char col_cyan[] = "#005577";
static const char *colors[][3] = {
static const char col_border_focus_light[] = "#df8c20"; // hsl(34 75% 50%)
static const char col_border_focus_dark[] = "#b25e34"; // hsl(20 55% 45%)
static const char *colorsdark[][3] = {
/* fg bg border */
[SchemeNorm] = { col_gray3, col_gray1, col_gray2 },
[SchemeSel] = { col_gray4, col_cyan, col_cyan },
[SchemeSel] = { col_gray4, col_cyan, col_border_focus_dark },
[SchemeHid] = { col_cyan, col_gray1, col_cyan },
};
static const char *colorslight[][3] = {
/* fg bg border */
[SchemeNorm] = { col_gray1, col_gray3, col_gray2 },
[SchemeSel] = { col_cyan, col_gray4, col_border_focus_light },
[SchemeHid] = { col_cyan, col_gray1, col_cyan },
};
/* tagging */
@@ -45,7 +55,7 @@ static const Layout layouts[] = {
};
/* key definitions */
#define MODKEY Mod1Mask
#define MODKEY Mod4Mask
#define TAGKEYS(KEY,TAG) \
{ MODKEY, KEY, view, {.ui = 1 << TAG} }, \
{ MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \
@@ -56,23 +66,41 @@ static const Layout layouts[] = {
#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 char dmenumon[2] = "0"; /* component of dmenu{dark,light}, manipulated in spawndmenu() */
static const char *dmenudark[] = { "dmenu_run", "-m", dmenumon, "-i", "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL };
static const char *dmenulight[] = { "dmenu_run", "-m", dmenumon, "-i", "-fn", dmenufont, "-nb", col_gray3, "-nf", col_gray1, "-sb", col_cyan, "-sf", col_gray4, NULL };
static const char *termcmd[] = { "st", NULL };
static const char *lockcmd[] = { "slock", NULL };
static const char *suspendcmd[] = { "systemctl", "suspend", NULL };
static const char *themecmd[] = { "lupan-set-theme", "toggle", NULL };
#include "shift-tools.c"
#include "exitdwm.c"
static const Key keys[] = {
/* modifier key function argument */
{ MODKEY, XK_p, spawn, {.v = dmenucmd } },
{ MODKEY, XK_p, spawndmenu, {0} },
{ MODKEY, XK_u, shiftview, { .i = +1 } },
{ MODKEY|ShiftMask, XK_u, shifttag, { .i = +1 } },
{ MODKEY|ShiftMask, XK_y, shifttag, { .i = -1 } },
{ MODKEY, XK_y, shiftview, { .i = -1 } },
{ MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } },
{ MODKEY, XK_b, togglebar, {0} },
{ MODKEY, XK_j, focusstack, {.i = +1 } },
{ MODKEY, XK_k, focusstack, {.i = -1 } },
{ MODKEY, XK_j, focusstackvis, {.i = +1 } },
{ MODKEY, XK_k, focusstackvis, {.i = -1 } },
{ MODKEY|ShiftMask, XK_j, focusstackhid, {.i = +1 } },
{ MODKEY|ShiftMask, XK_k, focusstackhid, {.i = -1 } },
{ MODKEY, XK_i, incnmaster, {.i = +1 } },
{ MODKEY, XK_d, incnmaster, {.i = -1 } },
{ MODKEY, XK_h, setmfact, {.f = -0.05} },
{ MODKEY|ShiftMask, XK_h, shiftboth, { .i = -1 } },
{ MODKEY|ControlMask, XK_h, shiftswaptags, { .i = -1 } },
{ MODKEY|ControlMask, XK_l, shiftswaptags, { .i = +1 } },
{ MODKEY|ShiftMask, XK_l, shiftboth, { .i = +1 } },
{ MODKEY, XK_l, setmfact, {.f = +0.05} },
{ MODKEY, XK_Return, zoom, {0} },
{ MODKEY, XK_Tab, view, {0} },
{ MODKEY, XK_g, goback, {0} },
{ MODKEY|ShiftMask, XK_c, killclient, {0} },
{ MODKEY, XK_t, setlayout, {.v = &layouts[0]} },
{ MODKEY, XK_f, setlayout, {.v = &layouts[1]} },
@@ -85,6 +113,9 @@ static const Key keys[] = {
{ MODKEY, XK_period, focusmon, {.i = +1 } },
{ MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } },
{ MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } },
{ MODKEY, XK_s, show, {0} },
{ MODKEY|ShiftMask, XK_s, showall, {0} },
{ MODKEY, XK_h, hide, {0} },
TAGKEYS( XK_1, 0)
TAGKEYS( XK_2, 1)
TAGKEYS( XK_3, 2)
@@ -94,7 +125,22 @@ static const Key keys[] = {
TAGKEYS( XK_7, 6)
TAGKEYS( XK_8, 7)
TAGKEYS( XK_9, 8)
{ ControlMask, XK_1, focusbynum, {.i = 0} },
{ ControlMask, XK_2, focusbynum, {.i = 1} },
{ ControlMask, XK_3, focusbynum, {.i = 2} },
{ ControlMask, XK_4, focusbynum, {.i = 3} },
{ ControlMask, XK_5, focusbynum, {.i = 4} },
{ ControlMask, XK_6, focusbynum, {.i = 5} },
{ ControlMask, XK_7, focusbynum, {.i = 6} },
{ ControlMask, XK_8, focusbynum, {.i = 7} },
{ ControlMask, XK_9, focusbynum, {.i = 8} },
{ MODKEY|ShiftMask, XK_q, quit, {0} },
{ MODKEY|ControlMask|ShiftMask, XK_q, quit, {1} },
{ MODKEY|ShiftMask, XK_e, exitdwm, {0} },
{ MODKEY|ControlMask|ShiftMask, XK_s, spawn, {.v = suspendcmd } },
{ MODKEY|ControlMask|ShiftMask, XK_l, spawn, {.v = lockcmd } },
{ MODKEY|ControlMask|ShiftMask, XK_t, spawn, {.v = themecmd } },
};
/* button definitions */
@@ -103,6 +149,7 @@ static const Button buttons[] = {
/* click event mask button function argument */
{ ClkLtSymbol, 0, Button1, setlayout, {0} },
{ ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} },
{ ClkWinTitle, 0, Button1, togglewin, {0} },
{ ClkWinTitle, 0, Button2, zoom, {0} },
{ ClkStatusText, 0, Button2, spawn, {.v = termcmd } },
{ ClkClientWin, MODKEY, Button1, movemouse, {0} },

View File

@@ -1,5 +1,5 @@
# dwm version
VERSION = 6.4
VERSION = 6.6
# Customize below to fit your system

116
drw.c
View File

@@ -9,54 +9,40 @@
#include "util.h"
#define UTF_INVALID 0xFFFD
#define UTF_SIZ 4
static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0};
static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
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)
static int
utf8decode(const char *s_in, long *u, int *err)
{
for (*i = 0; *i < (UTF_SIZ + 1); ++(*i))
if (((unsigned char)c & utfmask[*i]) == utfbyte[*i])
return (unsigned char)c & ~utfmask[*i];
return 0;
}
static size_t
utf8validate(long *u, size_t i)
{
if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF))
*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;
static const unsigned char lens[] = {
/* 0XXXX */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 10XXX */ 0, 0, 0, 0, 0, 0, 0, 0, /* invalid */
/* 110XX */ 2, 2, 2, 2,
/* 1110X */ 3, 3,
/* 11110 */ 4,
/* 11111 */ 0, /* invalid */
};
static const unsigned char leading_mask[] = { 0x7F, 0x1F, 0x0F, 0x07 };
static const unsigned int overlong[] = { 0x0, 0x80, 0x0800, 0x10000 };
const unsigned char *s = (const unsigned char *)s_in;
int len = lens[*s >> 3];
*u = UTF_INVALID;
if (!clen)
return 0;
udecoded = utf8decodebyte(c[0], &len);
if (!BETWEEN(len, 1, UTF_SIZ))
*err = 1;
if (len == 0)
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;
}
@@ -238,11 +224,11 @@ drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int
int
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;
unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len;
int ty, ellipsis_x = 0;
unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len, hash, h0, h1;
XftDraw *d = NULL;
Fnt *usedfont, *curfont, *nextfont;
int utf8strlen, utf8charlen, render = x || y || w || h;
int utf8strlen, utf8charlen, utf8err, render = x || y || w || h;
long utf8codepoint = 0;
const char *utf8str;
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;
int charexists = 0, overflow = 0;
/* keep track of a couple codepoints for which we have no match. */
enum { nomatches_len = 64 };
static struct { long codepoint[nomatches_len]; unsigned int idx; } nomatches;
static unsigned int ellipsis_width = 0;
static unsigned int nomatches[128], ellipsis_width, invalid_width;
static const char invalid[] = "<EFBFBD>";
if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts)
return 0;
@@ -263,6 +248,8 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
} else {
XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
if (w < lpad)
return x + w;
d = XftDrawCreate(drw->dpy, drw->drawable,
DefaultVisual(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;
if (!ellipsis_width && render)
ellipsis_width = drw_fontset_getwidth(drw, "...");
if (!invalid_width && render)
invalid_width = drw_fontset_getwidth(drw, invalid);
while (1) {
ew = ellipsis_len = utf8strlen = 0;
ew = ellipsis_len = utf8err = utf8charlen = utf8strlen = 0;
utf8str = text;
nextfont = NULL;
while (*text) {
utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ);
utf8charlen = utf8decode(text, &utf8codepoint, &utf8err);
for (curfont = drw->fonts; curfont; curfont = curfont->next) {
charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint);
if (charexists) {
@@ -300,9 +289,9 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
else
utf8strlen = ellipsis_len;
} else if (curfont == usedfont) {
utf8strlen += utf8charlen;
text += utf8charlen;
ew += tmpw;
utf8strlen += utf8err ? 0 : utf8charlen;
ew += utf8err ? 0 : tmpw;
} else {
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;
else
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;
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)
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. */
charexists = 1;
for (i = 0; i < nomatches_len; ++i) {
/* avoid calling XftFontMatch if we know we won't find a match */
if (utf8codepoint == nomatches.codepoint[i])
goto no_match;
}
hash = (unsigned int)utf8codepoint;
hash = ((hash >> 16) ^ hash) * 0x21F0AAAD;
hash = ((hash >> 15) ^ hash) * 0xD35A2D97;
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();
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;
} else {
xfont_free(usedfont);
nomatches.codepoint[++nomatches.idx % nomatches_len] = utf8codepoint;
nomatches[nomatches[h0] ? h1 : h0] = utf8codepoint;
no_match:
usedfont = drw->fonts;
}

10
dwm.1
View File

@@ -142,6 +142,9 @@ Add/remove all windows with nth tag to/from the view.
.TP
.B Mod1\-Shift\-q
Quit dwm.
.TP
.B Mod1\-Control\-Shift\-q
Restart dwm.
.SS Mouse commands
.TP
.B Mod1\-Button1
@@ -155,6 +158,13 @@ Resize focused window while dragging. Tiled windows will be toggled to the float
.SH CUSTOMIZATION
dwm is customized by creating a custom config.h and (re)compiling the source
code. This keeps it fast, secure and simple.
.SH SIGNALS
.TP
.B SIGHUP - 1
Restart the dwm process.
.TP
.B SIGTERM - 15
Cleanly terminate the dwm process.
.SH SEE ALSO
.BR dmenu (1),
.BR st (1)

497
dwm.c
View File

@@ -50,19 +50,20 @@
#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)))
#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]))
#define HIDDEN(C) ((getstate(C->win) == IconicState))
#define LENGTH(X) (sizeof X / sizeof X[0])
#define MOUSEMASK (BUTTONMASK|PointerMotionMask)
#define WIDTH(X) ((X)->w + 2 * (X)->bw)
#define HEIGHT(X) ((X)->h + 2 * (X)->bw)
#define WIDTH(X) ((X)->w + 2 * (X)->bw + gappx)
#define HEIGHT(X) ((X)->h + 2 * (X)->bw + gappx)
#define TAGMASK ((1 << LENGTH(tags)) - 1)
#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
/* enums */
enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
enum { SchemeNorm, SchemeSel }; /* color schemes */
enum { SchemeNorm, SchemeSel, SchemeHid }; /* color schemes */
enum { NetSupported, NetWMName, NetWMState, NetWMCheck,
NetWMFullscreen, NetActiveWindow, NetWMWindowType,
NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
NetWMWindowTypeDialog, NetClientList, NetClientInfo, NetLast }; /* EWMH atoms */
enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
@@ -117,6 +118,8 @@ struct Monitor {
int nmaster;
int num;
int by; /* bar geometry */
int btw; /* width of tasks portion of bar */
int bt; /* number of tasks */
int mx, my, mw, mh; /* screen size */
int wx, wy, ww, wh; /* window area */
unsigned int seltags;
@@ -124,6 +127,7 @@ struct Monitor {
unsigned int tagset[2];
int showbar;
int topbar;
int hidsel;
Client *clients;
Client *sel;
Client *stack;
@@ -153,6 +157,7 @@ static void checkotherwm(void);
static void cleanup(void);
static void cleanupmon(Monitor *mon);
static void clientmessage(XEvent *e);
static void colormodehandler(int sig);
static void configure(Client *c);
static void configurenotify(XEvent *e);
static void configurerequest(XEvent *e);
@@ -168,13 +173,18 @@ static void expose(XEvent *e);
static void focus(Client *c);
static void focusin(XEvent *e);
static void focusmon(const Arg *arg);
static void focusstack(const Arg *arg);
static void focusstackvis(const Arg *arg);
static void focusstackhid(const Arg *arg);
static void focusstack(int inc, int vis);
static Atom getatomprop(Client *c, Atom prop);
static int getrootptr(int *x, int *y);
static long getstate(Window w);
static int gettextprop(Window w, Atom atom, char *text, unsigned int size);
static void goback(const Arg *arg);
static void grabbuttons(Client *c, int focused);
static void grabkeys(void);
static void hide(const Arg *arg);
static void hidewin(Client *c);
static void incnmaster(const Arg *arg);
static void keypress(XEvent *e);
static void killclient(const Arg *arg);
@@ -198,21 +208,32 @@ static void scan(void);
static int sendevent(Client *c, Atom proto);
static void sendmon(Client *c, Monitor *m);
static void setclientstate(Client *c, long state);
static void setclienttagprop(Client *c);
static void setcolormode(void);
static void setfocus(Client *c);
static void setfullscreen(Client *c, int fullscreen);
static void setlayout(const Arg *arg);
static void setmfact(const Arg *arg);
static void setup(void);
static void seturgent(Client *c, int urg);
static void show(const Arg *arg);
static void showall(const Arg *arg);
static void showwin(Client *c);
static void showhide(Client *c);
static void sigchld(int unused);
static void sighup(int unused);
static void sigterm(int unused);
static void spawn(const Arg *arg);
static void spawndmenu(const Arg *arg);
static void tag(const Arg *arg);
static void tagmon(const Arg *arg);
static void tile(Monitor *m);
static void togglebar(const Arg *arg);
static void togglefloating(const Arg *arg);
static void focusbynum(const Arg *arg);
static void toggletag(const Arg *arg);
static void toggleview(const Arg *arg);
static void togglewin(const Arg *arg);
static void unfocus(Client *c, int setfocus);
static void unmanage(Client *c, int destroyed);
static void unmapnotify(XEvent *e);
@@ -227,6 +248,7 @@ static void updatetitle(Client *c);
static void updatewindowtype(Client *c);
static void updatewmhints(Client *c);
static void view(const Arg *arg);
static void warp(const Client *c);
static Client *wintoclient(Window w);
static Monitor *wintomon(Window w);
static int xerror(Display *dpy, XErrorEvent *ee);
@@ -260,13 +282,16 @@ static void (*handler[LASTEvent]) (XEvent *) = {
[UnmapNotify] = unmapnotify
};
static Atom wmatom[WMLast], netatom[NetLast];
static int restart = 0;
static int running = 1;
static Cur *cursor[CurLast];
static Clr **scheme;
static Clr **scheme, **schemedark, **schemelight;
static Display *dpy;
static Drw *drw;
static Monitor *mons, *selmon;
static Monitor *mons, *selmon, *prevmon;
static Window root, wmcheckwin;
static const char **dmenucmd;
static int colormodechanged;
/* configuration, allows nested code to access above variables */
#include "config.h"
@@ -428,6 +453,7 @@ buttonpress(XEvent *e)
/* focus monitor if necessary */
if ((m = wintomon(ev->window)) && m != selmon) {
unfocus(selmon->sel, 1);
prevmon = selmon;
selmon = m;
focus(NULL);
}
@@ -441,10 +467,25 @@ buttonpress(XEvent *e)
arg.ui = 1 << i;
} else if (ev->x < x + TEXTW(selmon->ltsymbol))
click = ClkLtSymbol;
else if (ev->x > selmon->ww - (int)TEXTW(stext))
/* 2px right padding */
else if (ev->x > selmon->ww - TEXTW(stext) + lrpad - 2)
click = ClkStatusText;
else
click = ClkWinTitle;
else {
x += TEXTW(selmon->ltsymbol);
c = m->clients;
if (c) {
do {
if (!ISVISIBLE(c))
continue;
else
x +=(1.0 / (double)m->bt) * m->btw;
} while (ev->x > x && (c = c->next));
click = ClkWinTitle;
arg.v = c;
}
}
} else if ((c = wintoclient(ev->window))) {
focus(c);
restack(selmon);
@@ -454,7 +495,7 @@ buttonpress(XEvent *e)
for (i = 0; i < LENGTH(buttons); i++)
if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button
&& CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))
buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg);
buttons[i].func((click == ClkTagBar || click == ClkWinTitle) && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg);
}
void
@@ -486,9 +527,12 @@ cleanup(void)
cleanupmon(mons);
for (i = 0; i < CurLast; i++)
drw_cur_free(drw, cursor[i]);
for (i = 0; i < LENGTH(colors); i++)
free(scheme[i]);
free(scheme);
for (i = 0; i < LENGTH(colorsdark); i++) {
free(schemedark[i]);
free(schemelight[i]);
}
free(schemedark);
free(schemelight);
XDestroyWindow(dpy, wmcheckwin);
drw_free(drw);
XSync(dpy, False);
@@ -531,6 +575,12 @@ clientmessage(XEvent *e)
}
}
void
colormodehandler(int sig)
{
colormodechanged = 1;
}
void
configure(Client *c)
{
@@ -698,7 +748,7 @@ dirtomon(int dir)
void
drawbar(Monitor *m)
{
int x, w, tw = 0;
int x, w, tw = 0, n = 0, scm;
int boxs = drw->fonts->h / 9;
int boxw = drw->fonts->h / 6 + 2;
unsigned int i, occ = 0, urg = 0;
@@ -715,6 +765,8 @@ drawbar(Monitor *m)
}
for (c = m->clients; c; c = c->next) {
if (ISVISIBLE(c))
n++;
occ |= c->tags;
if (c->isurgent)
urg |= c->tags;
@@ -735,16 +787,36 @@ drawbar(Monitor *m)
x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0);
if ((w = m->ww - tw - x) > bh) {
if (m->sel) {
drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]);
drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0);
if (m->sel->isfloating)
drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0);
if (n > 0) {
int remainder = w % n;
int tabw = (1.0 / (double)n) * w + 1;
for (c = m->clients; c; c = c->next) {
if (!ISVISIBLE(c))
continue;
if (m->sel == c)
scm = SchemeSel;
else if (HIDDEN(c))
scm = SchemeHid;
else
scm = SchemeNorm;
drw_setscheme(drw, scheme[scm]);
if (remainder >= 0) {
if (remainder == 0) {
tabw--;
}
remainder--;
}
drw_text(drw, x, 0, tabw, bh, lrpad / 2, c->name, 0);
x += tabw;
}
} else {
drw_setscheme(drw, scheme[SchemeNorm]);
drw_rect(drw, x, 0, w, bh, 1, 1);
}
}
m->bt = n;
m->btw = w;
drw_map(drw, m->barwin, 0, 0, m->ww, bh);
}
@@ -770,6 +842,7 @@ enternotify(XEvent *e)
m = c ? c->mon : wintomon(ev->window);
if (m != selmon) {
unfocus(selmon->sel, 1);
prevmon = selmon;
selmon = m;
} else if (!c || c == selmon->sel)
return;
@@ -790,12 +863,22 @@ void
focus(Client *c)
{
if (!c || !ISVISIBLE(c))
for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext);
if (selmon->sel && selmon->sel != c)
for (c = selmon->stack; c && (!ISVISIBLE(c) || HIDDEN(c)); c = c->snext);
if (selmon->sel && selmon->sel != c) {
unfocus(selmon->sel, 0);
if (selmon->hidsel) {
hidewin(selmon->sel);
if (c)
arrange(c->mon);
selmon->hidsel = 0;
}
}
if (c) {
if (c->mon != selmon)
if (c->mon != selmon) {
prevmon = selmon;
selmon = c->mon;
}
if (c->isurgent)
seturgent(c, 0);
detachstack(c);
@@ -831,33 +914,59 @@ focusmon(const Arg *arg)
if ((m = dirtomon(arg->i)) == selmon)
return;
unfocus(selmon->sel, 0);
prevmon = selmon;
selmon = m;
focus(NULL);
warp(selmon->sel);
}
void
focusstack(const Arg *arg)
focusstackvis(const Arg *arg) {
focusstack(arg->i, 0);
}
void
focusstackhid(const Arg *arg) {
focusstack(arg->i, 1);
}
void
focusstack(int inc, int hid)
{
Client *c = NULL, *i;
if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen))
// if no client selected AND exclude hidden client; if client selected but fullscreened
if ((!selmon->sel && !hid) || (selmon->sel && selmon->sel->isfullscreen && lockfullscreen))
return;
if (arg->i > 0) {
for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next);
if (!selmon->clients)
return;
if (inc > 0) {
if (selmon->sel)
for (c = selmon->sel->next;
c && (!ISVISIBLE(c) || (!hid && HIDDEN(c)));
c = c->next);
if (!c)
for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next);
for (c = selmon->clients;
c && (!ISVISIBLE(c) || (!hid && HIDDEN(c)));
c = c->next);
} else {
for (i = selmon->clients; i != selmon->sel; i = i->next)
if (ISVISIBLE(i))
c = i;
if (selmon->sel) {
for (i = selmon->clients; i != selmon->sel; i = i->next)
if (ISVISIBLE(i) && !(!hid && HIDDEN(i)))
c = i;
} else
c = selmon->clients;
if (!c)
for (; i; i = i->next)
if (ISVISIBLE(i))
if (ISVISIBLE(i) && !(!hid && HIDDEN(i)))
c = i;
}
if (c) {
focus(c);
restack(selmon);
if (HIDDEN(c)) {
showwin(c);
c->mon->hidsel = 1;
}
}
}
@@ -928,6 +1037,22 @@ gettextprop(Window w, Atom atom, char *text, unsigned int size)
return 1;
}
void
goback(const Arg *arg)
{
if (prevmon == NULL) {
Arg a = {0};
view(&a);
} else if (prevmon != selmon) {
unfocus(selmon->sel, 0);
Monitor *p = selmon;
selmon = prevmon;
focus(NULL);
prevmon = p;
warp(selmon->sel);
}
}
void
grabbuttons(Client *c, int focused)
{
@@ -977,6 +1102,36 @@ grabkeys(void)
}
}
void
hide(const Arg *arg)
{
hidewin(selmon->sel);
focus(NULL);
arrange(selmon);
}
void
hidewin(Client *c) {
if (!c || HIDDEN(c))
return;
Window w = c->win;
static XWindowAttributes ra, ca;
// more or less taken directly from blackbox's hide() function
XGrabServer(dpy);
XGetWindowAttributes(dpy, root, &ra);
XGetWindowAttributes(dpy, w, &ca);
// prevent UnmapNotify events
XSelectInput(dpy, root, ra.your_event_mask & ~SubstructureNotifyMask);
XSelectInput(dpy, w, ca.your_event_mask & ~StructureNotifyMask);
XUnmapWindow(dpy, w);
setclientstate(c, IconicState);
XSelectInput(dpy, root, ra.your_event_mask);
XSelectInput(dpy, w, ca.your_event_mask);
XUngrabServer(dpy);
}
void
incnmaster(const Arg *arg)
{
@@ -1068,6 +1223,26 @@ manage(Window w, XWindowAttributes *wa)
updatewindowtype(c);
updatesizehints(c);
updatewmhints(c);
{
int format;
unsigned long *data, n, extra;
Monitor *m;
Atom atom;
if (XGetWindowProperty(dpy, c->win, netatom[NetClientInfo], 0L, 2L, False, XA_CARDINAL,
&atom, &format, &n, &extra, (unsigned char **)&data) == Success && n == 2) {
c->tags = *data;
for (m = mons; m; m = m->next) {
if (m->num == *(data+1)) {
c->mon = m;
break;
}
}
}
if (n > 0)
XFree(data);
}
setclienttagprop(c);
XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask);
grabbuttons(c, 0);
if (!c->isfloating)
@@ -1079,12 +1254,14 @@ manage(Window w, XWindowAttributes *wa)
XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend,
(unsigned char *) &(c->win), 1);
XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */
setclientstate(c, NormalState);
if (!HIDDEN(c))
setclientstate(c, NormalState);
if (c->mon == selmon)
unfocus(selmon->sel, 0);
c->mon->sel = c;
arrange(c->mon);
XMapWindow(dpy, c->win);
if (!HIDDEN(c))
XMapWindow(dpy, c->win);
focus(NULL);
}
@@ -1136,6 +1313,8 @@ motionnotify(XEvent *e)
return;
if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) {
unfocus(selmon->sel, 1);
if (m != selmon)
prevmon = selmon;
selmon = m;
focus(NULL);
}
@@ -1197,6 +1376,7 @@ movemouse(const Arg *arg)
XUngrabPointer(dpy, CurrentTime);
if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) {
sendmon(c, m);
prevmon = selmon;
selmon = m;
focus(NULL);
}
@@ -1205,7 +1385,7 @@ movemouse(const Arg *arg)
Client *
nexttiled(Client *c)
{
for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next);
for (; c && (c->isfloating || !ISVISIBLE(c) || HIDDEN(c)); c = c->next);
return c;
}
@@ -1225,6 +1405,10 @@ propertynotify(XEvent *e)
Window trans;
XPropertyEvent *ev = &e->xproperty;
if (colormodechanged) {
setcolormode();
colormodechanged = 0;
}
if ((ev->window == root) && (ev->atom == XA_WM_NAME))
updatestatus();
else if (ev->state == PropertyDelete)
@@ -1258,6 +1442,17 @@ propertynotify(XEvent *e)
void
quit(const Arg *arg)
{
if(arg->i) restart = 1;
// fix: reloading dwm keeps all the hidden clients hidden
Monitor *m;
Client *c;
for (m = mons; m; m = m->next) {
if (m) {
for (c = m->stack; c; c = c->next)
if (c && HIDDEN(c)) showwin(c);
}
}
running = 0;
}
@@ -1286,12 +1481,36 @@ void
resizeclient(Client *c, int x, int y, int w, int h)
{
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;
/* Get number of clients for the selected monitor */
for (n = 0, nbc = nexttiled(selmon->clients); nbc; nbc = nexttiled(nbc->next), n++);
/* Do nothing if layout is floating */
if (c->isfloating || selmon->lt[selmon->sellt]->arrange == NULL) {
gapincr = gapoffset = 0;
} else {
/* Remove border and gap if layout is monocle or only one client */
if (selmon->lt[selmon->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);
configure(c);
XSync(dpy, False);
@@ -1349,6 +1568,7 @@ resizemouse(const Arg *arg)
while (XCheckMaskEvent(dpy, EnterWindowMask, &ev));
if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) {
sendmon(c, m);
prevmon = selmon;
selmon = m;
focus(NULL);
}
@@ -1375,6 +1595,8 @@ restack(Monitor *m)
wc.sibling = c->win;
}
}
if (m == selmon && (m->tagset[m->seltags] & m->sel->tags) && m->lt[m->sellt]->arrange != &monocle)
warp(m->sel);
XSync(dpy, False);
while (XCheckMaskEvent(dpy, EnterWindowMask, &ev));
}
@@ -1429,6 +1651,7 @@ sendmon(Client *c, Monitor *m)
c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */
attach(c);
attachstack(c);
setclienttagprop(c);
focus(NULL);
arrange(NULL);
}
@@ -1442,6 +1665,32 @@ setclientstate(Client *c, long state)
PropModeReplace, (unsigned char *)data, 2);
}
void
setcolormode(void)
{
static const char *file = ".lightmode";
static char *path = NULL;
const char *home;
size_t size;
if (!path && (home = getenv("HOME"))) {
size = strlen(home) + 1 + strlen(file) + 1;
path = malloc(size);
if (!path)
die("dwm: malloc failed");
snprintf(path, size, "%s/%s", home, file);
}
if (access(path, F_OK) == 0) {
scheme = schemelight;
dmenucmd = dmenulight;
} else {
scheme = schemedark;
dmenucmd = dmenudark;
}
}
int
sendevent(Client *c, Atom proto)
{
@@ -1550,9 +1799,17 @@ setup(void)
sa.sa_handler = SIG_IGN;
sigaction(SIGCHLD, &sa, NULL);
/* set color mode on SIGUSR1 */
sigemptyset(&sa.sa_mask);
sa.sa_handler = colormodehandler;
sigaction(SIGUSR1, &sa, NULL);
/* clean up any zombies (inherited from .xinitrc etc) immediately */
while (waitpid(-1, NULL, WNOHANG) > 0);
signal(SIGHUP, sighup);
signal(SIGTERM, sigterm);
/* init screen */
screen = DefaultScreen(dpy);
sw = DisplayWidth(dpy, screen);
@@ -1579,14 +1836,19 @@ setup(void)
netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False);
netatom[NetClientInfo] = XInternAtom(dpy, "_NET_CLIENT_INFO", False);
/* init cursors */
cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr);
cursor[CurResize] = drw_cur_create(drw, XC_sizing);
cursor[CurMove] = drw_cur_create(drw, XC_fleur);
/* init appearance */
scheme = ecalloc(LENGTH(colors), sizeof(Clr *));
for (i = 0; i < LENGTH(colors); i++)
scheme[i] = drw_scm_create(drw, colors[i], 3);
schemedark = ecalloc(LENGTH(colorsdark), sizeof(Clr *));
schemelight = ecalloc(LENGTH(colorslight), sizeof(Clr *));
for (i = 0; i < LENGTH(colorsdark); i++) {
schemedark[i] = drw_scm_create(drw, colorsdark[i], 3);
schemelight[i] = drw_scm_create(drw, colorslight[i], 3);
}
setcolormode();
/* init bars */
updatebars();
updatestatus();
@@ -1602,6 +1864,7 @@ setup(void)
XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32,
PropModeReplace, (unsigned char *) netatom, NetLast);
XDeleteProperty(dpy, root, netatom[NetClientList]);
XDeleteProperty(dpy, root, netatom[NetClientInfo]);
/* select events */
wa.cursor = cursor[CurNormal]->cursor;
wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask
@@ -1626,6 +1889,42 @@ seturgent(Client *c, int urg)
XFree(wmh);
}
void
show(const Arg *arg)
{
if (selmon->hidsel)
selmon->hidsel = 0;
showwin(selmon->sel);
}
void
showall(const Arg *arg)
{
Client *c = NULL;
selmon->hidsel = 0;
for (c = selmon->clients; c; c = c->next) {
if (ISVISIBLE(c))
showwin(c);
}
if (!selmon->sel) {
for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next);
if (c)
focus(c);
}
restack(selmon);
}
void
showwin(Client *c)
{
if (!c || !HIDDEN(c))
return;
XMapWindow(dpy, c->win);
setclientstate(c, NormalState);
arrange(c->mon);
}
void
showhide(Client *c)
{
@@ -1644,13 +1943,33 @@ showhide(Client *c)
}
}
void
sigchld(int unused)
{
if (signal(SIGCHLD, sigchld) == SIG_ERR)
die("can't install SIGCHLD handler:");
while (0 < waitpid(-1, NULL, WNOHANG));
}
void
sighup(int unused)
{
Arg a = {.i = 1};
quit(&a);
}
void
sigterm(int unused)
{
Arg a = {.i = 0};
quit(&a);
}
void
spawn(const Arg *arg)
{
struct sigaction sa;
if (arg->v == dmenucmd)
dmenumon[0] = '0' + selmon->num;
if (fork() == 0) {
if (dpy)
close(ConnectionNumber(dpy));
@@ -1666,11 +1985,29 @@ spawn(const Arg *arg)
}
}
void
setclienttagprop(Client *c)
{
long data[] = { (long) c->tags, (long) c->mon->num };
XChangeProperty(dpy, c->win, netatom[NetClientInfo], XA_CARDINAL, 32,
PropModeReplace, (unsigned char *) data, 2);
}
void
spawndmenu(const Arg *arg)
{
dmenumon[0] = '0' + selmon->num;
spawn(&(const Arg){.v = dmenucmd});
}
void
tag(const Arg *arg)
{
Client *c;
if (selmon->sel && arg->ui & TAGMASK) {
c = selmon->sel;
selmon->sel->tags = arg->ui & TAGMASK;
setclienttagprop(c);
focus(NULL);
arrange(selmon);
}
@@ -1735,6 +2072,19 @@ togglefloating(const Arg *arg)
arrange(selmon);
}
void
focusbynum(const Arg *arg)
{
int i;
Client *c;
i = 0;
c = nexttiled(selmon->clients);
for (; c && i < arg->i; c = nexttiled(c->next), i++);
focus(c);
}
void
toggletag(const Arg *arg)
{
@@ -1745,6 +2095,7 @@ toggletag(const Arg *arg)
newtags = selmon->sel->tags ^ (arg->ui & TAGMASK);
if (newtags) {
selmon->sel->tags = newtags;
setclienttagprop(selmon->sel);
focus(NULL);
arrange(selmon);
}
@@ -1756,12 +2107,30 @@ toggleview(const Arg *arg)
unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK);
if (newtagset) {
prevmon = NULL;
selmon->tagset[selmon->seltags] = newtagset;
focus(NULL);
arrange(selmon);
}
}
void
togglewin(const Arg *arg)
{
Client *c = (Client*)arg->v;
if (c == selmon->sel) {
hidewin(c);
focus(NULL);
arrange(c->mon);
} else {
if (HIDDEN(c))
showwin(c);
focus(c);
restack(selmon);
}
}
void
unfocus(Client *c, int setfocus)
{
@@ -1851,7 +2220,7 @@ updatebarpos(Monitor *m)
}
void
updateclientlist()
updateclientlist(void)
{
Client *c;
Monitor *m;
@@ -1864,6 +2233,16 @@ updateclientlist()
(unsigned char *) &(c->win), 1);
}
#ifdef XINERAMA
// Custom comparator: sort monitors by x_org to match physical layout left-to-right
static int
compare_xinerama_x(const void *a, const void *b) {
const XineramaScreenInfo *sa = (const XineramaScreenInfo *)a;
const XineramaScreenInfo *sb = (const XineramaScreenInfo *)b;
return sa->x_org - sb->x_org;
}
#endif /* XINERAMA */
int
updategeom(void)
{
@@ -1880,6 +2259,8 @@ updategeom(void)
for (n = 0, m = mons; m; m = m->next, n++);
/* only consider unique geometries as separate screens */
unique = ecalloc(nn, sizeof(XineramaScreenInfo));
// Sort monitors by x_org so dwm handles screens in left-to-right order
qsort(info, nn, sizeof(XineramaScreenInfo), compare_xinerama_x);
for (i = 0, j = 0; i < nn; i++)
if (isuniquegeom(unique, j, &info[i]))
memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo));
@@ -2055,6 +2436,7 @@ view(const Arg *arg)
{
if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
return;
prevmon = NULL;
selmon->seltags ^= 1; /* toggle sel tagset */
if (arg->ui & TAGMASK)
selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
@@ -2062,6 +2444,28 @@ view(const Arg *arg)
arrange(selmon);
}
void
warp(const Client *c)
{
int x, y;
if (!c) {
XWarpPointer(dpy, None, root, 0, 0, 0, 0, selmon->wx + selmon->ww / 2, selmon->wy + selmon->wh / 2);
return;
}
if (!getrootptr(&x, &y) ||
(x > c->x - c->bw &&
y > c->y - c->bw &&
x < c->x + c->w + c->bw*2 &&
y < c->y + c->h + c->bw*2) ||
(y > c->mon->by && y < c->mon->by + bh) ||
(c->mon->topbar && !y))
return;
XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w / 2, c->h / 2);
}
Client *
wintoclient(Window w)
{
@@ -2159,6 +2563,7 @@ main(int argc, char *argv[])
#endif /* __OpenBSD__ */
scan();
run();
if(restart) execvp(argv[0], argv);
cleanup();
XCloseDisplay(dpy);
return EXIT_SUCCESS;

87
exitdwm.c Normal file
View File

@@ -0,0 +1,87 @@
# include <stdio.h>
# include <string.h>
void exitdwm ()
{
# if \
defined S_LOCK || \
defined S_RESTART_DWM || \
defined S_OFFSCREEN || \
defined S_EXIT || \
defined S_REBOOT || \
defined S_SHUTDOWN || \
defined S_LOCK_ICON || \
defined S_RESTART_DWM_ICON || \
defined S_OFFSCREEN_ICON || \
defined S_EXIT_ICON || \
defined S_REBOOT_ICON || \
defined S_SHUTDOWN_ICON || \
defined S_FORMAT || \
defined S_FORMAT_CLEAR
# error (conflicting macro names)
# endif
# define S_LOCK "Lock"
# define S_RESTART_DWM "restart Dwm"
# define S_OFFSCREEN "Off-screen"
# define S_EXIT "Exit"
# define S_REBOOT "Reboot"
# define S_SHUTDOWN "Shutdown"
# define S_LOCK_ICON "\uf023" // <= FontAwesome icons
# define S_RESTART_DWM_ICON "\uf01e"
# define S_OFFSCREEN_ICON "\uf108"
# define S_EXIT_ICON "\uf2f5"
# define S_REBOOT_ICON "\uf021"
# define S_SHUTDOWN_ICON "\uf011"
# define S_FORMAT(ACTION) S_##ACTION##_ICON " " S_##ACTION
# define S_FORMAT_CLEAR "sed 's/^..//'"
FILE * exit_menu = popen (
"echo \""
S_FORMAT (LOCK) "\n"
S_FORMAT (RESTART_DWM) "\n"
S_FORMAT (OFFSCREEN) "\n"
S_FORMAT (EXIT) "\n"
S_FORMAT (REBOOT) "\n"
S_FORMAT (SHUTDOWN)
"\" | dmenu -p exit: | " S_FORMAT_CLEAR
,
"r"
);
char exit_action [16];
if (
exit_menu == NULL ||
fscanf (exit_menu, "%15[a-zA-Z -]", exit_action) == EOF
) {
fputs ("Error. Failure in exit_dwm.", stderr);
goto close_streams;
}
if (strcmp (exit_action, S_LOCK) == 0) system ("slock & sleep .5; xset dpms force off");
else if (strcmp (exit_action, S_RESTART_DWM) == 0) quit (& (const Arg) {1});
else if (strcmp (exit_action, S_OFFSCREEN) == 0) system ("sleep .5; xset dpms force off");
else if (strcmp (exit_action, S_EXIT) == 0) quit (& (const Arg) {0});
else if (strcmp (exit_action, S_REBOOT) == 0) system ("systemctl reboot");
else if (strcmp (exit_action, S_SHUTDOWN) == 0) system ("systemctl poweroff -i");
close_streams:
pclose (exit_menu);
# undef S_LOCK
# undef S_RESTART_DWM
# undef S_OFFSCREEN
# undef S_EXIT
# undef S_REBOOT
# undef S_SHUTDOWN
# undef S_LOCK_ICON
# undef S_RESTART_DWM_ICON
# undef S_OFFSCREEN_ICON
# undef S_EXIT_ICON
# undef S_REBOOT_ICON
# undef S_SHUTDOWN_ICON
# undef S_FORMAT
# undef S_FORMAT_CLEAR
}

135
shift-tools.c Normal file
View File

@@ -0,0 +1,135 @@
/* Sends a window to the next/prev tag */
void
shifttag(const Arg *arg)
{
Arg shifted;
shifted.ui = selmon->tagset[selmon->seltags];
if (arg->i > 0) /* left circular shift */
shifted.ui = ((shifted.ui << arg->i) | (shifted.ui >> (LENGTH(tags) - arg->i)));
else /* right circular shift */
shifted.ui = (shifted.ui >> (- arg->i) | shifted.ui << (LENGTH(tags) + arg->i));
tag(&shifted);
}
/* Sends a window to the next/prev tag that has a client, else it moves it to the next/prev one. */
void
shifttagclients(const Arg *arg)
{
Arg shifted;
Client *c;
unsigned int tagmask = 0;
shifted.ui = selmon->tagset[selmon->seltags];
for (c = selmon->clients; c; c = c->next)
if (!(c->tags))
tagmask = tagmask | c->tags;
if (arg->i > 0) /* left circular shift */
do {
shifted.ui = (shifted.ui << arg->i)
| (shifted.ui >> (LENGTH(tags) - arg->i));
} while (tagmask && !(shifted.ui & tagmask));
else /* right circular shift */
do {
shifted.ui = (shifted.ui >> (- arg->i)
| shifted.ui << (LENGTH(tags) + arg->i));
} while (tagmask && !(shifted.ui & tagmask));
tag(&shifted);
}
/* Navigate to the next/prev tag */
void
shiftview(const Arg *arg)
{
Arg shifted;
shifted.ui = selmon->tagset[selmon->seltags];
if (arg->i > 0) /* left circular shift */
shifted.ui = (shifted.ui << arg->i) | (shifted.ui >> (LENGTH(tags) - arg->i));
else /* right circular shift */
shifted.ui = (shifted.ui >> (- arg->i) | shifted.ui << (LENGTH(tags) + arg->i));
view(&shifted);
}
/* Navigate to the next/prev tag that has a client, else moves it to the next/prev tag */
void
shiftviewclients(const Arg *arg)
{
Arg shifted;
Client *c;
unsigned int tagmask = 0;
shifted.ui = selmon->tagset[selmon->seltags];
for (c = selmon->clients; c; c = c->next)
if (!(c->tags))
tagmask = tagmask | c->tags;
if (arg->i > 0) /* left circular shift */
do {
shifted.ui = (shifted.ui << arg->i)
| (shifted.ui >> (LENGTH(tags) - arg->i));
} while (tagmask && !(shifted.ui & tagmask));
else /* right circular shift */
do {
shifted.ui = (shifted.ui >> (- arg->i)
| shifted.ui << (LENGTH(tags) + arg->i));
} while (tagmask && !(shifted.ui & tagmask));
view(&shifted);
}
/* move the current active window to the next/prev tag and view it. More like following the window */
void
shiftboth(const Arg *arg)
{
Arg shifted;
shifted.ui = selmon->tagset[selmon->seltags];
if (arg->i > 0) /* left circular shift */
shifted.ui = ((shifted.ui << arg->i) | (shifted.ui >> (LENGTH(tags) - arg->i)));
else /* right circular shift */
shifted.ui = ((shifted.ui >> (- arg->i) | shifted.ui << (LENGTH(tags) + arg->i)));
tag(&shifted);
view(&shifted);
}
//helper function for shiftswaptags.
//see: https://github.com/moizifty/DWM-Build/blob/65379c62640788881486401a0d8c79333751b02f/config.h#L48
void
swaptags(const Arg *arg)
{
Client *c;
unsigned int newtag = arg->ui & TAGMASK;
unsigned int curtag = selmon->tagset[selmon->seltags];
if (newtag == curtag || !curtag || (curtag & (curtag-1)))
return;
for (c = selmon->clients; c != NULL; c = c->next) {
if ((c->tags & newtag) || (c->tags & curtag))
c->tags ^= curtag ^ newtag;
if (!c->tags)
c->tags = newtag;
}
//move to the swaped tag
//selmon->tagset[selmon->seltags] = newtag;
focus(NULL);
arrange(selmon);
}
/* swaps "tags" (all the clients) with the next/prev tag. */
void
shiftswaptags(const Arg *arg)
{
Arg shifted;
shifted.ui = selmon->tagset[selmon->seltags];
if (arg->i > 0) /* left circular shift */
shifted.ui = ((shifted.ui << arg->i) | (shifted.ui >> (LENGTH(tags) - arg->i)));
else /* right circular shift */
shifted.ui = ((shifted.ui >> (- arg->i) | shifted.ui << (LENGTH(tags) + arg->i)));
swaptags(&shifted);
// uncomment if you also want to "go" (view) the tag where the the clients are going
//view(&shifted);
}

13
util.c
View File

@@ -1,4 +1,5 @@
/* See LICENSE file for copyright and license details. */
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@@ -10,17 +11,17 @@ void
die(const char *fmt, ...)
{
va_list ap;
int saved_errno;
saved_errno = errno;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
fputc(' ', stderr);
perror(NULL);
} else {
fputc('\n', stderr);
}
if (fmt[0] && fmt[strlen(fmt)-1] == ':')
fprintf(stderr, " %s", strerror(saved_errno));
fputc('\n', stderr);
exit(1);
}

1
util.h
View File

@@ -3,6 +3,7 @@
#define MAX(A, B) ((A) > (B) ? (A) : (B))
#define MIN(A, B) ((A) < (B) ? (A) : (B))
#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B))
#define LENGTH(X) (sizeof (X) / sizeof (X)[0])
void die(const char *fmt, ...);
void *ecalloc(size_t nmemb, size_t size);