saturday, 21 february 2015

herbstluft is German for “autumn air” and a unique moniker for a tiling window manager. It differs from bspwm which I have been using extensively for quite awhile now, in that, it is a manual tiling window manager (as opposed to dynamic) – which, interestingly, bspwm is derived from.

The term manual is somewhat of a misnomer because herbstluftwm will populate a frame according to the default layout specified for it (vertical, horizontal, max and grid) and a frame, unless otherwise split, will occupy a screen (monitor) by default. Though, as you open windows, especially while in vertical or horizontal layout, it becomes apparent why you need to manage the layout: everything quickly becomes cramped and the window spaces effectively unusable. Grid layout is a more forgiving, especially if you have a large display.

But what makes herbstluftwm interesting is the degree of control one has over the pixel space of one’s physical monitors. Virtual monitor regions can be defined for a single physical screen and, within each, individual frames and sub-frames which contain application windows.

It is, therefore, possible to effect display manipulations at the frame or monitor level, leaving the adjacent frames and monitor regions intact, allowing for a greater degree of screen real estate control. This is something that I did not appreciate as a dynamic tiling window manager user until now.

herbstluftwm has a similar client/server architecture to bspwm (or should I say, bspwm to herbstluftwm) in that it is controlled via a messenging mechanism provided by the herbstclient command. Coming from bspwm, I have really come to appreciate this approach. Hotkeys via keybinds and console commands allow one to manipulate the desktop environment with ease – and test potential keybinds and scripts.

Two particular features that have stood out immediately are the ability to chain commands and lock/unlock screen refreshes. This eliminates the need for many simple scripts and allows for visually smooth screen transitions devoid of intermediate screen actions. Absolutely brilliant! (but my tiling window manager familiarity is limited to four at the moment, so I cannot speak for other window managers).

One of the first things I did was to transcribe my bspwm configuration to herbstluftwm – a quick way to dive under the hood. A lot of my bspwm configuration was unnecessary because herbstluftwm already supported my particular workflow preferences natively.

Two frames with Conky

I implemented a dynamic Conky panel with my original set of rules tailored to herbstluftwm..

  • maintain a fixed default margin around the monitor desktop, adjusting the secondary monitor’s margin to match the visual spacing of the primary monitor (using the pixel ratio between the two)
  • the right padding is dependent on whether the Conky panel is visible or is the default monitor margin
  • with the Conky panel visible, full screen mode fits within the default margins of the desktop
  • with the Conky panel hidden, full screen mode fills the entire screen in the typical fashion as with other window managers
  • the secondary monitor exhibits the typical frame and fullscreen modes

Two frames in full screen

Unlike my bspwm configuration, the inner window gaps are not dynamically adjusted as windows are added or removed, but set to a fixed spacing. This is because windows fill a frame and a wider spacing is used to denote (separate) frame regions. There is all manner of border and background control for windows and frame spaces available to more boldly mark the layout but I prefer to have as simple (distraction free) a presentation as possible and, for today, prefer to use window and frame spacing as a means of layout identification. It works very well visually and has a quietness about it.. sort of like autumn air.

Fullscreen with Conky


set monitors

is the primary controller called by other scripts for managing the static border of the specified display by setting the effective monitor regions with an easily configurable desktop_margin pixel setting..

#!/bin/sh desktop_margin=70 primary=$(herbstclient list_monitors | grep '^0:' | cut -d' ' -f2) secondary=$(herbstclient list_monitors | grep '^1:' | cut -d' ' -f2)   conky_width=$(( $(grep 'maximum_width' ~/.conkyrc | awk '{ print $2 }') + $(grep 'border_outer_margin' ~/.conkyrc | awk '{ print $2 }') * 2 )) frame_gap=$(grep '^hc set frame_gap' ~/.config/herbstluftwm/autostart | awk '{ print $4 }') window_gap=$(grep '^hc set window_gap' ~/.config/herbstluftwm/autostart | awk '{ print $4 }') window_frame=$(( $window_gap + $frame_gap )) margin=$(( $desktop_margin - $window_frame )) window_margin=$(( $window_frame + $margin ))   case "$@" in   conky) primary=$(( 2560 - $conky_width + $frame_gap - $margin ))x$(( 1600 - $margin*2 ))+$(( 1680 + $margin ))+$margin ;;   conky*fullscreen) primary=$(( 2560 - $conky_width - $window_gap - $window_margin ))x$(( 1600 - $window_margin*2 ))+$(( 1680 + $window_margin ))+$window_margin ;;   fullframe) primary=$(( 2560 - $margin*2 ))x$(( 1600 - $margin*2 ))+$(( 1680 + $margin ))+$margin ;;   fullscreen) primary=2560x1600+1680+0 ;;   secondary) margin=$(echo $margin | awk '{ print int($1 * 0.25 / 0.282 + 0.5) }')   secondary=$(( 1680 - $margin*2 ))x$(( 1050 - $margin*2 ))+$margin+$margin ;;   secondary*fullscreen) secondary=1680x1050+0+0 ;; esac   herbstclient set_monitors $primary $secondary

Note the border adjustment for the secondary monitor so that its margin matches the primary monitor margin visually (by the ratio of the monitors’ dot pitches)!

toggle conky

hides and unhides the Conky panel, and sets the necessary primary monitor region..

#!/bin/sh function conky_monitor() {   if [[ $(herbstclient attr clients.focus.fullscreen) = true ]]; then   set_monitors conky fullscreen   else   set_monitors conky   fi }   if [[ $(xrandr | grep '*' | cut -dx -f1 | sort | tail -1) -gt 1024 ]]; then   if xdotool search --onlyvisible --classname 'Conky' windowunmap; then   if [[ $(herbstclient attr clients.focus.fullscreen) = true ]]; then   set_monitors fullscreen   else   set_monitors fullframe   fi   else   if ! xdotool search --classname 'Conky' windowmap; then   conky -q -c ~/.conkyrc &   xdotool search --sync --onlyvisible --classname 'Conky'   fi   if herbstclient list_monitors | grep '^0:.*\[FOCUS\]'; then   conky_monitor   else   herbstclient chain lock . focus_monitor 0   conky_monitor   herbstclient chain . focus_monitor 1 . unlock   fi   fi else   xdotool search --onlyvisible --classname 'Conky' windowunmap \   || xdotool search --classname 'Conky' windowmap \   || conky -q -c ~/.conkyrc & fi

Calling toggle_conky and set_monitors secondary during autostart initializes the displays to their monitor margins. Everything it good to go from thereon.

toggle fullscreen

sets the appropriate monitor region for fullscreen and frame based layout modes (as frames define window and frame gaps) for the monitor in focus..

#!/bin/sh herbstclient lock if [[ $(xrandr | grep '*' | cut -dx -f1 | sort | tail -1) -gt 1024 ]]; then   if herbstclient list_monitors | grep '^0:.*\[FOCUS\]'; then   if xdotool search --onlyvisible --classname 'Conky'; then   if [[ $(herbstclient attr clients.focus.fullscreen) = true ]]; then   set_monitors conky   else   set_monitors conky fullscreen   fi   else   if [[ $(herbstclient attr clients.focus.fullscreen) = true ]];   then   set_monitors fullframe   else   set_monitors fullscreen   fi   fi   else   if [[ $(herbstclient attr clients.focus.fullscreen) = true ]]; then   set_monitors secondary   else   set_monitors secondary fullscreen   fi   fi fi herbstclient chain . fullscreen toggle . unlock

toggle max

allows switching to max layout, returning to the current layout without need for scrolling through the layout schemes (typically vertical, horizontal, max and grid) to hunt for it!. My workflow typically moves between a specific layout for a frame and max..

#!/bin/sh if [[ $(herbstclient attr clients.focus.fullscreen) = true ]]; then   toggle_fullscreen else   layout=$(herbstclient dump '' '@' | sed 's/[ ]* \([^:]*\).*/\1/')   tag=/tmp/herbstluftwm:tag:$(herbstclient list_monitors | grep '\[FOCUS\]' | cut -d'"' -f2)   if [ $layout != max ](http://thedarnedestthing.com/ $layout != max ); then   echo $layout > $tag   herbstclient set_layout max   elif [ -f $tag ](http://thedarnedestthing.com/ -f $tag ); then   herbstclient set_layout $(cat $tag)   rm -f $tag   else   herbstclient cycle_layout 1   fi fi

Note: if in fullscreen, just toggle out of it!

A persistent state file for the tag containing the current layout is retrieved for restoring the layout. Using /tmp ensures any unused dangling instances are purged – though, purging in the .xinitrc or autostart is good practice.

The keybind layout control is now defined as..

herbstclient keybind Mod1-space spawn toggle_max herbstclient keybind Mod1-Shift-space cycle_layout 1 grid vertical horizontal

conky bits

to display the active tag and window application (offset positioning is specific to the .conkyrc setup)..

${if_match "${exec herbstclient list_monitors | grep '\[FOCUS\]' | cut -d: -f1}" == "1"} ${voffset -211}${font Ubuntu:size=48,weight:normal}${color3}${offset 136}${exec herbstclient list_monitors | grep '\[FOCUS\]' | cut -d'"' -f2}${font Ubuntu:size=16,weight:normal}${color4}${exec herbstclient list_monitors | grep -v '\[FOCUS\]' | cut -d'"' -f2} ${voffset -44} ${font Ubuntu:size=7,weight:normal}${color3}${alignr}${exec herbstclient substitute CLASS clients.focus.class echo CLASS} ${voffset 106} ${else} ${voffset -211}${font Ubuntu:size=48,weight:normal}${color4}${offset 136}${font Ubuntu:size=16,weight:normal}${exec herbstclient list_monitors | grep -v '\[FOCUS\]' | cut -d'"' -f2}${voffset -46}${font Ubuntu:size=48,weight:normal}${color3}${exec herbstclient list_monitors | grep '\[FOCUS\]' | cut -d'"' -f2} ${voffset -162} ${font Ubuntu:size=7,weight:normal}${color3}${alignr}${exec herbstclient substitute CLASS clients.focus.class echo CLASS} ${voffset 106} ${endif}

So I am livin’ in herbstluftwm for now – that, and conditioning myself on my new Poker 2 keyboard – and finding it a delight. Window manager preferences can be a fickle thing amongst the Linux community. The shear choice beckons comparison and biases.

Is herbstluftwm better than bspwm? Both are great tiling window managers. Both are very malleable. Both will stay on my machines. They are both very different and competent window managers and, I am finding myself surprisingly comfortable with manual tiling. I think herbstluftwm’s feature set becomes more self-evident with larger display real estate.

Manual tiling can add a few extra keystrokes here and there. But the benefit is that it is easier to obtain precise placement and control of window objects because the window manager is not deciding that for you.

But sometimes I like to be lazy too..

»»  herbstluftwm hiding

comment ?