I love keyboard-based interfaces.
That’s why, pretty soon after publishing this post, I moved to using a tiling window manager, and never looked back.
I chose bspwm for various reasons I won’t go into in this post, but one of the strong points that I see in its design is that it does only one thing - manage windows. The other stuff (namely, managing system key bindings) it leaves for other tools to do.
To complement bspwm’s “missing feature”, the developer created another tool, sxhkd, which helps managing key bindings on any Linux system, but works especially well with bspwm (of course).
There are tones of articles on how to set up bspwm with sxhkd, so I won’t go into basic configuration here. Instead, I want to share some snippets of my configuration, that may help inspire people, or even convince them to switch to bspwm+sxhkd.

Some of the snippets I wrote myself, while some I found online. For the latter I’ll add a link to the author’s snippet.
One last thing: most of the snippets involve around bspwm, but some are not, so I hope that even users of other window managers will find them useful.


Easily order windows

Ordering windows

bspwm has this Fibonacci-style window opening order, where new windows get smaller and smaller.
Sometimes, people want to manually reorder windows to something like a grid of four windows. However, the common way of doing that is quite cumbersome where each step has its own separate keybinding:

  1. Preselect a space for the new window.
  2. Go to another window and mark it.
  3. Move the marked window to the preselected space.

I found myself always doing step 2 and 3 without thinking too much, so I thought why not have a dedicated binding just for doing that.

# Move current window to a pre-selected space
super + shift + m
    bspc node -n last.!automatic

m for Move

Thanks to u/SataMaxx who pointed out to me on Reddit that the original snippet introduced here was inefficient, and suggested a better one.

Rotating windows tree

Rotating windows

I came up with this little trick as a result of a question asked not too long ago on Reddit.
It basically rotates the order of the windows on a single workspace clockwise/counterclockwise.

# Rotate tree
super + shift + {d,a}
    bspc node @/ -C {forward,backward}

a/d do not have a special meaning, just easy right/left to do with one hand

Move window to a workspace & switch to it

Arguably the most trivial thing to do with a window manager, but in bspwm it was quite a hassle to figure out. Fortunately, someone on Reddit did it already. Read below for an update.
I also added another binding of my own to send current window to the last used monitor. Note that this means that the monitor has to be “active” first before this works.

# Send to desktop
super + shift + {1-9,0}
    bspc node -d '^{1-9,10}' --follow

# Send to monitor
super + shift + equal
    bspc node -m last --follow

Thanks to u/altercharlie for pointing out on Reddit that the original first snippet here was way too complicated, and suggested the use of --follow instead.

Expanding and contracting windows

Expanding/contracting windows

Another one of those trivial tasks that are somewhat annoying to figure out how to do.
I came across a limitation of sxhkd here, where there are two sets of outputs for each key (one is a fallback, for when the other doesn’t work). Because of that, I had to resort to a not-so-elegant solution, but I found this trick to be working quite reliably for me.

# Expand/contract a window by moving one of its side outward/inward
super + s : {h,j,k,l}
    STEP=20; SELECTION={1,2,3,4}; \
    bspc node -z $(echo "left -$STEP 0,bottom 0 $STEP,top 0 -$STEP,right $STEP 0" | cut -d',' -f$SELECTION) || \
    bspc node -z $(echo "right -$STEP 0,top 0 $STEP,bottom 0 -$STEP,left $STEP 0" | cut -d',' -f$SELECTION)

Pressing super+s gets us into “resize mode” which is done via Vim-keys.

Note: Sometimes it’s easy to forget to exit this “resize mode” and system would seem unresponsive, with hjkl keys not registering. Always remember to hit Esc when finishing.

My take on creating a scratchpad

First, I have to admit that this one is going a little bit off track, since we’re discussing sxhkd, but I just had to include it here since I use it a lot on my sxhkdrc.
For those who don’t know, scratchpad is a feature that first debuted in the i3 window manager (afaik). It allows sending any window to an “invisible workspace” and quickly retrieve it whenever needed. For example, I use Telegram all the time, so instead of having it on a single workspace I have to navigate to each time, I can quickly show or hide it with a single keystroke.
My approach to this mimics i3’s functionality (inspired by this thread on Reddit), but while i3 allows only one program at a time to be sent to the scratchpad, I wanted to have infinite.

Usage: bspwm-toggle-visibility process_name [executable_name] [--take-first]
    process_name       As recognized by 'xdo' command
    executable_name    As used for launching from terminal
    --take-first       In case 'xdo' returns multiple process IDs

In short, my script takes a process name (which is a class name or instance name, obtainable by xprop) and searches for it. Then, it applies a rule based on its status:

Status Rule
Non-existent Launch
Visible Hide
Hidden Show

Sometimes a process name will differ from its executable, so there’s also an option to specify an executable name.
Also, some programs will spawn multiple instances, while only the first one actually matters, so I implemented a --take-first switcher to apply the show/hide only on the first instance of that program.


if [ $# = 0 ]; then
    cat <<EOF
Usage: $(basename "${0}") process_name [executable_name] [--take-first]
    process_name       As recognized by 'xdo' command
    executable_name    As used for launching from terminal
    --take-first       In case 'xdo' returns multiple process IDs
    exit 0

# Get id of process by class name and then fallback to instance name
id=$(xdo id -N "${1}" || xdo id -n "${1}")


while [ -n "${1}" ]; do
    case ${1} in
        id=$(head -1 <<<"${id}" | cut -f1 -d' ')

if [ -z "${id}" ]; then
    while read -r instance; do
        bspc node "${instance}" --flag hidden --to-monitor focused --focus
    done <<<"${id}"

→ Requires xdo for searching windows’ PIDs. (can be replaced by xdotool if necessary)

I named this script bspwm-toggle-visibility and here are some examples of how I use it with sxhkd:

# Toggle Telegram
alt + c
    bspwm-toggle-visibility TelegramDesktop telegram-desktop

# Toggle Tagaini Jisho
super + r ; t
    bspwm-toggle-visibility tagainijisho --take-first

# Toggle Ptpython
super + r ; p
    bspwm-toggle-visibility floating_ptpython "$TERMCMD --class floating_ptpython ptpython"

Other stuff

Show a help menu using rofi

sxhkd help menu using rofi

This trick comes in super handy in the beginning, and then when needing to quickly hint rarely-used key strokes.
Its original author used fzf with a floating terminal, but I use it with rofi, which I think is slightly more convenient.
I also included a small change (pun intended) to make more text fit in the rofi window. This, of course, can be changed.
Unlike other tricks, this trick must be included in a separate bash script. I have it in a file called sxhkd-help which is inside my PATH, but it can also be called using a full path (like ~/scripts/sxhkd-help).


awk '/^[a-z]/ && last {print "<small>",$0,"\t",last,"</small>"} {last=""} /^#/{last=$0}' ~/.config/sxhkd/sxhkdrc{,.common} |
    column -t -s $'\t' |
    rofi -dmenu -i -markup-rows -no-show-icons -width 1000 -lines 15 -yoffset 40

Note: In my case, sxhkd config files are split across two separate files, so I’m using them both (sxhkdrc and sxhkdrc.common). More on that in the extra section.
Note_2: rofi options are unique to my setup.

And inside sxhkdrc:

# Show help
super + slash

→ Requires rofi

This binding makes sense because on qwerty keyboards, slash (/) is also the same key as question mark (?).

Reloading sxhkdrc

This is just classic stuff, reloading sxhkd using sxhkd. Useful after making changes to sxhkdrc.
However, I remember seeing some weird techniques out there, while sxhkd has this built-in.
Here’s the relevant excerpt from sxhkd(1):

If sxhkd receives a SIGUSR1 (resp. SIGUSR2) signal, it will reload its configuration file (resp. toggle the grabbing state of all its bindings).

So all we need to do to reload sxhkdrc is send sxhkd a SIGUSR1 signal, and why not throw in a nice little notification on the way? ;)

# Reload sxhkdrc
super + shift + r
    pkill -usr1 -x sxhkd; notify-send 'sxhkd' 'Reloaded config'

→ Requires libnotify (for notify-send)

Access clipmenu via rofi

clipmenu with rofi

clipmenu is an awesome clipboard manager that also integrates with rofi. I use it to create a small clipboard history menu at cursor position.

# Show clipmenu
alt + v
    CM_LAUNCHER=rofi clipmenu \
        -location 1 \
        -m -3 \
        -no-show-icons \
        -theme-str '* \{ font: 10px; \}' \
        -theme-str 'listview \{ spacing: 0; \}' \
        -theme-str 'window \{ width: 20em; \}'

→ Requires clipmenu

Generate a random password to clipboard

This one is more of a shell trick (and I do have it as a shell alias as well), but it’s very handy to also have the ability to generate a random password just by hitting some keystrokes and then pasting them into wherever.

# Generate password
super + r ; g ; p
    tr -dc "a-zA-Z0-9_#@.-" < /dev/urandom | head -c 14 | xclip -selection clipboard

Requires xclip (for interfacing with system clipboard)

g ; p for Generate Password

Change to a random wallpaper

changing wallpapers

I’m exporting my wallpapers location at ~/.wallpapers inside my .xprofile:export WALLPAPERS="$HOME"/.wallpapers.
Then, I choose a random file from this directory, and set it as a wallpaper.

# Change wallpaper
super + r ; c ; b
    RAND=$(find "$WALLPAPERS" -xtype f | sort -R | head -1) \
        xwallpaper --zoom "$WALLPAPERS/$RAND"

→ Requires xwallpaper (for setting up image files as wallpaper on Xorg)

c ; b for Change Background

The same can be done to the lockscreen wallpaper, but depends on the particular setup.
I use betterlockscreen and since it can take quite a while to process the given image, I added a notification at the end.

# Change lock screen wallpaper
super + r ; c ; l
    betterlockscreen -u $WALLPAPERS; \
    notify-send 'betterlockscreen' 'Changed background'

→ Requires betterlockscreen & libnotify (for notify-send)

c ; l for Change Lockscreen

Close/lock screen

Closing the screen just makes the display go blank, while locking it actually brings up the lock screen (betterlockscreen in my case).
Extremely important at work - locking up the screen when leaving desk.

# Close screen
super + shift + c
    sleep 0.5; xset dpms force standby

# Lock screen
super + shift + x
    betterlockscreen -l dim -t ''

→ Requires xset (for closing the screen) & betterlockscreen

c for Close, x just because it’s right next to it :D

Note: Closing the screen requires half a second sleep time, otherwise the screen could be accidentally awaken from the initiating keystrokes.

Mimic Chrome OS shortcuts

I really love small and compact keyboards. However, one of their downsides is that they lack some of the useful keys, such as Home/End, and even Delete.
Chrome OS solved this problem by introducing some interesting keybindings for those missing keys, and I love them so much that I find myself using them even when I have dedicated keys on my keyboard.

# Emulate home/end keys
ctrl + alt + @{Up,Down}
    xte 'keyup Control_L' 'keyup Alt_L' 'key {Home,End}'

# Emulate home/end keys with shift
ctrl + alt + shift + @{Up,Down}
    xte 'keyup Control_L' 'keyup Alt_L' 'key {Home,End}'

# Emulate delete key
~alt + BackSpace
    xte 'keyup Alt_L' 'key Delete' 'keydown Alt_L'

→ Requires xautomation (xte tool)

Note: Performance of this tweak could use some improvement, mainly due to the usage of the old xautomation tool. I tried using xdotool and xdo instead, but both of them offered far worse responsiveness.

Extra: Organizing and running sxhkd

I have multiple systems running sxhkd. Most of my configuration applies to all of them, but inevitably some machines require small modifications.
While searching for a solution that’ll allow me to share the “core” configuration between machines, I noticed that sxhkd supported loading config files from multiple different sources at once.
I split my config file into 2 files, one I called sxhkdrc.common and the other sxhkdrc. The first contains the configuration common among all my machines, and the other is unique to each machine, adds extra stuff AND also rewrites bindings from the common config file.
The latter is extremely important, because it allows me to have the same keybinding do different things on different machines.

Here’s a simple example to help better illustrate what I mean.
On this section I showed my snippet for changing the lock screen wallpaper.
In that snippet I choose a wallpaper from my “normal” wallpaper collection. However, on my desktop I have a multi-monitor setup, and therefore want to choose a wallpaper from my widescreen wallpapers directory instead. So, on my desktop’s sxhkdrc I rewrite that binding. Here’s how it looks:

# Change lock screen wallpaper
super + r ; c ; l
    betterlockscreen -u $WALLPAPERS; \
    notify-send 'betterlockscreen' 'Changed background'

sxhkdrc (desktop)
# Change lock screen wallpaper
super + r ; c ; l
    betterlockscreen -u $WALLPAPERS/ultrawide; \
    notify-send 'betterlockscreen' 'Changed background'

It’s important to make sure that the common config file is loaded as an EXTRA_CONFIG, since the config file supplied with the -c switcher supersedes it.
Here’s how I run my sxhkd:

sxhkd -c $HOME/.config/sxhkd/sxhkdrc $HOME/.config/sxhkd/sxhkdrc.common