../𝚂𝚃𝚇
source

sxhkd-ish

another place the shell lives forever

Published 2021-01-26, last edit 2022-07-20

For years now, I’ve used bspwm as my daily driver window manager. It does not ship with a builtin way to set keybindings – instead, the recommended path is to use another program by it’s author, sxhkd, to bind key presses to process calls. Such calls are launched with SHELL -c COMMAND, where SHELL is set by $SXHKD_SHELL or $SHELL. Here’s a pair of bindings from my sxhkdrc:

super + Escape
    notify-send "reloading sxhkd!"; pkill -USR1 -x sxhkd

super + {_,shift,alt,ctrl} + {h,n,e,l}
    {focus,move,resize,presel}.sh {west,south,north,east}

One of sxhkd’s most attractive features for me is the slick templating you can have. That second example assigns my preferred directional keys (colemak-dhk) to call scripts assigned to do different actions. The full expansion is as follows:

super + h
    focus.sh west
super + n
    focus.sh south
super + e
    focus.sh north
super + l
    focus.sh east
super + shift + h
    move.sh west
super + shift + n
    move.sh south
super + shift + e
    move.sh north
super + shift + l
    move.sh east
super + alt + h
    resize.sh west
super + alt + n
    resize.sh south
super + alt + e
    resize.sh north
super + alt + l
    resize.sh east
super + ctrl + h
    presel.sh west
super + ctrl + n
    presel.sh south
super + ctrl + e
    presel.sh north
super + ctrl + l
    presel.sh east

WEW that’s a lot. Thanks Baskerbae.

∗ ∗ ∗

Many of the newer wayland window managers have you assign keys individually via a client, mostly in a shape that sort of hails from the i3 config format. I’ll cherrypick sway’s, but you can see comparable formats in example configs for cardboard, river, and wayfire as well:

bindsym $mod+$left focus left
bindsym $mod+$down focus down
bindsym $mod+$up focus up
bindsym $mod+$right focus right

This snippet comes from the example config, but if you prepend the lines with swaymsg you get something you can run in a script. In the week or so I gave sway a drive, most of my configuring was done this way – with a swayrc file containing swaymsg client calls – the prospect of porting my comparably compact sxhkdrc file was annoying though. So, like the lazy programmer I am, I squinted at it a little bit and realized the sxhkd templating format looked a little… familiar. It’s supremely similar to shell templating!:

$ echo foo{bar,baz}
foobar foobaz

$ echo {,fae}{bar,baz}
bar baz faebar faebaz

$ echo super+{_,shift,alt,ctrl}+{h,n,e,l}
super+_+h super+_+n super+_+e super+_+l super+shift+h super+shift+n super+shift+e super+shift+l super+alt+h super+alt+n super+alt+e super+alt+l super+ctrl+h super+ctrl+n super+ctrl+e super+ctrl+l

Armed with this knowledge, I glued together something terrible for my swayrc file:

bind() {
    args=("$@")
    for i in $(seq 0 "$(( (${#args[@]} / 2) - 1 ))"); do
        key=$(echo "${args[$i]}" | sed 's/super/Mod4/' )

        val_index=$(( i + (${#args[@]} / 2) ))
        val=${args[$val_index]}

        echo swaymsg bindsym "$key" "$bindprefix$val"
        swaymsg bindsym "$key" "$bindprefix$val"
    done
}

# bind exec
binde() {
    bindprefix='exec ' bind "$@"
}

And then, as long as I escaped the spaces to connect the terms, I could get away with this as my config:

binde super+{,shift+,alt+,ctrl+}{h,n,e,l} \
    {focus,move,resize,presel}.sh\ {west,south,north,east}

Hooray!