🌿 herbie 🌿
A herbstluftwm interactive environment thingy.
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 arofi
modi, possibly with several together as arofi
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
.
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)
- Produce a tag called
dev
populated with a task of the same name. - As above but populate the task
dev
to the tagmydev
- 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 ofherbie
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:
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
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