source: project/wiki/wmiirc @ 8653

Last change on this file since 8653 was 8653, checked in by sjamaan, 12 years ago

Untabify (wiki doesn't seem to see that as whitespace)

File size: 22.1 KB
Line 
1[[tags: egg]]
2
3== wmiirc
4
5[[toc:]]
6
7=== Description
8
9A library for writing configuration scripts for [[http://wmii.suckless.org|wmii, window manager improved 2]].
10
11See also the [[wmiirc snippets]] page for useful code snippets you can
12use in your own wmiirc scripts.
13
14=== Author
15
16[[Peter Bex]]
17
18=== Requirements
19
20Requires the [[http://www.call-with-current-continuation.org/eggs/9p|9p]] egg.
21
22Works with wmii 3.6 (and possibly 3.5) only.
23
24=== Download
25
26[[http://www.call-with-current-continuation.org/eggs/wmiirc.egg|wmiirc.egg]]
27
28=== Documentation
29
30[[http://wmii.suckless.org|wmii]] is a minimalist windowmanager for
31the X window system that, instead of implementing a lot of policy in
32the WM, allows you to control it completely through a virtual
33filesystem it exports over the [[9p]] protocol. Because of this, you
34can script it using any language or tool that can speak this protocol
35and define (almost) any behaviour you want.
36
37This egg is an attempt to make an abstraction that lets you write
38scripts that can control wmii without having to study the structure of
39the 9p filesystem that wmii exports.  Instead, you can do everything
40by calling procedures from this egg.  Of course, if you know the
41filesystem structure you can still choose to access the filesystem
42directly if you need to do something extreme.
43
44==== Concepts
45
46Because wmii works slightly different than most other window managers,
47it is useful to start by exploring its fundamental concepts.
48
49First off, wmii is a so-called ''dynamic'' window manager.  This means
50it manages the windows for you, so you don't have to bother about
51placing them on your screen.  In default mode, it will divide the
52screen up in equal parts and tile the windows so they are not
53obscured.  However, it also has a ''floated'' mode, which is the only
54mode classic window managers support.  This will simply place the
55windows somewhere on the screen such that they can overlap.  You will
56have to drag them around and/or resize them manually (or write a smart
57script that can position them sanely).
58
59===== Tags and views
60
61wmii manages windows (or ''clients'' in X speak) by ''tagging'' them.
62Any window can have several tags, but it always displays the contents
63of only one tag on the screen.  This is called a ''view'' on that tag.
64This feature provides a superset of the functionality that other
65window managers offer with ''virtual desktops''.  Because windows can
66have multiple tags, it is possible for one window to show up in
67multiple views, effectively causing it to be "sticky" for only a
68selected number of views.
69
70===== Modes
71
72As explained above, wmii supports dynamic management as well as
73floated window management.  Floating windows all live in a special tag
74sometimes identified by the name '~' (tilde).  In dynamic mode there
75are three "submodes".  In 'default' mode the windows are tiled, or
76divided equally across the screen.  When a new window is created, the
77other windows are made smaller to accommodate for the new one, which
78is given an equal amount of space as all the other windows, and the
79window is placed such that it does not obscure any of the other
80windows.  In 'stacked' mode, all windows are placed behind eachother
81in a staircased fashion so '''only''' their titlebars show.  The
82window that has focus is placed in front of all the others, so it
83takes up almost all of your screen.  Clicking on any titlebar sends
84that window to the front, and restacks such that all other windows'
85titlebars are still visible.  In 'maximized' mode you only see one
86window with its titlebar.
87
88===== States
89
90Individual windows can also have different states.  When a window is
91in the 'fullscreen' state, '''all''' window decoration and other stuff
92is hidden so your entire screen is used by one window of one application.
93When a window has the 'urgent' state, it has its "urgent hint" set, which
94is a hint to the window manager that this window's state demands immediate
95attention from the user (for example, in an IM client a private message
96may have arrived).  Wmii can take action when this state is set and it can
97distinguish between client-requested urgency and manager-requested urgency.
98
99===== Columns
100
101The screen is further divided up into columns.  When you first start wmii,
102the screen has only one column so you don't see it.  However, when you send
103a window to the right or the left, it will create a new column it will live
104in from then on.  The first column will be resized so the columns can fit
105next to eachother.  Wmii allows you to define rules what the sizes of these
106columns will be.  Each column has its own mode, so you can have one column
107maximized or stacked while the column next to it is in tiled mode.
108
109===== Bars and tabs
110
111The bottom of the screen contains a bar, which is actually ''two'' bars: one
112on the left and one on the right (''lbar'' and ''rbar'').  These bars consist
113of ''tabs''.  Tabs can contain text in a given color and are clickable.
114Their name does not have to match their text contents, but it is advisable
115to keep these matched for your own sanity.  In the default config, the left
116tab shows a list of available tags/views, with the current one highlighted.
117When you click a tab, a view on the corresponding tag is shown.  By default
118the right bar shows a status that is continuously updated with the current
119time and system load.
120
121===== Events
122
123Whenever anything happens in wmii, an event is fired which your wmiirc
124can catch and act upon.  Events are fired when a key is pressed (but
125only when it is grabbed by the WM, otherwise it goes to applications),
126when a window is (de)selected, when a tag is (de)selected, when a tag
127is created/destroyed etc.  The exact events are listed with the
128{{wmii:event-handlers}} procedure.
129
130===== Special names and limitations
131
132wmii reserves a couple of names and syntaxes, which means you can't
133use arbitrary names for tags, tabs and some other things.  It uses the
134plus symbol (+) as a separator for tag names.  This cannot be escaped
135in any way, which means the + can simply not be used inside names.
136The special name ''sel'' is reserved for the currently selected
137tag/view/client etc. This has an alias as the exclamation mark, but
138its use is deprecated.  Contents of tabs can be prefixed with three
1396-digit hexadecimal numbers which are prefixed with a pound sign (#).
140If they are, this indicates the colors of that tab.  This egg tries to
141take care of doing the formatting of this so you can simply pass
142integer values as colorcodes instead.  Spaces are not allowed in
143several names either.  It's probably safest to avoid all these special
144characters and names altogether.
145
146==== Initialization
147
148To get a connection to the server, call the following:
149
150  procedure: (wmii:connect [inport outport])
151
152If you provide an inport and outport, it will connect to a 9p server
153on those input/output ports.  If you do not provide them, it will try
154to connect to the wmii 9p server on the default location, namely a unix
155domain socket named {{/tmp/ns.USERNAME.DISPLAY/wmii}}, where USERNAME
156is your Unix username and DISPLAY is the current X display string.
157
158'''important''' wmiirc does ''not'' support multiple threads calling
159wmiirc procedures.  It also does not support multiple processes doing
160this, so if you fork you'll need to call (wmii:connect) first in the
161child process (or the parent process) to create a new connection before
162doing anything else.
163
164After initializing, you can set up column rules, tag rules and event
165handlers.  After having done this, you can enter the event loop using
166
167  procedure: (wmii:event-loop [kill-others])
168
169This procedure will only return when another wmiirc wants to take over
170from the current one.  The {{kill-others}} parameter tells wmiirc to
171tell other wmiirc scripts (whether written using this egg or not) to
172quit.  It defaults to {{#t}}.
173
174==== Column rules
175
176You can define the percentage of the screen each column should take up
177by setting up column rules.  This can be done per view, using a simple
178alist and the following procedure:
179
180  procedure: (wmii:colrules-set! alist)
181
182The keys of the alist are strings that are interpreted by wmii as a
183regular expression of view names on which the column rules should
184apply.  The values of the alist are integers or lists of integers
185which represent percentages of the screen width the columns can take.
186The percentages apply to the columns in the same order, from left to
187right.
188
189Example:
190  (wmii:colrules-set! '(("www" . (20 80))
191                        ("email" . (10 20 70))
192                        ("graphics" . 100) ;; can also be (100)
193                        (".*" . (30 50 20))))
194
195You can request the current column rules with the following procedure:
196
197  procedure: (wmii:colrules)
198
199This returns an alist of the same type as accepted by wmii:colrules-set!.
200
201==== Tag rules
202
203Tag rules work much like column rules, except they specify which
204tags a client should get when it's first created.
205
206  procedure: (wmii:tagrules-set! alist)
207
208The alist here takes again strings as keys which are interpreted as
209regexes by wmii.  The values are either strings or lists of strings
210which are taken to be tagnames. Example:
211
212(wmii:tagrules-set! `(("XMMS.*" . ("music" "~"))
213                      ("display.*" . ("graphics" "~"))
214                      ("Gimp.*" . ("graphics" "~"))
215                      ("xjump.*" . ("games" "~"))
216                      ("MPlayer.*" . "~")
217                      ("VICE.*" . ("games" "~"))
218                      (".*" . "sel") ;; Default to current tag
219                      (".*" . "1"))) ;; If no tag exists yet, start with '1'
220
221Just like colrules, tagrules can be listed:
222
223  procedure: (wmii:tagrules)
224
225Returns an alist that looks just like the one from {{wmii:tagrules-set!}}.
226
227==== Event handlers
228
229The core of wmiirc script writing is in the event handlers.  You can register
230those with this procedure:
231
232  procedure: (wmii:event-handlers-set! alist [grab-keys])
233
234The alist has keys that are either simply symbols that name the event
235or lists that provide a full match for the incoming event.  The first
236rule that matches an incoming event is used, the others are ignored.
237The values of the alist are lambdas which handle the event.  The
238{{grab-keys}} parameter defaults to {{#t}} and indicates if key
239handlers should be filtered from the event handlers and the keys used
240by them should be grabbed.  If a key is not grabbed, it will never be
241visible by the wmiirc script; it will be passed to the client directly
242without being passed to the script by wmii.
243
244The events that you can expect are as follows:
245
246; {{(create-tag TAGNAME)}} : The tag with the name {{TAGNAME}} has just
247been created.
248
249; {{(destroy-tag TAGNAME)}} : The tag with name {{TAGNAME}} has just been
250destroyed.
251
252; {{(focus-tag TAGNAME)}} : The focus was changed to the tag with name {{TAGNAME}}
253
254; {{(unfocus-tag TAGNAME)}} : The tag with name {{TAGNAME}} which had focus
255is now not in focus anymore.
256
257; {{urgent CLIENT CLIENT-REQUEST?}} : The client with identifier {{CLIENT}}
258just got the ''urgency hint'' set. {{CLIENT-REQUEST?}} is #{{t}} if the
259client request it, {{#f}} if the window manager did.
260
261; {{not-urgent CLIENT CLIENT-REQUEST?}} : The client with identifier {{CLIENT}}
262just got the ''urgency hint'' removed. {{CLIENT-REQUEST?}} is #{{t}} if the
263client removed it, {{#f}} if the window manager did.
264
265; {{urgent-tag TAGNAME CLIENT-REQUEST?}} : The tag with name {{TAGNAME}} has
266a client which just got the ''urgency hint'' set.  {{CLIENT-REQUEST}} is
267{{#t}} if the client requested it, {{#f}} if the urgency hint was set by the
268window manager itself.
269
270; {{not-urgent-tag TAGNAME CLIENT-REQUEST?}} : The tag with name {{TAGNAME}}
271has a client which just lost the ''urgency hint''.  {{CLIENT}} is {{#t}} if
272the client removed it, {{#f}} if the urgency hint was removed by the window
273manager itself.
274
275; {{left-bar-click BUTTON TAB}} : The user clicked on the left bar.
276{{BUTTON}} is an integer which indicates the mouse button the user pressed
277(1 = left, 2 = middle, 3 = right).  {{TAB}} is the name of the tab on
278which was clicked.
279
280; {{right-bar-click BUTTON TAB}} : The user clicked on the right bar.
281{{BUTTON}} is an integer which indicates the mouse button the user pressed
282(1 = left, 2 = middle, 3 = right).  {{TAB}} is the name of the tab on
283which was clicked.
284
285; {{client-mouse-down CLIENT BUTTON}} : The user pressed a mouse button
286while his mouse cursor was on the titlebar of a window. {{CLIENT}}
287indicates the client on which was clicked, {{BUTTON}} is an integer which
288indicates the mouse button the user pressed (1 = left, 2 = middle, 3 = right).
289
290; {{client-mouse-click CLIENT BUTTON}} : The user completed a mouse click
291on the titlebar of a window (he did not leave the titlebar before releasing
292the button).  {{CLIENT}} indicates the client on which was clicked,
293{{BUTTON}} is an integer which indicates the mouse button the user pressed
294(1 = left, 2 = middle, 3 = right).
295
296; {{(key keys ...)}} : A key was pressed.  The exact key pressed is encoded
297as several strings with key names.  Usually it is easiest to handle
298the keys with rest arg notation to capture them in a list.
299
300See the [[#Example|example section]] for a good example of how to use
301these events.
302
303.... MORE TO COME SOON ....
304
305=== Example
306
307This example simply translates the default wmiirc script that's shipped
308with wmii from shell to Chicken:
309
310<enscript highlight=scheme>
311#!/usr/pkg/bin/csi -s
312
313(use wmiirc srfi-18)
314
315(wmii:connect)
316
317(define modkey "Mod2")
318
319(define directions
320  `((up . "k")
321    (down . "j")
322    (left . "h")
323    (right . "l")))
324
325(define wmii-normcolors  '(#x888888 #x222222 #x333333))
326(define wmii-focuscolors '(#xffffff #x285577 #x4c7899))
327
328(define wmii-background #x333333)
329(define wmii-font "-*-fixed-medium-r-*-*-13-*-*-*-*-*-*-*")
330
331(define (wmii9menu options . rest)
332  (let-optionals rest ((default #f))
333    (receive (in out pid)
334             (process
335              "wmii9menu"
336              `(,@(if default (list "-initial" default) '())
337                "-sf" ,(wmii:color->string (first wmii-focuscolors))
338                "-sb" ,(wmii:color->string (second wmii-focuscolors))
339                "-nf" ,(wmii:color->string (first wmii-normcolors))
340                "-nb" ,(wmii:color->string (second wmii-normcolors))
341                "-font" ,wmii-font
342                ,@options))
343             (close-output-port out)
344             (let ((chosen (read-line in)))
345               (close-input-port in)
346               (and (string? chosen) chosen)))))
347
348(define (dmenu options . rest)
349  (receive (in out pid)
350           (process
351            "dmenu"
352            `("-b"
353              "-sf" ,(wmii:color->string (first wmii-focuscolors))
354              "-sb" ,(wmii:color->string (second wmii-focuscolors))
355              "-nf" ,(wmii:color->string (first wmii-normcolors))
356              "-nb" ,(wmii:color->string (second wmii-normcolors))
357              "-fn" ,wmii-font))
358           (display (string-join options "\n") out)
359           (close-output-port out)
360           (let ((chosen (read-line in)))
361             (close-input-port in)
362             (and (string? chosen) chosen))))
363
364(define client-menu
365 (let ((last-option "nonexistingoption"))
366   (lambda (client)
367     (let ((option (wmii9menu '("Nop" "Delete" "Fullscreen") last-option)))
368       (when option
369         (cond
370          ((string=? option "Delete") (wmii:kill client))
371          ((string=? option "Fullscreen") (wmii:change-state "Fullscreen" #t client)))
372         (set! last-option option))))))
373
374(define wmii-term "xterm")
375
376(wmii:colrules-set! `((".*" . (58 42))))
377
378(wmii:tagrules-set! `(("XMMS.*" . "~")
379                      ("MPlayer.*" . "~")
380                      (".*" . "sel")
381                      (".*" . "1")))
382
383;; We need to do this in order to avoid getting lots of zombie processes
384(define (run . args) (process-wait (process-fork (lambda () (apply process-run args)))))
385
386(define status
387  (let ((status-pid #f))
388    (lambda ()
389      (and status-pid (process-signal status-pid))
390      (set! status-pid
391            (process-fork
392             (lambda ()
393               (wmii:connect)
394               (let loop ()
395                 (wmii:write-tab "rbar" "status"
396                                 (with-input-from-pipe "echo -n $(uptime | sed 's/.*://; s/,//g') '|' $(date)" read-string))
397                 (sleep 1)
398                 (loop))))))))
399
400(wmii:event-handlers-set!
401 `((create-tag
402    . ,(lambda (event tag) (wmii:write-tab "lbar" tag tag wmii-normcolors)))
403   (destroy-tag
404    . ,(lambda (event tag) (wmii:destroy-tab "lbar" tag)))
405   (focus-tag
406    . ,(lambda (event tag)
407         (if (member tag (wmii:tabs "lbar"))
408             (wmii:write-tab "lbar" tag tag wmii-focuscolors))))
409   (unfocus-tag
410    . ,(lambda (event tag)
411         (if (member tag (wmii:tabs "lbar"))
412             (wmii:write-tab "lbar" tag tag wmii-normcolors))))
413   (urgent-tag
414    . ,(lambda (event tag client?) (wmii:write-tab "lbar" tag (string-append "*" tag))))
415   (not-urgent-tag
416    . ,(lambda (event tag client?) (wmii:write-tab "lbar" tag tag)))
417   (left-bar-click
418    . ,(lambda (event button tab) (wmii:goto-tag tab)))
419   (client-mouse-down
420    . ,(lambda (event client button)
421         (case button
422           ((3) (client-menu client)))))
423   ((key ,modkey "Control" "t")
424    . ,(let ((prev #f))
425         (lambda _
426           (let ((keys (wmii:grabbed-keys)))
427             (if prev
428                 (begin (wmii:grabbed-keys-set! prev)
429                        (set! prev #f))
430                 (begin (set! prev keys)
431                        (wmii:grabbed-keys-set! `((,modkey "Control" "t")))))))))
432   ((key ,modkey "space")
433    . ,(lambda _ (wmii:navigate-to "toggle")))
434   ((key ,modkey "d")
435    . ,(lambda _ (wmii:tag-settings-set! '(("colmode" . ("sel" "default"))))))
436   ((key ,modkey "s")
437    . ,(lambda _ (wmii:tag-settings-set! '(("colmode" . ("sel" "stack"))))))
438   ((key ,modkey "m")
439    . ,(lambda _ (wmii:tag-settings-set! '(("colmode" . ("sel" "max"))))))
440   ((key ,modkey "a")
441    . ,(lambda _
442         (let ((action (dmenu (append
443                               `("rehash" "exec" "status" "quit")
444                               (proglist (string-split (getenv "WMII_CONFPATH") ":"))))))
445           (when action
446             (cond
447              ((string=? action "rehash") (update-programs))
448              ((string-prefix? "exec " action)
449               (wmii:exec (string-drop action 5)))
450              ((string=? action "status") (status))
451              ((string=? action "quit")
452               (wmii:quit)
453               (exit))
454              (else (run (sprintf "env PATH=${WMII_CONFPATH}:${PATH} ~A" action))))))))
455   ((key ,modkey "p")
456    . ,(lambda _
457         (and-let* ((program (dmenu programs))) (run program))))
458   ((key ,modkey "t")
459    . ,(lambda _
460         (and-let* ((tag (dmenu (wmii:tags)))) (wmii:goto-tag tag))))
461   ((key ,modkey "Return")
462    . ,(lambda _ (run wmii-term)))
463   ((key ,modkey "Shift" "space")
464    . ,(lambda _ (wmii:send-to "toggle")))
465   ((key ,modkey "f")
466    . ,(lambda _ (wmii:change-state "Fullscreen" 'toggle)))
467   ((key ,modkey "Shift" "c")
468    . ,(lambda _ (wmii:kill)))
469   ((key ,modkey "Shift" "t")
470    . ,(lambda _
471         (and-let* ((tag (dmenu (wmii:tags)))) (wmii:client-tags-set! (list tag)))))
472   ,@(map (lambda (x)
473            `((key ,modkey ,(cdr x))
474              . ,(lambda _ (wmii:navigate-to (->string (car x))))))
475          directions)
476   ,@(map (lambda (x)
477            `((key ,modkey "Shift" ,(cdr x))
478              . ,(lambda _ (wmii:send-to (->string (car x))))))
479          directions)   
480   ,@(map (lambda (x)
481            `((key ,modkey ,(->string x))
482              . ,(lambda _ (wmii:goto-tag x))))
483          (iota 10))
484   ,@(map (lambda (x)
485            `((key ,modkey "Shift" ,(->string x))
486              . ,(lambda _ (wmii:client-tags-set! (list (->string x)))))) (iota 10))))
487
488(wmii:global-settings-set!
489 `((font . ,wmii-font)
490   (focuscolors . ,wmii-focuscolors)
491   (normcolors . ,wmii-normcolors)
492   (grabmod . ,modkey)
493   (border . "1")))
494
495(define (proglist path)
496  (sort!
497   (delete-duplicates!
498    (flatten
499     (map (lambda (dir)
500            (if ((conjoin directory? file-execute-access? file-read-access?) dir)
501                (map pathname-strip-directory
502                     (find-files dir (conjoin (complement directory?) file-execute-access?) cons '() 0))
503                '()))
504          path))
505    string=?)
506   string<?))
507
508(define programs '())
509(define (update-programs)
510  (thread-start!
511   (make-thread
512    (lambda () (set! programs (proglist (string-split (getenv "PATH") ":")))))))
513(update-programs)
514
515(let ((curtag (wmii:tag)))
516  (for-each (cut wmii:destroy-tab "lbar" <>) (wmii:tabs "lbar"))
517  (for-each (lambda (t)
518              (if (string=? curtag t)
519                  (wmii:write-tab "lbar" t t wmii-focuscolors)
520                  (wmii:write-tab "lbar" t t wmii-normcolors)))
521            (wmii:tags)))
522
523(run (sprintf "xsetroot -solid '~A'" (wmii:color->string wmii-background)))
524
525(status)
526(wmii:event-loop)
527</enscript>
528
529For some other cool snippets, have a look at the [[wmiirc snippets]] page.
530
531=== Changelog
532
5330.1 Initial release
534
535=== License
536
537  Copyright (c) 2008, Peter Bex
538  All rights reserved.
539 
540  Redistribution and use in source and binary forms, with or without
541  modification, are permitted provided that the following conditions are
542  met:
543 
544  Redistributions of source code must retain the above copyright
545  notice, this list of conditions and the following disclaimer.
546 
547  Redistributions in binary form must reproduce the above copyright
548  notice, this list of conditions and the following disclaimer in the
549  documentation and/or other materials provided with the distribution.
550 
551  Neither the name of the author nor the names of its contributors may
552  be used to endorse or promote products derived from this software
553  without specific prior written permission.
554 
555  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
556  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
557  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
558  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
559  COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
560  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
561  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
562  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
563  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
564  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
565  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
566  OF THE POSSIBILITY OF SUCH DAMAGE.
Note: See TracBrowser for help on using the repository browser.