🌿 herbie 🌿
A herbstluftwm interactive environment thingy.

Table of Contents

Intro

herbie is helper for managing a herbstluftwm interactive environment. Interactivity is provide through keybindings and rofi and the rofi-menu Python interface.

Documentation

You are reading it. This file is it. You can see it rendered by github or more beautifully by Emacs (with the help of fniessen's ReadTheOrg)

Usage

The herbie package provides a herbie command line program. Run it to print a brief help message:

Usage: herbie [OPTIONS] COMMAND [ARGS]...

Options:
  -h, --hc TEXT        Set the herbstclient executable name
  -u, --ui [term|gui]  Set user interface
  -c, --config PATH    Set configuration file
  --help               Show this message and exit.

Commands:
  fini     Finish a task screen by closing all windows and removing the tag.
  hc       Pass-through to herbstclient.
  ifini    Interactively finish a task screen by closing all windows and...
  itask    Interactively figure how to fill screen with task layout.
  layout   A rofi modi for loading, saving, dropping layouts.
  loop     Run a looper.
  loops    List known loopers.
  svgicon  Render a tag as an SVG.
  tags     Print tags one line at a time in order.
  task     Produce a predefined task layout.
  tasks    List tasks available in the configuration.
  totag    Make tag active or if already then use previously active tag.
  version  Print the version.
  waction  Select and perform an action on a window.
  wbring   Bring a window to a tag.
  wselect  Select a window for focus.
  wswap    Swap focused window with neighbor in given direction.

The above is current as of herbie version

0.0.0

As long as this version is held at 0.0.0 expect breaking changes.

Concepts

herbie functionality is organized into these concepts:

tasks
these are things you do, not herbie. herbie will help you do your tasks by making it easy to set up and tear down a task-specific tag.
layouts
similar to tasks, they are like a "save game" for herbstluftwm. They let you capture any number of snapshots of your tags' layouts so that you can return to any of them later.
modi
some interaction with herbie is to use it as a rofi modi, possibly with several together as a rofi combi.
commands
these are one-shot manipulations.
loops
herbie can provide for ongoing behavior by issuing commands in response to herbstluftwm events aka hooks. Some concept implementations require some loops to be running.

The remaining sections describe each concept and how to use its features.

Tasks

herbie's concept of a "task" is not something herbie does but something you do. A task a herbstluftwm "tag" holding a certain pre-defined layout of frames which are populated with specific application windows. Here is the result of a task called dev.

ss-thumb.png

The number of frames, their ratios and orientation and their window contents are all determined by user-provided configuration. With a single command one can remake this layout and one can have multiple tags that use the layout.

Assure a task

Running:

$ herbie task dev        (1)
$ herbie task dev mydev  (2)
$ herbie task dev mydev  (3)
  1. Produce a tag called dev populated with a task of the same name.
  2. As above but populate the task dev to the tag mydev
  3. Rerun and any changes to the layout or closed applications will be restored.

Finishing a task

When herbie is asked to "finish" a task, all windows are closed and the tag is removed (merged to another).

For example, let's do some development work and then get back to more important matters.

$ herbie task dev

##(hack, test, commit, push)##

$ herbie fini -g irc dev

##(chat, brag, lurk)##

Defining tasks

Tasks are defined as a tree data structure using sexp syntax in an almost exactly identical form as one may see from the command herbstclient dump. The difference is that any place where you might see a window ID (0xdeadbeaf) you may place zero or more herbie window names with which herbie may associate a command to supply the window and a number of attributes to form an herbstluftwm rule.

Tasks and windows are provided in a herbie configuration file. An example is provided in example.herbierc. The [tasks] section holds a list of available tasks. Again this is not quite the output of herbstclient dump:

dweb = (clients window:firefox)
rss = (split horizontal:0.75:1
        (clients window:firefox)
        (split vertical:0.50:0
          (clients window:liferea)
          (clients )))

A window reference is satisfied by a section named like [window <name>]. for example, the reference windows in the above example tasks may be provided like:

[window firefox]
title = Mozilla Firefox
command = firefox -P default-esr

[window liferea]
class = Liferea
command = liferea

In addition to command you may provide any property supported by herbstluftwm in forming the condition of a rule

Interactivity

herbie is user-interactive. The default UI is a GUI using rofi and libnotify. An alternative UI uses the terminal. To activate them, add the global command line argument -u like in:

$ herbie tasks          # use default GUI
$ herbie -u term tasks  # use terminal

It is intended to transition from having herbie explicitly execute rofi to having herbie act as a rofi modi. As of now there is a mix of both interfaces. See the section Modi below.

Some herbie commands which take arguments have fully user-interactive versions of the same name but with the prefix "i". For example:

$ herbie itask
$ herbie ifini

These are usefully bound to keys as described below.

Both itask and ifini prompt the user for a task or tag and both accepts an optional second tag in the response. See their documentation for how this optional second tag is interpreted.

As an example, assume we already have a tag named dev and want to make a new tag using the dev task. This is done using the herbie itask command (default UI uses the terminal) like:

$ herbie itask
bweb
dev
dweb
irc
rss

Task: dev dev2

This will produce the dev task not on tag dev but on tag dev2. Sometime later we may remove this dev2 tag with ifini. In this example we use the gui UI:

$ herbie ifini
Tag to finish: dev2 dev

This closes out dev2 and leaves us in the previously existing tag called dev. If not final tag is given then the first tag which is not the one to be finalize provides our landing spot.

Calling from herbstluftwm

In your autostart you may have some keybindings to run herbie-dmenu like:

hc keybind $Mod-i       spawn herbie itask 
hc keybind $Mod-Shift-i spawn herbie ifini

Loops

A herbie loop is a long-running function that responds to the events emitted by herbstluftwm. Like "tasks" describe above one runs a loop by naming it.

Available loops

The available loops are printed with:

herbie -u term loops
dump
stamp
stamp_switch
switch
dump
like herbstclient --idle
stamp
add a my_focus_time to each client and tag holding a time stamp of last focus. Other parts of herbie relie on this running for ordering.
switch
attempt "Sawfish-like" window switching, it requires stamp.

Running loops

A herbie loop may be run on the command line and will exit when a reload hook event is received from herbstlufwm. To run herbie loops add them at the end of your autostart file such as below where panel.sh is started.

herbie loop stamp &
herbie loop switch &

Modi

herbie talks to rofi as a modi. To summarize, here are some example keybindings.

hc keybind $Mod-w        spawn herbie waction

hc keybind $Mod-b        spawn rofi -modi "h:herbie wbring" -show h
hc keybind $Mod-n        spawn rofi -modi "h:herbie wselect" -show h
hc keybind $Mod-Shift-n  spawn rofi -modi "h:herbie wselect -t all" -show h
hc keybind $Mod-k        spawn rofi -modi "h:herbie layout -a save" -show h
hc keybind $Mod-y        spawn rofi -modi "h:herbie layout -a load" -show h 
hc keybind $Mod-l        spawn rofi -modi "h:herbie layout" -show h
waction
a window context menu (not yet modi-fied)
wbring
bring a window to the tag
wselect
give focus to a window
layout
operate on layouts, see section below

We may, of course, use rofi combi to combine multiple herbie modi. herbie may also offer nested windows such as in the default call of herbie layout. More on layouts next.

Layouts

Frame layouts can be saved to herbstlufwm tags for later loading back to the screen. This allows manual tweaks to be captured and easily reapplied or cycled between them. Layout storage in herbstlufwm is transient and will not survive a restart, but does survive a reload. Layout can also be explicitly removed from the store.

When layouts are displayed through rofi an SVG icon is created on the fly from the layout tree in order to give you a hint, in addition to the name you gave when saving them. Here is an example rofi page showing what is available for loading:

layouts.png

Commands

What remains are simply commands.

totag

This is like hc use or hc use_index except if the requested tag is already in focus then instead hc use_previous is called. In the usual autostart loop which binds to numbers:

tag_keys=( {1..9} 0 )
for tind in "${!tag_keys[@]}" 
do
    key="${tag_keys[$tind]}"
    hc keybind "$Mod-$key" spawn herbie totag "$tind"
done

See snippets for a faster way to do this directly with herbstclient.

svgicon

The svgicon command is not particularly meant to be used interactively. It simply creates an icon representing a tag.

$ herbie svgicon
/home/bv/.icons/herbie-dev-100x100.svg
# display ~/.icons/herbie-dev-100x100.svg

Sorry, your browser does not support SVG.

Caveats

Stability

herbie is currently a personal test bed. Please don't use it expecting it to not change. If the version number strays from 0.0.0 then some stability may be expected.

Speed

Python has a very slow startup. I'm seeing 70 ms just to do nothing and herbie hc dump takes about 200ms. This is not due to herbstlufwm as herbstclient dump takes about 10ms. This slowness will likely be noticeable for herbie commands that operate directly from a key binding (eg totag or wswap). It probably will not be as noticeable for commands that work through rofi since they require additional time anyways to receive user input.

In the snippets below are some ways to perform some of the herbie commands more directly.

Snippets

Not related to herbie per se but this section collects some useful snippets of herbstclient commands. Some have been implemented (for better or worse) in herbie and others are possible candidates for implementation. Most are gleaned from IRC and apologies for any missed attribution.

Swap windows

Often we end up wanting to "swap" two windows in some way. Two things are given: the currently focused client and a direction in which to find a neighbor client. The layout is rewritten with the two window IDs swapped and applied.

On IRC thorsten` gave this example which is implemented for a "left-swap":

hc keybind Ctrl-Alt-Super-l \
  substitute OLDWIN clients.focus.winid chain \
  , focus right \
  , substitute NEWWIN clients.focus.winid \
  spawn  ~/.config/herbstluftwm/swapwindows.sh OLDWIN NEWWIN  

With that script looking something like:

old="$1" ; new="$2" ; l=$(herbstclient dump | sed "s,\\<$old\\>,0xTMP,;s,\\<$new\\>,$old,;s,\\<0xTMP\\>,$new,") ; herbstclient load "$l"   

Or all together:

herbstclient keybind Ctrl-Alt-Super-l substitute OLDWIN clients.focus.winid chain , focus right , substitute NEWWIN clients.focus.winid spawn bash -c 'l=$(herbstclient dump | sed "s,\\<$1\\>,0xTMP,;s,\\<$2\\>,$1,;s,\\<0xTMP\\>,$2,") ; herbstclient load "$l"' -- OLDWIN NEWWIN

Float a window and set it to a fixed size

herbstclient substitute ID clients.focus.winid and . set_attr clients.focus.floating on . spawn xdotool windowsize ID 1000 500

A faster herbie totag

The herbie totag command is noticeably slow. The same behavior can be achieved about 10x faster in a couple ways by having herbstclient perform the logic.

The ask-first approach

tag_keys=( {1..9} 0 )
for tind in "${!tag_keys[@]}" 
do
    key="${tag_keys[$tind]}"
    hc keybind "$Mod-$key" substitute TAG tags.$tind.name \
      or , \
        and . compare monitors.focus.tag = TAG \
            . use_previous \
         , use TAG  
done

The try-first approach

Another take on this comes from IRC (yushyin) replaces the hc keybind in the above with this core:

hc keybind "$Mod-$key" substitute PRE tags.focus.index \
  chain + use_index "$tind" + or , compare tags.focus.index != PRE , use_previous 

Author: BV

Created: 2021-02-15 Mon 14:55

Validate