How to set up scratchpads - bakkeby/dusk GitHub Wiki

This quick guide goes through the basics of setting up scratchpads.

General idea

The general idea of scratchpads is to have a window that you can quickly open to make some quick notes and then hide again, much like a physical scratchpad or notebook.

When it comes to window managers the term scratchpads have become synonymous with windows that pop in and out of existence on demand using keybindings.

In practice any window can be used as a scratchpad, for example a web browser or even a combination of multiple windows.

How it works

There are some basic principles with regards to how it works that may help you understand why scratchpads are configured the way they are.

Let's say that we want a keybinding MOD+w to open the program kwrite as a scratchpad.

Having configured the scratchpad correctly we have the expected scenario of:

  • the user hits the MOD+w keybinding (1)
  • the window manager will search for a client (window) that is associated with the given scratchpad
  • if such a client is found then it will be revealed (or toggled away if already visible)
  • but if such a client does not exist then it will spawn the program instead (2)
    • the spawned process should open a window and
    • that window will ask to be shown (i.e. to be mapped) and
    • the window will be managed by the window manager and
    • we need a client rule (3) to associate the window with the given scratchpad so that the window manager may find it the next time the user hits that same keybinding

In terms of configuration we need three separate things for this to work:

  • (1) - a keybinding
  • (2) - a command to spawn if we do not have a client associated with the scratchpad
  • (3) - a client rule to associate the new window with the given scratchpad

Configuration

Let's continue with the example of using kwrite as a scratchpad toggled using MOD+w.

The scratchpad commmand

static const char *spcmd_w[] = {"w", "kwrite", "--title", "scratchpad_w", NULL};

^ (2) the command to execute if the scratchpad window does not exist.

Let's break this down.

static const char *spcmd_w[] = ...
                   ^^^^^^^

This is just the variable name and this can be whatever you want. We will refer to this later when setting up the keybindings.

Here we name it spcmd, short for scratchpad command, followed by _w just to signify that the command is intended to be used for the MOD+w keyboard shortcut.

                         ... = {"w", "kwrite", ...
                                ^^^

A scratchpad is defined through a scratch "key" that is set for one or more clients.

This leading single character ("w" in this case) is the scratch key that we are going to associate the scratchpad command with.

You can use any character you want, but it is good practice to use the same character as the keyboard shortcut that you will use to toggle this scratchpad.

The reason for this is that it will be easier later on to tell which scratchpad commands, keyboard shortcuts and client rules belong together.

                         ... = {"w", "kwrite", "--title", "scratchpad_w", NULL};
                                      ^^^^^^    ^^^^^^^    ^^^^^^^^^^^^   ^^^^

Then follows the the actual command to be executed, which in this case is kwrite --title scratchpad_w. The title argument is application specific and allows us to set a unique window title for the program. We need a unique window title later on when setting up the client rule.

The double quotes and commas between words, followed by a NULL value, is required by execvp which will handle the execution of the command. Adding "kwrite --title scratchpad_w" will not work because there are no commands in the system named like that (with spaces and all).

The keyboard shortcut

The keyboard shortcuts are set up using a macro named SCRATCHKEYS.

  SCRATCHKEYS(MODKEY, XK_w, spcmd_w)

^ (1) the keybinding to toggle/spawn the scratchpad.

Fairly straightforward. We pass the MODKEY, the keysym we want to associate the scratchpad with and the name of the command we created earlier.

The macro looks like this and will set up three keybindings to toggle, set and remove scratchpads.

#define SCRATCHKEYS(MOD,KEY,CMD) \
    { KeyPress,   MOD,                      KEY,      togglescratch,       {.v = CMD } }, \
    { KeyPress,   MOD|Ctrl,                 KEY,      setscratch,          {.v = CMD } }, \
    { KeyPress,   MOD|Ctrl|Shift,           KEY,      removescratch,       {.v = CMD } }, \

The client rule

  { .title = "scratchpad_w", .scratchkey = 'w', .flags = Floating },

^ (3) client rule(s) to associate the window with the given scratch key.

Here we filter on the title of "scratchpad_w" - because we explicitly change the window title of the program through the command.

If the new window matches the given rule then the scratch key of w will be set for the client so that the window manager will be able to find the client the next time the keybinding is pressed.

Additionally the window will be made floating through the flags (optional, you can have tiled scratchpads).

Tip: If the program allows for the WM class to be changed via command line arguments then it is generally better to use that than changing the title - this because there are often other things that may influence the window title of a program.

Tip: Alternative approach

It is possible to skip the creation of the command variable and bundle it straight with the keybinding.

To do that we will replace the existing SCRATCHPADS macro with the following:

#define SCRATCHKEYS(MOD,KEY,SKEY,...) \
    { KeyPress,   MOD,                      KEY,      togglescratch,       {.v = (const char*[]){ SKEY, __VA_ARGS__, NULL } } }, \
    { KeyPress,   MOD|Ctrl,                 KEY,      setscratch,          {.v = (const char*[]){ SKEY, NULL } } }, \
    { KeyPress,   MOD|Ctrl|Shift,           KEY,      removescratch,       {.v = (const char*[]){ SKEY, NULL } } }, \

and add keybindings with inline commands like this:

    SCRATCHKEYS(Super,                          XK_w,  "w",      "kwrite", "--title", "Scratchpad (w)")
    SCRATCHKEYS(Super,                          XK_e,  "e",      "kwrite", "--title", "Scratchpad (e)")
    SCRATCHKEYS(Super,                          XK_r,  "r",      "kwrite", "--title", "Scratchpad (r)")

General recommendations

As demonstrated in the examples above adding a prefix or suffix indicating which scratchpad a given command is associated with makes the configuration a bit more manageable.

For example seeing the command of spcmd_x in the config file lets you draw the conclusion that it is in relation to the scratchpad with the MOD+x keybinding.

The same goes with finding a client rule that refers to "scratchpad_x" or "scratchpad (x)".

Doing this consistently makes it easier to spot what commands, keybindings and client rules are associated with each other should you need to make changes.

Troubleshooting

Here are a few issues that are fairly regular.

Scratchpad command won't spawn

Problem: I have set up scratchpads but when I hit the keybinding nothing happens.

1) Check that you have set up a keybinding to spawn the command

2) Check that the command and arguments are correctly spaced with commas and separate strings for each argument.

E.g. this will not work:

static const char *spcmd_w[] = {"w", "st -n 'spterm (w)' -g 120x34", NULL };

but this should:

static const char *spcmd_w[] = {"w", "st", "-n", "spterm (w)", "-g", "120x34", NULL };

Double check that the command works on the command line.

Note that if you are executing a shell script then that shell script may not be in the PATH environment variable that is visible to your window manager. This may be different from what you may have set in bash or some other shell.

If that is the case then try checking if using an absolute path to the script works. If that solves the issue then you may want to consider updating the path in your ~/.profile file.

Scratchpads keep opening

Problem: I have set up scratchpads, but every time I hit the keybinding a new window spawns instead of hiding the existing window.

1) Most likely your client rule does not match the window and thus the client is not associated with the given scratchpad.

Double check the client rules and note that:

  • other rules may be interfering and
  • some windows come through with no title, no class and no instance (e.g. spotify) and
  • some windows may change title, class and instance after having been mapped (e.g. LibreOffice applications)

My scratchpad window interferes with the normal window

Problem: I use a particular application as a normal client, but I also want a second instance to work as a scratchpad. When I launch the normal client it becomes the scratchpad which I do not want.

1) Double check the client rules that identify the window as a scratchpad

Generally you will want to be able to uniquely identify the scratchpad window using the class, instance and/or window title (or other filters, if available). Some, but not all, windows will allow for some of these properties to be overridden using command line arguments.

If such command line arguments are available then you should definitely use them.

In cases where the application does not allow new instances to be spawned with a unique class, instance or window title then it may be that the only real solution is to assign scratchpads on demand during runtime.


Also see the scratchpads page for more details.


Back to Guides.

⚠️ **GitHub.com Fallback** ⚠️