summaryrefslogtreecommitdiff
path: root/doc/customizing.tex
blob: da1bfc6a5219db549ac8a4e8c2b46c1fd8f0780e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
\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=‘#00000081654f #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 <<!
  Mod4-space
  Mod4-d
  Mod4-s
  Mod4-m
  Mod4-a
  Mod4-p
  Mod4-t
  Mod4-Return
  Mod4-Shift-space
  Mod4-f
  Mod4-Shift-c
  Mod4-Shift-t
  Mod4-h
  Mod4-j
  Mod4-k
  Mod4-l
  Mod4-Shift-h
  Mod4-Shift-j
  Mod4-Shift-k
  Mod4-Shift-l
  !
  for i in 1 2 3 4 5 6 7 8 9 0; do
      echo Mod4-$i
      echo Mod4-Shift-$i
  done
  } | wmiir write /keys
\end{Fragment}

and lay a framework for processing their events:

\begin{Fragment}{Key Events}
  Key) # Key ‹Key Name›
      case $1 in
      «Motion Keys»
      «Client Movement Keys»
      «Column Mode Keys»
      «Client Command Keys»
      «Command Execution Keys»
      «Tag Selection Keys»
      «Tagging Keys»
      esac;;
\end{Fragment}

\section{Click Menus}

Sometimes, you have your hand on the mouse and don't want to
reach for the keyboard. To help cope, \wmii\ provides a
mouse-driven, single-click menu. The default configuration uses
it for client and tag menus.

\begin{Fragment}{Click Menu Initialization}
  clickmenu() {
      if res=$(wmii9menu -- “$@”); then eval “$res”; fi
  }
\end{Fragment}

\section{Control Files}

\label{sec:controlfiles}

Several directories including the root, have control files,
named |ctl|. These files are used to control the object (e.g., a
client or tag) represented by the directory. Each line of the
file, with the possible section of the first, represents a
control variable and its value. In the case of all but the root
|/ctl| file, the first line represents the id of the directory.
In the case of |/tag/foo/ctl|, for instance, the first line
should read |foo|. This is useful when dealing with the special
|sel/| directories. For instance, when |foo| is the selected
tag, the special |/tag/sel| directory is a link to |/tag/foo|,
and the first line of |/tag/sel/ctl| will read |foo|, just as
if you'd accessed |/tag/foo/ctl| directly.

The rest of the lines, the control variables, can be modified by
writing new values to the control file. For instance, if a
client is fullscreen, its control file will contain the line:

\begin{code}
  fullscreen on
\end{code}

\noindent To restore the client from fullscreen, either of the
following lines may be written to its control file:

\begin{code}
  fullscreen off
  fullscreen toggle
\end{code}

When next read, the |fullscreen on| line will have been replaced
with |fullscreen off|.  No care need be taken to preserve the
other contents of the file. They're generated anew each time
it's read.

\section{Clients}

\def\clientlabel{/client/$\langle\mathit{client}\rangle$/}
\index{filesystem!/client/*/@\clientlabel|(}
Clients are represented by directories under the |/client/|
tree. Subdirectory names represent the client's X11 window ID.
The special |sel/| directory represents the currently selected
client. The files in these directories are:

\begin{description}
  \item[ctl] The client's control file, containing the following
    properties:
    \index{filesystem!/client/*/@\clientlabel!ctl}
    \begin{description}
      \item[allow] The set of unusual actions the client is
        allowed to perform, in the same format as the tag set.
        \begin{description}
     \item[activate] The client is allowed to activate
       itself—that is, focus its window and, as the case may
       require, uncollapse it and select a tag it resides on.
       This flag must be set on a client if you wish it able to
       activate itself from the system tray.
        \end{description}
      \item[floating] Defines whether this client is likely to
        float when attached to a new view. May be |on|, |off|,
        |always|, or |never|.  Ordinarilly, the value changes
        automatically whenever the window is moved between the
        floating and managed layers. However, setting a value of
        |always| or |never| overrides this behavior.
      \item[fullscreen] The client's fullscreen state. When
        |on|, the client is displayed fullscreen on all of its
        views. Possible values are |on|, |off|, and |toggle|.
      \item[group] The client's group ID, or |0| if not part of
        a group. Clients tend to open with the same tags and in
        the same columns as the last active member of their
        group. Setting this property is only useful when done
        via the rules file.
      \item[kill] When written, the window is closed politely,
        if possible.
      \item[pid] Read-only value of the PID of the program that
        owns the window, if the value is available and the
        process is on the same machine as wmii.
      \item[slay] When written, the client is disconnected
        peremptorily. If the client's PID is available and the
        process is the same machine as wmii, its parent process
        is killed
      \item[tags] The client's tags. The same as the tags file.
      \item[urgent] The client's urgency state. When |on|, the
        client's layout box will be highlighted. Possible values
        are |on|, |off|, and |toggle|.
    \end{description}
  \item[props] The client's window class (the X11
    |WM_CLASS|\footnote{\ICCCM{4.1.2.5}}
    property) and title string, separated by colons. This file
    is not writable.
    \index{filesystem!/client/*/@\clientlabel!props}
  \item[label] The client's window title. May be written to
    change the client's title.
    \index{filesystem!/client/*/@\clientlabel!label}
  \item[tags]
    \index{filesystem!/client/*/@\clientlabel!tags}
    The client's tags. Tag names are separated by |+|, |-|, or
    |^| signs. Tag names which directly follow a |+| sign are
    added, while whose following a |-| sign are removed and
    those following a |^| are toggled.  If the value written
    begins with one of these characters, the value is appended
    to the clients tags rather than replacing them. 
    
    Tags formatted as |/‹regex›/| are treated as regular
    expressions, which place the client on any extant matching
    tag\footnote{While a client with a regex tag will always
    appear in all matching views, it will not keep those views
    in existence. When the last client explicitly tagged with a
    view is removed, the view is deleted as soon as it becomes
    inactive.}.  Regular expression tags which directly follow a
    minus sign are treated as exclusion expressions. For
    example, the tag string |+/foo/-/food/| will match the tag
    |foobar|, but not the tag |foodstand|.
\end{description}

\index{filesystem!/client/*/@\clientlabel|)}

\subsection{Key Bindings}

To control clients, we'll add the following key bindings:

\begin{Fragment}{Client Command Keys}
  Mod4-Shift-c) wmiir xwrite /client/sel/ctl kill;;
  Mod4-f) wmiir xwrite /client/sel/ctl Fullscreen toggle;;
\end{Fragment}

And to manage their tags, we'll need:

\begin{Fragment}{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##*-};;
\end{Fragment}

\subsection{Click Menus}

\index{events!ClientMouseDown}
\begin{Fragment}{Client Menu Events}
  ClientMouseDown) # ClientMouseDown ‹Client ID› ‹Button›
    [ $2 = 3 ] && clickmenu \
      “Delete:wmiir xwrite /client/$1/ctl kill” \
      “Kill: wmiirxwrite /client/$1/ctl slay” \
      “Fullscreen:wmiir xwrite /client/$1/ctl fullscreen on”
\end{Fragment}

\subsection{Unresponsive Clients}

\index{events!UnresponsiveClient|(}
When \wmii\ tries to close a window, it waits 8 seconds for the
client to respond, and then lets its scripts decide what to do
with it. The stock scripts prompt the user for input:

\begin{Fragment}{Unresponsive Clients}
  UnresponsiveClient) # UnresponsiveClient ‹Client ID›
  {
      # Use wihack to make the xmessage a transient window of
      # the problem client. This will force it to open in the
      # floaing layer of whatever views the client is attached to
      resp=$(wihack -transient $1 \
             xmessage -nearmouse -buttons Kill,Wait -print \
             “The following client is not responding.” \
             “What would you like to do?$(echo)\
             $(wmiir read /client/$1/label))
      [ $resp = Kill ] && wmiir xwrite /client/$1/ctl slay
  } &;;
\end{Fragment}
\index{events!UnresponsiveClient|)}

\section{Views}

\def\taglabel{/tag/$\langle\mathit{tag}\rangle$/}
\index{filesystem!/tag/*/@\taglabel|(}
Views are represented by directories under the |/tag/| tree. The
special |sel/| directory represents the currently selected
client. The |sel| tag is treated similarly elsewhere. The files
in these directories are:

\begin{description}
  \item[ctl]
    The view's control file. The properties are:
    \index{filesystem!/tag/*/@\taglabel!ctl|(}
    \begin{description}
      \item[select ‹Area›] Select the column ‹Area›, where
        ‹Area› is a 1-based column index, or |~| for the floating
        area. It may be optionally preceded by ‹Screen›|:|, where
        ‹Screen› is a 0-based Xinerama screen index, or “sel”. When
        omitted, ‹Screen› defaults to 0, the primary screen.
      \item[select ‹Area› ‹Client Index›] Select the column ‹Area›, and
        the ‹Client Index›th client.
      \item[select client ‹Client ID›] Select the client with the
        X11 window ID ‹Client ID›.
      \item[select ‹Direction›]
        Select the client in ‹Direction› where ‹Direction› may be
        one of ‹up $\wedge$ down $\wedge$ left $\wedge$ right›.
      \item[send client ‹Client ID› ‹Area›] Send ‹Client ID› to
        ‹Area›. ‹Area› may be |sel| for the selected area, and
        |client ‹Client ID›| may be |sel| for the currently selected
        client.
      \item[send client ‹Client ID› ‹Direction›]
        Send ‹Client ID› to a column or position in its column in
        the given direction.
      \item[send client ‹Client ID› toggle] If ‹Client ID› is
        floating, send it to the managed layer. If it's managed,
        send it to the floating layer.
      \item[swap client ‹Client ID› \ldots] The same as the |send|
        commands, but swap ‹Client ID› with the client at the given
        location.
      \item[colmode ‹Area› ‹Mode›] Set ‹Area›'s mode to ‹Mode›,
        where ‹Mode› is a string of values similar to tag
        specifications. Values which may be added and removed are as
        follows for managed areas:

        \begin{description}
          \item[stack] One and only one client in the area is
            uncollapsed at any given time. When a new client is
            selected, it is uncollapsed and the previously selected
            client is collapsed.
          \item[max] Collapsed clients are hidden from view
            entirely. Uncollapsed clients display an indicator
            {\it‹n›/‹m›}, where ‹m› is the number of collapsed
            clients directly above and below the client, plus one,
            and ‹n› is the client's index in the stack.
          \item[default] Like subtracting the stack mode, but all
            clients in the column are given equal height.
        \end{description}

        For the floating area, the values are the same, except that
        in |max| mode, floating clients are hidden when the managed
        layer is selected.
      \item[grow ‹Frame› ‹Direction› {[‹Amount›]}] Grow ‹Frame› in
        the given direction, by ‹Amount›. ‹Amount› may be any
        integer, positive or negative. If suffixed with |px|,
        it specifies an exact pixel amount, otherwise it specifies a
        “reasonable increment”. Defaults to 1.

        ‹Frame› may be one of:
        \begin{itemize}
          \item client ‹Client ID›
          \item ‹Area› ‹Client Index›
        \end{itemize}
      \item[nudge ‹Frame› ‹Direction› {[‹Amount›]}] Like
        |grow|, but move the client in ‹Direction› instead of
        resizing it.
  \end{description}
  \index{filesystem!/tag/*/@\taglabel!ctl|)}
\end{description}

\index{filesystem!/tag/*/@\taglabel|)}

\subsection{Key Bindings}

We'll use the following key bindings to interact with views:

\begin{Fragment}{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;;
\end{Fragment}

\begin{Fragment}{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;;
\end{Fragment}

\begin{Fragment}{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;;
\end{Fragment}

\subsection{Click Menus}

\index{events!LeftBarMouseDown}
\begin{Fragment}{Tag Menu Events}
  LeftBarMouseDown) # LeftBarMouseDown ‹Button› ‹Bar Name›
    [ $1 = 3 ] && clickmenu \
      “Delete:delete_view $2
\end{Fragment}

\section{Command and Program Execution}

Perhaps the most important function we need to provide for is
the execution of programs. Since \wmii\ users tend to use
terminals often, we'll add a direct shortcut to launch one.
Aside from that, we'll add a menu to launch arbitrary programs
(with completions) and a separate menu to launch wmii specific
commands.

We use |wmiir setsid| to launch programs with their own session
IDs to prevent untoward effects when this script dies.

\begin{Fragment}{Command Execution Initialization}
  terminal() { wmiir setsid xterm “$@” }
  proglist() {
      IFS=:
      wmiir proglist $1 | sort | uniq
      unset IFS
  }
\end{Fragment}

\subsection{Key Bindings}
\begin{Fragment}{Command Execution Keys}
  Mod4-Return) terminal & ;;
  Mod4-p) eval exec wmiir setsid “$(proglist $PATH | wimenu)” &;;
  Mod4-a) {
      set -- $(proglist $WMII_CONFPATH | wimenu)
      which=$(which which)
      prog=$(PATH=$WMII_CONFPATH $which $1); shift
      eval exec $prog “$@”
  } &;;
\end{Fragment}

\section{The Root}

The root filesystem contains the following:

\index{!filesystem!/|(}
\begin{description}
  \item[ctl] The control file. The properties are:
    \index{filesystem!/!ctl}
    \begin{description}
      \item[bar on ‹top $\wedge$ bottom›] Controls where the bar
        is shown.
      \item[border] The border width, in pixels, of floating
        clients.
      \item[colmode ‹Mode›] The default column mode for newly
        created columns.
      \item[focuscolors ‹Color Tuple›] The colors of focused
        clients.
      \item[normcolors ‹Color Tuple›] The colors of unfocused
        clients and the default color of bar buttons.
      \item[font ‹Font›] The font used throughout \wmii. If
        prefixed with |xft:|, the Xft font renderer is used, and
        fonts may be antialiased. Xft font names follow the
        fontconfig formula. For instance, 10pt, italic Lucida
        Sans would be specified as

        \begin{code}
          xft:Lucida Sans-10:italic
        \end{code}

        See \man 1 {fc-match}.

      \item[grabmod ‹Modifier Keys›] The key which must be
        pressed to move and resize windows with the mouse
        without clicking hot spots.
      \item[incmode ‹Mode›] Controls how X11 increment hints are
        handled in managed mode. Possible values are:
        \begin{description}
          \item[ignore] Increment hints are ignored entirely.
            Clients are stretched to fill their full allocated
            space.
          \item[show] Gaps are shown around managed client
            windows when their increment hints prevent them from
            filling their entire allocated space.
          \item[squeeze] When increment hints cause gaps to show
            around clients, \wmii\ will try to adjust the sizes
            of the clients in the column to minimize lost space.
        \end{description}
      \item[view ‹Tag›] Change the currently visible view.
      \item[exec ‹Command›] Replaces this \wmii\ instance with
        ‹Command›. ‹Command› is split according to rc quoting
        rules, and no expansion occurs. If the command fails to
        execute, \wmii\ will respawn.
      \item[spawn ‹Command›] Spawns ‹Command› as it would spawn
        |wmiirc| at startup. If ‹Command› is a single argument
        and doesn't begin with |/| or |./|,%
        \hskip 1ex|$WMII_CONF|\-|PATH| is
        searched for the executable. Otherwise, the whole
        argument is passed to the shell for evaluation.
    \end{description}
  \item[keys]  The global keybindings. See section \ref{sec:keybindings}.
               \index{filesystem!/!keys|primary}
  \item[event] The global event feed. See section \ref{sec:keybindings}.
               \index{filesystem!/!event|primary}
  \item[colrules]
    \index{filesystem!/!colrules}
        The |/colrules| file contains a list of
        rules which affect the width of newly created columns.
        Rules have the form:

        \begin{quote}\texttt{
          /‹regex›/ -> ‹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 <<!
  border 2
  focuscolors $focuscolors
  normcolors $normcolors
  font $font
  grabmod Mod4
  !
\end{Fragment}

\subsection{Key Bindings}

And we need a few more key bindings to select our views:

\begin{Fragment}{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 $tags;;
  Mod4-[0-9])
    wmiir xwrite /ctl view ${1##*-};;
\end{Fragment}

\section{Tieing it All Together}

\begin{code}
  #!/bin/sh
  «Click Menu Initialization»
  «Command Execution Initialization»

  «Configuration»

  «Bind Keys»
  «Event Loop»
\end{code}

\section{The End Result}

For clarity, here is the end result:

\begin{code}
  #!/bin/sh
  # «Click Menu Initialization»
  clickmenu() {
      if res=$(wmii9menu -- “$@”); then eval “$res”; fi
  }
  # «Command Execution Initialization»
  terminal() { wmiir setsid xterm “$@” }
  proglist() {
      IFS=:
      wmiir proglist $1 | sort | uniq
      unset IFS
  }

  # «Configuration»
  # «Theme Definitions»
  normcolors=‘#000000 #c1c48b #81654f’
  focuscolors=‘#000000 #81654f #000000’
  background=‘#333333’
  font=‘drift,-*-fixed-*-*-*-*-9-*-*-*-*-*-*-*’

  xsetroot -solid $background
  wmiir write /ctl <<!
  border 2
  focuscolors $focuscolors
  normcolors $normcolors
  font $font
  grabmod Mod4
  !

  # «Bind Keys»
  {
  cat <<!
  Mod4-space
  Mod4-d
  Mod4-s
  Mod4-m
  Mod4-a
  Mod4-p
  Mod4-t
  Mod4-Return
  Mod4-Shift-space
  Mod4-f
  Mod4-Shift-c
  Mod4-Shift-t
  Mod4-h
  Mod4-j
  Mod4-k
  Mod4-l
  Mod4-Shift-h
  Mod4-Shift-j
  Mod4-Shift-k
  Mod4-Shift-l
  !
  for i in 1 2 3 4 5 6 7 8 9 0; do
      echo Mod4-$i
      echo Mod4-Shift-$i
  done
  } | wmiir write /keys

  # «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»
      # «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;;

      # «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;;

      # «Unresponsive Clients»
      UnresponsiveClient) # UnresponsiveClient ‹Client ID›
      {
          # Use wihack to make the xmessage a transient window of
          # the problem client. This will force it to open in the
          # floaing layer of whatever views the client is attached to
          resp=$(wihack -transient $1 \
                 xmessage -nearmouse -buttons Kill,Wait -print \
                 “The following client is not responding.” \
                 “What would you like to do?$(echo)” \
                 $(wmiir read /client/$1/label))
          [ $resp = Kill ] && wmiir xwrite /client/$1/ctl slay
      } &;;

      # «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 = $!;;

      # «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}