\chapter{Customizing \wmii} There are several configuration schemes available for \wmii. If you're only looking to add basic key bindings, status monitors, \emph{et cetera}, you should have no trouble modifying the stock configuration for your language of choice. If you're looking for deeper knowledge of \wmii's control interface though, this section is for you. We'll proceed by building a configuration script in \POSIX\ |sh| syntax and then move on to a discussion of the higher level constructs in the stock configuration scripts. For the purposes of pedagogy, we'll construct the script in the literate programming style of Knuth, whereby we construct the code in fragments and explain each one in detail. For your convenience, each fragment name is linked to its definition. \section{Events} The \wmii\ control interface is largely event driven. Each event is represented by a single, plain-text line written to the |/event| file. You can think of this file as a named pipe. When reading it, you won't receive an EOF\footnote{End of File} until \wmii\ exits. Moreover, any lines written to the file will be transmitted to everyone currently reading from it. Notable events include key presses, the creation and destruction of windows, and changes of focus and views. We'll start building our configuration with an event processing framework: \begin{Fragment}{Event Loop} # Broadcast a custom event wmiir xwrite /event Start wmiirc # Turn off globbing set -f # Open /event for reading wmiir read /event | # Read the events line by line while read line; do # Split the line into words, store in $@ set -- $line event=$1; shift line = "$(echo $line | sed ‘s/^[^ ]* //’ | tr -d ‘\n’)" # Process the event case $event in Start) # Quit when a new instance starts [ $1 = wmiirc ] && exit;; «Event Handlers» esac done \end{Fragment} Now, we need to consider which types of events we'll need to handle: \begin{Fragment}{Event Handlers} «View Button Events» «Urgency Events» «Unresponsive Clients» «Notice Events» «Key Events» «Client Menu Events» «Tag Menu Events» \end{Fragment} \section{Bar Items} The bar is described by the files in the two directories |/lbar/| and |/rbar/| for buttons on the left and right side of the bar, respectively. The files act as control files (section \ref{sec:controlfiles}) with the contents: \begin{code} color ‹Color Tuple› label ‹Label› \end{code} A ‹Color Tuple› is defined as: \begin{code} ‹Color Tuple› ≔ ‹foreground color› ‹background color› ‹border color› ‹* Color› ≔ ‹RGB color› | ‹RGBA color› ‹RGB color› ≔ #‹6 character RGB hex color code› ‹RGBA color› ≔ rgba:‹red›/‹green›/‹blue›/‹alpha› \end{code} \noindent where all of the colors are represented as lowercase, hexidecimal values. In the case of RGBA colors, they may be 1--4 characters long, though they will be standardized internally to 2 characters. \medskip Let's define our basic theme information now: \begin{Fragment}{Theme Definitions} normcolors=‘#000000 #c1c48b #81654f’ focuscolors=‘#000000 #81654f #000000’ background=‘#333333’ font=‘drift,-*-fixed-*-*-*-*-9-*-*-*-*-*-*-*’ \end{Fragment} \subsection{View Buttons} With a basic understanding of bar items in mind, we can write our view event handlers: \index{events!CreateTag} \index{events!DestroyTag} \index{events!FocusTag} \index{events!UnfocusTag} \begin{Fragment}{View Button Events} CreateTag) # CreateTag ‹Tag Name› echo $normcolors $1 | wmiir create /lbar/$1;; DestroyTag) # DestroyTag ‹Tag Name› wmiir rm /lbar/$1;; FocusTag) # FocusTag ‹Tag Name› wmiir xwrite /lbar/$1 $focuscolors $1;; UnfocusTag) # UnfocusTag ‹Tag Name› wmiir xwrite /lbar/$1 $normcolors $1;; \end{Fragment} \subsection{Urgency} \index{events!UrgentTag|(} \index{events!NotUrgentTag|(} Windows can specify that they require attention, and in X11 parlance, this is called urgency\footnote{\ICCCM{4.1.2.4}}. When a window requests attention as such, or declares that it's been satisfied, \wmii\ broadcasts an event for the client and an event for each view that it belongs to. It also fills in the layout box of any client deemed urgent. It's the job of a script to decide how to handle urgency events above and beyond that basic measure. The standard scripts simply mark urgent views with an asterisk: \begin{Fragment}{Urgency Events} # The urgency events are ‘Client’ events when the program # owning the window sets its urgency state. They're ‘Manager’ # events when wmii or the wmii user sets the state. UrgentTag) # UrgentTag ‹‘Client’ or ‘Manager’› ‹Tag Name› wmiir xwrite /lbar/$2 $2;; NotUrgentTag) # NotUrgentTag ‹‘Client’ or ‘Manager’› ‹Tag Name› wmiir xwrite /lbar/$2 $2;; \end{Fragment} \index{events!UrgentTag|)} \index{events!NotUrgentTag|)} \subsection{Notices} The standard scripts provide a custom Notice event for displaying status information. The events appear in the long bar between the left and right sides for five seconds. \begin{Fragment}{Notice Events} Notice) wmiir xwrite /rbar/!notice $line kill $xpid 2>/dev/null # Let's hope this isn't reused... { sleep 5; wmiir xwrite /rbar/!notice ‘ ’; } & xpid = $!;; \end{Fragment} \section{Keys} \label{sec:keybindings} \index{key bindings} \index{filesystem!/!keys} \index{filesystem!/!event} Now to the part you've no doubt been waiting for: binding keys. When binding keys, you need to be aware of two files, |/keys| and |/event|. The former defines which keys \wmii\ needs to grab, and the latter broadcasts the events when they're pressed. Key names are specified as a series of modifiers followed by a key name, all separated by hyphens. Valid modifier names are |Control|, |Shift|, |Mod1| (usually Alt), |Mod2|, |Mod3|, |Mod4| (usually the Windows® key), and |Mod5|. Modifier keys can be changed via |xmodmap(1)|, the details of which are beyond the scope of this document. Key names can be detected by running |xev| from a terminal, pressing the desired key, and looking at the output (it's in the parentheses, after the keysym). Or, more simply, you can run the \man 1 {wikeyname} utility bundled with \wmii\ and press the key you wish to bind. Examples of key bindings: \begin{description} \item[Windows® key + Capital A] |Mod4-Shift-A| \item[Control + Alt + Space] |Mod1-Control-Space| \end{description} Now, let's bind the keys we plan on using: \begin{Fragment}{Bind Keys} { cat < ‹width›{\color{gray}[}+‹width›{\color{gray}]*}} \end{quote} Where, \begin{code} ‹width› ≔ ‹percent of screen› | ‹pixels›px \end{code} When a new column, ‹n›, is created on a view whose name matches ‹regex›, it is given the ‹n›th supplied ‹width›. If there is no ‹n›th width, it is given $1/\mbox{‹ncol›th}$ of the screen. \item[rules] \index{filesystem!/!rules} The |/rules| file contains a list of rules similar to the colrules. These rules set properties for a client when it is created. Rules are specified: \begin{quote}\texttt{ /‹regex›/ -> ‹key›{\color{gray}=}‹value› {\color{gray}\ldots}} \end{quote} When a client's ‹name›:‹class›:‹title› matches ‹regex›, the matching rules are applied. For each ‹key›=‹value› pair, the |ctl| file property matching ‹key› is set to ‹value›. Additionally, the following keys are accepted and have special meaning: \begin{description} \item[continue] Normally, when a matching rule is encountered, rule matching stops. When the continue key is provided (with any value), matching continues at the next rule. \item[force-tags] Like tags, but overrides any settings obtained obtained from the client's group or from the |_WMII_TAGS| window property. \end{description} \end{description} \index{!filesystem!/|)} \subsection{Configuration} We'll need to let \wmii\ know about our previously defined theme information: \begin{Fragment}{Configuration} «Theme Definitions» xsetroot -solid $background wmiir write /ctl </dev/null # Let's hope this isn't reused... { sleep 5; wmiir xwrite /rbar/!notice ‘ ’; } & xpid = $!;; # «Key Events» Key) # Key ‹Key Name› case $1 in # «Motion Keys» Mod4-h) wmiir xwrite /tag/sel/ctl select left;; Mod4-l) wmiir xwrite /tag/sel/ctl select right;; Mod4-k) wmiir xwrite /tag/sel/ctl select up;; Mod4-j) wmiir xwrite /tag/sel/ctl select down;; Mod4-space) wmiir xwrite /tag/sel/ctl select toggle;; # «Client Movement Keys» Mod4-Shift-h) wmiir xwrite /tag/sel/ctl send sel left;; Mod4-Shift-l) wmiir xwrite /tag/sel/ctl send sel right;; Mod4-Shift-k) wmiir xwrite /tag/sel/ctl send sel up;; Mod4-Shift-j) wmiir xwrite /tag/sel/ctl send sel down;; Mod4-Shift-space) wmiir xwrite /tag/sel/ctl send sel toggle;; # «Column Mode Keys» Mod4-d) wmiir xwrite /tag/sel/ctl colmode sel -stack-max;; Mod4-s) wmiir xwrite /tag/sel/ctl colmode sel stack-max;; Mod4-m) wmiir xwrite /tag/sel/ctl colmode sel stack+max;; # «Client Command Keys» Mod4-Shift-c) wmiir xwrite /client/sel/ctl kill;; Mod4-f) wmiir xwrite /client/sel/ctl fullscreen toggle;; # «Command Execution Keys» Mod4-Return) terminal & ;; Mod4-p) eval exec wmiir setsid “$(proglist $PATH | wimenu)” &;; Mod4-a) { set -- $(proglist $WMII_CONFPATH | wimenu) prog=$(PATH=$WMII_CONFPATH which $1); shift eval exec $prog “$@” } &;; # «Tag Selection Keys» Mod4-t) # Prompt the user for a tag tags=$(wmiir ls /tag | sed ‘s,/,,; /^sel$/d’ | wimenu) # Write it to the filesystem. wmiir xwrite /ctl view $tag;; Mod4-[0-9]) wmiir xwrite /ctl view ${1##*-};; # «Tagging Keys» Mod4-Shift-t) # Get the selected client's id c=$(wmiir read /client/sel/ctl | sed 1q) # Prompt the user for new tags tags=$(wmiir ls /tag | sed ‘s,/,,; /^sel$/d’ | wimenu) # Write them to the client wmiir xwrite /client/$c/tags $tag;; Mod4-Shift-[0-9]) wmiir xwrite /client/sel/tags ${1##*-};; esac;; # «Client Menu Events» ClientMouseDown) # ClientMouseDown ‹Client ID› ‹Button› [ $2 = 3 ] && clickmenu \ “Delete:wmiir xwrite /client/$1/ctl kill” \ “Kill:wmiir xwrite /client/$1/ctl slay” \ “Fullscreen:wmiir xwrite /client/$1/ctl fullscreen on” # «Tag Menu Events» LeftBarMouseDown) # LeftBarMouseDown ‹Button› ‹Bar Name› [ $1 = 3 ] && clickmenu \ “Delete:delete_view $2” esac done \end{code}