Protesilaos Stavrou
Philosopher. Polymath.

Advanced features of my BSPWM session

Prot's Dots For Debian - Book index

My dotfiles’ stow package called “bin” includes the following set of scripts that alter the behaviour of the window manager:

~/cpdfd $ tree -aF bin
bin
├── bin/
│   ├── bspwm_dynamic_desktops*
│   ├── bspwm_external_rules*
│   ├── bspwm_focus_mode*
│   ├── bspwm_multifaceted_operation*
│   ├── bspwm_reorder_desktops*
│   ├── bspwm_smart_move*
│   ├── bspwm_smart_presel*
# Trimmed output

Here I explain what each item does and how it is implemented in my system. All hotkeys mentioned herein are defined in my sxhkdrc_bspc.

Dynamic desktops

By default, BSPWM uses a fixed number of desktops. A set is assigned to a monitor at startup. This is a perfectly usable design, especially for single-monitor setups. It can, however, be inefficient:

  • You seldom need, say, ten desktops at all times. Your normal work requires only three or four of them. So why keep the rest? And why try to work around this by retroactively concealing them from the bspc report, the panel, etc.?
  • On multi-head setups, you might find it preferable to decouple desktops from monitors. You might, for instance, require five desktops on the larger monitor and only one on the smallest one. Again, why commit to a specific number at startup and try to adapt your workflow to it.

Dynamic desktops solve these potential issues. You start with a single desktop per monitor and go from there in accordance with your evolving requirements.

The script is designed to insert/remove desktops on a per monitor basis. This means that its operations are always confined to the focused monitor. As such, if you are on desktop 1 on monitor one, and switch to desktop 2 (using super + {0-9}), then desktop 2 will be placed on monitor one as well. If you wanted to have it on monitor two, you would need to bring focus to it first (cycle through monitors with the key chord super + comma).

Alternatively, you can spawn a desktop on the next monitor without switching focus to it, by pressing super + ctrl + {0-9}. Such a desktop will hold a receptacle (discussed below).

Sending the focused window to a dynamically-created desktop is straightforward: super + shift + {0-9}. Again, this is limited to the given monitor, while I have decided not to implement a direct action for sending the focused window to a new desktop on the next monitor. There is a trade-off between feature completeness and complexity.

In short, dynamic desktops should have a natural feel to them. You just move around and they adapt accordingly. As a bonus, I have added a “back-and-forth” behaviour, so if you input the same number of the desktop twice while using super + {0-9}, focus will move to the last desktop and back again.

Dynamic desktops and the concomitant back-and-forth behaviour were inspired by i3wm.

Reorder desktops

This is a simple script that will reorder the desktops in numerical order. You will probably have no use for this, because it is already part of bspwm_dynamic_desktops from where it was extracted. Still, it is provided as a standalone utility in case you ever find yourself manually performing the necessary bspc actions that add/remove desktops.

Manual tiling (smart presel and external rules)

BSPWM automatically tiles windows in a spiral layout. Older windows move inwardly, while the newest one occupies the outermost part of the spiral (at the given level). However, the window manager also offers a couple of features for manually placing windows on the screen. Those are referred to as preselections and receptacles respectively.

A preselection is, by default, an area that is a fraction of the focused node. The preselection provides feedback on where the next window will be placed. When that event takes place, the focused window is split at the given ratio and focus switches to the new window.

In contradistinction, receptacles are empty frames that occupy an area of the screen, as per the tiling scheme, until they are expressly told to host a window, at which point they cease to exist. Think of them as placeholders for windows yet-to-be. To send an item to the position of the receptacle, we need to pass a specific command: the unscripted tiling method will not affect receptacles.

I have decided to carve a niche for each of these features and design distinct courses of action around them. The two scripts involved in the process are bspwm_smart_presel and bspwm_external_rules.

“Smart presel” contains logic to evaluate the following and lead to the relevant consequences:

  • Is there a marked window (using the node flag “marked”, which is assigned with super + a ; m)? If yes, then use the first that matches this criterion. Otherwise operate on the focused window.
  • Does a receptacle exist? If yes, use it to place the window in its stead. Else put the window in the position of the first matching preselection.

A preselection is given with super + alt + {h,j,k,l}. It is cancelled by inputting the same direction twice. To insert a receptacle, press super + n ; r. Remove it at any time with super + n ; super + r (you can then resize nodes, adjust the split ratios etc. as mentioned in the chapter basics of my BSPWM). If a preselection is available, then a new receptacle will be placed there.

To place the marked or focused window in the position of the available receptacle or preselection type super + n ; i. The difference between the two is revealed when performing such actions across desktops. A receptacle will not call focus to itself, whereas a preselection will.

The assumption that underpins this design is that a receptacle can be placed in a desktop, then a program that has a rather slow startup time is launched (e.g. the web browser). The user is free to switch to another desktop in the meantime.

This way you can continue your work and get back to that slow application at a later point. Multi-head setups render this apparent. For example, you “pre-load” the smaller monitor, switch to the larger one, work there and the application will appear on the small monitor, taking the place of the receptacle. No interruptions, no things moving around. This is precise and deliberate.

Note that for such a scenario you do not need to press super + n ; i, because the external rules come into play, automatically placing the next window in the position of the first available receptacle (or preselection if no receptacle exists). You can, nonetheless, still use super + n ; i. Place a receptacle in a new desktop, switch to another and then type that key sequence to insert the marked or focused window there.

At any rate, all this is a means of fleshing out BSPWM’s notions of manual tiling actions. You are free to tweak things to your liking.

Multifaceted operations (multi-node actions)

This is, dare I say, a beautifully simple script that facilitates window management over multiple selection paths. Its various actions are assigned to key chords and concern the following:

  • super + n ; super + shift + r: kill all receptacles (as opposed to only remove the last one, with super + n ; super + r).
  • super + n ; super + q: close all non-focused, non-marked windows on the focused desktop. This is an extension of the standard key for closing the focused window: super + q with the added bonus of respecting the “marked” flag (it is assumed you are marking windows for a reason).
  • super + n ; super + shift + q: same as above, but kills them instead. For context, the key for killing just the focused window is super + shift + q.
  • super + n ; l: assign the “locked” flag to all windows in the present desktop. A locked window cannot be closed with any of the commands mentioned earlier (though it can be killed). Recall from the chapter on the basics of my BSPWM that to assign such a flag to the focused window, you press super + a ; l (inputting twice toggles the behaviour).
  • super + n ; shift + l: remove the “locked” flag from all windows on the present desktop.
  • super + n ; m: summon all “marked” windows, regardless of where they are, to the present desktop. You can mark a window by passing to it that flag: super + a ; m.
  • super + n ; super + {0-9}: bring all the windows of the given desktop (the given number) to the focused one. This is an extension of the basic key for focusing desktops: super + {0-9}.
  • super + n ; super + shift + {0-9}: send the windows of the present desktop to the given one. This incorporates the dynamic desktops mentioned above, so inputting a number will just create that desktop on the focused monitor.

With those granted, I recommend you try learning things one command at a time. Otherwise you will most certainly mix up all the key chords and key chord chains. Start with the basics. You will then realise that the more advanced ones build on that foundation.

Smart move

You move focus around windows with super + {h,j,k,l}. Additionally, you swap the position between the focused window and the one in the given direction with super + shift + {h,j,k,l}. These are standard actions that are provided out-of-the-box. Their two limitations are:

  1. They do not work with receptacles.
  2. You cannot remove a window from the automatic tiling scheme. You must instead switch places with an existing one.

So what I have decided to do is provide a wrapper around the standard commands for focusing and switching windows, but also account for receptacles.

If you input the keys that switch windows in a direction where no window exists, a receptacle will be spawned to occupy that place. Inputting the same direction again will move the focused window that is adjacent to the receptacle to the space occupied by the latter. At that point the receptacle will disappear.

This provides an intuitive way of moving a window to the position of an adjacent receptacle, rather than having to memorise a key chord chain (though that still has its use case for instances where you do not want to move a window adjacent to a receptacle).

Furthermore, this offers the upside of creating three column/row layouts by just moving things around. Consider this standard layout for three windows:

_____ _____
|   | | B |
| A | |___|
|   | | C |
|___| |___|

You can focus B or C and input super + shift + l to insert a receptacle to their right. Do so again to move the adjacent focused window there, to get a three column layout. You can then even out the proportions of the splits between the windows with super + s ; b.

Try working with bspwm_smart_move for a while. If you find that it poses an impediment to your workflow you can find easy-to-follow instructions inside sxhkdrc_bspc.

Focus mode

This is a “distraction-free” environment that removes the top panel, window gaps, and any padding from the desktop. All you see is windows with overlapping borders between them. The idea is to remove all complementary elements and focus on the content of your work. For example, I often use this when I am doing long-form writing.

Toggle the focus mode with super + e ; f.

Is BSPWM the best tiling window manager?

Short answer: it depends.

Long answer: You cannot possibly address such questions in abstract. Much hinges on the desired workflow and the needs each user has. One’s disposition must be accounted for: whether they want to experiment and script certain patterns of behaviour, and the like.

The purpose of this chapter, and this book at-large, is to make a point about the degree to which we can approximate a desktop environment by utilising disparate programs in a synergistic way.

BSPWM works perfectly fine without scripting anything. It does, nonetheless, offer that option, which opens up a range of possibilities for advanced window management.