bspwm gym

tuesday, 13 january 2015

the messenging aspect of bspwm via shell scripts and keyboard handler allow a lot of clever desktop gymnastics. One added benefit is that it is fairly difficult to bork the actual window manager. Illegal bspc commands just fail gracefully – no harm done. No recompilation required. And you can test out code snippets in a terminal window before committing the code to your config files.

dynamic window gaps

as more windows are opened or closed, shrinking and expanding the window_gap between them manages the visible content in a pleasing organic way (versus what quickly appears as an unnaturally large window_gap as individual windows get significantly smaller and squeezed within a fixed desktop border). When I saw an xmonad layout with this behavior, I had to have it in bspwm..

#!/bin/sh function linear() { echo “($X + $@) - ($W - 1) * $@” | bc } function binary() { echo “($X * 2) / (2 ($W -1))” | bc } function pitch() { echo “$X * 0.25 / 0.282” | bc } function resolution() { echo “$X * 1680 / 2560” | bc } bspc control --subscribe | while read line; do X=94 [[ $(bspc query –monitors –desktop focused) = DVI-1 ]] || X=$(pitch) # alternatively X=$(resolution) W=$(bspc query –desktop focused –windows | wc -l) G=$(binary) # alternatively G=$(linear 10) [[ $G -lt 1 ]] && G=1 bspc config –desktop focused window_gap $G conky-padding done

The trick here is to launch the bspwm session loop script at the end of the bspwmrc configuration (in my case, just before the autostart to populate all the desktop windows). By polling the bspwm status via “bspc control”, desktop/window focus can be tracked and the appropriate window_gap calculated.

As additional windows are opened, desktop real estate (area) extends towards the edges of the screen to accommodate them – in this case a simple binary calculation is applied (versus a linear step). For a multihead setup with differing display panels, the two window gap for the primary monitor (94, in this case) is arbitrarily adjusted for the secondary monitor by pitch ratio to maintain visually identical gaps between the two differing LCD panels – an alternate resolution ratio function tightens up the gaps for the smaller (lower resolution) display providing a different visual balance. It’s all a matter of personal preference!

pseudo frame

tiling always maximizes utilization of the desktop area so that all applications are visible. To create empty space on the desktop a window can be placed in a pseudo_tile which floats the application window within the tile creating what looks like an empty border around the window (as long as the tile is larger than the application window).

Alternatively, empty tiles can be created with an invisible application, in this case, an invisible terminal (which is coupled with the bspwmrc rule “border=false”)..

alt + shift + o urxvtc -shading 100 -title 'pseudoframe' -name 'pseudoframe' -e sh -c 'tput civis; sleep 365d'

The terminal cursor is hidden and the application persists effectively for an indeterminate amount of time. There is probably a native application out there that accomplishes this but this is how I’ve implemented the placeholder function.

Desktop with pseudo frame

Sometimes when working in distraction free mode, a single application window can be too large (on a 30” monitor). Creating an empty tile is easier than resizing a floating window. Of course, just opening an empty terminal would accomplish the same thing, albeit, not have as much eye candy effect, making a tiler look like a stacking window manager!

default layout

bspwm balance, preselect and window edge push/pull actions permit custom window placement and sizing, overriding the default binary space partitioning layout.

To restore the default binary space layout after several manual tiling actions, this script recreates all tiles on the desktop using the default split ratio à la xmonad. The current window in focus will occupy the largest tile..

alt + shift + e D=$(bspc query –desktops –desktop focused); W=$(bspc query –windows –window focused); bspc desktop –rename $D'; bspc monitor –add-desktops $D; for i in $(bspc query –windows –desktop $D'); do bspc window $i –to-desktop $D; done; bspc desktop $D –flip vertical; bspc desktop $D –equalize; bspc desktop $D –focus; bspc window $W –focus; bspc window –swap biggest; bspc desktop $D\' --remove

hide selected window

it is easy enough to move windows to another desktop to mimic minimizing windows (to free up desktop tiling real estate). Desktop “0” is often used for such purposes or, in a multihead environment, you can move the window to the adjacent desktop (monitor). But what if you want to simulate minimizing windows from multiple desktops to restore them later without needing to remember and manually move them from desktop “0”..

alt + shift + m D=$(bspc query –desktops –desktop focused); W=$(bspc query –windows –window focused); bspc query –desktops | grep -q “‘$D” || bspc monitor –add-desktops '$D; bspc window $W --to-desktop \'$D

unhide selected desktop windows

the answer to the above is to use bspwm’s ability to create desktops on the fly using the tick mark in this case (or whatever scheme you want to label your desktops with) for ‘desktop name..

super + alt + shift + m D=$(bspc query –desktops –desktop focused); if bspc query –desktops | grep -q “‘$D”; then for i in $(bspc query –windows –desktop '$D); do bspc window $i –to-desktop $D; break; done; bspc window $i –focus; bspc window –swap biggest; (( $(bspc query –windows –desktop '$D | wc -l) )) || bspc desktop '$D –remove; fi

The stock bspwm status bar panel automatically shows the ‘desktop name when windows have been hidden. How cool is that! To simply restore all the minimized windows belonging to the desktop..

super + shift + m D=$(bspc query –desktops –desktop focused); if bspc query –desktops | grep -q “‘$D”; then for i in $(bspc query –windows –desktop '$D); do bspc window $i –to-desktop $D; bspc window $i –focus; bspc window –swap biggest; done; bspc desktop '$D –remove; fi

wait for

applications launched by autostart follow the default binary space partitioning scheme for the desktop, with each successive application opened occupying the maximum window.

The order views become visible to bspwm is application load time dependent (independent of the launch order), so waiting for an application’s visibility provides a means of controlling which application opens last to occupy the maximum window..

#!/bin/sh start=$SECONDS [[ $2 ]] && cutoff=$2 || cutoff=10 while ! bspc query -T | grep $1; do sleep 0.1s [[ $(( $SECONDS - $start )) -gt $cutoff ]] && exit 1 done exit 0

e.g. waitfor ‘RSS’ && luakit

The script self terminates after a predetermined amount of time, in case the referenced application was never launched or failed to open – which the subsequent action can be conditionally dependent on.


the applications belonging to a desktop may not benefit from the default maximum window. The presel script below is a means of preselecting an application window side (up, down, left, right) for opening the next application in, allowing splitting of an application window frame..

#!/bin/sh if waitfor $1 $3; then bspc window $(bspc query -T | grep $1 | awk ‘{ print $4; }’) –focus bspc window –presel $2 fi

e.g. presel ‘Pavucontrol’ ‘down’ && xfce4-mixer

Some amount of tweaking the launch order of applications may be necessary to achieve the desired layout positions.

Combined with bspc rules, bspwm can open up commonly used applications, initializing the desktops the same way every time a bspwm session is started – however your workflow requires!

»»  bspwm not bar

comment ?