dwm Keyboard and Mouse Configuration^

When you use a traditional desktop environment, there is some form of settings configuration that includes a set of keyboard shortcuts where you open a modal and run the shortcut and assign it to a particular operation.

Most desktop environments on Linux and FreeBSD also include the functionality to have a shortcut run some arbitrary script or command. This feature is generally unknown or unheard of in the realm of proprietary operating systems where such extended functionality is more rigidly controlled.

Modifier Key^

When you want to send a command to dwm, generally you press a specific key that shifts the keyboard into its modified state. An example of this in other environments would be the Cmd key on macOS.

In the context of dwm, this is defined as the MODKEY macro:

config.h^
48#define MODKEY Mod1Mask

This sets MODKEY to Alt. This is a very reasonable place to put a key that you’re liable to press often—right under your thumb on a US keyboard.

Personally, I prefer Mod4Mask, which maps to the Super or Windows key, for this purpose, but I have a number of odd issues due to fragmentation around muscle memory that stems from my spending weekday hours on a Mac for work instead of the infinitely preferable Linux-only model that was a feature of past employment.

Commands^

The commands section of config.h sets up variables for commands that dwm runs for you. By default, there are two: dmenu and the terminal.

Dmenu Configuration^

Most all desktop environments and window managers have some means of launching applications. It’s a base requirement to actually use the system. The dmenucmd variable sets up the call to launch dmenu, which is in turn used to launch other applications in dwm.

Just before dmenucmd, there is another variable called dmenumon. This is passed into the -m option in dmenu, which specifies which monitor dmenu should be displayed on. It defaults to 0, which is the primary monitor rather than the active monitor.

config.h^
59static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */
60static const char *dmenucmd[] = {
61   "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb",
62   col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf",
63   col_gray4, NULL
64};

There’s not much to say about this code otherwise, it sets up the dmenu_run binary and sets a number of default flags to match the dwm theme so that it appears consistent with the compiled state of dwm.

Terminal Command^

The termcmd variable specifies the default command to run when the user requests a terminal. For users of window managers, the preference is often for a number of lightweight terminals launched ad hoc to suit the particular needs of the moment.

config.h^
65static const char *termcmd[]  = { "st", NULL };

Since Suckless also develops and maintains a terminal emulator, st this is the default value. You change this to xfce-terminal, xterm, ghostty, or whatever terminal emulator suits your preferences.

Helpers^

In addition to the terminal command, the config.h file also includes helper macros to aid in whatever operations you need to run as a keyboard shortcut.

Shell Helper^

The SHCMD macro is used in cases where you want to execute a shell command in the background. It takes a cmd argument and expands into the array used to call a script through /bin/sh.

config.h^
56#define SHCMD(cmd) { \
57   .v = (const char*[]){ \
58      "/bin/sh", "-c", cmd, NULL \
59   } }

A common use case for this macro is when you mean to run a background script.

For example, a good use of the SHCMD macro would be when you need to a call script to call isync to synchronize your local maildir with a remote IMAP server, run Notmuch to update the index, and then report the number of unread messages through a string passed into the notification daemon.

Tag Helper^

TAGKEYS is a helper macro used to configure tag operations in the keyboard shortcuts. It’s main purpose is to set several common keyboard shortcuts through one call to the key and tag number.

config.h^
49#define TAGKEYS(KEY,TAG) \
50     { MODKEY,                       KEY,      view,           {.ui = 1 << TAG} }, \
51     { MODKEY|ControlMask,           KEY,      toggleview,     {.ui = 1 << TAG} }, \
52     { MODKEY|ShiftMask,             KEY,      tag,            {.ui = 1 << TAG} }, \
53     { MODKEY|ControlMask|ShiftMask, KEY,      toggletag,      {.ui = 1 << TAG} },

So for example, a call to TAGKEYS(XK_1, 1) would expand so that:

Alt

1

Views tag 1

AltCtrl

1

Toggles the view to include tag 1 with the current tag

Alt Shift

1

Moves the current window to tag 1

Alt Shift Ctrl

1

Adds the current window to tag 1 without removing it from the current tag

Keyboard Shortcuts^

Keyboard shortcuts are specified in the key array. When you want to add a shortcut to your build, you do so by adding a Key structure to this array.

config.h^
62static const Key keys[] = {
63     /* modifier         key        function  argument */
64     { MODKEY,           XK_p,      spawn,    {.v = dmenucmd } },
65     { MODKEY|ShiftMask, XK_Return, spawn,    {.v = termcmd } },

The structure uses the following pattern:

  • The first entry is set using a bitwise operator that combines the modifier keys needed for the shortcut. The MODKEY macro was defined above and defaults to the Alt key. You can then apply the ShiftMask and/or ControlMask to refine the modification.

  • The key specifies the key on the keyboard that you press with the modifier. The XK_p for example, corresponds to the p key.

  • The function is the specific C function in the dwm code base that gets called when the user presses the given key with the given modifiers. The spawn() function, for example, runs an application or script.

  • The arguments refer to the arguments that are passed to the function, such as the argument to launch dmenu or st or the argument to increment a tag.

At the bottom of this array, you’ll see the TAGKEYS() helper macro in use to define common operations that apply to all tags.

config.h^
94     TAGKEYS(            XK_7,                6)
95     TAGKEYS(            XK_8,                7)
96     TAGKEYS(            XK_9,                8)
97     { MODKEY|ShiftMask, XK_q,      quit,    {0} },
98};

Mouse Controls^

The last section in the default config.h covers mouse controls. This is set on the buttons array. Each item in this array is a Button structure.

config.h^
102static const Button buttons[] = {
103     /* click         event mask  button   function        argument */
104     { ClkLtSymbol,   0,          Button1, setlayout,      {0} },
105     { ClkLtSymbol,   0,          Button3, setlayout,      {.v = &layouts[2]} },
106     { ClkWinTitle,   0,          Button2, zoom,           {0} },
107     { ClkStatusText, 0,          Button2, spawn,          {.v = termcmd } },
108     { ClkClientWin,  MODKEY,     Button1, movemouse,      {0} },
109     { ClkClientWin,  MODKEY,     Button2, togglefloating, {0} },
110     { ClkClientWin,  MODKEY,     Button3, resizemouse,    {0} },
111     { ClkTagBar,     0,          Button1, view,           {0} },
112     { ClkTagBar,     0,          Button3, toggleview,     {0} },
113     { ClkTagBar,     MODKEY,     Button1, tag,            {0} },
114     { ClkTagBar,     MODKEY,     Button3, toggletag,      {0} },
115};

The structure parallels the keyboard configuration. Though with a slightly altered arrangement.

  • The first item refers to the category of click event that occurs, specifically where on the default status bar or client window the user clicks.

  • Then there is a bitwise event mask that is either 0 for no keys held or some combination of modifier keys.

  • The third item indicates the button the user clicks.

  • Then, the C function dwm calls when the modifier, mouse click, and click event occur.

  • Lastly, the arguments dwm should pass into the function.

This functionality is mostly for the convenience of the user who is already working with the mouse, such as in a web browser. In general, users of tiling window managers tend to operate from the philosophy that the need for reaching from the keyboard to the mouse is a design flaw. Why remove your hands from the keyboard where you can perform so many operations so much faster to a mouse where you have to search for a thing and then can only do one thing at a time?

dwm Layout Configuration git: Decentralized Version Control