Hi all, I thought I'd follow up on my previous post regarding xlib and a guile window manager.
First of all, I have guile-xlib working with guile-2.0; I'm calling my branch "guile2-xlib." You can use git and pull it from http://github.com/mwitmer/guile2-xlib or download an archive at http://markwitmer.com/dist/guile2-xlib-0.1.tar.gz. I'm still toying with the idea of writing some xcb bindings, time permitting! As far as the window manager goes, you need very little code to get started with something extremely basic. Here are a few lines that let you open some X applications, though it lacks resizing/positioning (guile-xlib doesn't support that... yet). --8<---------------cut here---------------start------------->8--- (define-module (guile-wm wm) #:use-module (xlib xlib)) (define-once wm-display #f) (define-once wm-display-string (or (getenv "DISPLAY") ":0")) (define-once wm-event-hook (make-hook 1)) (define rc-file-location (string-append (passwd:dir (getpw (getuid))) "/.guilewmrc")) (define-public (wm-init!) "Connect to a running X server and begin listening for events" (set! wm-display (x-open-display! wm-display-string)) (let ((wm-root (x-root-window wm-display))) (x-select-input! wm-root (logior ButtonPressMask ExposureMask KeyPressMask)) (wm-event-hook-refresh) (if (file-exists? rc-file-location) (load rc-file-location)) (dynamic-wind (lambda () (x-flush! wm-display)) (lambda () (x-event-loop! wm-display wm-event-hook)) (lambda () (x-close-display! wm-display))))) (define-public (wm-event-hook-refresh) "Refresh the event hook with the hooks listed in wm-event-hooks" (reset-hook! wm-event-hook) (for-each (lambda (hook) (add-hook! wm-event-hook hook)) wm-event-hooks)) (define-public (wm-shell-command command) "Execute COMMAND in a shell" (if (= (primitive-fork) 0) (let ((env (cons (format #f "DISPLAY=~a.~a" wm-display-string (x-screen-number-of-screen (x-screen-of-display wm-display))) (environ)))) (execle "/bin/sh" env "/bin/sh" "-c" command)))) ;; guile-xlib doesn't have support for keysyms yet, so I just use raw ;; keycodes here (define default-key-map `((24 . ,(lambda (event) (x-event-loop-quit! (x-event:button event)))) (26 ,wm-shell-command "emacs") (28 ,wm-shell-command "xterm"))) (define (mapped-key-handler map) "Return a key handler that maps keycodes to commands" (lambda (event) (if (= (x-event:type event) KeyPress) (let ((command (assq-ref map (x-event:keycode event)))) (if command (if (list? command) (apply (car command) (cdr command)) (command event))))))) (define-public wm-event-hooks (list (mapped-key-handler default-key-map))) --8<---------------cut here---------------end--------------->8--- You also need a startup script like this: --8<---------------cut here---------------start------------->8--- (use-modules (guile-wm wm)) (wm-init!) --8<---------------cut here---------------end--------------->8--- The fun part is that you can put a line like guile --listen=37147 -L /path/to/module/.../ \ /path/to/startup/script/wm.scm in your .xinitrc or xsession file, and then you can connect to that listening process from Geiser or something like that and hack the wm while it's running. A lot of this is inspired by Stumpwm, a pretty nifty tiling wm written in common lisp that's similarly configurable. I'll put the wm code on github as well as I get more features added. Feedback and suggestions are welcome! Mark Witmer