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