Compare commits

..
27 Commits
Author SHA1 Message Date
lupan b0cc093917 use alacritty term 2026-10-19 09:29:09 +02:00
lupan 2adf18bee6 local: letter tags and mod semicolon chords 2026-10-19 09:26:06 +02:00
Lukasz Kasprzak 9610f461c2 keychord: bind a sequence of keys, and dispatch events while waiting
Original keychord patch by Hai Nguyen <hhai2105@gmail.com>, updated for
dwm 6.4+ grabkeys() by Aaron Zueger <contact@azureorange.xyz>. This
revision rebases onto dwm 6.8 and fixes an event-loss bug in the chord
wait loop.

The wait loop used to discard every event that was not a KeyPress:

    while (running && !XNextEvent(dpy, &event) && !ran)
            if(event.type == KeyPress)
                    break;

Two consequences, both reproduced on Xephyr:

1. Unrecoverable input freeze. grabbuttons() grabs unfocused clients
   GrabModeSync/GrabModeSync, so clicking one freezes pointer AND
   keyboard until the grabbing client calls XAllowEvents. dwm does that
   in buttonpress() -- but if the ButtonPress is discarded here,
   buttonpress() never runs. The grab stays frozen forever and the loop
   can never exit, because a frozen keyboard cannot deliver the KeyPress
   it is waiting for. Only killing dwm ends it; XSendEvent is discarded
   by this same loop and XTEST queues behind the frozen device.
   Repro: press a chord prefix, then click any unfocused client.
   Observed: XGrabKeyboard -> GrabFrozen, XQueryPointer -> Button1 held.

2. Orphaned windows. A MapRequest arriving mid-chord is dropped, so a
   window opening during a chord is never managed. DestroyNotify,
   Expose and ConfigureRequest are lost the same way.

Dispatch through handler[] as run() does, breaking on KeyPress before
dispatch so keypress() cannot recurse. The `&& !ran` term is dropped:
ran is always 0 on entry and nothing in the body can set it.
2026-10-19 09:10:49 +02:00
lupan 636dcaf763 WIP my local config changes 2026-10-19 08:10:10 +02:00
BrunoCooper17 239b74dcb2 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.
2026-10-19 08:08:58 +02:00
lupan b6ed779125 apply patch dwm-winview-gaplessgrid-gridall-6.5.diff 2026-10-19 08:08:58 +02:00
explosion-mental f8024479de 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. 2026-10-19 08:08:58 +02:00
lupan 8c32040130 agree: dwm-goback with dwm-warp 2026-10-19 08:08:58 +02:00
Max Schillinger 31f21a2173 add patch/function 'goback' 2026-10-19 08:08:58 +02:00
lupan d94d37dc22 agree: dwm-autodarkmode with dwm-awesomebar 2026-10-19 08:08:58 +02:00
dimarogiv c166ddaa91 With this patch you can focus on any of the currently open windows on the current tagset with just one key combination 2026-10-19 08:08:58 +02:00
Cyril Cressent 6c96da6eb5 Port the uselessgap patch to 6.2 2026-10-19 08:08:58 +02:00
Jonas Dujava c5de5db150 Warp patch
Warps the mouse cursor to the center of the currently focused
window or screen when the mouse cursor is
  (a) on a different screen, or
  (b) on top of a different window.

This version properly handles warping to windows that have not been
mapped yet (before it resulted in a change of the stack order).
See the discussion in (thanks goes to Bakkeby):
    https://github.com/bakkeby/patches/issues/60
2026-10-19 08:08:58 +02:00
lupan 0cbe388286 apply dwm-xinerama-monitors-xorder-6.4.diff 2026-10-19 08:08:58 +02:00
lupan e1bf153993 agree: change dwm-autodarkmode signal to USR1 and leave restarsig signal to HUP 2026-10-19 08:08:58 +02:00
lupan bd3bdc3e31 apply patch dwm-autodarkmode-20250224-6.5.diff 2026-10-19 08:08:58 +02:00
lupan 0f4acebf05 apply patch dwm-awesomebar-20230431-6.4.diff 2026-10-19 08:01:16 +02:00
lupan 43ace02937 apply patch dwm-exitmenu-6.3.diff 2026-10-19 08:01:16 +02:00
Arda Atci f6aaf3e786 preserve clients on old tags when renewing dwm
By default, when dwm is recompiled-restarted all clients will
lose it's current tag and collapse to first tag. This patch preserves
clients on old tags, however note that layout order is not preserved.
2026-10-19 08:01:16 +02:00
Christopher Drelich afcfb6fda8 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
2026-10-19 08:01:16 +02:00
Hiltjo Posthuma bca7cf2bd9 support spawning dmenu with more than 10 monitors 2026-10-16 13:26:00 +02:00
Hiltjo Posthuma 1d07715ebd print an error if fork failed (resource / process limits) 2026-10-16 13:26:00 +02:00
Hiltjo Posthuma ebfe671988 check return value of XGetModifierMapping 2026-10-16 13:26:00 +02:00
Hiltjo Posthuma d587dd65a4 fix a possible memory leak if a list is empty for XmbTextPropertyToTextList 2026-10-16 13:26:00 +02:00
Hiltjo Posthuma ce41d34791 properly NUL terminate ltsymbol
Although this is config error in config.h if the symbol is too long.
Make sure it is correct.

Note that the symbol would then get truncated.
2026-10-16 13:26:00 +02:00
Hiltjo Posthuma 3fdf0241fc ignore malformed ConfigureNotify with width or height <= 0
This could crash dwm.

cc repro.c -o repro -lX11; ./repro

int
main(void)
{
	Display *d;
	int screen;
	Window root;

	d = XOpenDisplay(NULL);
	if (!d) {
		fprintf(stderr, "XOpenDisplay failed\n");
		return 1;
	}
	screen = DefaultScreen(d);
	root = RootWindow(d, screen);

	XConfigureEvent ce = {0};
	ce.type = ConfigureNotify;
	ce.display = d;
	ce.event = root;
	ce.window = root;
	ce.width = 0;
	ce.height = 0;
	XSendEvent(d, root, False, StructureNotifyMask, (XEvent *)&ce);
	XFlush(d);

	XCloseDisplay(d);

	return 0;
}
2026-10-16 13:25:55 +02:00
Hiltjo Posthuma 7283c8c0ac check and restrict size hint values
Could not reproduce a crash, but may mess up window geometry, etc.

Test program below to give an idea:

cc repro.c -o repro -lX11; ./repro

int
main(void)
{
	Display *d;
	int screen;
	Window root;
	Window win;

	d = XOpenDisplay(NULL);
	if (!d) {
		fprintf(stderr, "XOpenDisplay failed\n");
		return 1;
	}
	screen = DefaultScreen(d);
	root = RootWindow(d, screen);

	win = XCreateSimpleWindow(d, root, 0, 0, 100, 100, 0, 0, 0);
	if (!win) {
		fprintf(stderr, "XCreateSimpleWindow failed\n");
		XCloseDisplay(d);
		return 1;
	}

	//XMapWindow(d, win);
	XMapRaised(d, win);

	XSizeHints h = {0};
	h.flags = PResizeInc | PBaseSize | PMinSize;
	h.width_inc = -1;
	h.height_inc = -1;
	h.base_width = INT_MIN + 500; /* window is 500px wide */
	h.base_height = 0;

	h.min_width = ~0L;
	h.min_height = ~0L;

	XSetWMNormalHints(d, win, &h);

	sleep(3);

	//XDestroyWindow(d, win);

	XCloseDisplay(d);

	return 0;
}
2026-10-16 13:23:34 +02:00
13 changed files with 1039 additions and 177 deletions

No files matched your search

+101 -56
View File
@@ -1,7 +1,9 @@
/* See LICENSE file for copyright and license details. */
#include "gaplessgrid.c"
/* appearance */
static const unsigned int borderpx = 1; /* border pixel of windows */
static const unsigned int borderpx = 2; /* border pixel of windows */
static const unsigned int gappx = 2; /* gaps between windows */
static const unsigned int snap = 32; /* snap pixel */
static const int showbar = 1; /* 0 means no bar */
static const int topbar = 1; /* 0 means bottom bar */
@@ -12,20 +14,23 @@ static const char col_gray2[] = "#444444";
static const char col_gray3[] = "#bbbbbb";
static const char col_gray4[] = "#eeeeee";
static const char col_cyan[] = "#005577";
static const char col_border_focus_light[] = "#df8c20"; // hsl(34 75% 50%)
static const char col_border_focus_dark[] = "#b25e34"; // hsl(20 55% 45%)
static const char *colorsdark[][3] = {
/* fg bg border */
[SchemeNorm] = { col_gray3, col_gray1, col_gray2 },
[SchemeSel] = { col_gray4, col_cyan, col_cyan },
[SchemeSel] = { col_gray4, col_cyan, col_border_focus_dark },
[SchemeHid] = { col_cyan, col_gray1, col_cyan },
};
static const char *colorslight[][3] = {
/* fg bg border */
[SchemeNorm] = { col_gray1, col_gray3, col_gray2 },
[SchemeSel] = { col_cyan, col_gray4, col_cyan },
[SchemeSel] = { col_cyan, col_gray4, col_border_focus_light },
[SchemeHid] = { col_cyan, col_gray1, col_cyan },
};
/* tagging */
static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
static const char *tags[] = { "q", "w", "e", "r", "t", "a", "s", "d", "f", "g", "z", "x", "c", "v", "b" };
static const Rule rules[] = {
/* xprop(1):
@@ -49,68 +54,108 @@ static const Layout layouts[] = {
{ "[]=", tile }, /* first entry is default */
{ "><>", NULL }, /* no layout function means floating behavior */
{ "[M]", monocle },
{ "HHH", gaplessgrid },
};
/* 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 MODKEY Mod4Mask
#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 } }
/* commands */
static char dmenumon[2] = "0"; /* component of dmenu{dark,light}, manipulated in spawndmenu() */
static const char *dmenudark[] = { "dmenu_run", "-m", dmenumon, "-i", "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL };
static const char *dmenulight[] = { "dmenu_run", "-m", dmenumon, "-i", "-fn", dmenufont, "-nb", col_gray3, "-nf", col_gray1, "-sb", col_cyan, "-sf", col_gray4, NULL };
static const char *termcmd[] = { "st", NULL };
static char dmenumon[8] = "0"; /* component of dmenu{dark,light}, manipulated in spawndmenu() */
static const char *dmenudark[] = { "dmenu_run", "-l", "10", "-m", dmenumon, "-i", "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL };
static const char *dmenulight[] = { "dmenu_run", "-l", "10", "-m", dmenumon, "-i", "-fn", dmenufont, "-nb", col_gray3, "-nf", col_gray1, "-sb", col_cyan, "-sf", col_gray4, NULL };
static const char *termcmd[] = { "alacritty", NULL };
static const char *lockcmd[] = { "slock", NULL };
static const char *suspendcmd[] = { "systemctl", "suspend", NULL };
static const char *themecmd[] = { "lupan-set-theme", "toggle", NULL };
#include "shift-tools.c"
#include "exitdwm.c"
static const Key keys[] = {
/* modifier key function argument */
{ MODKEY, XK_p, spawndmenu, {0} },
{ MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } },
{ MODKEY, XK_b, togglebar, {0} },
{ MODKEY, XK_j, focusstackvis, {.i = +1 } },
{ MODKEY, XK_k, focusstackvis, {.i = -1 } },
{ MODKEY|ShiftMask, XK_j, focusstackhid, {.i = +1 } },
{ MODKEY|ShiftMask, XK_k, focusstackhid, {.i = -1 } },
{ MODKEY, XK_i, incnmaster, {.i = +1 } },
{ MODKEY, XK_d, incnmaster, {.i = -1 } },
{ MODKEY, XK_h, setmfact, {.f = -0.05} },
{ MODKEY, 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 } },
{ MODKEY, XK_s, show, {0} },
{ MODKEY|ShiftMask, XK_s, showall, {0} },
{ MODKEY, XK_h, hide, {0} },
TAGKEYS( XK_1, 0)
TAGKEYS( XK_2, 1)
TAGKEYS( XK_3, 2)
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} },
{ MODKEY|ControlMask|ShiftMask, XK_q, quit, {1} },
{ MODKEY|ShiftMask, XK_e, exitdwm, {0} },
#include "movestack.c"
static Keychord *keychords[] = {
/* Keys function argument */
&((Keychord){1, {{MODKEY, XK_p}}, spawndmenu, {0} }),
&((Keychord){1, {{MODKEY, XK_u}}, shiftview, { .i = +1 } }),
&((Keychord){1, {{MODKEY|ShiftMask, XK_u}}, shifttag, { .i = +1 } }),
&((Keychord){1, {{MODKEY|ShiftMask, XK_y}}, shifttag, { .i = -1 } }),
&((Keychord){1, {{MODKEY, XK_y}}, shiftview, { .i = -1 } }),
&((Keychord){1, {{MODKEY|ShiftMask, XK_Return}}, spawn, {.v = termcmd } }),
&((Keychord){2, {{MODKEY, XK_semicolon}, {0, XK_b}}, togglebar, {0} }),
&((Keychord){1, {{MODKEY, XK_j}}, focusstackvis, {.i = +1 } }),
&((Keychord){1, {{MODKEY, XK_k}}, focusstackvis, {.i = -1 } }),
&((Keychord){1, {{MODKEY|ControlMask, XK_j}}, focusstackhid, {.i = +1 } }),
&((Keychord){1, {{MODKEY|ControlMask, XK_k}}, focusstackhid, {.i = -1 } }),
&((Keychord){1, {{MODKEY, XK_i}}, incnmaster, {.i = +1 } }),
&((Keychord){2, {{MODKEY, XK_semicolon}, {0, XK_d}}, incnmaster, {.i = -1 } }),
&((Keychord){1, {{MODKEY, XK_h}}, setmfact, {.f = -0.05} }),
&((Keychord){1, {{MODKEY|ShiftMask, XK_h}}, shiftboth, {.i = -1} }),
&((Keychord){1, {{MODKEY|ControlMask, XK_h}}, shiftswaptags, {.i = -1} }),
&((Keychord){1, {{MODKEY, XK_l}}, setmfact, {.f = +0.05} }),
&((Keychord){1, {{MODKEY|ControlMask, XK_l}}, shiftswaptags, { .i = +1 } }),
&((Keychord){1, {{MODKEY|ShiftMask, XK_l}}, shiftboth, { .i = +1 } }),
&((Keychord){1, {{MODKEY|ShiftMask, XK_j}}, movestack, {.i = +1 } }),
&((Keychord){1, {{MODKEY|ShiftMask, XK_k}}, movestack, {.i = -1 } }),
&((Keychord){1, {{MODKEY, XK_Return}}, zoom, {0} }),
&((Keychord){1, {{MODKEY, XK_Tab}}, view, {0} }),
&((Keychord){2, {{MODKEY, XK_semicolon}, {0, XK_w}}, goback, {0} }),
&((Keychord){2, {{MODKEY, XK_semicolon}, {MODKEY|ShiftMask, XK_c}}, killclient, {0} }),
&((Keychord){2, {{MODKEY, XK_semicolon}, {0, XK_t}}, setlayout, {.v = &layouts[0]} }),
&((Keychord){2, {{MODKEY, XK_semicolon}, {0, XK_f}}, setlayout, {.v = &layouts[1]} }),
&((Keychord){1, {{MODKEY, XK_m}}, setlayout, {.v = &layouts[2]} }),
&((Keychord){2, {{MODKEY, XK_semicolon}, {0, XK_g}}, setlayout, {.v = &layouts[3]} }),
&((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, XK_s}}, show, {0} }),
&((Keychord){1, {{MODKEY|ShiftMask, XK_s}}, showall, {0} }),
&((Keychord){2, {{MODKEY, XK_semicolon}, {MODKEY|ControlMask, XK_s}}, hide, {0} }),
&((Keychord){2, {{MODKEY, XK_semicolon}, {MODKEY|ShiftMask, XK_q}}, quit, {0} }),
&((Keychord){2, {{MODKEY, XK_semicolon}, {MODKEY|ControlMask|ShiftMask, XK_q}}, quit, {1} }),
&((Keychord){2, {{MODKEY, XK_semicolon}, {MODKEY|ShiftMask, XK_e}}, exitdwm, {0} }),
&((Keychord){1, {{MODKEY, XK_o}}, winview, {0} }),
&((Keychord){2, {{MODKEY, XK_semicolon}, {0, XK_a}}, gridall, {0} }),
&((Keychord){2, {{MODKEY, XK_semicolon}, {0, XK_r}}, winviewmono, {0} }),
&((Keychord){2, {{MODKEY, XK_semicolon}, {ControlMask|ShiftMask, XK_s}}, spawn, {.v = suspendcmd } }),
&((Keychord){1, {{MODKEY|ControlMask|ShiftMask, XK_l}}, spawn, {.v = lockcmd } }),
&((Keychord){2, {{MODKEY, XK_semicolon}, {ControlMask|ShiftMask, XK_t}}, spawn, {.v = themecmd } }),
TAGKEYS( XK_q, 0)
TAGKEYS( XK_w, 1)
TAGKEYS( XK_e, 2)
TAGKEYS( XK_r, 3)
TAGKEYS( XK_t, 4)
TAGKEYS( XK_a, 5)
TAGKEYS( XK_s, 6)
TAGKEYS( XK_d, 7)
TAGKEYS( XK_f, 8)
TAGKEYS( XK_g, 9)
TAGKEYS( XK_z, 10)
TAGKEYS( XK_x, 11)
TAGKEYS( XK_c, 12)
TAGKEYS( XK_v, 13)
TAGKEYS( XK_b, 14)
&((Keychord){1, {{ ControlMask, XK_1}}, focusbynum, {.i = 0} }),
&((Keychord){1, {{ ControlMask, XK_2}}, focusbynum, {.i = 1} }),
&((Keychord){1, {{ ControlMask, XK_3}}, focusbynum, {.i = 2} }),
&((Keychord){1, {{ ControlMask, XK_4}}, focusbynum, {.i = 3} }),
&((Keychord){1, {{ ControlMask, XK_5}}, focusbynum, {.i = 4} }),
&((Keychord){1, {{ ControlMask, XK_6}}, focusbynum, {.i = 5} }),
&((Keychord){1, {{ ControlMask, XK_7}}, focusbynum, {.i = 6} }),
&((Keychord){1, {{ ControlMask, XK_8}}, focusbynum, {.i = 7} }),
&((Keychord){1, {{ ControlMask, XK_9}}, focusbynum, {.i = 8} }),
};
/* button definitions */
+84 -80
View File
@@ -7,8 +7,8 @@ static const unsigned int gappx = 2; /* gaps between windows */
static const unsigned int snap = 32; /* snap pixel */
static const int showbar = 1; /* 0 means no bar */
static const int topbar = 1; /* 0 means bottom bar */
static const char *fonts[] = { "monospace:size=11" };
static const char dmenufont[] = "monospace:size=11";
static const char *fonts[] = { "monospace:size=10" };
static const char dmenufont[] = "monospace:size=10";
static const char col_gray1[] = "#222222";
static const char col_gray2[] = "#444444";
static const char col_gray3[] = "#bbbbbb";
@@ -30,7 +30,7 @@ static const char *colorslight[][3] = {
};
/* tagging */
static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
static const char *tags[] = { "q", "w", "e", "r", "t", "a", "s", "d", "f", "g", "z", "x", "c", "v", "b" };
static const Rule rules[] = {
/* xprop(1):
@@ -59,99 +59,103 @@ static const Layout layouts[] = {
/* key definitions */
#define MODKEY Mod4Mask
#define TAGKEYS(KEY,TAG) \
{ MODKEY, KEY, view, {.ui = 1 << TAG} }, \
{ MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \
{ MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \
{ MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} },
#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 } }
/* commands */
static char dmenumon[2] = "0"; /* component of dmenu{dark,light}, manipulated in spawndmenu() */
static char dmenumon[8] = "0"; /* component of dmenu{dark,light}, manipulated in spawndmenu() */
static const char *dmenudark[] = { "dmenu_run", "-l", "10", "-m", dmenumon, "-i", "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL };
static const char *dmenulight[] = { "dmenu_run", "-l", "10", "-m", dmenumon, "-i", "-fn", dmenufont, "-nb", col_gray3, "-nf", col_gray1, "-sb", col_cyan, "-sf", col_gray4, NULL };
static const char *termcmd[] = { "alacritty", NULL };
static const char *lockcmd[] = { "slock", NULL };
static const char *suspendcmd[] = { "systemctl", "suspend", NULL };
static const char *themecmd[] = { "lupan-set-theme", "toggle", NULL };
static const char *changeresulutioncmd[] = { "change-resolution", NULL };
#include "shift-tools.c"
#include "exitdwm.c"
#include "movestack.c"
static const Key keys[] = {
/* modifier key function argument */
{ MODKEY, XK_p, spawndmenu, {0} },
{ MODKEY, XK_u, shiftview, { .i = +1 } },
{ MODKEY|ShiftMask, XK_u, shifttag, { .i = +1 } },
{ MODKEY|ShiftMask, XK_y, shifttag, { .i = -1 } },
{ MODKEY, XK_y, shiftview, { .i = -1 } },
{ MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } },
{ MODKEY, XK_b, togglebar, {0} },
{ MODKEY, XK_j, focusstackvis, {.i = +1 } },
{ MODKEY, XK_k, focusstackvis, {.i = -1 } },
{ MODKEY|ControlMask, XK_j, focusstackhid, {.i = +1 } },
{ MODKEY|ControlMask, XK_k, focusstackhid, {.i = -1 } },
{ MODKEY, XK_i, incnmaster, {.i = +1 } },
{ MODKEY, XK_d, incnmaster, {.i = -1 } },
{ MODKEY, XK_h, setmfact, {.f = -0.05} },
{ MODKEY|ShiftMask, XK_h, shiftboth, { .i = -1 } },
{ MODKEY|ControlMask, XK_h, shiftswaptags, { .i = -1 } },
{ MODKEY|ControlMask, XK_l, shiftswaptags, { .i = +1 } },
{ MODKEY|ShiftMask, XK_l, shiftboth, { .i = +1 } },
{ MODKEY, XK_l, setmfact, {.f = +0.05} },
{ MODKEY|ShiftMask, XK_j, movestack, {.i = +1 } },
{ MODKEY|ShiftMask, XK_k, movestack, {.i = -1 } },
{ MODKEY, XK_Return, zoom, {0} },
{ MODKEY, XK_Tab, view, {0} },
{ MODKEY, XK_w, goback, {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_g, setlayout, {.v = &layouts[3]} },
{ 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 } },
{ MODKEY, XK_s, show, {0} },
{ MODKEY|ShiftMask, XK_s, showall, {0} },
{ MODKEY|ControlMask, XK_s, hide, {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)
{ ControlMask, XK_1, focusbynum, {.i = 0} },
{ ControlMask, XK_2, focusbynum, {.i = 1} },
{ ControlMask, XK_3, focusbynum, {.i = 2} },
{ ControlMask, XK_4, focusbynum, {.i = 3} },
{ ControlMask, XK_5, focusbynum, {.i = 4} },
{ ControlMask, XK_6, focusbynum, {.i = 5} },
{ ControlMask, XK_7, focusbynum, {.i = 6} },
{ ControlMask, XK_8, focusbynum, {.i = 7} },
{ ControlMask, XK_9, focusbynum, {.i = 8} },
{ MODKEY|ShiftMask, XK_q, quit, {0} },
{ MODKEY|ControlMask|ShiftMask, XK_q, quit, {1} },
{ MODKEY|ShiftMask, XK_e, exitdwm, {0} },
{ MODKEY, XK_o, winview, {0} },
{ MODKEY, XK_a, gridall, {} },
{ MODKEY, XK_r, winviewmono, {} },
{ MODKEY, XK_n, spawn, {.v = changeresulutioncmd } },
{ MODKEY|ControlMask|ShiftMask, XK_s, spawn, {.v = suspendcmd } },
{ MODKEY|ControlMask|ShiftMask, XK_l, spawn, {.v = lockcmd } },
{ MODKEY|ControlMask|ShiftMask, XK_t, spawn, {.v = themecmd } },
static Keychord *keychords[] = {
/* Keys function argument */
&((Keychord){1, {{MODKEY, XK_p}}, spawndmenu, {0} }),
&((Keychord){1, {{MODKEY, XK_u}}, shiftview, { .i = +1 } }),
&((Keychord){1, {{MODKEY|ShiftMask, XK_u}}, shifttag, { .i = +1 } }),
&((Keychord){1, {{MODKEY|ShiftMask, XK_y}}, shifttag, { .i = -1 } }),
&((Keychord){1, {{MODKEY, XK_y}}, shiftview, { .i = -1 } }),
&((Keychord){1, {{MODKEY|ShiftMask, XK_Return}}, spawn, {.v = termcmd } }),
&((Keychord){2, {{MODKEY, XK_semicolon}, {0, XK_b}}, togglebar, {0} }),
&((Keychord){1, {{MODKEY, XK_j}}, focusstackvis, {.i = +1 } }),
&((Keychord){1, {{MODKEY, XK_k}}, focusstackvis, {.i = -1 } }),
&((Keychord){1, {{MODKEY|ControlMask, XK_j}}, focusstackhid, {.i = +1 } }),
&((Keychord){1, {{MODKEY|ControlMask, XK_k}}, focusstackhid, {.i = -1 } }),
&((Keychord){1, {{MODKEY, XK_i}}, incnmaster, {.i = +1 } }),
&((Keychord){2, {{MODKEY, XK_semicolon}, {0, XK_d}}, incnmaster, {.i = -1 } }),
&((Keychord){1, {{MODKEY, XK_h}}, setmfact, {.f = -0.05} }),
&((Keychord){1, {{MODKEY|ShiftMask, XK_h}}, shiftboth, {.i = -1} }),
&((Keychord){1, {{MODKEY|ControlMask, XK_h}}, shiftswaptags, {.i = -1} }),
&((Keychord){1, {{MODKEY, XK_l}}, setmfact, {.f = +0.05} }),
&((Keychord){1, {{MODKEY|ControlMask, XK_l}}, shiftswaptags, { .i = +1 } }),
&((Keychord){1, {{MODKEY|ShiftMask, XK_l}}, shiftboth, { .i = +1 } }),
&((Keychord){1, {{MODKEY|ShiftMask, XK_j}}, movestack, {.i = +1 } }),
&((Keychord){1, {{MODKEY|ShiftMask, XK_k}}, movestack, {.i = -1 } }),
&((Keychord){1, {{MODKEY, XK_Return}}, zoom, {0} }),
&((Keychord){1, {{MODKEY, XK_Tab}}, view, {0} }),
&((Keychord){2, {{MODKEY, XK_semicolon}, {0, XK_w}}, goback, {0} }),
&((Keychord){2, {{MODKEY, XK_semicolon}, {MODKEY|ShiftMask, XK_c}}, killclient, {0} }),
&((Keychord){2, {{MODKEY, XK_semicolon}, {0, XK_t}}, setlayout, {.v = &layouts[0]} }),
&((Keychord){2, {{MODKEY, XK_semicolon}, {0, XK_f}}, setlayout, {.v = &layouts[1]} }),
&((Keychord){1, {{MODKEY, XK_m}}, setlayout, {.v = &layouts[2]} }),
&((Keychord){2, {{MODKEY, XK_semicolon}, {0, XK_g}}, setlayout, {.v = &layouts[3]} }),
&((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, XK_s}}, show, {0} }),
&((Keychord){1, {{MODKEY|ShiftMask, XK_s}}, showall, {0} }),
&((Keychord){2, {{MODKEY, XK_semicolon}, {MODKEY|ControlMask, XK_s}}, hide, {0} }),
&((Keychord){2, {{MODKEY, XK_semicolon}, {MODKEY|ShiftMask, XK_q}}, quit, {0} }),
&((Keychord){2, {{MODKEY, XK_semicolon}, {MODKEY|ControlMask|ShiftMask, XK_q}}, quit, {1} }),
&((Keychord){2, {{MODKEY, XK_semicolon}, {MODKEY|ShiftMask, XK_e}}, exitdwm, {0} }),
&((Keychord){1, {{MODKEY, XK_o}}, winview, {0} }),
&((Keychord){2, {{MODKEY, XK_semicolon}, {0, XK_a}}, gridall, {0} }),
&((Keychord){2, {{MODKEY, XK_semicolon}, {0, XK_r}}, winviewmono, {0} }),
&((Keychord){2, {{MODKEY, XK_semicolon}, {ControlMask|ShiftMask, XK_s}}, spawn, {.v = suspendcmd } }),
&((Keychord){1, {{MODKEY|ControlMask|ShiftMask, XK_l}}, spawn, {.v = lockcmd } }),
&((Keychord){2, {{MODKEY, XK_semicolon}, {ControlMask|ShiftMask, XK_t}}, spawn, {.v = themecmd } }),
TAGKEYS( XK_q, 0)
TAGKEYS( XK_w, 1)
TAGKEYS( XK_e, 2)
TAGKEYS( XK_r, 3)
TAGKEYS( XK_t, 4)
TAGKEYS( XK_a, 5)
TAGKEYS( XK_s, 6)
TAGKEYS( XK_d, 7)
TAGKEYS( XK_f, 8)
TAGKEYS( XK_g, 9)
TAGKEYS( XK_z, 10)
TAGKEYS( XK_x, 11)
TAGKEYS( XK_c, 12)
TAGKEYS( XK_v, 13)
TAGKEYS( XK_b, 14)
&((Keychord){1, {{ ControlMask, XK_1}}, focusbynum, {.i = 0} }),
&((Keychord){1, {{ ControlMask, XK_2}}, focusbynum, {.i = 1} }),
&((Keychord){1, {{ ControlMask, XK_3}}, focusbynum, {.i = 2} }),
&((Keychord){1, {{ ControlMask, XK_4}}, focusbynum, {.i = 3} }),
&((Keychord){1, {{ ControlMask, XK_5}}, focusbynum, {.i = 4} }),
&((Keychord){1, {{ ControlMask, XK_6}}, focusbynum, {.i = 5} }),
&((Keychord){1, {{ ControlMask, XK_7}}, focusbynum, {.i = 6} }),
&((Keychord){1, {{ ControlMask, XK_8}}, focusbynum, {.i = 7} }),
&((Keychord){1, {{ ControlMask, XK_9}}, focusbynum, {.i = 8} }),
};
/* button definitions */
+124
View File
@@ -0,0 +1,124 @@
/* See LICENSE file for copyright and license details. */
/* appearance */
static const unsigned int borderpx = 1; /* border pixel of 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 */
static const char *fonts[] = { "monospace:size=10" };
static const char dmenufont[] = "monospace:size=10";
static const char col_gray1[] = "#222222";
static const char col_gray2[] = "#444444";
static const char col_gray3[] = "#bbbbbb";
static const char col_gray4[] = "#eeeeee";
static const char col_cyan[] = "#005577";
static const char *colors[][3] = {
/* fg bg border */
[SchemeNorm] = { col_gray3, col_gray1, col_gray2 },
[SchemeSel] = { col_gray4, col_cyan, col_cyan },
};
/* tagging */
static const char *tags[] = { "q", "w", "e", "r", "t", "a", "s", "d", "f", "g", "z", "x", "c", "v", "b" };
static const Rule rules[] = {
/* xprop(1):
* WM_CLASS(STRING) = instance, class
* WM_NAME(STRING) = title
*/
/* class instance title tags mask isfloating monitor */
{ "Gimp", NULL, NULL, 0, 1, -1 },
{ "Firefox", NULL, NULL, 1 << 8, 0, -1 },
};
/* layout(s) */
static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */
static const int nmaster = 1; /* number of clients in master area */
static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */
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[] = {
/* symbol arrange function */
{ "[]=", tile }, /* first entry is default */
{ "><>", NULL }, /* no layout function means floating behavior */
{ "[M]", monocle },
};
/* key definitions */
#define MODKEY Mod4Mask
#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 } }
/* commands */
static char dmenumon[8]; /* 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[] = { "alacritty", NULL };
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_semicolon}, {0, XK_e}}, spawn, {.v = termcmd } }),
&((Keychord){2, {{MODKEY, XK_semicolon}, {0, 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_u}}, 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){2, {{MODKEY, XK_semicolon}, {0, XK_t}}, setlayout, {.v = &layouts[0]} }),
&((Keychord){2, {{MODKEY, XK_semicolon}, {0, XK_f}}, setlayout, {.v = &layouts[1]} }),
&((Keychord){2, {{MODKEY, XK_semicolon}, {0, 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_y}}, 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){2, {{MODKEY, XK_semicolon}, {MODKEY|ShiftMask, XK_q}}, quit, {0} }),
TAGKEYS( XK_q, 0)
TAGKEYS( XK_w, 1)
TAGKEYS( XK_e, 2)
TAGKEYS( XK_r, 3)
TAGKEYS( XK_t, 4)
TAGKEYS( XK_a, 5)
TAGKEYS( XK_s, 6)
TAGKEYS( XK_d, 7)
TAGKEYS( XK_f, 8)
TAGKEYS( XK_g, 9)
TAGKEYS( XK_z, 10)
TAGKEYS( XK_x, 11)
TAGKEYS( XK_c, 12)
TAGKEYS( XK_v, 13)
TAGKEYS( XK_b, 14)
};
/* button definitions */
/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */
static const Button buttons[] = {
/* click event mask button function argument */
{ ClkLtSymbol, 0, Button1, setlayout, {0} },
{ ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} },
{ ClkWinTitle, 0, Button2, zoom, {0} },
{ ClkStatusText, 0, Button2, spawn, {.v = termcmd } },
{ ClkClientWin, MODKEY, Button1, movemouse, {0} },
{ ClkClientWin, MODKEY, Button2, togglefloating, {0} },
{ ClkClientWin, MODKEY, Button3, resizemouse, {0} },
{ ClkTagBar, 0, Button1, view, {0} },
{ ClkTagBar, 0, Button3, toggleview, {0} },
{ ClkTagBar, MODKEY, Button1, tag, {0} },
{ ClkTagBar, MODKEY, Button3, toggletag, {0} },
};
BIN
View File
Binary file not shown.
Executable
BIN
View File
Binary file not shown.
+272
View File
@@ -0,0 +1,272 @@
From 9c020a20a2e1091e8b47edfea5e8795d8b451bc9 Mon Sep 17 00:00:00 2001
From: Lukasz Kasprzak <lukas@labunix.xyz>
Date: Fri, 17 Jul 2026 15:45:22 +0200
Subject: [PATCH] keychord: bind a sequence of keys, and dispatch events while
waiting
Original keychord patch by Hai Nguyen <hhai2105@gmail.com>, updated for
dwm 6.4+ grabkeys() by Aaron Zueger <contact@azureorange.xyz>. This
revision rebases onto dwm 6.8 and fixes an event-loss bug in the chord
wait loop.
The wait loop used to discard every event that was not a KeyPress:
while (running && !XNextEvent(dpy, &event) && !ran)
if(event.type == KeyPress)
break;
Two consequences, both reproduced on Xephyr:
1. Unrecoverable input freeze. grabbuttons() grabs unfocused clients
GrabModeSync/GrabModeSync, so clicking one freezes pointer AND
keyboard until the grabbing client calls XAllowEvents. dwm does that
in buttonpress() -- but if the ButtonPress is discarded here,
buttonpress() never runs. The grab stays frozen forever and the loop
can never exit, because a frozen keyboard cannot deliver the KeyPress
it is waiting for. Only killing dwm ends it; XSendEvent is discarded
by this same loop and XTEST queues behind the frozen device.
Repro: press a chord prefix, then click any unfocused client.
Observed: XGrabKeyboard -> GrabFrozen, XQueryPointer -> Button1 held.
2. Orphaned windows. A MapRequest arriving mid-chord is dropped, so a
window opening during a chord is never managed. DestroyNotify,
Expose and ConfigureRequest are lost the same way.
Dispatch through handler[] as run() does, breaking on KeyPress before
dispatch so keypress() cannot recurse. The `&& !ran` term is dropped:
ran is always 0 on entry and nothing in the body can set it.
---
config.def.h | 81 ++++++++++++++++++++++++++--------------------------
dwm.c | 79 +++++++++++++++++++++++++++++++++++++++++---------
2 files changed, 106 insertions(+), 54 deletions(-)
diff --git a/config.def.h b/config.def.h
index 81c3fc0..7c7ec36 100644
--- a/config.def.h
+++ b/config.def.h
@@ -47,11 +47,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 } }
@@ -61,41 +61,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 53b393e..243b12c 100644
--- a/dwm.c
+++ b/dwm.c
@@ -101,9 +101,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;
@@ -266,6 +271,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,58 @@ 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();
+ /* Events arriving mid-chord must be dispatched, not dropped:
+ * discarding a ButtonPress that activated the GrabModeSync grab
+ * from grabbuttons() freezes pointer and keyboard permanently,
+ * since only buttonpress()'s XAllowEvents can release it. */
+ while (running && !XNextEvent(dpy, &event)) {
+ if (event.type == KeyPress)
+ break;
+ if (handler[event.type])
+ handler[event.type](&event); /* call handler */
+ }
+ r = w;
+ Keychord **holder = rpointer;
+ rpointer = wpointer;
+ wpointer = holder;
+ }
+ currentkey = 0;
+ grabkeys();
}
void
--
2.47.3
+3
View File
@@ -110,6 +110,9 @@ Increase master area size.
.B Mod1\-h
Decrease master area size.
.TP
.B Mod1\-o
Select view of the window in focus. The list of tags to be displayed is matched to the window tag list.
.TP
.B Mod1\-Return
Zooms/cycles focused window to/from master area (tiled layouts only).
.TP
+237 -41
View File
@@ -52,8 +52,8 @@
#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]))
#define HIDDEN(C) ((getstate(C->win) == IconicState))
#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)
@@ -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;
@@ -179,6 +184,7 @@ static Atom getatomprop(Client *c, Atom prop);
static int getrootptr(int *x, int *y);
static long getstate(Window w);
static int gettextprop(Window w, Atom atom, char *text, unsigned int size);
static void goback(const Arg *arg);
static void grabbuttons(Client *c, int focused);
static void grabkeys(void);
static void hide(const Arg *arg);
@@ -228,6 +234,7 @@ static void tagmon(const Arg *arg);
static void tile(Monitor *m);
static void togglebar(const Arg *arg);
static void togglefloating(const Arg *arg);
static void focusbynum(const Arg *arg);
static void toggletag(const Arg *arg);
static void toggleview(const Arg *arg);
static void togglewin(const Arg *arg);
@@ -245,12 +252,16 @@ static void updatetitle(Client *c);
static void updatewindowtype(Client *c);
static void updatewmhints(Client *c);
static void view(const Arg *arg);
static void warp(const Client *c);
static Client *wintoclient(Window w);
static Monitor *wintomon(Window w);
static void winview(const Arg* arg);
static int xerror(Display *dpy, XErrorEvent *ee);
static int xerrordummy(Display *dpy, XErrorEvent *ee);
static int xerrorstart(Display *dpy, XErrorEvent *ee);
static void zoom(const Arg *arg);
static void gridall(const Arg *arg);
static void winviewmono(const Arg *arg);
/* variables */
static const char broken[] = "broken";
@@ -284,10 +295,11 @@ static Cur *cursor[CurLast];
static Clr **scheme, **schemedark, **schemelight;
static Display *dpy;
static Drw *drw;
static Monitor *mons, *selmon;
static Monitor *mons, *selmon, *prevmon;
static Window root, wmcheckwin;
static const char **dmenucmd;
static int colormodechanged;
unsigned int currentkey = 0;
/* configuration, allows nested code to access above variables */
#include "config.h"
@@ -417,7 +429,8 @@ arrange(Monitor *m)
void
arrangemon(Monitor *m)
{
strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol);
strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof(m->ltsymbol) - 1);
m->ltsymbol[sizeof(m->ltsymbol) - 1] = '\0';
if (m->lt[m->sellt]->arrange)
m->lt[m->sellt]->arrange(m);
}
@@ -449,6 +462,7 @@ buttonpress(XEvent *e)
/* focus monitor if necessary */
if ((m = wintomon(ev->window)) && m != selmon) {
unfocus(selmon->sel, 1);
prevmon = selmon;
selmon = m;
focus(NULL);
}
@@ -605,6 +619,9 @@ configurenotify(XEvent *e)
/* TODO: updategeom handling sucks, needs to be simplified */
if (ev->window == root) {
if (ev->width <= 0 || ev->height <= 0)
return;
dirty = (sw != ev->width || sh != ev->height);
sw = ev->width;
sh = ev->height;
@@ -688,7 +705,8 @@ createmon(void)
m->topbar = topbar;
m->lt[0] = &layouts[0];
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) - 1);
m->ltsymbol[sizeof(m->ltsymbol) - 1] = '\0';
return m;
}
@@ -837,6 +855,7 @@ enternotify(XEvent *e)
m = c ? c->mon : wintomon(ev->window);
if (m != selmon) {
unfocus(selmon->sel, 1);
prevmon = selmon;
selmon = m;
} else if (!c || c == selmon->sel)
return;
@@ -869,8 +888,10 @@ focus(Client *c)
}
}
if (c) {
if (c->mon != selmon)
if (c->mon != selmon) {
prevmon = selmon;
selmon = c->mon;
}
if (c->isurgent)
seturgent(c, 0);
detachstack(c);
@@ -906,8 +927,10 @@ focusmon(const Arg *arg)
if ((m = dirtomon(arg->i)) == selmon)
return;
unfocus(selmon->sel, 0);
prevmon = selmon;
selmon = m;
focus(NULL);
warp(selmon->sel);
}
void
@@ -1019,8 +1042,9 @@ gettextprop(Window w, Atom atom, char *text, unsigned int size)
return 0;
if (name.encoding == XA_STRING) {
strncpy(text, (char *)name.value, size - 1);
} else if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) {
strncpy(text, *list, size - 1);
} else if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success) {
if (n > 0 && *list)
strncpy(text, *list, size - 1);
XFreeStringList(list);
}
text[size - 1] = '\0';
@@ -1028,6 +1052,22 @@ gettextprop(Window w, Atom atom, char *text, unsigned int size)
return 1;
}
void
goback(const Arg *arg)
{
if (prevmon == NULL) {
Arg a = {0};
view(&a);
} else if (prevmon != selmon) {
unfocus(selmon->sel, 0);
Monitor *p = selmon;
selmon = prevmon;
focus(NULL);
prevmon = p;
warp(selmon->sel);
}
}
void
grabbuttons(Client *c, int focused)
{
@@ -1054,7 +1094,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;
@@ -1064,15 +1105,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);
}
}
@@ -1129,17 +1173,58 @@ 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();
/* Events arriving mid-chord must be dispatched, not dropped:
* discarding a ButtonPress that activated the GrabModeSync grab
* from grabbuttons() freezes pointer and keyboard permanently,
* since only buttonpress()'s XAllowEvents can release it. */
while (running && !XNextEvent(dpy, &event)) {
if (event.type == KeyPress)
break;
if (handler[event.type])
handler[event.type](&event); /* call handler */
}
r = w;
Keychord **holder = rpointer;
rpointer = wpointer;
wpointer = holder;
}
currentkey = 0;
grabkeys();
}
void
@@ -1288,6 +1373,8 @@ motionnotify(XEvent *e)
return;
if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) {
unfocus(selmon->sel, 1);
if (m != selmon)
prevmon = selmon;
selmon = m;
focus(NULL);
}
@@ -1349,6 +1436,7 @@ movemouse(const Arg *arg)
XUngrabPointer(dpy, CurrentTime);
if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) {
sendmon(c, m);
prevmon = selmon;
selmon = m;
focus(NULL);
}
@@ -1453,12 +1541,36 @@ void
resizeclient(Client *c, int x, int y, int w, int h)
{
XWindowChanges wc;
unsigned int n;
unsigned int gapoffset;
unsigned int gapincr;
Client *nbc;
c->oldx = c->x; c->x = wc.x = x;
c->oldy = c->y; c->y = wc.y = y;
c->oldw = c->w; c->w = wc.width = w;
c->oldh = c->h; c->h = wc.height = h;
wc.border_width = c->bw;
/* Get number of clients for the selected monitor */
for (n = 0, nbc = nexttiled(selmon->clients); nbc; nbc = nexttiled(nbc->next), n++);
/* Do nothing if layout is floating */
if (c->isfloating || selmon->lt[selmon->sellt]->arrange == NULL) {
gapincr = gapoffset = 0;
} else {
/* Remove border and gap if layout is monocle or only one client */
if (selmon->lt[selmon->sellt]->arrange == monocle || n == 1) {
gapoffset = 0;
gapincr = -2 * borderpx;
wc.border_width = 0;
} else {
gapoffset = gappx;
gapincr = 2 * gappx;
}
}
c->oldx = c->x; c->x = wc.x = x + gapoffset;
c->oldy = c->y; c->y = wc.y = y + gapoffset;
c->oldw = c->w; c->w = wc.width = w - gapincr;
c->oldh = c->h; c->h = wc.height = h - gapincr;
XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc);
configure(c);
XSync(dpy, False);
@@ -1516,6 +1628,7 @@ resizemouse(const Arg *arg)
while (XCheckMaskEvent(dpy, EnterWindowMask, &ev));
if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) {
sendmon(c, m);
prevmon = selmon;
selmon = m;
focus(NULL);
}
@@ -1542,6 +1655,8 @@ restack(Monitor *m)
wc.sibling = c->win;
}
}
if (m == selmon && (m->tagset[m->seltags] & m->sel->tags) && m->lt[m->sellt]->arrange != &monocle)
warp(m->sel);
XSync(dpy, False);
while (XCheckMaskEvent(dpy, EnterWindowMask, &ev));
}
@@ -1912,8 +2027,16 @@ void
spawn(const Arg *arg)
{
struct sigaction sa;
pid_t pid;
if (fork() == 0) {
if (arg->v == dmenucmd)
snprintf(dmenumon, sizeof(dmenumon), "%d", selmon->num);
if ((pid = fork()) == -1) {
fprintf(stderr, "dwm: fork failed: %s\n", strerror(errno));
return;
}
if (pid == 0) {
if (dpy)
close(ConnectionNumber(dpy));
setsid();
@@ -2015,6 +2138,19 @@ togglefloating(const Arg *arg)
arrange(selmon);
}
void
focusbynum(const Arg *arg)
{
int i;
Client *c;
i = 0;
c = nexttiled(selmon->clients);
for (; c && i < arg->i; c = nexttiled(c->next), i++);
focus(c);
}
void
toggletag(const Arg *arg)
{
@@ -2037,6 +2173,7 @@ toggleview(const Arg *arg)
unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK);
if (newtagset) {
prevmon = NULL;
selmon->tagset[selmon->seltags] = newtagset;
focus(NULL);
arrange(selmon);
@@ -2259,7 +2396,8 @@ updatenumlockmask(void)
XModifierKeymap *modmap;
numlockmask = 0;
modmap = XGetModifierMapping(dpy);
if (!(modmap = XGetModifierMapping(dpy)))
return;
for (i = 0; i < 8; i++)
for (j = 0; j < modmap->max_keypermod; j++)
if (modmap->modifiermap[i * modmap->max_keypermod + j]
@@ -2278,32 +2416,32 @@ updatesizehints(Client *c)
/* size is uninitialized, ensure that size.flags aren't used */
size.flags = PSize;
if (size.flags & PBaseSize) {
c->basew = size.base_width;
c->baseh = size.base_height;
c->basew = MAX(0, size.base_width);
c->baseh = MAX(0, size.base_height);
} else if (size.flags & PMinSize) {
c->basew = size.min_width;
c->baseh = size.min_height;
c->basew = MAX(0, size.min_width);
c->baseh = MAX(0, size.min_height);
} else
c->basew = c->baseh = 0;
if (size.flags & PResizeInc) {
c->incw = size.width_inc;
c->inch = size.height_inc;
c->incw = MAX(0, size.width_inc);
c->inch = MAX(0, size.height_inc);
} else
c->incw = c->inch = 0;
if (size.flags & PMaxSize) {
c->maxw = size.max_width;
c->maxh = size.max_height;
c->maxw = MAX(0, size.max_width);
c->maxh = MAX(0, size.max_height);
} else
c->maxw = c->maxh = 0;
if (size.flags & PMinSize) {
c->minw = size.min_width;
c->minh = size.min_height;
c->minw = MAX(0, size.min_width);
c->minh = MAX(0, size.min_height);
} else if (size.flags & PBaseSize) {
c->minw = size.base_width;
c->minh = size.base_height;
c->minw = MAX(0, size.base_width);
c->minh = MAX(0, size.base_height);
} else
c->minw = c->minh = 0;
if (size.flags & PAspect) {
if ((size.flags & PAspect) && size.min_aspect.x > 0 && size.max_aspect.y > 0) {
c->mina = (float)size.min_aspect.y / size.min_aspect.x;
c->maxa = (float)size.max_aspect.x / size.max_aspect.y;
} else
@@ -2365,6 +2503,7 @@ view(const Arg *arg)
{
if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
return;
prevmon = NULL;
selmon->seltags ^= 1; /* toggle sel tagset */
if (arg->ui & TAGMASK)
selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
@@ -2372,6 +2511,28 @@ view(const Arg *arg)
arrange(selmon);
}
void
warp(const Client *c)
{
int x, y;
if (!c) {
XWarpPointer(dpy, None, root, 0, 0, 0, 0, selmon->wx + selmon->ww / 2, selmon->wy + selmon->wh / 2);
return;
}
if (!getrootptr(&x, &y) ||
(x > c->x - c->bw &&
y > c->y - c->bw &&
x < c->x + c->w + c->bw*2 &&
y < c->y + c->h + c->bw*2) ||
(y > c->mon->by && y < c->mon->by + bh) ||
(c->mon->topbar && !y))
return;
XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w / 2, c->h / 2);
}
Client *
wintoclient(Window w)
{
@@ -2402,6 +2563,41 @@ wintomon(Window w)
return selmon;
}
/* Selects for the view of the focused window. The list of tags */
/* to be displayed is matched to the focused window tag list. */
void
winview(const Arg* arg){
Window win, win_r, win_p, *win_c;
unsigned nc;
int unused;
Client* c;
Arg a;
if (!XGetInputFocus(dpy, &win, &unused)) return;
while(XQueryTree(dpy, win, &win_r, &win_p, &win_c, &nc)
&& win_p != win_r) win = win_p;
if (!(c = wintoclient(win))) return;
a.ui = c->tags;
view(&a);
}
/* by desgua */
void
gridall(const Arg *arg)
{
setlayout(&(Arg){.v = &layouts[3]});
view(&(Arg){.ui = ~0});
}
void
winviewmono(const Arg *arg)
{
winview(&(Arg){0});
setlayout(&(Arg){.v = &layouts[2]});
}
/* There's no way to check accesses to destroyed windows, thus those cases are
* ignored (especially on UnmapNotify's). Other types of errors call Xlibs
* default error handler, which may call exit. */
BIN
View File
Binary file not shown.
+35
View File
@@ -0,0 +1,35 @@
void
gaplessgrid(Monitor *m) {
unsigned int n, cols, rows, cn, rn, i, cx, cy, cw, ch;
Client *c;
for(n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) ;
if(n == 0)
return;
/* grid dimensions */
for(cols = 0; cols <= n/2; cols++)
if(cols*cols >= n)
break;
if(n == 5) /* set layout against the general calculation: not 1:2:2, but 2:3 */
cols = 2;
rows = n/cols;
/* window geometries */
cw = cols ? m->ww / cols : m->ww;
cn = 0; /* current column number */
rn = 0; /* current row number */
for(i = 0, c = nexttiled(m->clients); c; i++, c = nexttiled(c->next)) {
if(i/rows + 1 > cols - n%cols)
rows = n/cols + 1;
ch = rows ? m->wh / rows : m->wh;
cx = m->wx + cn*cw;
cy = m->wy + rn*ch;
resize(c, cx, cy, cw - 2 * c->bw, ch - 2 * c->bw, False);
rn++;
if(rn >= rows) {
rn = 0;
cn++;
}
}
}
+48
View File
@@ -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);
}
}
+135
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);
}
BIN
View File
Binary file not shown.