Compare commits

...

44 Commits

Author SHA1 Message Date
267a2bdf6b chore: added feishin to rules 2026-03-14 15:14:48 +10:30
e2c1054a48 more window rules 2026-03-14 15:14:48 +10:30
f41edef81b minor changes to class names/ window rules 2026-03-14 15:14:48 +10:30
b0cdc0f0e8 calcurse 2026-03-14 15:14:48 +10:30
eeb28857a7 cleen 2026-03-14 15:14:48 +10:30
8622085241 bit better, double chords cause issues 2026-03-14 15:14:48 +10:30
a679f6be20 minor change trial 2026-03-14 15:14:48 +10:30
228aea8394 updates, updates, updates 2026-03-14 15:14:48 +10:30
dc94b7fdbf updates again, need to specify number of chords 2026-03-14 15:14:48 +10:30
0031cbf929 new things, thanks luke 2026-03-14 15:14:48 +10:30
691a58b07b aaah, now I understand 2026-03-14 15:14:48 +10:30
1cbaa3acbd merged and updated config 2026-03-14 15:14:48 +10:30
2b97e3a9f9 forgot to add patch file 2026-03-14 15:14:48 +10:30
b085b8d8c9 patched keychord, bye bye keychain :( 2026-03-14 15:14:48 +10:30
6276d8849f patched statuscmd 2026-03-14 15:14:48 +10:30
797a2ae4b9 patched uselessgap 2026-03-14 15:10:54 +10:30
5c184fa291 added patch file 2026-03-14 15:10:54 +10:30
a4a4af7c65 shift-tools patched? 2026-03-14 15:10:54 +10:30
f8cee0b76f patch file 2026-03-14 15:10:54 +10:30
4624584472 patched restartsig 2026-03-14 15:10:54 +10:30
ea21d381c2 added patch file 2026-03-14 15:10:54 +10:30
686059ecee patched pertag 2026-03-14 15:10:54 +10:30
2812daadfc added patch file 2026-03-14 15:10:54 +10:30
cc9b9e1b5f patched movestack 2026-03-14 15:10:54 +10:30
0913bf8c93 added patch file 2026-03-14 15:10:54 +10:30
2a908d8038 patched fullscreen 2026-03-14 15:10:54 +10:30
967b17b583 added patch file 2026-03-14 15:10:54 +10:30
fd2b6e3ffc patched attachtop 2026-03-14 15:10:54 +10:30
35c92282bc added patch file 2026-03-14 15:10:54 +10:30
ac09c960bb don't forget to fix keychain to keychord changes 2026-03-14 15:10:54 +10:30
0b6dcf27ec added my default config file, might need tweaking 2026-03-14 15:10:54 +10:30
Ruben Gonzalez
44dbc6809d buttonpress: fix status text click area mismatch
The status bar in drawbar() calculates the text width as TEXTW(stext)
- lrpad + 2. However, the click detection in buttonpress() used
TEXTW(stext) without adjusting for that padding.

This created an "extra" clickable area of some pixels to the left of
the status text that would incorrectly trigger ClkStatusText actions
instead of ClkWinTitle.

Steps to reproduce:
1. Set a status text: xsetroot -name "HELLO"
2. Move the mouse to the empty space with some pixels close to the
left of the word "HELLO" but in the title area.
3. Middle-click (or any binding for ClkStatusText).
4. You can see that the status bar action is triggered (default a
terminal spawns), even though you clicked in the window title area.

This fix ensures that the clickable area matches the visual text.
2026-03-13 18:27:18 +01:00
Ruben Gonzalez
2bb919e634 sendmon: resize fullscreen windows to target monitor
When a fullscreen window is moved to another monitor (e.g. via
tagmon), its geometry does not always match the new monitor's
dimensions.

Steps to reproduce:
1. Start dwm with two monitors (A and B).
2. Open a window on Monitor A.
3. Make the window fullscreen (e.g. Firefox with F11).
4. Move the window to Monitor B using the tagmon shortcut (Mod+Shift+>).
5. Go to the other monitor (B), observe that the window is still
visible on Monitor A and its contents, even though the window's title
is seen on Monitor B bar.
6. Go to the monitor A where the window is still in fullscreen, remove
the fullscreen and the window automatically will go to monitor B.

This fix ensures that fullscreen windows are correctly resized to the
new monitor's geometry during the move.
2026-03-10 19:52:48 +01:00
NRK
c3dd6a829b more overflow fix in getatomprop()
commit 244fa852 (and a9aa0d8) tried to fix overflow by checking
the number of items returned. however this is not sufficient
since the format may be lower than 32 bits.

to reproduce the crash, i used the reproducer given in commit
244fa85 but changed the XChangeProperty line to the following to
set the property to a 1 element 16 bit item:

	short si = 1;
	XChangeProperty(d, w, net_wm_state, XA_ATOM, 16,
		PropModeReplace, (unsigned char *)&si, 1);

this client reliably crashes dwm under ASAN since dwm is trying
to read a 32 bit value from a 16 bit one. fix it by checking for
format == 32 as well.

also change the access type from Atom to long, on my machine
Atom is typedef-ed to long already but that may not be true
everywere. the XGetWindowProperty manpage says format == 32 is
returned as `long` so use `long` directly.

(N.B: it also might be worth checking if the returned type is
 XA_ATOM as well, but i wasn't able to cause any crashes by
 setting different types so i'm leaving it out for now.)
2026-02-20 15:31:29 +01:00
NRK
5c9f30300b getstate: fix access type and remove redundant cast
WM_STATE is defined to be format == 32 which xlib returns as
`long` and so accessing it as `unsigned char` is incorrect.

and also &p is already an `unsigned char **` and so the cast was
completely redundant.

given the redundant cast, i assume `p` was `long *` at some time
but was changed to `unsigned char *` later, but the pointer
access (and the cast) wasn't updated.

also add a `format == 32` check as safety measure before
accessing, just in case.
2026-02-20 15:31:28 +01:00
NRK
397d618f1c fix not updating _NET_ACTIVE_WINDOW
currently clients that set the input field of WM_HINTS to true
(c->neverfocus) will never be updated as _NET_ACTIVE_WINDOW even
when they are focused. according to the ICCCM [0] the input
field of WM_HINTS tells the WM to either use or not use
XSetInputFocus(), it shouldn't have any relation to
_NET_ACTIVE_WINDOW. EWMH spec [1] also does not mention any
relationship between the two.

this issue was noticed when launching games via steam/proton and
noticing that _NET_ACTIVE_WINDOW was always wrong/stale (i.e not
updated to the game window).

for reference I've looked at bspwm [2] and it also seems to set
_NET_ACTIVE_WINDOW regardless of whether the client has WM_HINTS
input true or not.

[0]: https://x.org/releases/X11R7.6/doc/xorg-docs/specs/ICCCM/icccm.html#input_focus
[1]: https://specifications.freedesktop.org/wm/1.5/ar01s03.html#id-1.4.10
[2]: c5cf7d3943/src/tree.c (L659-L662)
2026-02-13 10:16:08 +01:00
Hiltjo Posthuma
f63cde9354 bump version to 6.8 2026-01-30 11:18:38 +01:00
Chris Down
a9aa0d8ffb dwm: Fix getatomprop regression from heap overflow fix
Commit 244fa852fe ("dwm: Fix heap buffer overflow in getatomprop")
introduced a check for dl > 0 before dereferencing the property pointer.
However, I missed that the variable dl is passed to XGetWindowProperty
for both nitems_return and bytes_after_return parameters:

    XGetWindowProperty(..., &dl, &dl, &p)

The final value in dl is bytes_after_return, not nitems_return. For a
successfully read property, bytes_after is typically 0 (indicating all
data was retrieved), so the check `dl > 0` is always false and dwm never
reads any atom properties. So this is safe, but not very helpful :-)

dl is probably just a dummy variable anyway, so fix by using a separate
variable for nitems, and check nitems > 0 as originally intended.
2026-01-16 14:13:51 +01:00
Hiltjo Posthuma
85fe518c1a bump version to 6.7
Put the maintainer at the top and bump years (time flies).
2026-01-10 11:31:44 +01:00
Chris Down
244fa852fe dwm: Fix heap buffer overflow in getatomprop
When getatomprop() is called, it invokes XGetWindowProperty() to
retrieve an Atom. If the property exists but has zero elements (length
0), Xlib returns Success and sets p to a valid, non-NULL memory address
containing a single null byte.

However, dl (that is, the number of items) is 0. dwm blindly casts p to
Atom* and dereferences it. While Xlib guarantees that p is safe to read
as a string (that is, it is null-terminated), it does _not_ guarantee it
is safe to read as an Atom (an unsigned long).

The Atom type is a typedef for unsigned long. Reading an Atom (which
thus will either likely be 4 or 8 bytes) from a 1-byte allocated buffer
results in a heap buffer overflow. Since property content is user
controlled, this allows any client to trigger an out of bounds read
simply by setting a property with format 32 and length 0.

An example client which reliably crashes dwm under ASAN:

    #include <X11/Xlib.h>
    #include <X11/Xatom.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>

    int main(void) {
        Display *d;
        Window root, w;
        Atom net_wm_state;

        d = XOpenDisplay(NULL);
        if (!d) return 1;

        root = DefaultRootWindow(d);
        w = XCreateSimpleWindow(d, root, 10, 10, 200, 200, 1, 0, 0);
        net_wm_state = XInternAtom(d, "_NET_WM_STATE", False);
        if (net_wm_state == None) return 1;

        XChangeProperty(d, w, net_wm_state, XA_ATOM, 32,
                        PropModeReplace, NULL, 0);
        XMapWindow(d, w);
        XSync(d, False);
        sleep(1);

        XCloseDisplay(d);
        return 0;
    }

In order to avoid this, check that the number of items returned is
greater than zero before dereferencing the pointer.
2026-01-10 11:27:23 +01:00
Hiltjo Posthuma
7c3abae4e6 drw.c: drw_scm_free: call free inside
Because drw_scm_create() allocates it.
2025-09-29 18:48:27 +02:00
Hiltjo Posthuma
93f26863d1 cleanup schemes and colors 2025-09-27 12:10:17 +02:00
Hiltjo Posthuma
74edc27caa config: make refreshrate for mouse move/resize a config option
Bump the default from 60 to 120.
2025-08-12 19:17:20 +02:00
Hiltjo Posthuma
693d94d350 bump version to 6.6 2025-08-09 14:34:03 +02:00
20 changed files with 2084 additions and 150 deletions

View File

@ -1,5 +1,6 @@
MIT/X Consortium License MIT/X Consortium License
© 2010-2026 Hiltjo Posthuma <hiltjo@codemadness.org>
© 2006-2019 Anselm R Garbe <anselm@garbe.ca> © 2006-2019 Anselm R Garbe <anselm@garbe.ca>
© 2006-2009 Jukka Salmi <jukka at salmi dot ch> © 2006-2009 Jukka Salmi <jukka at salmi dot ch>
© 2006-2007 Sander van Dijk <a dot h dot vandijk at gmail dot com> © 2006-2007 Sander van Dijk <a dot h dot vandijk at gmail dot com>
@ -11,7 +12,6 @@ MIT/X Consortium License
© 2008 Martin Hurton <martin dot hurton at gmail dot com> © 2008 Martin Hurton <martin dot hurton at gmail dot com>
© 2008 Neale Pickett <neale dot woozle dot org> © 2008 Neale Pickett <neale dot woozle dot org>
© 2009 Mate Nagy <mnagy at port70 dot net> © 2009 Mate Nagy <mnagy at port70 dot net>
© 2010-2016 Hiltjo Posthuma <hiltjo@codemadness.org>
© 2010-2012 Connor Lane Smith <cls@lubutu.com> © 2010-2012 Connor Lane Smith <cls@lubutu.com>
© 2011 Christoph Lohmann <20h@r-36.net> © 2011 Christoph Lohmann <20h@r-36.net>
© 2015-2016 Quentin Rameau <quinq@fifth.space> © 2015-2016 Quentin Rameau <quinq@fifth.space>

View File

@ -20,7 +20,7 @@ dwm: ${OBJ}
${CC} -o $@ ${OBJ} ${LDFLAGS} ${CC} -o $@ ${OBJ} ${LDFLAGS}
clean: clean:
rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz config.h
dist: clean dist: clean
mkdir -p dwm-${VERSION} mkdir -p dwm-${VERSION}

View File

@ -1,116 +1,213 @@
/* See LICENSE file for copyright and license details. */ /* See LICENSE file for copyright and license details. */
/* 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 snap = 32; /* snap pixel */ static const unsigned int gappx = 6; /* gaps between windows */
static const int showbar = 1; /* 0 means no bar */ static const unsigned int snap = 32; /* snap pixel */
static const int topbar = 1; /* 0 means bottom bar */ static const int showbar = 1; /* 0 means no bar */
static const char *fonts[] = { "monospace:size=10" }; static const int topbar = 1; /* 0 means bottom bar */
static const char dmenufont[] = "monospace:size=10"; static const char *fonts[] = {"FiraCode Nerd Font:size=10:antialias=true:hinting=true"};
static const char col_gray1[] = "#222222"; static const char dmenufont[] = "FiraCode Nerd Font:size=10:antialias=true:hinting=true";
static const char col_gray2[] = "#444444";
static const char col_gray3[] = "#bbbbbb"; static const char norm_fg[] = "#D5C4A1";
static const char col_gray4[] = "#eeeeee"; static const char norm_bg[] = "#262626";
static const char col_cyan[] = "#005577"; static const char norm_border[] = "#665c54";
static const char *colors[][3] = {
/* fg bg border */ static const char sel_fg[] = "#262626";
[SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, static const char sel_bg[] = "#FE8019";
[SchemeSel] = { col_gray4, col_cyan, col_cyan }, static const char sel_border[] = "#fbf1c7";
static const char *colors[][3] = {
/* fg bg border */
[SchemeNorm] = {norm_fg, norm_bg, norm_border}, // unfocused wins
[SchemeSel] = {sel_fg, sel_bg, sel_border}, // the focused win
}; };
/* tagging */ static const unsigned int baralpha = 0xd0;
static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; static const unsigned int borderalpha = 0xd0;
static const Rule rules[] = { /* tagging */
/* xprop(1): static const char *tags[] = {"1", "2", "3", "4", "5", "6", "7", "8", "9"};
* WM_CLASS(STRING) = instance, class
* WM_NAME(STRING) = title static const Rule rules[] = {
*/ /* xprop(1):
/* class instance title tags mask isfloating monitor */ * WM_CLASS(STRING) = instance, class
{ "Gimp", NULL, NULL, 0, 1, -1 }, * WM_NAME(STRING) = title
{ "Firefox", NULL, NULL, 1 << 8, 0, -1 }, */
/* class instance title tags mask isfloating monitor */
{"firefox", NULL, NULL, 1 << 0, 0, -1},
{"thunderbird", NULL, NULL, 1 << 6, 0, -1},
{"org.mozilla.Thunderbird", NULL, NULL, 1 << 6, 0, -1},
{"ManagerDesktop", NULL, NULL, 1 << 7, 0, -1},
{"Manager", NULL, NULL, 1 << 7, 0, -1},
{"InputLeap", NULL, NULL, 1 << 8, 1, -1},
{"Nextcloud", NULL, NULL, 1 << 8, 0, -1},
{"feishin", NULL, NULL, 1 << 2, 0, -1},
}; };
/* layout(s) */ /* layout(s) */
static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ static const float mfact = 0.50; /* 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 int refreshrate = 120; /* refresh rate (per second) for client move/resize */
static const Layout layouts[] = { static const Layout layouts[] = {
/* symbol arrange function */ /* symbol arrange function */
{ "[]=", tile }, /* first entry is default */ {"[]=", tile}, /* first entry is default */
{ "><>", NULL }, /* no layout function means floating behavior */ {"[M]", monocle},
{ "[M]", monocle }, {"><>", NULL}, /* no layout function means floating behavior */
}; };
/* key definitions */ /* key definitions */
#define MODKEY Mod1Mask #define MODKEY Mod1Mask
#define TAGKEYS(KEY,TAG) \ #define TAGKEYS(KEY,TAG) \
{ MODKEY, KEY, view, {.ui = 1 << TAG} }, \ &((Keychord){1, {{MODKEY, KEY}}, view, {.ui = 1 << TAG} }), \
{ MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ &((Keychord){1, {{MODKEY|ControlMask, KEY}}, toggleview, {.ui = 1 << TAG} }), \
{ MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ &((Keychord){1, {{MODKEY|ShiftMask, KEY}}, tag, {.ui = 1 << TAG} }), \
{ MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} }, &((Keychord){1, {{MODKEY|ControlMask|ShiftMask, KEY}}, toggletag, {.ui = 1 << TAG} }),
/* helper for spawning shell commands in the pre dwm-5.0 fashion */ /* helper for spawning shell commands in the pre dwm-5.0 fashion */
#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } #define SHCMD(cmd) { .v = (const char *[]) { "/bin/sh", "-c", cmd, NULL } }
/* some defaults */
#define TERMINAL "st"
#define STATUSBAR "dwmblocks"
/* commands */ /* commands */
static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */ 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 *dmenucmd[] = {"dmenu_run", NULL};
static const char *termcmd[] = { "st", NULL }; static const char *slockcmd[] = {"slock", NULL};
static const char *termcmd[] = {TERMINAL, "-e", "tmux-worker", NULL};
static const Key keys[] = { #include "movestack.c"
/* modifier key function argument */ #include "shift-tools.c"
{ MODKEY, XK_p, spawn, {.v = dmenucmd } }, #include <X11/XF86keysym.h>
{ MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } },
{ MODKEY, XK_b, togglebar, {0} }, static const Keychord *keychords[] = {
{ MODKEY, XK_j, focusstack, {.i = +1 } }, /* modifier key chord function argument */
{ MODKEY, XK_k, focusstack, {.i = -1 } }, // &((Keychord){1, {{MODKEY, XK_}}, spawn, {} }),
{ MODKEY, XK_i, incnmaster, {.i = +1 } }, &((Keychord){1, {{MODKEY | ControlMask | ShiftMask, XK_apostrophe}}, quit, {0} }),
{ MODKEY, XK_d, incnmaster, {.i = -1 } }, &((Keychord){1, {{MODKEY | ShiftMask, XK_apostrophe}}, quit, {1} }),
{ MODKEY, XK_h, setmfact, {.f = -0.05} },
{ MODKEY, XK_l, setmfact, {.f = +0.05} }, &((Keychord){1, {{MODKEY, XK_apostrophe}}, killclient, {0} }),
{ MODKEY, XK_Return, zoom, {0} },
{ MODKEY, XK_Tab, view, {0} }, &((Keychord){1, {{MODKEY, XK_a}}, spawn, {.v = dmenucmd} }),
{ MODKEY|ShiftMask, XK_c, killclient, {0} }, &((Keychord){1, {{MODKEY | ShiftMask, XK_Return}}, spawn, {.v = termcmd} }),
{ MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, &((Keychord){1, {{MODKEY, XK_Escape}}, spawn, {.v = slockcmd} }),
{ MODKEY, XK_f, setlayout, {.v = &layouts[1]} },
{ MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, &((Keychord){1, {{MODKEY, XK_l}}, shiftviewclients, {.i = +1} }),
{ MODKEY, XK_space, setlayout, {0} }, &((Keychord){1, {{MODKEY, XK_h}}, shiftviewclients, {.i = -1} }),
{ MODKEY|ShiftMask, XK_space, togglefloating, {0} }, &((Keychord){1, {{MODKEY | ShiftMask, XK_l}}, shiftview, {.i = +1} }),
{ MODKEY, XK_0, view, {.ui = ~0 } }, &((Keychord){1, {{MODKEY | ShiftMask, XK_h}}, shiftview, {.i = -1} }),
{ MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, &((Keychord){1, {{MODKEY | ControlMask, XK_l}}, shiftboth, {.i = +1} }),
{ MODKEY, XK_comma, focusmon, {.i = -1 } }, &((Keychord){1, {{MODKEY | ControlMask, XK_h}}, shiftboth, {.i = -1} }),
{ MODKEY, XK_period, focusmon, {.i = +1 } }, &((Keychord){1, {{MODKEY | ControlMask | ShiftMask, XK_l}}, shiftswaptags, {.i = +1} }),
{ MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, &((Keychord){1, {{MODKEY | ControlMask | ShiftMask, XK_h}}, shiftswaptags, {.i = -1} }),
{ MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } },
TAGKEYS( XK_1, 0) &((Keychord){1, {{MODKEY, XK_j}}, focusstack, {.i = +1} }),
TAGKEYS( XK_2, 1) &((Keychord){1, {{MODKEY, XK_k}}, focusstack, {.i = -1} }),
TAGKEYS( XK_3, 2) &((Keychord){1, {{MODKEY | ShiftMask, XK_v}}, incnmaster, {.i = +1} }),
TAGKEYS( XK_4, 3) &((Keychord){1, {{MODKEY | ShiftMask, XK_w}}, incnmaster, {.i = -1} }),
TAGKEYS( XK_5, 4) &((Keychord){1, {{MODKEY, XK_equal}}, setmfact, {.f = +0.05} }),
TAGKEYS( XK_6, 5) &((Keychord){1, {{MODKEY, XK_minus}}, setmfact, {.f = -0.05} }),
TAGKEYS( XK_7, 6) &((Keychord){1, {{MODKEY | ShiftMask, XK_j}}, movestack, {.i = +1} }),
TAGKEYS( XK_8, 7) &((Keychord){1, {{MODKEY | ShiftMask, XK_k}}, movestack, {.i = -1} }),
TAGKEYS( XK_9, 8) &((Keychord){1, {{MODKEY, XK_Return}}, zoom, {0} }),
{ MODKEY|ShiftMask, XK_q, quit, {0} }, &((Keychord){1, {{MODKEY, XK_Tab}}, view, {0} }),
&((Keychord){2, {{MODKEY, XK_w}, {MODKEY, XK_t}}, setlayout, {.v = &layouts[0]} }),
&((Keychord){2, {{MODKEY, XK_w}, {MODKEY, XK_m}}, setlayout, {.v = &layouts[1]} }),
&((Keychord){2, {{MODKEY, XK_w}, {MODKEY, XK_f}}, setlayout, {.v = &layouts[2]} }),
&((Keychord){1, {{MODKEY, XK_F11}}, fullscreen, {0} }),
&((Keychord){1, {{MODKEY, XK_space}}, setlayout, {0} }),
&((Keychord){1, {{MODKEY | ShiftMask, XK_space}}, togglefloating, {0} }),
&((Keychord){1, {{MODKEY, XK_b}}, togglebar, {0} }),
&((Keychord){1, {{MODKEY, XK_0}}, view, {.ui = ~0} }),
&((Keychord){1, {{MODKEY | ShiftMask, XK_0}}, tag, {.ui = ~0} }),
&((Keychord){1, {{MODKEY, XK_comma}}, focusmon, {.i = -1} }),
&((Keychord){1, {{MODKEY, XK_period}}, focusmon, {.i = +1} }),
&((Keychord){1, {{MODKEY | ShiftMask, XK_comma}}, tagmon, {.i = -1} }),
&((Keychord){1, {{MODKEY | ShiftMask, XK_period}}, tagmon, {.i = +1} }),
&((Keychord){1, {{MODKEY | ShiftMask, XK_s}}, spawn, {.v = (const char *[]){"sysact", NULL}} }),
&((Keychord){1, {{MODKEY | ShiftMask, XK_o}}, spawn, {.v = (const char *[]){"set-default-sink", NULL}} }),
&((Keychord){1, {{MODKEY | ShiftMask, XK_i}}, spawn, {.v = (const char *[]){"set-default-source", NULL}} }),
&((Keychord){2, {{MODKEY, XK_m}, {MODKEY, XK_a}}, spawn, {.v = (const char *[]){"dmenumusic", NULL}} }),
&((Keychord){2, {{MODKEY, XK_m}, {MODKEY, XK_p}}, spawn, {.v = (const char *[]){"mpc", "toggle", NULL}} }),
&((Keychord){2, {{MODKEY, XK_m}, {MODKEY, XK_l}}, spawn, {.v = (const char *[]){"mpc", "next", NULL}} }),
&((Keychord){2, {{MODKEY, XK_m}, {MODKEY, XK_h}}, spawn, {.v = (const char *[]){"mpc", "prev", NULL}} }),
&((Keychord){1, {{MODKEY, XF86XK_AudioPlay}}, spawn, {.v = (const char *[]){"mpc", "toggle", NULL}} }),
&((Keychord){1, {{MODKEY, XF86XK_AudioNext}}, spawn, {.v = (const char *[]){"mpc", "next", NULL}} }),
&((Keychord){1, {{MODKEY, XF86XK_AudioPrev}}, spawn, {.v = (const char *[]){"mpc", "prev", NULL}} }),
&((Keychord){2, {{MODKEY, XK_o}, {MODKEY, XK_l}}, spawn, {.v = (const char *[]){TERMINAL, "-e", "lf", NULL}} }),
&((Keychord){2, {{MODKEY, XK_o}, {MODKEY, XK_f}}, spawn, {.v = (const char *[]){"firefox", NULL}} }),
&((Keychord){2, {{MODKEY, XK_o}, {MODKEY, XK_n}}, spawn, {.v = (const char *[]){TERMINAL, "-e", "ncmpcpp", NULL}} }),
&((Keychord){2, {{MODKEY, XK_o}, {MODKEY, XK_m}}, spawn, {.v = (const char *[]){TERMINAL, "-e", "gotop", NULL}} }),
&((Keychord){2, {{MODKEY, XK_o}, {MODKEY, XK_t}}, spawn, {.v = (const char *[]){"thunar", NULL}} }),
&((Keychord){2, {{MODKEY, XK_o}, {MODKEY, XK_c}}, spawn, {.v = (const char *[]){"calcurse-select", NULL}} }),
&((Keychord){2, {{MODKEY, XK_d}, {MODKEY, XK_h}}, spawn, {.v = (const char *[]){"dmenuhandler", NULL}} }),
&((Keychord){2, {{MODKEY, XK_d}, {MODKEY, XK_m}}, spawn, {.v = (const char *[]){"mounter", NULL}} }),
&((Keychord){2, {{MODKEY, XK_d}, {MODKEY, XK_c}}, spawn, {.v = (const char *[]){"dmenumountcifs", NULL}} }),
&((Keychord){2, {{MODKEY, XK_d}, {MODKEY, XK_u}}, spawn, {.v = (const char *[]){"unmounter", NULL}} }),
&((Keychord){2, {{MODKEY, XK_d}, {MODKEY, XK_s}}, spawn, {.v = (const char *[]){"dmenusearch", "duckduckgo", NULL}} }),
&((Keychord){2, {{MODKEY, XK_d}, {MODKEY, XK_l}}, spawn, {.v = (const char *[]){"linkhandler", NULL}} }),
&((Keychord){2, {{MODKEY, XK_d}, {MODKEY, XK_b}}, spawn, SHCMD("dmenu-bluetooth -i -l 25") }),
&((Keychord){1, {{MODKEY | ShiftMask, XK_t}}, spawn, SHCMD("trayer-toggle trayer --edge top --align center --widthtype request " "--padding 6 --SetDockType true --SetPartialStrut true --expand " "true --transparent true --alpha 0 --tint 0x262626 --height 18 &") }),
&((Keychord){1, {{MODKEY | ControlMask, XK_period}}, spawn, {.v = (const char *[]){"sd", NULL}} }),
&((Keychord){1, {{MODKEY, XK_f}}, spawn, {.v = (const char *[]){"screenlayout", NULL}} }),
&((Keychord){1, {{MODKEY, XK_F1}}, spawn, {.v = (const char *[]){"kbswitcher", NULL}} }),
&((Keychord){1, {{0, XF86XK_AudioMute}}, spawn, SHCMD("wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle; kill -37 $(pidof " "dwmblocks)") }),
&((Keychord){1, {{0, XF86XK_AudioRaiseVolume}}, spawn, SHCMD("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+; kill -37 $(pidof " "dwmblocks)") }),
&((Keychord){1, {{0, XF86XK_AudioLowerVolume}}, spawn, SHCMD("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-; kill -37 $(pidof " "dwmblocks)") }),
&((Keychord){1, {{0, XF86XK_AudioMicMute}}, spawn, SHCMD("wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle; kill -37 $(pidof " "dwmblocks)") }),
&((Keychord){1, {{MODKEY, XK_v}}, spawn, {.v = (const char *[]){"setvol", NULL}} }),
&((Keychord){1, {{0, XF86XK_MonBrightnessUp}}, spawn, {.v = (const char *[]){"mod_backlight", "up", NULL}} }),
&((Keychord){1, {{0, XF86XK_MonBrightnessDown}}, spawn, {.v = (const char *[]){"mod_backlight", "down", NULL}} }),
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)
}; };
/* button definitions */ /* button definitions */
/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ /* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
static const Button buttons[] = { * ClkClientWin, or ClkRootWin */
/* click event mask button function argument */ static Button buttons[] = {
{ ClkLtSymbol, 0, Button1, setlayout, {0} }, /* click event mask button function argument */
{ ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, {ClkLtSymbol, 0, Button1, setlayout, {0}},
{ ClkWinTitle, 0, Button2, zoom, {0} }, {ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]}},
{ ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, {ClkWinTitle, 0, Button2, zoom, {0}},
{ ClkClientWin, MODKEY, Button1, movemouse, {0} }, {ClkStatusText, 0, Button1, sigstatusbar, {.i = 1}},
{ ClkClientWin, MODKEY, Button2, togglefloating, {0} }, {ClkStatusText, 0, Button2, sigstatusbar, {.i = 2}},
{ ClkClientWin, MODKEY, Button3, resizemouse, {0} }, {ClkStatusText, 0, Button3, sigstatusbar, {.i = 3}},
{ ClkTagBar, 0, Button1, view, {0} }, {ClkStatusText, 0, Button4, sigstatusbar, {.i = 4}},
{ ClkTagBar, 0, Button3, toggleview, {0} }, {ClkStatusText, 0, Button5, sigstatusbar, {.i = 5}},
{ ClkTagBar, MODKEY, Button1, tag, {0} }, {ClkStatusText, ShiftMask, Button1, sigstatusbar, {.i = 6}},
{ ClkTagBar, MODKEY, Button3, toggletag, {0} }, {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}},
}; };

View File

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

30
drw.c
View File

@ -178,8 +178,7 @@ drw_clr_create(Drw *drw, Clr *dest, const char *clrname)
die("error, cannot allocate color '%s'", clrname); die("error, cannot allocate color '%s'", clrname);
} }
/* Wrapper to create color schemes. The caller has to call free(3) on the /* Create color schemes. */
* returned color scheme when done using it. */
Clr * Clr *
drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount) drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
{ {
@ -187,7 +186,7 @@ drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
Clr *ret; Clr *ret;
/* need at least two colors for a scheme */ /* need at least two colors for a scheme */
if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor)))) if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(Clr))))
return NULL; return NULL;
for (i = 0; i < clrcount; i++) for (i = 0; i < clrcount; i++)
@ -195,6 +194,30 @@ drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
return ret; return ret;
} }
void
drw_clr_free(Drw *drw, Clr *c)
{
if (!drw || !c)
return;
/* c is typedef XftColor Clr */
XftColorFree(drw->dpy, DefaultVisual(drw->dpy, drw->screen),
DefaultColormap(drw->dpy, drw->screen), c);
}
void
drw_scm_free(Drw *drw, Clr *scm, size_t clrcount)
{
size_t i;
if (!drw || !scm)
return;
for (i = 0; i < clrcount; i++)
drw_clr_free(drw, &scm[i]);
free(scm);
}
void void
drw_setfontset(Drw *drw, Fnt *set) drw_setfontset(Drw *drw, Fnt *set)
{ {
@ -446,3 +469,4 @@ drw_cur_free(Drw *drw, Cur *cursor)
XFreeCursor(drw->dpy, cursor->cursor); XFreeCursor(drw->dpy, cursor->cursor);
free(cursor); free(cursor);
} }

2
drw.h
View File

@ -40,7 +40,9 @@ void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned in
/* Colorscheme abstraction */ /* Colorscheme abstraction */
void drw_clr_create(Drw *drw, Clr *dest, const char *clrname); void drw_clr_create(Drw *drw, Clr *dest, const char *clrname);
void drw_clr_free(Drw *drw, Clr *c);
Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount); Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount);
void drw_scm_free(Drw *drw, Clr *scm, size_t clrcount);
/* Cursor abstraction */ /* Cursor abstraction */
Cur *drw_cur_create(Drw *drw, int shape); Cur *drw_cur_create(Drw *drw, int shape);

88
dwm-attachtop-6.2.diff Normal file
View File

@ -0,0 +1,88 @@
From 17acbdcb56d0d2f39507a3f67ef329c14a213ef6 Mon Sep 17 00:00:00 2001
From: MLquest8 <miskuzius@gmail.com>
Date: Thu, 18 Jun 2020 15:34:18 +0400
Subject: [PATCH] attachtop. Attaches new client below the last master/on top
of the stack. In case of nmaster = 1 behaves like attachaside.
---
dwm.c | 29 +++++++++++++++++++++++++----
1 file changed, 25 insertions(+), 4 deletions(-)
diff --git a/dwm.c b/dwm.c
index 9fd0286..7ced982 100644
--- a/dwm.c
+++ b/dwm.c
@@ -49,7 +49,8 @@
#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask))
#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 ISVISIBLEONTAG(C, T) ((C->tags & T))
+#define ISVISIBLE(C) ISVISIBLEONTAG(C, C->mon->tagset[C->mon->seltags])
#define LENGTH(X) (sizeof X / sizeof X[0])
#define MOUSEMASK (BUTTONMASK|PointerMotionMask)
#define WIDTH(X) ((X)->w + 2 * (X)->bw)
@@ -147,6 +148,7 @@ static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interac
static void arrange(Monitor *m);
static void arrangemon(Monitor *m);
static void attach(Client *c);
+static void attachtop(Client *c);
static void attachstack(Client *c);
static void buttonpress(XEvent *e);
static void checkotherwm(void);
@@ -407,6 +409,25 @@ attach(Client *c)
c->mon->clients = c;
}
+void
+attachtop(Client *c)
+{
+ int n;
+ Monitor *m = selmon;
+ Client *below;
+
+ for (n = 1, below = c->mon->clients;
+ below && below->next && (below->isfloating || !ISVISIBLEONTAG(below, c->tags) || n != m->nmaster);
+ n = below->isfloating || !ISVISIBLEONTAG(below, c->tags) ? n + 0 : n + 1, below = below->next);
+ c->next = NULL;
+ if (below) {
+ c->next = below->next;
+ below->next = c;
+ }
+ else
+ c->mon->clients = c;
+}
+
void
attachstack(Client *c)
{
@@ -1063,7 +1084,7 @@ manage(Window w, XWindowAttributes *wa)
c->isfloating = c->oldstate = trans != None || c->isfixed;
if (c->isfloating)
XRaiseWindow(dpy, c->win);
- attach(c);
+ attachtop(c);
attachstack(c);
XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend,
(unsigned char *) &(c->win), 1);
@@ -1418,7 +1439,7 @@ sendmon(Client *c, Monitor *m)
detachstack(c);
c->mon = m;
c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */
- attach(c);
+ attachtop(c);
attachstack(c);
focus(NULL);
arrange(NULL);
@@ -1900,7 +1921,7 @@ updategeom(void)
m->clients = c->next;
detachstack(c);
c->mon = mons;
- attach(c);
+ attachtop(c);
attachstack(c);
}
if (m == selmon)
--
2.26.2

56
dwm-fullscreen-6.2.diff Normal file
View File

@ -0,0 +1,56 @@
From 54719285bd1a984e2efce6e8a8eab184fec11abf Mon Sep 17 00:00:00 2001
From: Sermak <sermak@jarvis.com>
Date: Mon, 8 Jul 2019 01:06:44 +0200
Subject: [PATCH] Simulate toggleable fullscreen mode
---
config.def.h | 1 +
dwm.c | 14 ++++++++++++++
2 files changed, 15 insertions(+)
diff --git a/config.def.h b/config.def.h
index 1c0b587..f774cc5 100644
--- a/config.def.h
+++ b/config.def.h
@@ -76,6 +76,7 @@ static Key keys[] = {
{ MODKEY, XK_t, setlayout, {.v = &layouts[0]} },
{ MODKEY, XK_f, setlayout, {.v = &layouts[1]} },
{ MODKEY, XK_m, setlayout, {.v = &layouts[2]} },
+ { MODKEY|ShiftMask, XK_f, fullscreen, {0} },
{ MODKEY, XK_space, setlayout, {0} },
{ MODKEY|ShiftMask, XK_space, togglefloating, {0} },
{ MODKEY, XK_0, view, {.ui = ~0 } },
diff --git a/dwm.c b/dwm.c
index 4465af1..04b1e06 100644
--- a/dwm.c
+++ b/dwm.c
@@ -199,6 +199,7 @@ static void sendmon(Client *c, Monitor *m);
static void setclientstate(Client *c, long state);
static void setfocus(Client *c);
static void setfullscreen(Client *c, int fullscreen);
+static void fullscreen(const Arg *arg);
static void setlayout(const Arg *arg);
static void setmfact(const Arg *arg);
static void setup(void);
@@ -1497,6 +1498,19 @@ setfullscreen(Client *c, int fullscreen)
}
}
+Layout *last_layout;
+void
+fullscreen(const Arg *arg)
+{
+ if (selmon->showbar) {
+ for(last_layout = (Layout *)layouts; last_layout != selmon->lt[selmon->sellt]; last_layout++);
+ setlayout(&((Arg) { .v = &layouts[2] }));
+ } else {
+ setlayout(&((Arg) { .v = last_layout }));
+ }
+ togglebar(arg);
+}
+
void
setlayout(const Arg *arg)
{
--
2.22.0

234
dwm-keychord-6.4.diff Normal file
View File

@ -0,0 +1,234 @@
From cb7ea178ac8e60cf123b333af64df8228762f669 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Aaron=20Z=C3=BCger?= <contact@azureorange.xyz>
Date: Wed, 19 Jul 2023 14:17:39 +0200
Subject: [PATCH] Update dwm-keychord patch to comply with the changes in dwm
from version 6.2 to version 6.4. Namely changes in the grabkey function to
match the newer implementation of said function.
---
config.def.h | 81 ++++++++++++++++++++++++++--------------------------
dwm.c | 72 +++++++++++++++++++++++++++++++++++++---------
2 files changed, 99 insertions(+), 54 deletions(-)
diff --git a/config.def.h b/config.def.h
index 9efa774..49f0558 100644
--- a/config.def.h
+++ b/config.def.h
@@ -46,11 +46,11 @@ static const Layout layouts[] = {
/* key definitions */
#define MODKEY Mod1Mask
-#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} },
+#define TAGKEYS(KEY,TAG) \
+ &((Keychord){1, {{MODKEY, KEY}}, view, {.ui = 1 << TAG} }), \
+ &((Keychord){1, {{MODKEY|ControlMask, KEY}}, toggleview, {.ui = 1 << TAG} }), \
+ &((Keychord){1, {{MODKEY|ShiftMask, KEY}}, tag, {.ui = 1 << TAG} }), \
+ &((Keychord){1, {{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 } }
@@ -60,41 +60,42 @@ 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 Key keys[] = {
- /* modifier key function argument */
- { MODKEY, XK_p, spawn, {.v = dmenucmd } },
- { 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_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} },
+static Keychord *keychords[] = {
+ /* Keys function argument */
+ &((Keychord){1, {{MODKEY, XK_p}}, spawn, {.v = dmenucmd } }),
+ &((Keychord){1, {{MODKEY|ShiftMask, XK_Return}}, spawn, {.v = termcmd } }),
+ &((Keychord){2, {{MODKEY, XK_e}, {MODKEY, XK_e}}, spawn, {.v = termcmd } }),
+ &((Keychord){1, {{MODKEY, XK_b}}, togglebar, {0} }),
+ &((Keychord){1, {{MODKEY, XK_j}}, focusstack, {.i = +1 } }),
+ &((Keychord){1, {{MODKEY, XK_k}}, focusstack, {.i = -1 } }),
+ &((Keychord){1, {{MODKEY, XK_i}}, incnmaster, {.i = +1 } }),
+ &((Keychord){1, {{MODKEY, XK_d}}, incnmaster, {.i = -1 } }),
+ &((Keychord){1, {{MODKEY, XK_h}}, setmfact, {.f = -0.05} }),
+ &((Keychord){1, {{MODKEY, XK_l}}, setmfact, {.f = +0.05} }),
+ &((Keychord){1, {{MODKEY, XK_Return}}, zoom, {0} }),
+ &((Keychord){1, {{MODKEY, XK_Tab}}, view, {0} }),
+ &((Keychord){1, {{MODKEY|ShiftMask, XK_c}}, killclient, {0} }),
+ &((Keychord){1, {{MODKEY, XK_t}}, setlayout, {.v = &layouts[0]} }),
+ &((Keychord){1, {{MODKEY, XK_f}}, setlayout, {.v = &layouts[1]} }),
+ &((Keychord){1, {{MODKEY, XK_m}}, setlayout, {.v = &layouts[2]} }),
+ &((Keychord){1, {{MODKEY, XK_space}}, setlayout, {0} }),
+ &((Keychord){1, {{MODKEY|ShiftMask, XK_space}}, togglefloating, {0} }),
+ &((Keychord){1, {{MODKEY, XK_0}}, view, {.ui = ~0 } }),
+ &((Keychord){1, {{MODKEY|ShiftMask, XK_0}}, tag, {.ui = ~0 } }),
+ &((Keychord){1, {{MODKEY, XK_comma}}, focusmon, {.i = -1 } }),
+ &((Keychord){1, {{MODKEY, XK_period}}, focusmon, {.i = +1 } }),
+ &((Keychord){1, {{MODKEY|ShiftMask, XK_comma}}, tagmon, {.i = -1 } }),
+ &((Keychord){1, {{MODKEY|ShiftMask, XK_period}}, tagmon, {.i = +1 } }),
+ &((Keychord){1, {{MODKEY|ShiftMask, XK_q}}, quit, {0} }),
+ 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)
};
/* button definitions */
diff --git a/dwm.c b/dwm.c
index f1d86b2..e4885a4 100644
--- a/dwm.c
+++ b/dwm.c
@@ -102,9 +102,14 @@ struct Client {
typedef struct {
unsigned int mod;
KeySym keysym;
+} Key;
+
+typedef struct {
+ unsigned int n;
+ const Key keys[5];
void (*func)(const Arg *);
const Arg arg;
-} Key;
+} Keychord;
typedef struct {
const char *symbol;
@@ -267,6 +272,7 @@ static Display *dpy;
static Drw *drw;
static Monitor *mons, *selmon;
static Window root, wmcheckwin;
+unsigned int currentkey = 0;
/* configuration, allows nested code to access above variables */
#include "config.h"
@@ -954,7 +960,8 @@ grabkeys(void)
{
updatenumlockmask();
{
- unsigned int i, j, k;
+ /* unsigned int i, j, k; */
+ unsigned int i, c, k;
unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
int start, end, skip;
KeySym *syms;
@@ -964,15 +971,18 @@ grabkeys(void)
syms = XGetKeyboardMapping(dpy, start, end - start + 1, &skip);
if (!syms)
return;
+
for (k = start; k <= end; k++)
- for (i = 0; i < LENGTH(keys); i++)
+ for (i = 0; i < LENGTH(keychords); i++)
/* skip modifier codes, we do that ourselves */
- if (keys[i].keysym == syms[(k - start) * skip])
- for (j = 0; j < LENGTH(modifiers); j++)
+ if (keychords[i]->keys[currentkey].keysym == syms[(k - start) * skip])
+ for (c = 0; c < LENGTH(modifiers); c++)
XGrabKey(dpy, k,
- keys[i].mod | modifiers[j],
+ keychords[i]->keys[currentkey].mod | modifiers[c],
root, True,
GrabModeAsync, GrabModeAsync);
+ if(currentkey > 0)
+ XGrabKey(dpy, XKeysymToKeycode(dpy, XK_Escape), AnyModifier, root, True, GrabModeAsync, GrabModeAsync);
XFree(syms);
}
}
@@ -999,17 +1009,51 @@ isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info)
void
keypress(XEvent *e)
{
- unsigned int i;
+ /* unsigned int i; */
+ XEvent event = *e;
+ unsigned int ran = 0;
KeySym keysym;
XKeyEvent *ev;
- ev = &e->xkey;
- keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);
- for (i = 0; i < LENGTH(keys); i++)
- if (keysym == keys[i].keysym
- && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)
- && keys[i].func)
- keys[i].func(&(keys[i].arg));
+ Keychord *arr1[sizeof(keychords) / sizeof(Keychord*)];
+ Keychord *arr2[sizeof(keychords) / sizeof(Keychord*)];
+ memcpy(arr1, keychords, sizeof(keychords));
+ Keychord **rpointer = arr1;
+ Keychord **wpointer = arr2;
+
+ size_t r = sizeof(keychords)/ sizeof(Keychord*);
+
+ while(1){
+ ev = &event.xkey;
+ keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);
+ size_t w = 0;
+ for (int i = 0; i < r; i++){
+ if(keysym == (*(rpointer + i))->keys[currentkey].keysym
+ && CLEANMASK((*(rpointer + i))->keys[currentkey].mod) == CLEANMASK(ev->state)
+ && (*(rpointer + i))->func){
+ if((*(rpointer + i))->n == currentkey +1){
+ (*(rpointer + i))->func(&((*(rpointer + i))->arg));
+ ran = 1;
+ }else{
+ *(wpointer + w) = *(rpointer + i);
+ w++;
+ }
+ }
+ }
+ currentkey++;
+ if(w == 0 || ran == 1)
+ break;
+ grabkeys();
+ while (running && !XNextEvent(dpy, &event) && !ran)
+ if(event.type == KeyPress)
+ break;
+ r = w;
+ Keychord **holder = rpointer;
+ rpointer = wpointer;
+ wpointer = holder;
+ }
+ currentkey = 0;
+ grabkeys();
}
void
--
2.41.0

View File

@ -0,0 +1,95 @@
From 9a4037dc0ef56f91c009317e78e9e3790dafbb58 Mon Sep 17 00:00:00 2001
From: BrunoCooper17 <BrunoCooper17@outlook.com>
Date: Mon, 15 Nov 2021 14:04:53 -0600
Subject: [PATCH] MoveStack patch
This plugin allows you to move clients around in the stack and swap them
with the master. It emulates the behavior off mod+shift+j and mod+shift+k
in Xmonad. movestack(+1) will swap the client with the current focus with
the next client. movestack(-1) will swap the client with the current focus
with the previous client.
---
config.def.h | 3 +++
movestack.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 51 insertions(+)
create mode 100644 movestack.c
diff --git a/config.def.h b/config.def.h
index a2ac963..33efa5b 100644
--- a/config.def.h
+++ b/config.def.h
@@ -60,6 +60,7 @@ 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 };
+#include "movestack.c"
static Key keys[] = {
/* modifier key function argument */
{ MODKEY, XK_p, spawn, {.v = dmenucmd } },
@@ -71,6 +72,8 @@ static Key keys[] = {
{ MODKEY, XK_d, incnmaster, {.i = -1 } },
{ MODKEY, XK_h, setmfact, {.f = -0.05} },
{ MODKEY, XK_l, setmfact, {.f = +0.05} },
+ { MODKEY|ShiftMask, XK_j, movestack, {.i = +1 } },
+ { MODKEY|ShiftMask, XK_k, movestack, {.i = -1 } },
{ MODKEY, XK_Return, zoom, {0} },
{ MODKEY, XK_Tab, view, {0} },
{ MODKEY|ShiftMask, XK_c, killclient, {0} },
diff --git a/movestack.c b/movestack.c
new file mode 100644
index 0000000..520f4ae
--- /dev/null
+++ b/movestack.c
@@ -0,0 +1,48 @@
+void
+movestack(const Arg *arg) {
+ Client *c = NULL, *p = NULL, *pc = NULL, *i;
+
+ if(arg->i > 0) {
+ /* find the client after selmon->sel */
+ for(c = selmon->sel->next; c && (!ISVISIBLE(c) || c->isfloating); c = c->next);
+ if(!c)
+ for(c = selmon->clients; c && (!ISVISIBLE(c) || c->isfloating); c = c->next);
+
+ }
+ else {
+ /* find the client before selmon->sel */
+ for(i = selmon->clients; i != selmon->sel; i = i->next)
+ if(ISVISIBLE(i) && !i->isfloating)
+ c = i;
+ if(!c)
+ for(; i; i = i->next)
+ if(ISVISIBLE(i) && !i->isfloating)
+ c = i;
+ }
+ /* find the client before selmon->sel and c */
+ for(i = selmon->clients; i && (!p || !pc); i = i->next) {
+ if(i->next == selmon->sel)
+ p = i;
+ if(i->next == c)
+ pc = i;
+ }
+
+ /* swap c and selmon->sel selmon->clients in the selmon->clients list */
+ if(c && c != selmon->sel) {
+ Client *temp = selmon->sel->next==c?selmon->sel:selmon->sel->next;
+ selmon->sel->next = c->next==selmon->sel?c:c->next;
+ c->next = temp;
+
+ if(p && p != c)
+ p->next = c;
+ if(pc && pc != selmon->sel)
+ pc->next = selmon->sel;
+
+ if(selmon->sel == selmon->clients)
+ selmon->clients = c;
+ else if(c == selmon->clients)
+ selmon->clients = selmon->sel;
+
+ arrange(selmon);
+ }
+}
\ No newline at end of file
--
2.33.1

View File

@ -0,0 +1,177 @@
diff --git a/dwm.c b/dwm.c
index 664c527..ac8e4ec 100644
--- a/dwm.c
+++ b/dwm.c
@@ -111,6 +111,7 @@ typedef struct {
void (*arrange)(Monitor *);
} Layout;
+typedef struct Pertag Pertag;
struct Monitor {
char ltsymbol[16];
float mfact;
@@ -130,6 +131,7 @@ struct Monitor {
Monitor *next;
Window barwin;
const Layout *lt[2];
+ Pertag *pertag;
};
typedef struct {
@@ -272,6 +274,15 @@ static Window root, wmcheckwin;
/* configuration, allows nested code to access above variables */
#include "config.h"
+struct Pertag {
+ unsigned int curtag, prevtag; /* current and previous tag */
+ int nmasters[LENGTH(tags) + 1]; /* number of windows in master area */
+ float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */
+ unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */
+ const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes */
+ int showbars[LENGTH(tags) + 1]; /* display bar for the current tag */
+};
+
/* compile-time check if all tags fit into an unsigned int bit array. */
struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; };
@@ -632,6 +643,7 @@ Monitor *
createmon(void)
{
Monitor *m;
+ unsigned int i;
m = ecalloc(1, sizeof(Monitor));
m->tagset[0] = m->tagset[1] = 1;
@@ -642,6 +654,20 @@ createmon(void)
m->lt[0] = &layouts[0];
m->lt[1] = &layouts[1 % LENGTH(layouts)];
strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
+ m->pertag = ecalloc(1, sizeof(Pertag));
+ m->pertag->curtag = m->pertag->prevtag = 1;
+
+ for (i = 0; i <= LENGTH(tags); i++) {
+ m->pertag->nmasters[i] = m->nmaster;
+ m->pertag->mfacts[i] = m->mfact;
+
+ m->pertag->ltidxs[i][0] = m->lt[0];
+ m->pertag->ltidxs[i][1] = m->lt[1];
+ m->pertag->sellts[i] = m->sellt;
+
+ m->pertag->showbars[i] = m->showbar;
+ }
+
return m;
}
@@ -967,7 +993,7 @@ grabkeys(void)
void
incnmaster(const Arg *arg)
{
- selmon->nmaster = MAX(selmon->nmaster + arg->i, 0);
+ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->nmaster + arg->i, 0);
arrange(selmon);
}
@@ -1502,9 +1528,9 @@ void
setlayout(const Arg *arg)
{
if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt])
- selmon->sellt ^= 1;
+ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag] ^= 1;
if (arg && arg->v)
- selmon->lt[selmon->sellt] = (Layout *)arg->v;
+ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v;
strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol);
if (selmon->sel)
arrange(selmon);
@@ -1523,7 +1549,7 @@ setmfact(const Arg *arg)
f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0;
if (f < 0.05 || f > 0.95)
return;
- selmon->mfact = f;
+ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag] = f;
arrange(selmon);
}
@@ -1702,7 +1728,7 @@ tile(Monitor *m)
void
togglebar(const Arg *arg)
{
- selmon->showbar = !selmon->showbar;
+ selmon->showbar = selmon->pertag->showbars[selmon->pertag->curtag] = !selmon->showbar;
updatebarpos(selmon);
XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh);
arrange(selmon);
@@ -1741,9 +1767,33 @@ void
toggleview(const Arg *arg)
{
unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK);
+ int i;
if (newtagset) {
selmon->tagset[selmon->seltags] = newtagset;
+
+ if (newtagset == ~0) {
+ selmon->pertag->prevtag = selmon->pertag->curtag;
+ selmon->pertag->curtag = 0;
+ }
+
+ /* test if the user did not select the same tag */
+ if (!(newtagset & 1 << (selmon->pertag->curtag - 1))) {
+ selmon->pertag->prevtag = selmon->pertag->curtag;
+ for (i = 0; !(newtagset & 1 << i); i++) ;
+ selmon->pertag->curtag = i + 1;
+ }
+
+ /* apply settings for this view */
+ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag];
+ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag];
+ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag];
+ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt];
+ selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1];
+
+ if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag])
+ togglebar(NULL);
+
focus(NULL);
arrange(selmon);
}
@@ -2038,11 +2088,37 @@ updatewmhints(Client *c)
void
view(const Arg *arg)
{
+ int i;
+ unsigned int tmptag;
+
if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
return;
selmon->seltags ^= 1; /* toggle sel tagset */
- if (arg->ui & TAGMASK)
+ if (arg->ui & TAGMASK) {
selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
+ selmon->pertag->prevtag = selmon->pertag->curtag;
+
+ if (arg->ui == ~0)
+ selmon->pertag->curtag = 0;
+ else {
+ for (i = 0; !(arg->ui & 1 << i); i++) ;
+ selmon->pertag->curtag = i + 1;
+ }
+ } else {
+ tmptag = selmon->pertag->prevtag;
+ selmon->pertag->prevtag = selmon->pertag->curtag;
+ selmon->pertag->curtag = tmptag;
+ }
+
+ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag];
+ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag];
+ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag];
+ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt];
+ selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1];
+
+ if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag])
+ togglebar(NULL);
+
focus(NULL);
arrange(selmon);
}

View File

@ -0,0 +1,139 @@
From 2991f37f0aaf44b9f9b11e7893ff0af8eb88f649 Mon Sep 17 00:00:00 2001
From: Christopher Drelich <cd@cdrakka.com>
Date: Wed, 23 May 2018 22:50:38 -0400
Subject: [PATCH] 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
---
config.def.h | 1 +
dwm.1 | 10 ++++++++++
dwm.c | 22 ++++++++++++++++++++++
3 files changed, 33 insertions(+)
diff --git a/config.def.h b/config.def.h
index a9ac303..e559429 100644
--- a/config.def.h
+++ b/config.def.h
@@ -94,6 +94,7 @@ static Key keys[] = {
TAGKEYS( XK_8, 7)
TAGKEYS( XK_9, 8)
{ MODKEY|ShiftMask, XK_q, quit, {0} },
+ { MODKEY|ControlMask|ShiftMask, XK_q, quit, {1} },
};
/* button definitions */
diff --git a/dwm.1 b/dwm.1
index 13b3729..36a331c 100644
--- a/dwm.1
+++ b/dwm.1
@@ -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)
diff --git a/dwm.c b/dwm.c
index bb95e26..286eecd 100644
--- a/dwm.c
+++ b/dwm.c
@@ -205,6 +205,8 @@ static void setup(void);
static void seturgent(Client *c, int urg);
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 tag(const Arg *arg);
static void tagmon(const Arg *arg);
@@ -260,6 +262,7 @@ 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;
@@ -1248,6 +1251,7 @@ propertynotify(XEvent *e)
void
quit(const Arg *arg)
{
+ if(arg->i) restart = 1;
running = 0;
}
@@ -1536,6 +1540,9 @@ setup(void)
/* clean up any zombies immediately */
sigchld(0);
+ signal(SIGHUP, sighup);
+ signal(SIGTERM, sigterm);
+
/* init screen */
screen = DefaultScreen(dpy);
sw = DisplayWidth(dpy, screen);
@@ -1637,6 +1644,20 @@ sigchld(int unused)
}
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)
{
if (arg->v == dmenucmd)
@@ -2139,6 +2160,7 @@ main(int argc, char *argv[])
setup();
scan();
run();
+ if(restart) execvp(argv[0], argv);
cleanup();
XCloseDisplay(dpy);
return EXIT_SUCCESS;
--
2.7.4

198
dwm-shif-tools-6.2.diff Normal file
View File

@ -0,0 +1,198 @@
From d57c8508c9f26be40667d402a2daaa2b27ae759f Mon Sep 17 00:00:00 2001
From: explosion-mental <explosion0mental@gmail.com>
Date: Wed, 11 Aug 2021 21:05:44 -0500
Subject: [PATCH] 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
https://github.com/moizifty/DWM-Build/blob/65379c62640788881486401a0d8c79333751b02f/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.
---
config.def.h | 9 ++++
shift-tools.c | 135 ++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 144 insertions(+)
create mode 100644 shift-tools.c
diff --git a/config.def.h b/config.def.h
index 1c0b587..1390d17 100644
--- a/config.def.h
+++ b/config.def.h
@@ -58,9 +58,14 @@ static const Layout layouts[] = {
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 };
+#include "shift-tools.c"
static Key keys[] = {
/* modifier key function argument */
+ { MODKEY, XK_o, shiftviewclients, { .i = +1 } },
+ { MODKEY|ShiftMask, XK_o, shiftview, { .i = +1 } },
+ { MODKEY|ShiftMask, XK_i, shiftview, { .i = -1 } },
+ { MODKEY, XK_i, shiftviewclients, { .i = -1 } },
{ MODKEY, XK_p, spawn, {.v = dmenucmd } },
{ MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } },
{ MODKEY, XK_b, togglebar, {0} },
@@ -69,6 +74,10 @@ static Key keys[] = {
{ 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} },
diff --git a/shift-tools.c b/shift-tools.c
new file mode 100644
index 0000000..cf130c8
--- /dev/null
+++ b/shift-tools.c
@@ -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);
+}
--
2.32.0

View File

@ -0,0 +1,220 @@
From ca2a2e6386a746ebfc3480787e5d99da11e7abee Mon Sep 17 00:00:00 2001
From: Justinas Grigas <dev@jstnas.com>
Date: Wed, 9 Oct 2024 01:00:20 +0100
Subject: [PATCH] [dwm][statuscmd] better click regions
The main improvement of this patch over the previous version 20210405 is that
the click region now ends on a matching signal raw byte.
The matching byte is optional, and without it dwm will behave as before.
To take advantage of this feature, scripts need to be modified to print the raw
byte at the end as well.
In addition, this patch cleanly applies onto master branch.
---
config.def.h | 6 ++-
dwm.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 104 insertions(+), 6 deletions(-)
diff --git a/config.def.h b/config.def.h
index 9efa774..d008275 100644
--- a/config.def.h
+++ b/config.def.h
@@ -55,6 +55,8 @@ static const Layout layouts[] = {
/* helper for spawning shell commands in the pre dwm-5.0 fashion */
#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
+#define STATUSBAR "dwmblocks"
+
/* 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 };
@@ -104,7 +106,9 @@ static const Button buttons[] = {
{ ClkLtSymbol, 0, Button1, setlayout, {0} },
{ ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} },
{ ClkWinTitle, 0, Button2, zoom, {0} },
- { ClkStatusText, 0, Button2, spawn, {.v = termcmd } },
+ { ClkStatusText, 0, Button1, sigstatusbar, {.i = 1} },
+ { ClkStatusText, 0, Button2, sigstatusbar, {.i = 2} },
+ { ClkStatusText, 0, Button3, sigstatusbar, {.i = 3} },
{ ClkClientWin, MODKEY, Button1, movemouse, {0} },
{ ClkClientWin, MODKEY, Button2, togglefloating, {0} },
{ ClkClientWin, MODKEY, Button3, resizemouse, {0} },
diff --git a/dwm.c b/dwm.c
index 1443802..94ee0c7 100644
--- a/dwm.c
+++ b/dwm.c
@@ -171,6 +171,7 @@ static void focusstack(const Arg *arg);
static Atom getatomprop(Client *c, Atom prop);
static int getrootptr(int *x, int *y);
static long getstate(Window w);
+static pid_t getstatusbarpid();
static int gettextprop(Window w, Atom atom, char *text, unsigned int size);
static void grabbuttons(Client *c, int focused);
static void grabkeys(void);
@@ -204,6 +205,7 @@ static void setmfact(const Arg *arg);
static void setup(void);
static void seturgent(Client *c, int urg);
static void showhide(Client *c);
+static void sigstatusbar(const Arg *arg);
static void spawn(const Arg *arg);
static void tag(const Arg *arg);
static void tagmon(const Arg *arg);
@@ -236,6 +238,9 @@ static void zoom(const Arg *arg);
/* variables */
static const char broken[] = "broken";
static char stext[256];
+static int statusw;
+static int statussig;
+static pid_t statuspid = -1;
static int screen;
static int sw, sh; /* X display screen geometry width, height */
static int bh; /* bar height */
@@ -422,6 +427,7 @@ buttonpress(XEvent *e)
Client *c;
Monitor *m;
XButtonPressedEvent *ev = &e->xbutton;
+ char *text, *s, ch;
click = ClkRootWin;
/* focus monitor if necessary */
@@ -440,9 +446,27 @@ 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))
+ else if (ev->x > selmon->ww - statusw) {
+ x = selmon->ww - statusw;
click = ClkStatusText;
- else
+ statussig = 0;
+ for (text = s = stext; *s && x <= ev->x; s++) {
+ if ((unsigned char)(*s) < ' ') {
+ ch = *s;
+ *s = '\0';
+ x += TEXTW(text) - lrpad;
+ *s = ch;
+ text = s + 1;
+ if (x >= ev->x)
+ break;
+ /* reset on matching signal raw byte */
+ if (ch == statussig)
+ statussig = 0;
+ else
+ statussig = ch;
+ }
+ }
+ } else
click = ClkWinTitle;
} else if ((c = wintoclient(ev->window))) {
focus(c);
@@ -708,9 +732,24 @@ drawbar(Monitor *m)
/* draw status first so it can be overdrawn by tags later */
if (m == selmon) { /* status is only drawn on selected monitor */
+ char *text, *s, ch;
drw_setscheme(drw, scheme[SchemeNorm]);
- tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */
- drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0);
+
+ x = 0;
+ for (text = s = stext; *s; s++) {
+ if ((unsigned char)(*s) < ' ') {
+ ch = *s;
+ *s = '\0';
+ tw = TEXTW(text) - lrpad;
+ drw_text(drw, m->ww - statusw + x, 0, tw, bh, 0, text, 0);
+ x += tw;
+ *s = ch;
+ text = s + 1;
+ }
+ }
+ tw = TEXTW(text) - lrpad + 2;
+ drw_text(drw, m->ww - statusw + x, 0, tw, bh, 0, text, 0);
+ tw = statusw;
}
for (c = m->clients; c; c = c->next) {
@@ -876,6 +915,30 @@ getatomprop(Client *c, Atom prop)
return atom;
}
+pid_t
+getstatusbarpid()
+{
+ char buf[32], *str = buf, *c;
+ FILE *fp;
+
+ if (statuspid > 0) {
+ snprintf(buf, sizeof(buf), "/proc/%u/cmdline", statuspid);
+ if ((fp = fopen(buf, "r"))) {
+ fgets(buf, sizeof(buf), fp);
+ while ((c = strchr(str, '/')))
+ str = c + 1;
+ fclose(fp);
+ if (!strcmp(str, STATUSBAR))
+ return statuspid;
+ }
+ }
+ if (!(fp = popen("pidof -s "STATUSBAR, "r")))
+ return -1;
+ fgets(buf, sizeof(buf), fp);
+ pclose(fp);
+ return strtol(buf, NULL, 10);
+}
+
int
getrootptr(int *x, int *y)
{
@@ -1643,6 +1706,20 @@ showhide(Client *c)
}
}
+void
+sigstatusbar(const Arg *arg)
+{
+ union sigval sv;
+
+ if (!statussig)
+ return;
+ sv.sival_int = arg->i;
+ if ((statuspid = getstatusbarpid()) <= 0)
+ return;
+
+ sigqueue(statuspid, SIGRTMIN+statussig, sv);
+}
+
void
spawn(const Arg *arg)
{
@@ -2004,8 +2081,25 @@ updatesizehints(Client *c)
void
updatestatus(void)
{
- if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext)))
+ if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) {
strcpy(stext, "dwm-"VERSION);
+ statusw = TEXTW(stext) - lrpad + 2;
+ } else {
+ char *text, *s, ch;
+
+ statusw = 0;
+ for (text = s = stext; *s; s++) {
+ if ((unsigned char)(*s) < ' ') {
+ ch = *s;
+ *s = '\0';
+ statusw += TEXTW(text) - lrpad;
+ *s = ch;
+ text = s + 1;
+ }
+ }
+ statusw += TEXTW(text) - lrpad + 2;
+
+ }
drawbar(selmon);
}
--
2.46.2

View File

@ -0,0 +1,101 @@
From 58414bee958f2e7ed91d6fe31f503ec4a406981b Mon Sep 17 00:00:00 2001
From: cirala <thim@cederlund.de>
Date: Fri, 19 Nov 2021 18:14:07 +0100
Subject: [PATCH] Fix for dwm-uselessgap
Previous versions of the patch doubles the
gap between the master and slave stacks.
---
config.def.h | 3 ++-
dwm.c | 38 +++++++++++++++++++++++++++++++-------
2 files changed, 33 insertions(+), 8 deletions(-)
diff --git a/config.def.h b/config.def.h
index a2ac963..17a205f 100644
--- a/config.def.h
+++ b/config.def.h
@@ -2,6 +2,7 @@
/* appearance */
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 int showbar = 1; /* 0 means no bar */
static const int topbar = 1; /* 0 means bottom bar */
@@ -34,7 +35,7 @@ static const Rule rules[] = {
/* 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 = 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 Layout layouts[] = {
diff --git a/dwm.c b/dwm.c
index 5e4d494..b626e89 100644
--- a/dwm.c
+++ b/dwm.c
@@ -52,8 +52,8 @@
#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]))
#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)
@@ -1277,12 +1277,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 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);
configure(c);
XSync(dpy, False);
@@ -1688,7 +1712,7 @@ tile(Monitor *m)
for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++)
if (i < m->nmaster) {
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)
my += HEIGHT(c);
} else {
--
2.33.1

10
dwm.1
View File

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

414
dwm.c
View File

@ -20,7 +20,7 @@
* *
* To understand everything else, start reading main(). * To understand everything else, start reading main().
*/ */
#include <errno.h>
#include <locale.h> #include <locale.h>
#include <signal.h> #include <signal.h>
#include <stdarg.h> #include <stdarg.h>
@ -49,10 +49,11 @@
#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) #define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask))
#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 ISVISIBLEONTAG(C, T) ((C->tags & T))
#define ISVISIBLE(C) ISVISIBLEONTAG(C, C->mon->tagset[C->mon->seltags])
#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)
@ -101,15 +102,21 @@ struct Client {
typedef struct { typedef struct {
unsigned int mod; unsigned int mod;
KeySym keysym; KeySym keysym;
} Key;
typedef struct {
unsigned int n;
const Key keys[5];
void (*func)(const Arg *); void (*func)(const Arg *);
const Arg arg; const Arg arg;
} Key; } Keychord;
typedef struct { typedef struct {
const char *symbol; const char *symbol;
void (*arrange)(Monitor *); void (*arrange)(Monitor *);
} Layout; } Layout;
typedef struct Pertag Pertag;
struct Monitor { struct Monitor {
char ltsymbol[16]; char ltsymbol[16];
float mfact; float mfact;
@ -129,6 +136,7 @@ struct Monitor {
Monitor *next; Monitor *next;
Window barwin; Window barwin;
const Layout *lt[2]; const Layout *lt[2];
Pertag *pertag;
}; };
typedef struct { typedef struct {
@ -146,6 +154,7 @@ static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interac
static void arrange(Monitor *m); static void arrange(Monitor *m);
static void arrangemon(Monitor *m); static void arrangemon(Monitor *m);
static void attach(Client *c); static void attach(Client *c);
static void attachtop(Client *c);
static void attachstack(Client *c); static void attachstack(Client *c);
static void buttonpress(XEvent *e); static void buttonpress(XEvent *e);
static void checkotherwm(void); static void checkotherwm(void);
@ -171,6 +180,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 pid_t getstatusbarpid();
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);
@ -199,11 +209,16 @@ 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);
static void setfullscreen(Client *c, int fullscreen); static void setfullscreen(Client *c, int fullscreen);
static void fullscreen(const Arg *arg);
static void setlayout(const Arg *arg); static void setlayout(const Arg *arg);
static void setmfact(const Arg *arg); static void setmfact(const Arg *arg);
static void setup(void); 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 sigchld(int unused);
static void sighup(int unused);
static void sigterm(int unused);
static void sigstatusbar(const Arg *arg);
static void spawn(const Arg *arg); static void spawn(const Arg *arg);
static void tag(const Arg *arg); static void tag(const Arg *arg);
static void tagmon(const Arg *arg); static void tagmon(const Arg *arg);
@ -236,6 +251,9 @@ static void zoom(const Arg *arg);
/* variables */ /* variables */
static const char broken[] = "broken"; static const char broken[] = "broken";
static char stext[256]; static char stext[256];
static int statusw;
static int statussig;
static pid_t statuspid = -1;
static int screen; static int screen;
static int sw, sh; /* X display screen geometry width, height */ static int sw, sh; /* X display screen geometry width, height */
static int bh; /* bar height */ static int bh; /* bar height */
@ -259,6 +277,7 @@ static void (*handler[LASTEvent]) (XEvent *) = {
[UnmapNotify] = unmapnotify [UnmapNotify] = unmapnotify
}; };
static Atom wmatom[WMLast], netatom[NetLast]; static Atom wmatom[WMLast], netatom[NetLast];
static int restart = 0;
static int running = 1; static int running = 1;
static Cur *cursor[CurLast]; static Cur *cursor[CurLast];
static Clr **scheme; static Clr **scheme;
@ -266,10 +285,20 @@ static Display *dpy;
static Drw *drw; static Drw *drw;
static Monitor *mons, *selmon; static Monitor *mons, *selmon;
static Window root, wmcheckwin; static Window root, wmcheckwin;
unsigned int currentkey = 0;
/* configuration, allows nested code to access above variables */ /* configuration, allows nested code to access above variables */
#include "config.h" #include "config.h"
struct Pertag {
unsigned int curtag, prevtag; /* current and previous tag */
int nmasters[LENGTH(tags) + 1]; /* number of windows in master area */
float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */
unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */
const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes */
int showbars[LENGTH(tags) + 1]; /* display bar for the current tag */
};
/* compile-time check if all tags fit into an unsigned int bit array. */ /* compile-time check if all tags fit into an unsigned int bit array. */
struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; };
@ -407,6 +436,25 @@ attach(Client *c)
c->mon->clients = c; c->mon->clients = c;
} }
void
attachtop(Client *c)
{
int n;
Monitor *m = selmon;
Client *below;
for (n = 1, below = c->mon->clients;
below && below->next && (below->isfloating || !ISVISIBLEONTAG(below, c->tags) || n != m->nmaster);
n = below->isfloating || !ISVISIBLEONTAG(below, c->tags) ? n + 0 : n + 1, below = below->next);
c->next = NULL;
if (below) {
c->next = below->next;
below->next = c;
}
else
c->mon->clients = c;
}
void void
attachstack(Client *c) attachstack(Client *c)
{ {
@ -422,6 +470,7 @@ buttonpress(XEvent *e)
Client *c; Client *c;
Monitor *m; Monitor *m;
XButtonPressedEvent *ev = &e->xbutton; XButtonPressedEvent *ev = &e->xbutton;
char *text, *s, ch;
click = ClkRootWin; click = ClkRootWin;
/* focus monitor if necessary */ /* focus monitor if necessary */
@ -440,9 +489,27 @@ 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 - statusw) {
x = selmon->ww - statusw;
click = ClkStatusText; click = ClkStatusText;
else statussig = 0;
for (text = s = stext; *s && x <= ev->x; s++) {
if ((unsigned char)(*s) < ' ') {
ch = *s;
*s = '\0';
x += TEXTW(text) - lrpad;
*s = ch;
text = s + 1;
if (x >= ev->x)
break;
/* reset on matching signal raw byte */
if (ch == statussig)
statussig = 0;
else
statussig = ch;
}
}
} else
click = ClkWinTitle; click = ClkWinTitle;
} else if ((c = wintoclient(ev->window))) { } else if ((c = wintoclient(ev->window))) {
focus(c); focus(c);
@ -486,7 +553,7 @@ cleanup(void)
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++)
free(scheme[i]); drw_scm_free(drw, scheme[i], 3);
free(scheme); free(scheme);
XDestroyWindow(dpy, wmcheckwin); XDestroyWindow(dpy, wmcheckwin);
drw_free(drw); drw_free(drw);
@ -633,6 +700,7 @@ Monitor *
createmon(void) createmon(void)
{ {
Monitor *m; Monitor *m;
unsigned int i;
m = ecalloc(1, sizeof(Monitor)); m = ecalloc(1, sizeof(Monitor));
m->tagset[0] = m->tagset[1] = 1; m->tagset[0] = m->tagset[1] = 1;
@ -643,6 +711,20 @@ createmon(void)
m->lt[0] = &layouts[0]; m->lt[0] = &layouts[0];
m->lt[1] = &layouts[1 % LENGTH(layouts)]; m->lt[1] = &layouts[1 % LENGTH(layouts)];
strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
m->pertag = ecalloc(1, sizeof(Pertag));
m->pertag->curtag = m->pertag->prevtag = 1;
for (i = 0; i <= LENGTH(tags); i++) {
m->pertag->nmasters[i] = m->nmaster;
m->pertag->mfacts[i] = m->mfact;
m->pertag->ltidxs[i][0] = m->lt[0];
m->pertag->ltidxs[i][1] = m->lt[1];
m->pertag->sellts[i] = m->sellt;
m->pertag->showbars[i] = m->showbar;
}
return m; return m;
} }
@ -708,9 +790,24 @@ drawbar(Monitor *m)
/* 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 */
char *text, *s, ch;
drw_setscheme(drw, scheme[SchemeNorm]); drw_setscheme(drw, scheme[SchemeNorm]);
tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */
drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); x = 0;
for (text = s = stext; *s; s++) {
if ((unsigned char)(*s) < ' ') {
ch = *s;
*s = '\0';
tw = TEXTW(text) - lrpad;
drw_text(drw, m->ww - statusw + x, 0, tw, bh, 0, text, 0);
x += tw;
*s = ch;
text = s + 1;
}
}
tw = TEXTW(text) - lrpad + 2;
drw_text(drw, m->ww - statusw + x, 0, tw, bh, 0, text, 0);
tw = statusw;
} }
for (c = m->clients; c; c = c->next) { for (c = m->clients; c; c = c->next) {
@ -863,19 +960,44 @@ focusstack(const Arg *arg)
Atom Atom
getatomprop(Client *c, Atom prop) getatomprop(Client *c, Atom prop)
{ {
int di; int format;
unsigned long dl; unsigned long nitems, dl;
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, if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM,
&da, &di, &dl, &dl, &p) == Success && p) { &da, &format, &nitems, &dl, &p) == Success && p) {
atom = *(Atom *)p; if (nitems > 0 && format == 32)
atom = *(long *)p;
XFree(p); XFree(p);
} }
return atom; return atom;
} }
pid_t
getstatusbarpid()
{
char buf[32], *str = buf, *c;
FILE *fp;
if (statuspid > 0) {
snprintf(buf, sizeof(buf), "/proc/%u/cmdline", statuspid);
if ((fp = fopen(buf, "r"))) {
fgets(buf, sizeof(buf), fp);
while ((c = strchr(str, '/')))
str = c + 1;
fclose(fp);
if (!strcmp(str, STATUSBAR))
return statuspid;
}
}
if (!(fp = popen("pidof -s "STATUSBAR, "r")))
return -1;
fgets(buf, sizeof(buf), fp);
pclose(fp);
return strtol(buf, NULL, 10);
}
int int
getrootptr(int *x, int *y) getrootptr(int *x, int *y)
{ {
@ -896,10 +1018,10 @@ getstate(Window w)
Atom real; Atom real;
if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState],
&real, &format, &n, &extra, (unsigned char **)&p) != Success) &real, &format, &n, &extra, &p) != Success)
return -1; return -1;
if (n != 0) if (n != 0 && format == 32)
result = *p; result = *(long *)p;
XFree(p); XFree(p);
return result; return result;
} }
@ -953,7 +1075,8 @@ grabkeys(void)
{ {
updatenumlockmask(); updatenumlockmask();
{ {
unsigned int i, j, k; /* unsigned int i, j, k; */
unsigned int i, c, k;
unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
int start, end, skip; int start, end, skip;
KeySym *syms; KeySym *syms;
@ -963,15 +1086,18 @@ grabkeys(void)
syms = XGetKeyboardMapping(dpy, start, end - start + 1, &skip); syms = XGetKeyboardMapping(dpy, start, end - start + 1, &skip);
if (!syms) if (!syms)
return; return;
for (k = start; k <= end; k++) for (k = start; k <= end; k++)
for (i = 0; i < LENGTH(keys); i++) for (i = 0; i < LENGTH(keychords); i++)
/* skip modifier codes, we do that ourselves */ /* skip modifier codes, we do that ourselves */
if (keys[i].keysym == syms[(k - start) * skip]) if (keychords[i]->keys[currentkey].keysym == syms[(k - start) * skip])
for (j = 0; j < LENGTH(modifiers); j++) for (c = 0; c < LENGTH(modifiers); c++)
XGrabKey(dpy, k, XGrabKey(dpy, k,
keys[i].mod | modifiers[j], keychords[i]->keys[currentkey].mod | modifiers[c],
root, True, root, True,
GrabModeAsync, GrabModeAsync); GrabModeAsync, GrabModeAsync);
if(currentkey > 0)
XGrabKey(dpy, XKeysymToKeycode(dpy, XK_Escape), AnyModifier, root, True, GrabModeAsync, GrabModeAsync);
XFree(syms); XFree(syms);
} }
} }
@ -979,7 +1105,7 @@ grabkeys(void)
void void
incnmaster(const Arg *arg) incnmaster(const Arg *arg)
{ {
selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->nmaster + arg->i, 0);
arrange(selmon); arrange(selmon);
} }
@ -998,17 +1124,51 @@ isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info)
void void
keypress(XEvent *e) keypress(XEvent *e)
{ {
unsigned int i; /* unsigned int i; */
XEvent event = *e;
unsigned int ran = 0;
KeySym keysym; KeySym keysym;
XKeyEvent *ev; XKeyEvent *ev;
ev = &e->xkey; Keychord *arr1[sizeof(keychords) / sizeof(Keychord*)];
keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); Keychord *arr2[sizeof(keychords) / sizeof(Keychord*)];
for (i = 0; i < LENGTH(keys); i++) memcpy(arr1, keychords, sizeof(keychords));
if (keysym == keys[i].keysym Keychord **rpointer = arr1;
&& CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) Keychord **wpointer = arr2;
&& keys[i].func)
keys[i].func(&(keys[i].arg)); size_t r = sizeof(keychords)/ sizeof(Keychord*);
while(1){
ev = &event.xkey;
keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);
size_t w = 0;
for (int i = 0; i < r; i++){
if(keysym == (*(rpointer + i))->keys[currentkey].keysym
&& CLEANMASK((*(rpointer + i))->keys[currentkey].mod) == CLEANMASK(ev->state)
&& (*(rpointer + i))->func){
if((*(rpointer + i))->n == currentkey +1){
(*(rpointer + i))->func(&((*(rpointer + i))->arg));
ran = 1;
}else{
*(wpointer + w) = *(rpointer + i);
w++;
}
}
}
currentkey++;
if(w == 0 || ran == 1)
break;
grabkeys();
while (running && !XNextEvent(dpy, &event) && !ran)
if(event.type == KeyPress)
break;
r = w;
Keychord **holder = rpointer;
rpointer = wpointer;
wpointer = holder;
}
currentkey = 0;
grabkeys();
} }
void void
@ -1073,7 +1233,7 @@ manage(Window w, XWindowAttributes *wa)
c->isfloating = c->oldstate = trans != None || c->isfixed; c->isfloating = c->oldstate = trans != None || c->isfixed;
if (c->isfloating) if (c->isfloating)
XRaiseWindow(dpy, c->win); XRaiseWindow(dpy, c->win);
attach(c); attachtop(c);
attachstack(c); attachstack(c);
XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend,
(unsigned char *) &(c->win), 1); (unsigned char *) &(c->win), 1);
@ -1171,7 +1331,7 @@ movemouse(const Arg *arg)
handler[ev.type](&ev); handler[ev.type](&ev);
break; break;
case MotionNotify: case MotionNotify:
if ((ev.xmotion.time - lasttime) <= (1000 / 60)) if ((ev.xmotion.time - lasttime) <= (1000 / refreshrate))
continue; continue;
lasttime = ev.xmotion.time; lasttime = ev.xmotion.time;
@ -1257,6 +1417,7 @@ propertynotify(XEvent *e)
void void
quit(const Arg *arg) quit(const Arg *arg)
{ {
if(arg->i) restart = 1;
running = 0; running = 0;
} }
@ -1285,12 +1446,36 @@ 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);
@ -1325,7 +1510,7 @@ resizemouse(const Arg *arg)
handler[ev.type](&ev); handler[ev.type](&ev);
break; break;
case MotionNotify: case MotionNotify:
if ((ev.xmotion.time - lasttime) <= (1000 / 60)) if ((ev.xmotion.time - lasttime) <= (1000 / refreshrate))
continue; continue;
lasttime = ev.xmotion.time; lasttime = ev.xmotion.time;
@ -1426,8 +1611,10 @@ sendmon(Client *c, Monitor *m)
detachstack(c); detachstack(c);
c->mon = m; c->mon = m;
c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */
attach(c); attachtop(c);
attachstack(c); attachstack(c);
if (c->isfullscreen)
resizeclient(c, m->mx, m->my, m->mw, m->mh);
focus(NULL); focus(NULL);
arrange(NULL); arrange(NULL);
} }
@ -1469,12 +1656,10 @@ sendevent(Client *c, Atom proto)
void void
setfocus(Client *c) setfocus(Client *c)
{ {
if (!c->neverfocus) { if (!c->neverfocus)
XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
XChangeProperty(dpy, root, netatom[NetActiveWindow], XChangeProperty(dpy, root, netatom[NetActiveWindow], XA_WINDOW, 32,
XA_WINDOW, 32, PropModeReplace, PropModeReplace, (unsigned char *)&c->win, 1);
(unsigned char *) &(c->win), 1);
}
sendevent(c, wmatom[WMTakeFocus]); sendevent(c, wmatom[WMTakeFocus]);
} }
@ -1506,13 +1691,26 @@ setfullscreen(Client *c, int fullscreen)
} }
} }
Layout *last_layout;
void
fullscreen(const Arg *arg)
{
if (selmon->showbar) {
for(last_layout = (Layout *)layouts; last_layout != selmon->lt[selmon->sellt]; last_layout++);
setlayout(&((Arg) { .v = &layouts[2] }));
} else {
setlayout(&((Arg) { .v = last_layout }));
}
togglebar(arg);
}
void void
setlayout(const Arg *arg) setlayout(const Arg *arg)
{ {
if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt])
selmon->sellt ^= 1; selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag] ^= 1;
if (arg && arg->v) if (arg && arg->v)
selmon->lt[selmon->sellt] = (Layout *)arg->v; selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v;
strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol);
if (selmon->sel) if (selmon->sel)
arrange(selmon); arrange(selmon);
@ -1531,7 +1729,7 @@ setmfact(const Arg *arg)
f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0;
if (f < 0.05 || f > 0.95) if (f < 0.05 || f > 0.95)
return; return;
selmon->mfact = f; selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag] = f;
arrange(selmon); arrange(selmon);
} }
@ -1552,6 +1750,9 @@ setup(void)
/* clean up any zombies (inherited from .xinitrc etc) immediately */ /* clean up any zombies (inherited from .xinitrc etc) immediately */
while (waitpid(-1, NULL, WNOHANG) > 0); while (waitpid(-1, NULL, WNOHANG) > 0);
signal(SIGHUP, sighup);
signal(SIGTERM, sigterm);
/* init screen */ /* init screen */
screen = DefaultScreen(dpy); screen = DefaultScreen(dpy);
sw = DisplayWidth(dpy, screen); sw = DisplayWidth(dpy, screen);
@ -1643,6 +1844,42 @@ 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
sigstatusbar(const Arg *arg)
{
union sigval sv;
if (!statussig)
return;
sv.sival_int = arg->i;
if ((statuspid = getstatusbarpid()) <= 0)
return;
sigqueue(statuspid, SIGRTMIN+statussig, sv);
}
void void
spawn(const Arg *arg) spawn(const Arg *arg)
{ {
@ -1700,7 +1937,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 {
@ -1714,7 +1951,7 @@ tile(Monitor *m)
void void
togglebar(const Arg *arg) togglebar(const Arg *arg)
{ {
selmon->showbar = !selmon->showbar; selmon->showbar = selmon->pertag->showbars[selmon->pertag->curtag] = !selmon->showbar;
updatebarpos(selmon); updatebarpos(selmon);
XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh);
arrange(selmon); arrange(selmon);
@ -1753,9 +1990,33 @@ void
toggleview(const Arg *arg) toggleview(const Arg *arg)
{ {
unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK);
int i;
if (newtagset) { if (newtagset) {
selmon->tagset[selmon->seltags] = newtagset; selmon->tagset[selmon->seltags] = newtagset;
if (newtagset == ~0) {
selmon->pertag->prevtag = selmon->pertag->curtag;
selmon->pertag->curtag = 0;
}
/* test if the user did not select the same tag */
if (!(newtagset & 1 << (selmon->pertag->curtag - 1))) {
selmon->pertag->prevtag = selmon->pertag->curtag;
for (i = 0; !(newtagset & 1 << i); i++) ;
selmon->pertag->curtag = i + 1;
}
/* apply settings for this view */
selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag];
selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag];
selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag];
selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt];
selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1];
if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag])
togglebar(NULL);
focus(NULL); focus(NULL);
arrange(selmon); arrange(selmon);
} }
@ -1868,7 +2129,7 @@ updategeom(void)
{ {
int dirty = 0; int dirty = 0;
#ifdef XINERAMA // #ifdef XINERAMA
if (XineramaIsActive(dpy)) { if (XineramaIsActive(dpy)) {
int i, j, n, nn; int i, j, n, nn;
Client *c; Client *c;
@ -1893,6 +2154,8 @@ updategeom(void)
else else
mons = createmon(); mons = createmon();
} }
for (i = 0, m = mons; i < nn && m; m = m->next, i++) for (i = 0, m = mons; i < nn && m; m = m->next, i++)
if (i >= n if (i >= n
|| unique[i].x_org != m->mx || unique[i].y_org != m->my || unique[i].x_org != m->mx || unique[i].y_org != m->my
@ -1923,7 +2186,7 @@ updategeom(void)
} }
free(unique); free(unique);
} else } else
#endif /* XINERAMA */ // #endif /* XINERAMA */
{ /* default monitor setup */ { /* default monitor setup */
if (!mons) if (!mons)
mons = createmon(); mons = createmon();
@ -2004,8 +2267,25 @@ updatesizehints(Client *c)
void void
updatestatus(void) 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);
statusw = TEXTW(stext) - lrpad + 2;
} else {
char *text, *s, ch;
statusw = 0;
for (text = s = stext; *s; s++) {
if ((unsigned char)(*s) < ' ') {
ch = *s;
*s = '\0';
statusw += TEXTW(text) - lrpad;
*s = ch;
text = s + 1;
}
}
statusw += TEXTW(text) - lrpad + 2;
}
drawbar(selmon); drawbar(selmon);
} }
@ -2052,11 +2332,37 @@ updatewmhints(Client *c)
void void
view(const Arg *arg) view(const Arg *arg)
{ {
int i;
unsigned int tmptag;
if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
return; return;
selmon->seltags ^= 1; /* toggle sel tagset */ selmon->seltags ^= 1; /* toggle sel tagset */
if (arg->ui & TAGMASK) if (arg->ui & TAGMASK) {
selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
selmon->pertag->prevtag = selmon->pertag->curtag;
if (arg->ui == ~0)
selmon->pertag->curtag = 0;
else {
for (i = 0; !(arg->ui & 1 << i); i++) ;
selmon->pertag->curtag = i + 1;
}
} else {
tmptag = selmon->pertag->prevtag;
selmon->pertag->prevtag = selmon->pertag->curtag;
selmon->pertag->curtag = tmptag;
}
selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag];
selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag];
selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag];
selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt];
selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1];
if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag])
togglebar(NULL);
focus(NULL); focus(NULL);
arrange(selmon); arrange(selmon);
} }
@ -2158,7 +2464,9 @@ main(int argc, char *argv[])
#endif /* __OpenBSD__ */ #endif /* __OpenBSD__ */
scan(); scan();
run(); run();
if(restart) execvp(argv[0], argv);
cleanup(); cleanup();
XCloseDisplay(dpy); XCloseDisplay(dpy);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

49
movestack.c Normal file
View File

@ -0,0 +1,49 @@
void
movestack(const Arg *arg) {
Client *c = NULL, *p = NULL, *pc = NULL, *i;
if(arg->i > 0) {
/* find the client after selmon->sel */
for(c = selmon->sel->next; c && (!ISVISIBLE(c) || c->isfloating); c = c->next);
if(!c)
for(c = selmon->clients; c && (!ISVISIBLE(c) || c->isfloating); c = c->next);
}
else {
/* find the client before selmon->sel */
for(i = selmon->clients; i != selmon->sel; i = i->next)
if(ISVISIBLE(i) && !i->isfloating)
c = i;
if(!c)
for(; i; i = i->next)
if(ISVISIBLE(i) && !i->isfloating)
c = i;
}
/* find the client before selmon->sel and c */
for(i = selmon->clients; i && (!p || !pc); i = i->next) {
if(i->next == selmon->sel)
p = i;
if(i->next == c)
pc = i;
}
/* swap c and selmon->sel selmon->clients in the selmon->clients list */
if(c && c != selmon->sel) {
Client *temp = selmon->sel->next==c?selmon->sel:selmon->sel->next;
selmon->sel->next = c->next==selmon->sel?c:c->next;
c->next = temp;
if(p && p != c)
p->next = c;
if(pc && pc != selmon->sel)
pc->next = selmon->sel;
if(selmon->sel == selmon->clients)
selmon->clients = c;
else if(c == selmon->clients)
selmon->clients = selmon->sel;
arrange(selmon);
}
}

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);
}

1
util.h
View File

@ -7,3 +7,4 @@
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);