wmii

git clone git://oldgit.suckless.org/wmii/
Log | Files | Refs | README | LICENSE

customizing.tex (33124B)


      1 \chapter{Customizing \wmii}
      2 
      3 There are several configuration schemes available for \wmii. If
      4 you're only looking to add basic key bindings, status monitors,
      5 \emph{et cetera}, you should have no trouble modifying the stock
      6 configuration for your language of choice. If you're looking for
      7 deeper knowledge of \wmii's control interface though, this
      8 section is for you. We'll proceed by building a configuration
      9 script in \POSIX\ |sh| syntax and then move on to a discussion
     10 of the higher level constructs in the stock configuration
     11 scripts.
     12 
     13 For the purposes of pedagogy, we'll construct the script in the
     14 literate programming style of Knuth, whereby we construct the
     15 code in fragments and explain each one in detail. For your
     16 convenience, each fragment name is linked to its definition.
     17 
     18 \section{Events}
     19 
     20 The \wmii\ control interface is largely event driven. Each event
     21 is represented by a single, plain-text line written to the
     22 |/event| file. You can think of this file as a named pipe. When
     23 reading it, you won't receive an EOF\footnote{End of File} until
     24 \wmii\ exits. Moreover, any lines written to the file will be
     25 transmitted to everyone currently reading from it. Notable
     26 events include key presses, the creation and destruction of
     27 windows, and changes of focus and views.
     28 
     29 We'll start building our configuration with an event processing
     30 framework:
     31 
     32 \begin{Fragment}{Event Loop}
     33   # Broadcast a custom event
     34   wmiir xwrite /event Start wmiirc
     35 
     36   # Turn off globbing
     37   set -f
     38   # Open /event for reading
     39   wmiir read /event |
     40   # Read the events line by line
     41   while read line; do
     42       # Split the line into words, store in $@
     43       set -- $line
     44       event=$1; shift
     45       line = "$(echo $line | sed ‘s/^[^ ]* //’ | tr -d ‘\n’)"
     46       # Process the event
     47       case $event in
     48       Start) # Quit when a new instance starts
     49           [ $1 = wmiirc ] && exit;;
     50       «Event Handlers»
     51       esac
     52   done
     53 \end{Fragment}
     54 
     55 Now, we need to consider which types of events we'll need to
     56 handle:
     57 
     58 \begin{Fragment}{Event Handlers}
     59   «View Button Events»
     60   «Urgency Events»
     61   «Unresponsive Clients»
     62   «Notice Events»
     63   «Key Events»
     64   «Client Menu Events»
     65   «Tag Menu Events»
     66 \end{Fragment}
     67 
     68 \section{Bar Items}
     69 
     70 The bar is described by the files in the two directories |/lbar/| and
     71 |/rbar/| for buttons on the left and right side of the bar,
     72 respectively. The files act as control files (section
     73 \ref{sec:controlfiles}) with the contents:
     74 
     75 \begin{code}
     76   color ‹Color Tuple›
     77   label ‹Label›
     78 \end{code}
     79 
     80 A ‹Color Tuple› is defined as:
     81 
     82 \begin{code}
     83   ‹Color Tuple› ≔ ‹foreground color› ‹background color› ‹border color›
     84   ‹* Color›     ≔ ‹RGB color› | ‹RGBA color›
     85   ‹RGB color›   ≔ #‹6 character RGB hex color code›
     86   ‹RGBA color›  ≔ rgba:‹red›/‹green›/‹blue›/‹alpha›
     87 \end{code}
     88 
     89 \noindent
     90 where all of the colors are represented as lowercase,
     91 hexidecimal values. In the case of RGBA colors, they may be 1--4
     92 characters long, though they will be standardized internally to
     93 2 characters.
     94 
     95 \medskip
     96 
     97 Let's define our basic theme information now:
     98 
     99 \begin{Fragment}{Theme Definitions}
    100   normcolors=‘#000000 #c1c48b #81654f’
    101   focuscolors=‘#000000 #81654f #000000’
    102   background=‘#333333’
    103   font=‘drift,-*-fixed-*-*-*-*-9-*-*-*-*-*-*-*’
    104 \end{Fragment}
    105 
    106 \subsection{View Buttons}
    107 
    108 With a basic understanding of bar items in mind, we can write
    109 our view event handlers:
    110 
    111 \index{events!CreateTag}
    112 \index{events!DestroyTag}
    113 \index{events!FocusTag}
    114 \index{events!UnfocusTag}
    115 \begin{Fragment}{View Button Events}
    116   CreateTag)  # CreateTag ‹Tag Name›
    117       echo $normcolors $1 | wmiir create /lbar/$1;;
    118   DestroyTag) # DestroyTag ‹Tag Name›
    119       wmiir rm /lbar/$1;;
    120   FocusTag)   # FocusTag ‹Tag Name›
    121       wmiir xwrite /lbar/$1 $focuscolors $1;;
    122   UnfocusTag) # UnfocusTag ‹Tag Name›
    123       wmiir xwrite /lbar/$1 $normcolors $1;;
    124 \end{Fragment}
    125 
    126 \subsection{Urgency}
    127 
    128 \index{events!UrgentTag|(}
    129 \index{events!NotUrgentTag|(}
    130 Windows can specify that they require attention, and in X11
    131 parlance, this is called urgency\footnote{\ICCCM{4.1.2.4}}. When
    132 a window requests attention as such, or declares that it's been
    133 satisfied, \wmii\ broadcasts an event for the client and an
    134 event for each view that it belongs to. It also fills in the
    135 layout box of any client deemed urgent. It's the job of a script
    136 to decide how to handle urgency events above and beyond that
    137 basic measure. The standard scripts simply mark urgent views
    138 with an asterisk:
    139 
    140 \begin{Fragment}{Urgency Events}
    141   # The urgency events are ‘Client’ events when the program
    142   # owning the window sets its urgency state. They're ‘Manager’
    143   # events when wmii or the wmii user sets the state.
    144   UrgentTag)    # UrgentTag ‹‘Client’ or ‘Manager’› ‹Tag Name›
    145       wmiir xwrite /lbar/$2 $2;;
    146   NotUrgentTag) # NotUrgentTag ‹‘Client’ or ‘Manager’› ‹Tag Name›
    147       wmiir xwrite /lbar/$2 $2;;
    148 \end{Fragment}
    149 \index{events!UrgentTag|)}
    150 \index{events!NotUrgentTag|)}
    151 
    152 \subsection{Notices}
    153 
    154 The standard scripts provide a custom Notice event for
    155 displaying status information. The events appear in the long bar
    156 between the left and right sides for five seconds.
    157 
    158 \begin{Fragment}{Notice Events}
    159   Notice)
    160       wmiir xwrite /rbar/!notice $line
    161       kill $xpid 2>/dev/null # Let's hope this isn't reused...
    162       { sleep 5; wmiir xwrite /rbar/!notice ‘ ’; } &
    163       xpid = $!;;
    164 \end{Fragment}
    165 
    166 \section{Keys}
    167 
    168 \label{sec:keybindings}
    169 \index{key bindings}
    170 \index{filesystem!/!keys}
    171 \index{filesystem!/!event}
    172 Now to the part you've no doubt been waiting for: binding keys.
    173 When binding keys, you need to be aware of two files, |/keys|
    174 and |/event|. The former defines which keys \wmii\ needs to
    175 grab, and the latter broadcasts the events when they're pressed.
    176 
    177 Key names are specified as a series of modifiers followed by a
    178 key name, all separated by hyphens. Valid modifier names are
    179 |Control|, |Shift|, |Mod1| (usually Alt), |Mod2|, |Mod3|, |Mod4|
    180 (usually the Windows® key), and |Mod5|. Modifier keys can be
    181 changed via |xmodmap(1)|, the details of which are beyond the
    182 scope of this document.
    183 
    184 Key names can be detected by running |xev| from a
    185 terminal, pressing the desired key, and looking at the output
    186 (it's in the parentheses, after the keysym). Or, more simply,
    187 you can run the \man 1 {wikeyname} utility bundled with \wmii\
    188 and press the key you wish to bind.
    189 
    190 Examples of key bindings:
    191 
    192 \begin{description}
    193   \item[Windows® key + Capital A] |Mod4-Shift-A|
    194   \item[Control + Alt + Space]    |Mod1-Control-Space|
    195 \end{description}
    196 
    197 Now, let's bind the keys we plan on using:
    198 
    199 \begin{Fragment}{Bind Keys}
    200   {
    201   cat <<!
    202   Mod4-space
    203   Mod4-d
    204   Mod4-s
    205   Mod4-m
    206   Mod4-a
    207   Mod4-p
    208   Mod4-t
    209   Mod4-Return
    210   Mod4-Shift-space
    211   Mod4-f
    212   Mod4-Shift-c
    213   Mod4-Shift-t
    214   Mod4-h
    215   Mod4-j
    216   Mod4-k
    217   Mod4-l
    218   Mod4-Shift-h
    219   Mod4-Shift-j
    220   Mod4-Shift-k
    221   Mod4-Shift-l
    222   !
    223   for i in 1 2 3 4 5 6 7 8 9 0; do
    224       echo Mod4-$i
    225       echo Mod4-Shift-$i
    226   done
    227   } | wmiir write /keys
    228 \end{Fragment}
    229 
    230 and lay a framework for processing their events:
    231 
    232 \begin{Fragment}{Key Events}
    233   Key) # Key ‹Key Name›
    234       case $1 in
    235       «Motion Keys»
    236       «Client Movement Keys»
    237       «Column Mode Keys»
    238       «Client Command Keys»
    239       «Command Execution Keys»
    240       «Tag Selection Keys»
    241       «Tagging Keys»
    242       esac;;
    243 \end{Fragment}
    244 
    245 \section{Click Menus}
    246 
    247 Sometimes, you have your hand on the mouse and don't want to
    248 reach for the keyboard. To help cope, \wmii\ provides a
    249 mouse-driven, single-click menu. The default configuration uses
    250 it for client and tag menus.
    251 
    252 \begin{Fragment}{Click Menu Initialization}
    253   clickmenu() {
    254       if res=$(wmii9menu -- “$@”); then eval “$res”; fi
    255   }
    256 \end{Fragment}
    257 
    258 \section{Control Files}
    259 
    260 \label{sec:controlfiles}
    261 
    262 Several directories including the root, have control files,
    263 named |ctl|. These files are used to control the object (e.g., a
    264 client or tag) represented by the directory. Each line of the
    265 file, with the possible section of the first, represents a
    266 control variable and its value. In the case of all but the root
    267 |/ctl| file, the first line represents the id of the directory.
    268 In the case of |/tag/foo/ctl|, for instance, the first line
    269 should read |foo|. This is useful when dealing with the special
    270 |sel/| directories. For instance, when |foo| is the selected
    271 tag, the special |/tag/sel| directory is a link to |/tag/foo|,
    272 and the first line of |/tag/sel/ctl| will read |foo|, just as
    273 if you'd accessed |/tag/foo/ctl| directly.
    274 
    275 The rest of the lines, the control variables, can be modified by
    276 writing new values to the control file. For instance, if a
    277 client is fullscreen, its control file will contain the line:
    278 
    279 \begin{code}
    280   fullscreen on
    281 \end{code}
    282 
    283 \noindent To restore the client from fullscreen, either of the
    284 following lines may be written to its control file:
    285 
    286 \begin{code}
    287   fullscreen off
    288   fullscreen toggle
    289 \end{code}
    290 
    291 When next read, the |fullscreen on| line will have been replaced
    292 with |fullscreen off|.  No care need be taken to preserve the
    293 other contents of the file. They're generated anew each time
    294 it's read.
    295 
    296 \section{Clients}
    297 
    298 \def\clientlabel{/client/$\langle\mathit{client}\rangle$/}
    299 \index{filesystem!/client/*/@\clientlabel|(}
    300 Clients are represented by directories under the |/client/|
    301 tree. Subdirectory names represent the client's X11 window ID.
    302 The special |sel/| directory represents the currently selected
    303 client. The files in these directories are:
    304 
    305 \begin{description}
    306   \item[ctl] The client's control file, containing the following
    307     properties:
    308     \index{filesystem!/client/*/@\clientlabel!ctl}
    309     \begin{description}
    310       \item[allow] The set of unusual actions the client is
    311         allowed to perform, in the same format as the tag set.
    312         \begin{description}
    313      \item[activate] The client is allowed to activate
    314        itself—that is, focus its window and, as the case may
    315        require, uncollapse it and select a tag it resides on.
    316        This flag must be set on a client if you wish it able to
    317        activate itself from the system tray.
    318         \end{description}
    319       \item[floating] Defines whether this client is likely to
    320         float when attached to a new view. May be |on|, |off|,
    321         |always|, or |never|.  Ordinarilly, the value changes
    322         automatically whenever the window is moved between the
    323         floating and managed layers. However, setting a value of
    324         |always| or |never| overrides this behavior.
    325       \item[fullscreen] The client's fullscreen state. When
    326         |on|, the client is displayed fullscreen on all of its
    327         views. Possible values are |on|, |off|, and |toggle|.
    328       \item[group] The client's group ID, or |0| if not part of
    329         a group. Clients tend to open with the same tags and in
    330         the same columns as the last active member of their
    331         group. Setting this property is only useful when done
    332         via the rules file.
    333       \item[kill] When written, the window is closed politely,
    334         if possible.
    335       \item[pid] Read-only value of the PID of the program that
    336         owns the window, if the value is available and the
    337         process is on the same machine as wmii.
    338       \item[slay] When written, the client is disconnected
    339         peremptorily. If the client's PID is available and the
    340         process is the same machine as wmii, its parent process
    341         is killed
    342       \item[tags] The client's tags. The same as the tags file.
    343       \item[urgent] The client's urgency state. When |on|, the
    344         client's layout box will be highlighted. Possible values
    345         are |on|, |off|, and |toggle|.
    346     \end{description}
    347   \item[props] The client's window class (the X11
    348     |WM_CLASS|\footnote{\ICCCM{4.1.2.5}}
    349     property) and title string, separated by colons. This file
    350     is not writable.
    351     \index{filesystem!/client/*/@\clientlabel!props}
    352   \item[label] The client's window title. May be written to
    353     change the client's title.
    354     \index{filesystem!/client/*/@\clientlabel!label}
    355   \item[tags]
    356     \index{filesystem!/client/*/@\clientlabel!tags}
    357     The client's tags. Tag names are separated by |+|, |-|, or
    358     |^| signs. Tag names which directly follow a |+| sign are
    359     added, while whose following a |-| sign are removed and
    360     those following a |^| are toggled.  If the value written
    361     begins with one of these characters, the value is appended
    362     to the clients tags rather than replacing them. 
    363     
    364     Tags formatted as |/‹regex›/| are treated as regular
    365     expressions, which place the client on any extant matching
    366     tag\footnote{While a client with a regex tag will always
    367     appear in all matching views, it will not keep those views
    368     in existence. When the last client explicitly tagged with a
    369     view is removed, the view is deleted as soon as it becomes
    370     inactive.}.  Regular expression tags which directly follow a
    371     minus sign are treated as exclusion expressions. For
    372     example, the tag string |+/foo/-/food/| will match the tag
    373     |foobar|, but not the tag |foodstand|.
    374 \end{description}
    375 
    376 \index{filesystem!/client/*/@\clientlabel|)}
    377 
    378 \subsection{Key Bindings}
    379 
    380 To control clients, we'll add the following key bindings:
    381 
    382 \begin{Fragment}{Client Command Keys}
    383   Mod4-Shift-c) wmiir xwrite /client/sel/ctl kill;;
    384   Mod4-f) wmiir xwrite /client/sel/ctl Fullscreen toggle;;
    385 \end{Fragment}
    386 
    387 And to manage their tags, we'll need:
    388 
    389 \begin{Fragment}{Tagging Keys}
    390   Mod4-Shift-t)
    391     # Get the selected client's id
    392     c=$(wmiir read /client/sel/ctl | sed 1q)
    393     # Prompt the user for new tags
    394     tags=$(wmiir ls /tag | sed ‘s,/,,; /^sel$/d’ | wimenu)
    395     # Write them to the client
    396     wmiir xwrite /client/$c/tags $tag;;
    397   Mod4-Shift-[0-9])
    398     wmiir xwrite /client/sel/tags ${1##*-};;
    399 \end{Fragment}
    400 
    401 \subsection{Click Menus}
    402 
    403 \index{events!ClientMouseDown}
    404 \begin{Fragment}{Client Menu Events}
    405   ClientMouseDown) # ClientMouseDown ‹Client ID› ‹Button›
    406     [ $2 = 3 ] && clickmenu \
    407       “Delete:wmiir xwrite /client/$1/ctl kill” \
    408       “Kill: wmiirxwrite /client/$1/ctl slay” \
    409       “Fullscreen:wmiir xwrite /client/$1/ctl fullscreen on”
    410 \end{Fragment}
    411 
    412 \subsection{Unresponsive Clients}
    413 
    414 \index{events!UnresponsiveClient|(}
    415 When \wmii\ tries to close a window, it waits 8 seconds for the
    416 client to respond, and then lets its scripts decide what to do
    417 with it. The stock scripts prompt the user for input:
    418 
    419 \begin{Fragment}{Unresponsive Clients}
    420   UnresponsiveClient) # UnresponsiveClient ‹Client ID›
    421   {
    422       # Use wihack to make the xmessage a transient window of
    423       # the problem client. This will force it to open in the
    424       # floaing layer of whatever views the client is attached to
    425       resp=$(wihack -transient $1 \
    426              xmessage -nearmouse -buttons Kill,Wait -print \
    427              “The following client is not responding.” \
    428              “What would you like to do?$(echo)” \
    429              $(wmiir read /client/$1/label))
    430       [ $resp = Kill ] && wmiir xwrite /client/$1/ctl slay
    431   } &;;
    432 \end{Fragment}
    433 \index{events!UnresponsiveClient|)}
    434 
    435 \section{Views}
    436 
    437 \def\taglabel{/tag/$\langle\mathit{tag}\rangle$/}
    438 \index{filesystem!/tag/*/@\taglabel|(}
    439 Views are represented by directories under the |/tag/| tree. The
    440 special |sel/| directory represents the currently selected
    441 client. The |sel| tag is treated similarly elsewhere. The files
    442 in these directories are:
    443 
    444 \begin{description}
    445   \item[ctl]
    446     The view's control file. The properties are:
    447     \index{filesystem!/tag/*/@\taglabel!ctl|(}
    448     \begin{description}
    449       \item[select ‹Area›] Select the column ‹Area›, where
    450         ‹Area› is a 1-based column index, or |~| for the floating
    451         area. It may be optionally preceded by ‹Screen›|:|, where
    452         ‹Screen› is a 0-based Xinerama screen index, or “sel”. When
    453         omitted, ‹Screen› defaults to 0, the primary screen.
    454       \item[select ‹Area› ‹Client Index›] Select the column ‹Area›, and
    455         the ‹Client Index›th client.
    456       \item[select client ‹Client ID›] Select the client with the
    457         X11 window ID ‹Client ID›.
    458       \item[select ‹Direction›]
    459         Select the client in ‹Direction› where ‹Direction› may be
    460         one of ‹up $\wedge$ down $\wedge$ left $\wedge$ right›.
    461       \item[send client ‹Client ID› ‹Area›] Send ‹Client ID› to
    462         ‹Area›. ‹Area› may be |sel| for the selected area, and
    463         |client ‹Client ID›| may be |sel| for the currently selected
    464         client.
    465       \item[send client ‹Client ID› ‹Direction›]
    466         Send ‹Client ID› to a column or position in its column in
    467         the given direction.
    468       \item[send client ‹Client ID› toggle] If ‹Client ID› is
    469         floating, send it to the managed layer. If it's managed,
    470         send it to the floating layer.
    471       \item[swap client ‹Client ID› \ldots] The same as the |send|
    472         commands, but swap ‹Client ID› with the client at the given
    473         location.
    474       \item[colmode ‹Area› ‹Mode›] Set ‹Area›'s mode to ‹Mode›,
    475         where ‹Mode› is a string of values similar to tag
    476         specifications. Values which may be added and removed are as
    477         follows for managed areas:
    478 
    479         \begin{description}
    480           \item[stack] One and only one client in the area is
    481             uncollapsed at any given time. When a new client is
    482             selected, it is uncollapsed and the previously selected
    483             client is collapsed.
    484           \item[max] Collapsed clients are hidden from view
    485             entirely. Uncollapsed clients display an indicator
    486             {\it‹n›/‹m›}, where ‹m› is the number of collapsed
    487             clients directly above and below the client, plus one,
    488             and ‹n› is the client's index in the stack.
    489           \item[default] Like subtracting the stack mode, but all
    490             clients in the column are given equal height.
    491         \end{description}
    492 
    493         For the floating area, the values are the same, except that
    494         in |max| mode, floating clients are hidden when the managed
    495         layer is selected.
    496       \item[grow ‹Frame› ‹Direction› {[‹Amount›]}] Grow ‹Frame› in
    497         the given direction, by ‹Amount›. ‹Amount› may be any
    498         integer, positive or negative. If suffixed with |px|,
    499         it specifies an exact pixel amount, otherwise it specifies a
    500         “reasonable increment”. Defaults to 1.
    501 
    502         ‹Frame› may be one of:
    503         \begin{itemize}
    504           \item client ‹Client ID›
    505           \item ‹Area› ‹Client Index›
    506         \end{itemize}
    507       \item[nudge ‹Frame› ‹Direction› {[‹Amount›]}] Like
    508         |grow|, but move the client in ‹Direction› instead of
    509         resizing it.
    510   \end{description}
    511   \index{filesystem!/tag/*/@\taglabel!ctl|)}
    512 \end{description}
    513 
    514 \index{filesystem!/tag/*/@\taglabel|)}
    515 
    516 \subsection{Key Bindings}
    517 
    518 We'll use the following key bindings to interact with views:
    519 
    520 \begin{Fragment}{Motion Keys}
    521   Mod4-h) wmiir xwrite /tag/sel/ctl select left;;
    522   Mod4-l) wmiir xwrite /tag/sel/ctl select right;;
    523   Mod4-k) wmiir xwrite /tag/sel/ctl select up;;
    524   Mod4-j) wmiir xwrite /tag/sel/ctl select down;;
    525   Mod4-space) wmiir xwrite /tag/sel/ctl select toggle;;
    526 \end{Fragment}
    527 
    528 \begin{Fragment}{Client Movement Keys}
    529   Mod4-Shift-h) wmiir xwrite /tag/sel/ctl send sel left;;
    530   Mod4-Shift-l) wmiir xwrite /tag/sel/ctl send sel right;;
    531   Mod4-Shift-k) wmiir xwrite /tag/sel/ctl send sel up;;
    532   Mod4-Shift-j) wmiir xwrite /tag/sel/ctl send sel down;;
    533   Mod4-Shift-space) wmiir xwrite /tag/sel/ctl send sel toggle;;
    534 \end{Fragment}
    535 
    536 \begin{Fragment}{Column Mode Keys}
    537   Mod4-d) wmiir xwrite /tag/sel/ctl colmode sel -stack-max;;
    538   Mod4-s) wmiir xwrite /tag/sel/ctl colmode sel stack-max;;
    539   Mod4-m) wmiir xwrite /tag/sel/ctl colmode sel stack+max;;
    540 \end{Fragment}
    541 
    542 \subsection{Click Menus}
    543 
    544 \index{events!LeftBarMouseDown}
    545 \begin{Fragment}{Tag Menu Events}
    546   LeftBarMouseDown) # LeftBarMouseDown ‹Button› ‹Bar Name›
    547     [ $1 = 3 ] && clickmenu \
    548       “Delete:delete_view $2”
    549 \end{Fragment}
    550 
    551 \section{Command and Program Execution}
    552 
    553 Perhaps the most important function we need to provide for is
    554 the execution of programs. Since \wmii\ users tend to use
    555 terminals often, we'll add a direct shortcut to launch one.
    556 Aside from that, we'll add a menu to launch arbitrary programs
    557 (with completions) and a separate menu to launch wmii specific
    558 commands.
    559 
    560 We use |wmiir setsid| to launch programs with their own session
    561 IDs to prevent untoward effects when this script dies.
    562 
    563 \begin{Fragment}{Command Execution Initialization}
    564   terminal() { wmiir setsid xterm “$@” }
    565   proglist() {
    566       IFS=:
    567       wmiir proglist $1 | sort | uniq
    568       unset IFS
    569   }
    570 \end{Fragment}
    571 
    572 \subsection{Key Bindings}
    573 \begin{Fragment}{Command Execution Keys}
    574   Mod4-Return) terminal & ;;
    575   Mod4-p) eval exec wmiir setsid “$(proglist $PATH | wimenu)” &;;
    576   Mod4-a) {
    577       set -- $(proglist $WMII_CONFPATH | wimenu)
    578       which=$(which which)
    579       prog=$(PATH=$WMII_CONFPATH $which $1); shift
    580       eval exec $prog “$@”
    581   } &;;
    582 \end{Fragment}
    583 
    584 \section{The Root}
    585 
    586 The root filesystem contains the following:
    587 
    588 \index{!filesystem!/|(}
    589 \begin{description}
    590   \item[ctl] The control file. The properties are:
    591     \index{filesystem!/!ctl}
    592     \begin{description}
    593       \item[bar on ‹top $\wedge$ bottom›] Controls where the bar
    594         is shown.
    595       \item[border] The border width, in pixels, of floating
    596         clients.
    597       \item[colmode ‹Mode›] The default column mode for newly
    598         created columns.
    599       \item[focuscolors ‹Color Tuple›] The colors of focused
    600         clients.
    601       \item[normcolors ‹Color Tuple›] The colors of unfocused
    602         clients and the default color of bar buttons.
    603       \item[font ‹Font›] The font used throughout \wmii. If
    604         prefixed with |xft:|, the Xft font renderer is used, and
    605         fonts may be antialiased. Xft font names follow the
    606         fontconfig formula. For instance, 10pt, italic Lucida
    607         Sans would be specified as
    608 
    609         \begin{code}
    610           xft:Lucida Sans-10:italic
    611         \end{code}
    612 
    613         See \man 1 {fc-match}.
    614 
    615       \item[grabmod ‹Modifier Keys›] The key which must be
    616         pressed to move and resize windows with the mouse
    617         without clicking hot spots.
    618       \item[incmode ‹Mode›] Controls how X11 increment hints are
    619         handled in managed mode. Possible values are:
    620         \begin{description}
    621           \item[ignore] Increment hints are ignored entirely.
    622             Clients are stretched to fill their full allocated
    623             space.
    624           \item[show] Gaps are shown around managed client
    625             windows when their increment hints prevent them from
    626             filling their entire allocated space.
    627           \item[squeeze] When increment hints cause gaps to show
    628             around clients, \wmii\ will try to adjust the sizes
    629             of the clients in the column to minimize lost space.
    630         \end{description}
    631       \item[view ‹Tag›] Change the currently visible view.
    632       \item[exec ‹Command›] Replaces this \wmii\ instance with
    633         ‹Command›. ‹Command› is split according to rc quoting
    634         rules, and no expansion occurs. If the command fails to
    635         execute, \wmii\ will respawn.
    636       \item[spawn ‹Command›] Spawns ‹Command› as it would spawn
    637         |wmiirc| at startup. If ‹Command› is a single argument
    638         and doesn't begin with |/| or |./|,%
    639         \hskip 1ex|$WMII_CONF|\-|PATH| is
    640         searched for the executable. Otherwise, the whole
    641         argument is passed to the shell for evaluation.
    642     \end{description}
    643   \item[keys]  The global keybindings. See section \ref{sec:keybindings}.
    644                \index{filesystem!/!keys|primary}
    645   \item[event] The global event feed. See section \ref{sec:keybindings}.
    646                \index{filesystem!/!event|primary}
    647   \item[colrules]
    648     \index{filesystem!/!colrules}
    649         The |/colrules| file contains a list of
    650         rules which affect the width of newly created columns.
    651         Rules have the form:
    652 
    653         \begin{quote}\texttt{
    654           /‹regex›/ -> ‹width›{\color{gray}[}+‹width›{\color{gray}]*}}
    655         \end{quote}
    656 
    657         Where,
    658 
    659         \begin{code}
    660     ‹width› ≔ ‹percent of screen› | ‹pixels›px
    661         \end{code}
    662 
    663         When a new column, ‹n›, is created on a view whose name
    664         matches ‹regex›, it is given the ‹n›th supplied ‹width›.
    665         If there is no ‹n›th width, it is given
    666         $1/\mbox{‹ncol›th}$ of the screen.
    667 
    668   \item[rules]
    669     \index{filesystem!/!rules}
    670     The |/rules| file contains a list of
    671     rules similar to the colrules. These rules set
    672     properties for a client when it is created.
    673     Rules are specified:
    674 
    675     \begin{quote}\texttt{
    676       /‹regex›/ -> ‹key›{\color{gray}=}‹value› {\color{gray}\ldots}}
    677     \end{quote}
    678 
    679     When a client's ‹name›:‹class›:‹title› matches
    680     ‹regex›, the matching rules are applied. For each
    681     ‹key›=‹value› pair, the |ctl| file property matching
    682     ‹key› is set to ‹value›.  Additionally,  the  following
    683     keys are accepted and have special meaning:
    684 
    685     \begin{description}
    686       \item[continue]
    687         Normally, when a matching rule  is  encountered,
    688         rule  matching  stops.  When the continue key is
    689         provided (with any value), matching continues at
    690         the next rule.
    691       \item[force-tags]
    692         Like  tags,  but overrides any settings obtained
    693         obtained from the client's  group  or  from  the
    694         |_WMII_TAGS| window property.
    695     \end{description}
    696 
    697 \end{description}
    698 
    699 \index{!filesystem!/|)}
    700 
    701 \subsection{Configuration}
    702 
    703 We'll need to let \wmii\ know about our previously defined theme
    704 information:
    705 
    706 \begin{Fragment}{Configuration}
    707   «Theme Definitions»
    708 
    709   xsetroot -solid $background
    710   wmiir write /ctl <<!
    711   border 2
    712   focuscolors $focuscolors
    713   normcolors $normcolors
    714   font $font
    715   grabmod Mod4
    716   !
    717 \end{Fragment}
    718 
    719 \subsection{Key Bindings}
    720 
    721 And we need a few more key bindings to select our views:
    722 
    723 \begin{Fragment}{Tag Selection Keys}
    724   Mod4-t)
    725     # Prompt the user for a tag
    726     tags=$(wmiir ls /tag | sed ‘s,/,,; /^sel$/d’ | wimenu)
    727     # Write it to the filesystem.
    728     wmiir xwrite /ctl view $tags;;
    729   Mod4-[0-9])
    730     wmiir xwrite /ctl view ${1##*-};;
    731 \end{Fragment}
    732 
    733 \section{Tieing it All Together}
    734 
    735 \begin{code}
    736   #!/bin/sh
    737   «Click Menu Initialization»
    738   «Command Execution Initialization»
    739 
    740   «Configuration»
    741 
    742   «Bind Keys»
    743   «Event Loop»
    744 \end{code}
    745 
    746 \section{The End Result}
    747 
    748 For clarity, here is the end result:
    749 
    750 \begin{code}
    751   #!/bin/sh
    752   # «Click Menu Initialization»
    753   clickmenu() {
    754       if res=$(wmii9menu -- “$@”); then eval “$res”; fi
    755   }
    756   # «Command Execution Initialization»
    757   terminal() { wmiir setsid xterm “$@” }
    758   proglist() {
    759       IFS=:
    760       wmiir proglist $1 | sort | uniq
    761       unset IFS
    762   }
    763 
    764   # «Configuration»
    765   # «Theme Definitions»
    766   normcolors=‘#000000 #c1c48b #81654f’
    767   focuscolors=‘#000000 #81654f #000000’
    768   background=‘#333333’
    769   font=‘drift,-*-fixed-*-*-*-*-9-*-*-*-*-*-*-*’
    770 
    771   xsetroot -solid $background
    772   wmiir write /ctl <<!
    773   border 2
    774   focuscolors $focuscolors
    775   normcolors $normcolors
    776   font $font
    777   grabmod Mod4
    778   !
    779 
    780   # «Bind Keys»
    781   {
    782   cat <<!
    783   Mod4-space
    784   Mod4-d
    785   Mod4-s
    786   Mod4-m
    787   Mod4-a
    788   Mod4-p
    789   Mod4-t
    790   Mod4-Return
    791   Mod4-Shift-space
    792   Mod4-f
    793   Mod4-Shift-c
    794   Mod4-Shift-t
    795   Mod4-h
    796   Mod4-j
    797   Mod4-k
    798   Mod4-l
    799   Mod4-Shift-h
    800   Mod4-Shift-j
    801   Mod4-Shift-k
    802   Mod4-Shift-l
    803   !
    804   for i in 1 2 3 4 5 6 7 8 9 0; do
    805       echo Mod4-$i
    806       echo Mod4-Shift-$i
    807   done
    808   } | wmiir write /keys
    809 
    810   # «Event Loop»
    811   # Broadcast a custom event
    812   wmiir xwrite /event Start wmiirc
    813 
    814   # Turn off globbing
    815   set -f
    816   # Open /event for reading
    817   wmiir read /event |
    818   # Read the events line by line
    819   while read line; do
    820       # Split the line into words, store in $@
    821       set -- $line
    822       event=$1; shift
    823       line = "$(echo $line | sed ‘s/^[^ ]* //’ | tr -d ‘\n’)"
    824 
    825       # Process the event
    826       case $event in
    827       Start) # Quit when a new instance starts
    828           [ $1 = wmiirc ] && exit;;
    829 
    830       # «Event Handlers»
    831       # «View Button Events»
    832       CreateTag)  # CreateTag ‹Tag Name›
    833           echo $normcolors $1 | wmiir create /lbar/$1;;
    834       DestroyTag) # DestroyTag ‹Tag Name›
    835           wmiir rm /lbar/$1;;
    836       FocusTag)   # FocusTag ‹Tag Name›
    837           wmiir xwrite /lbar/$1 $focuscolors $1;;
    838       UnfocusTag) # UnfocusTag ‹Tag Name›
    839           wmiir xwrite /lbar/$1 $normcolors $1;;
    840 
    841       # «Urgency Events»
    842       # The urgency events are ‘Client’ events when the program
    843       # owning the window sets its urgency state. They're ‘Manager’
    844       # events when wmii or the wmii user sets the state.
    845       UrgentTag)    # UrgentTag ‹‘Client’ or ‘Manager’› ‹Tag Name›
    846           wmiir xwrite /lbar/$2 $2;;
    847       NotUrgentTag) # NotUrgentTag ‹‘Client’ or ‘Manager’› ‹Tag Name›
    848           wmiir xwrite /lbar/$2 $2;;
    849 
    850       # «Unresponsive Clients»
    851       UnresponsiveClient) # UnresponsiveClient ‹Client ID›
    852       {
    853           # Use wihack to make the xmessage a transient window of
    854           # the problem client. This will force it to open in the
    855           # floaing layer of whatever views the client is attached to
    856           resp=$(wihack -transient $1 \
    857                  xmessage -nearmouse -buttons Kill,Wait -print \
    858                  “The following client is not responding.” \
    859                  “What would you like to do?$(echo)” \
    860                  $(wmiir read /client/$1/label))
    861           [ $resp = Kill ] && wmiir xwrite /client/$1/ctl slay
    862       } &;;
    863 
    864       # «Notice Events»
    865       Notice)
    866           wmiir xwrite /rbar/!notice $line
    867           kill $xpid 2>/dev/null # Let's hope this isn't reused...
    868           { sleep 5; wmiir xwrite /rbar/!notice ‘ ’; } &
    869           xpid = $!;;
    870 
    871       # «Key Events»
    872       Key) # Key ‹Key Name›
    873           case $1 in
    874           # «Motion Keys»
    875           Mod4-h) wmiir xwrite /tag/sel/ctl select left;;
    876           Mod4-l) wmiir xwrite /tag/sel/ctl select right;;
    877           Mod4-k) wmiir xwrite /tag/sel/ctl select up;;
    878           Mod4-j) wmiir xwrite /tag/sel/ctl select down;;
    879           Mod4-space) wmiir xwrite /tag/sel/ctl select toggle;;
    880 
    881           # «Client Movement Keys»
    882           Mod4-Shift-h) wmiir xwrite /tag/sel/ctl send sel left;;
    883           Mod4-Shift-l) wmiir xwrite /tag/sel/ctl send sel right;;
    884           Mod4-Shift-k) wmiir xwrite /tag/sel/ctl send sel up;;
    885           Mod4-Shift-j) wmiir xwrite /tag/sel/ctl send sel down;;
    886           Mod4-Shift-space) wmiir xwrite /tag/sel/ctl send sel toggle;;
    887 
    888           # «Column Mode Keys»
    889           Mod4-d) wmiir xwrite /tag/sel/ctl colmode sel -stack-max;;
    890           Mod4-s) wmiir xwrite /tag/sel/ctl colmode sel stack-max;;
    891           Mod4-m) wmiir xwrite /tag/sel/ctl colmode sel stack+max;;
    892 
    893           # «Client Command Keys»
    894           Mod4-Shift-c) wmiir xwrite /client/sel/ctl kill;;
    895           Mod4-f) wmiir xwrite /client/sel/ctl fullscreen toggle;;
    896 
    897           # «Command Execution Keys»
    898           Mod4-Return) terminal & ;;
    899           Mod4-p) eval exec wmiir setsid “$(proglist $PATH | wimenu)” &;;
    900           Mod4-a) {
    901               set -- $(proglist $WMII_CONFPATH | wimenu)
    902               prog=$(PATH=$WMII_CONFPATH which $1); shift
    903               eval exec $prog “$@”
    904           } &;;
    905 
    906           # «Tag Selection Keys»
    907           Mod4-t)
    908             # Prompt the user for a tag
    909             tags=$(wmiir ls /tag | sed ‘s,/,,; /^sel$/d’ | wimenu)
    910             # Write it to the filesystem.
    911             wmiir xwrite /ctl view $tag;;
    912           Mod4-[0-9])
    913             wmiir xwrite /ctl view ${1##*-};;
    914 
    915           # «Tagging Keys»
    916           Mod4-Shift-t)
    917             # Get the selected client's id
    918             c=$(wmiir read /client/sel/ctl | sed 1q)
    919             # Prompt the user for new tags
    920             tags=$(wmiir ls /tag | sed ‘s,/,,; /^sel$/d’ | wimenu)
    921             # Write them to the client
    922             wmiir xwrite /client/$c/tags $tag;;
    923           Mod4-Shift-[0-9])
    924             wmiir xwrite /client/sel/tags ${1##*-};;
    925 
    926           esac;;
    927 
    928       # «Client Menu Events»
    929       ClientMouseDown) # ClientMouseDown ‹Client ID› ‹Button›
    930         [ $2 = 3 ] && clickmenu \
    931           “Delete:wmiir xwrite /client/$1/ctl kill” \
    932           “Kill:wmiir xwrite /client/$1/ctl slay” \
    933           “Fullscreen:wmiir xwrite /client/$1/ctl fullscreen on”
    934 
    935       # «Tag Menu Events»
    936       LeftBarMouseDown) # LeftBarMouseDown ‹Button› ‹Bar Name›
    937         [ $1 = 3 ] && clickmenu \
    938           “Delete:delete_view $2”
    939       esac
    940   done
    941 \end{code}
    942