View raw

{{{hl}}} configuration file

Table of Contents

# -*- mode: org; -*-

#+PROPERTY: header-args :tangle "~/.config/herbstluftwm/autostart" #+PROPERTY: header-args+ :shebang "#!/usr/bin/env bash"

#+SETUPFILE: /.emacs.d/resources/templates/documents/gnu-default.setup #+INCLUDE: /.emacs.d/resources/templates/documents/default.org_title

#+MACRO: hl Herbstluftwm

Introduction

This is my literate configuration for {{{hl}}} and other programs started at the beginning of a graphical session. {{{hl}}} is a manual tiling window manager and is configured via the herbstclient program.

#+begin_center \Huge\textbf{Herbstluft}\\ \normalsize\textit{autumn air} #+end_center

Early setup

Definitions

We begin by defining a shorthand name for the herbstclient program:

#+NAME: hc

  hc() {
      herbstclient "$@"
  }

Basic

We make {{{hl}}} execute code associated with the reload hook.

  hc emit_hook reload

We set the color of the default window bar.

  xsetroot -solid '#5A8E3A'

Keybindings

We remove all existing keybindings.

  hc keyunbind --all

Choice of Modkey. Mod1 is Alt, Mod4 is Super.

  Mod=Mod4

Basic interaction with {{{hl}}}

  hc keybind $Mod-Shift-q quit
  hc keybind $Mod-Shift-r reload
  hc keybind $Mod-Shift-c close

Close focused window or remove frame

  hc keybind $Mod-q close_or_remove

Application shortcuts

{{{hl}}} wrapper around dmenu.

  hc keybind $Mod-d spawn "/etc/xdg/herbstluftwm/dmenu_run_hlwm"

Terminal

We use st.

  hc keybind $Mod-Return spawn "st"

TODO Emacs

We assign shortcuts to commonly used applications.

  hc keybind $Mod-e spawn "emacsclient -nc -s main"

Movement

Basic movement in tiling and floating mode focusing clients.

  hc keybind $Mod-Left  focus left
  hc keybind $Mod-Down  focus down
  hc keybind $Mod-Up    focus up
  hc keybind $Mod-Right focus right
  hc keybind $Mod-h     focus left
  hc keybind $Mod-j     focus down
  hc keybind $Mod-k     focus up
  hc keybind $Mod-l     focus right

Moving clients in tiling and floating mode.

  hc keybind $Mod-Shift-Left  shift left
  hc keybind $Mod-Shift-Down  shift down
  hc keybind $Mod-Shift-Up    shift up
  hc keybind $Mod-Shift-Right shift right
  hc keybind $Mod-Shift-h     shift left
  hc keybind $Mod-Shift-j     shift down
  hc keybind $Mod-Shift-k     shift up
  hc keybind $Mod-Shift-l     shift right

Splitting frames

Create an empty frame at the specified direction.

  hc keybind $Mod-u split bottom 0.5
  hc keybind $Mod-o split left   0.5

We let the current frame explode into subframes.

  hc keybind $Mod-Control-space split explode

Resizing frames and clients

Resizing frames and floating clients.

  resizestep=0.02
  hc keybind $Mod-Control-h     resize left +$resizestep
  hc keybind $Mod-Control-j     resize down +$resizestep
  hc keybind $Mod-Control-k     resize up +$resizestep
  hc keybind $Mod-Control-l     resize right +$resizestep
  hc keybind $Mod-Control-Left  resize left +$resizestep
  hc keybind $Mod-Control-Down  resize down +$resizestep
  hc keybind $Mod-Control-Up    resize up +$resizestep
  hc keybind $Mod-Control-Right resize right +$resizestep

Tags

Basic definitions

  tag_names=( {1..5} )
  tag_keys=( {1..5} 0 )

  hc rename default "${tag_names[0]}" || true
  for i in "${!tag_names[@]}" ; do
      hc add "${tag_names[$i]}"
      key="${tag_keys[$i]}"
      if ! [ -z "$key" ] ; then
          hc keybind "$Mod-$key" use_index "$i"
          hc keybind "$Mod-Shift-$key" move_index "$i"
      fi
  done

Cycle tags

  hc keybind $Mod-period use_index +1 --skip-visible
  hc keybind $Mod-comma  use_index -1 --skip-visible

Layouting

  hc keybind $Mod-r remove
  hc keybind $Mod-s floating toggle # Schwebend = floating!
  hc keybind $Mod-f fullscreen toggle
  hc keybind $Mod-Shift-f set_attr clients.focus.floating toggle
  hc keybind $Mod-Shift-m set_attr clients.focus.minimized true
  hc keybind $Mod-Control-m jumpto last-minimized
  hc keybind $Mod-p pseudotile toggle

The following cycles through the available layouts within a frame, but skips layouts, if the layout change wouldn't affect the actual window positions. I.e. if there are two windows within a frame, the grid layout is skipped.

  hc keybind $Mod-space                                      \
     or , and . compare tags.focus.curframe_wcount = 2       \
     . cycle_layout +1 vertical horizontal max vertical grid \
     , cycle_layout +1

Mouse bindings

  hc mouseunbind --all
  hc mousebind $Mod-Button1 move
  hc mousebind $Mod-Button2 zoom
  hc mousebind $Mod-Button3 resize

Cycle the active screens

  hc keybind $Mod-Tab       cycle_all +1
  hc keybind $Mod-Shift-Tab cycle_all -1

This is a ``shorter'' shortcut.

  hc keybind $Mod-c cycle_all +1

Jump to to next urgent program

Super useful when getting prompted for permissions in a browser on a different tag!

  hc keybind $Mod-i jumpto urgent

Theme

  hc attr theme.tiling.reset   1
  hc attr theme.floating.reset 1

Colors

  color_main='#666611'
  xsetroot -solid $color_main

Frames

  hc set frame_border_active_color '#222222cc'
  hc set frame_border_normal_color '#101010cc'
  hc set frame_bg_normal_color     '#565656aa'
  hc set frame_bg_active_color     '#345F0Caa'
  hc set frame_border_width         1
  hc set always_show_frame          on
  hc set frame_bg_transparent       on
  hc set frame_transparent_width    3

We have a grand total of 4 pixels dedicated to the frame borders, per frame---so six pixels wasted at most.

Gap between frames

We set no gap between frames and other frames/monitors.

  hc set frame_gap 0

Windows

Basic

  hc attr theme.title_height           24
  hc attr theme.title_font            'Hack:pixelsize=20'  # example using Xft
  # hc attr theme.title_font          '-*-fixed-medium-r-*-*-13-*-*-*-*-*-*-*'
  hc attr theme.padding_top            10  # space below the title's baseline (i.e. text depth)
  hc attr theme.active.color          '#345F0Cef'
  hc attr theme.title_color           '#ffffff'
  hc attr theme.normal.color          '#323232dd'
  hc attr theme.urgent.color          '#7811A1dd'
  hc attr theme.normal.title_color    '#898989'
  hc attr theme.inner_width            1
  hc attr theme.inner_color            black
  hc attr theme.border_width           1
  hc attr theme.floating.border_width  12
  hc attr theme.floating.outer_width   1
  hc attr theme.floating.outer_color   black
  hc attr theme.active.inner_color    '#789161'
  hc attr theme.urgent.inner_color    '#9A65B0'
  hc attr theme.normal.inner_color    '#606060'

Copy inner_color to outer_color.

  for state in active urgent normal ; do
      hc substitute C theme.${state}.inner_color \
         attr theme.${state}.outer_color C
  done
  hc attr theme.active.outer_width 1
  hc attr theme.background_color '#141414'

Appearance

Gap between windows, tiled in a frame.

  hc set window_gap 24

We could reserve extra room on the edges common to all the tiled windows. (Despite being a frame parameter, it really applies to the sum total of tiled windows in a given frame, so it belongs to this section).

  hc set frame_padding 0

If only one frame exists on a given monitor, no border gap is added to the unique frame. But since we set the frame gap to 0, this has no effect.

  hc set smart_frame_surroundings off

If only one window exists in a given frame, the border gap between the window and the frame may be omitted.

  hc set smart_window_surroundings off
  hc set mouse_recenter_gap 0

Rules

First, we cancel all previously bound rules.

  hc unrule -F
  #hc rule class=XTerm tag=3 # move all xterms to tag 3
  hc rule focus=on # normally focus new clients
  hc rule floatplacement=smart
  #hc rule focus=off # normally do not focus new clients
  # give focus to most common terminals
  #hc rule class~'(.*[Rr]xvt.*|.*[Tt]erm|Konsole)' focus=on
  hc rule windowtype~'_NET_WM_WINDOW_TYPE_(DIALOG|UTILITY|SPLASH)' floating=on
  hc rule windowtype='_NET_WM_WINDOW_TYPE_DIALOG' focus=on
  hc rule windowtype~'_NET_WM_WINDOW_TYPE_(NOTIFICATION|DOCK|DESKTOP)' manage=off

  hc set tree_style '╾│ ├└╼─┐'

Unlock monitors

  hc unlock

Other programs

COMMENT Panel

  #  xset +fp /home/blendux/.fonts/
    kill $(pgrep --full "panel.sh")
    bash ~/.config/herbstluftwm/panel.sh

This panel is adapted from the one provided by default by {{{hl}}}, located at file:/etc/xdg/herbstluftwm/panel.sh.

I'm using lemonbar-xft, which provides XFT support.

  font='-*-hack-medium-r-*-*-*-*-*-*-*-*-*-*'
  font_xft='Hack'
  # extract colors from hlwm and omit alpha-value
  bgcolor=$(herbstclient get frame_border_normal_color|sed 's,^\(\#[0-9a-f]\{6\}\)[0-9a-f]\{2\}$,\1,')
  selbg=$(herbstclient get window_border_active_color|sed 's,^\(\#[0-9a-f]\{6\}\)[0-9a-f]\{2\}$,\1,')
  selfg='#101010'

  # Quick maffs
  monitor_width=$(herbstclient monitor_rect 0 | awk '{ print $3 }')
  monitor_height=$(herbstclient monitor_rect 0 | awk '{ print $4 }')
  window_gap=$(herbstclient get window_gap)
  let "bar_width = monitor_width - (2 * $window_gap)"
  bar_height=42
  offset_horizontal=$window_gap
  offset_vertical=0
  bar_dimensions=${bar_width}x${bar_height}+${offset_horizontal}+${offset_vertical}


  hlwm_datetime_short () {
      date +"%R | %F"
  }

  hlwm_datetime_long () {
      date +"%A, %d %B %Y | %R"
  }

  hlwm_battery_status () {
      acpi | awk -F', ' '{ print $3 }'
  }

  hlwm_battery_level () {
      acpi | awk -F', ' '{ print $2 }'
  }

  (while true; do
       echo "%{c}$(hlwm_datetime_short)%{r}$(hlwm_battery_status) $(hlwm_battery_level)"
       sleep 1
   done
  ) | lemonbar -f ${font_xft} -g $bar_dimensions # -B $selbg

#+RESULTS:

  quote() {
      local q="$(printf '%q ' "$@")"
      printf '%s' "${q% }"
  }

  if [[ -f /usr/lib/bash/sleep ]]; then
      # load and enable 'sleep' builtin (does not support unit suffixes: h, m, s!)
      # requires pkg 'bash-builtins' on debian; included in 'bash' on arch.
      enable -f /usr/lib/bash/sleep sleep
  fi

  hc_quoted="$(quote "${herbstclient_command[@]:-herbstclient}")"
  hc() { "${herbstclient_command[@]:-herbstclient}" "$@" ;}
  monitor=${1:-0}
  geometry=( $(hc monitor_rect "$monitor") )
  if [ -z "$geometry" ] ;then
     echo "Invalid monitor $monitor"
     exit 1
  fi
     # geometry has the format W H X Y
     x=${geometry[0]}
     y=${geometry[1]}
     panel_width=${geometry[2]}
     panel_height=32
     # font="-*-fixed-medium-*-*-*-24-*-*-*-*-*-*-*"
     font='-*-hack-medium-r-*-*-*-*-*-*-*-*-*-*'
     # font='Hack:pixelsize=20'
     # extract colors from hlwm and omit alpha-value
     bgcolor=$(hc get frame_border_normal_color|sed 's,^\(\#[0-9a-f]\{6\}\)[0-9a-f]\{2\}$,\1,')
     selbg=$(hc get window_border_active_color|sed 's,^\(\#[0-9a-f]\{6\}\)[0-9a-f]\{2\}$,\1,')
     selfg='#101010'

     ####
     # Try to find textwidth binary.
     # In e.g. Ubuntu, this is named dzen2-textwidth.
     if which textwidth &> /dev/null ; then
         textwidth="textwidth";
     elif which dzen2-textwidth &> /dev/null ; then
         textwidth="dzen2-textwidth";
     elif which xftwidth &> /dev/null ; then # For guix
         textwidth="xftwidth";
     else
         echo "This script requires the textwidth tool of the dzen2 project."
         exit 1
     fi
     ####
     # true if we are using the svn version of dzen2
     # depending on version/distribution, this seems to have version strings like
     # "dzen-" or "dzen-x.x.x-svn"
     if dzen2 -v 2>&1 | head -n 1 | grep -q '^dzen-\([^,]*-svn\|\),'; then
         dzen2_svn="true"
     else
         dzen2_svn=""
     fi

     if awk -Wv 2>/dev/null | head -1 | grep -q '^mawk'; then
         # mawk needs "-W interactive" to line-buffer stdout correctly
         # http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=593504
         uniq_linebuffered() {
             awk -W interactive '$0 != l { print ; l=$0 ; fflush(); }' "$@"
         }
     else
         # other awk versions (e.g. gawk) issue a warning with "-W interactive", so
         # we don't want to use it there.
         uniq_linebuffered() {
             awk '$0 != l { print ; l=$0 ; fflush(); }' "$@"
         }
     fi

     hc pad $monitor $panel_height

     {
         ### Event generator ###
         # based on different input data (mpc, date, hlwm hooks, ...) this generates events, formed like this:
         #   <eventname>\t<data> [...]
         # e.g.
         #   date    ^fg(#efefef)18:33^fg(#909090), 2013-10-^fg(#efefef)29

         #mpc idleloop player &
         while true ; do
             # output is checked once a second, but a "date" event is only
             # generated if the output changed compared to the previous run.
             printf 'date\t^fg(#efefef)%(%H:%M)T^fg(#909090), %(%Y-%m)T-^fg(#efefef)%(%d)T\n'
             sleep 1 || break
         done > >(uniq_linebuffered) &
         childpid=$!
         hc --idle
         kill $childpid
     } 2> /dev/null | {
         IFS=$'\t' read -ra tags <<< "$(hc tag_status $monitor)"
         visible=true
         date=""
         windowtitle=""
         while true ; do

             ### Output ###
             # This part prints dzen data based on the _previous_ data handling run,
             # and then waits for the next event to happen.

             separator="^bg()^fg($selbg)|"
             # draw tags
             for i in "${tags[@]}" ; do
                 case ${i:0:1} in
                     '#')
                         echo -n "^bg($selbg)^fg($selfg)"
                         ;;
                     '+')
                         echo -n "^bg(#9CA668)^fg(#141414)"
                         ;;
                     ':')
                         echo -n "^bg()^fg(#ffffff)"
                         ;;
                     '!')
                         echo -n "^bg(#FF0675)^fg(#141414)"
                         ;;
                     ,*)
                         echo -n "^bg()^fg(#ababab)"
                         ;;
                 esac
                 if [ ! -z "$dzen2_svn" ] ; then
                     # clickable tags if using SVN dzen
                     echo -n "^ca(1,$hc_quoted focus_monitor \"$monitor\" && "
                     echo -n "$hc_quoted use \"${i:1}\") ${i:1} ^ca()"
                 else
                     # non-clickable tags if using older dzen
                     echo -n " ${i:1} "
                 fi
             done
             echo -n "$separator"
             echo -n "^bg()^fg() ${windowtitle//^/^^}"
             # small adjustments
             right="$separator^bg() $date $separator"
             right_text_only=$(echo -n "$right" | sed 's.\^[^(]*([^)]*)..g')
             # get width of right aligned text.. and add some space..
             width=$($textwidth "$font" "$right_text_only    ")
             echo -n "^pa($(($panel_width - $width)))$right"
             echo

             ### Data handling ###
             # This part handles the events generated in the event loop, and sets
             # internal variables based on them. The event and its arguments are
             # read into the array cmd, then action is taken depending on the event
             # name.
             # "Special" events (quit_panel/togglehidepanel/reload) are also handled
             # here.

             # wait for next event
             IFS=$'\t' read -ra cmd || break
             # find out event origin
             case "${cmd[0]}" in
                 tag*)
                     #echo "resetting tags" >&2
                     IFS=$'\t' read -ra tags <<< "$(hc tag_status $monitor)"
                     ;;
                 date)
                     #echo "resetting date" >&2
                     date="${cmd[@]:1}"
                     ;;
                 quit_panel)
                     exit
                     ;;
                 togglehidepanel)
                     currentmonidx=$(hc list_monitors | sed -n '/\[FOCUS\]$/s/:.*//p')
                     if [ "${cmd[1]}" -ne "$monitor" ] ; then
                         continue
                     fi
                     if [ "${cmd[1]}" = "current" ] && [ "$currentmonidx" -ne "$monitor" ] ; then
                         continue
                     fi
                     echo "^togglehide()"
                     if $visible ; then
                         visible=false
                         hc pad $monitor 0
                     else
                         visible=true
                         hc pad $monitor $panel_height
                     fi
                     ;;
                 reload)
                     exit
                     ;;
                 focus_changed|window_title_changed)
                     windowtitle="${cmd[@]:2}"
                     ;;
                 #player)
                 #    ;;
             esac
         done

         ### dzen2 ###
         # After the data is gathered and processed, the output of the previous block
         # gets piped to dzen2.

     } | dzen2 -w $panel_width -x $x -y $y -fn "$font" -h $panel_height \
               -e "button3=;button4=exec:$hc_quoted use_index -1;button5=exec:$hc_quoted use_index +1" \
               -ta l -bg "$bgcolor" -fg '#efefef'

COMMENT Dock

  if [ $(pgrep plank) ]
  then
      pkill plank
  else
      plank &
  fi

System information

  if [ $(pgrep conky) ]
  then
      pkill conky
  fi
      conky &

Compositor

  if [ $(pgrep picom) ]
  then
      pkill picom
  fi
      picom &
      

TODO Desktop Background

  ~/.fehbg &