[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

First steps towards a window manager

From: Mark Witmer
Subject: First steps towards a window manager
Date: Thu, 31 Jan 2013 15:35:44 -0500
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/24.2 (gnu/linux)

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 or download an archive at 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))
    (if (file-exists? rc-file-location) (load rc-file-location))
      (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 
        (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))
--8<---------------cut here---------------end--------------->8---

The fun part is that you can put a line like

guile --listen=37147 -L /path/to/module/.../ \

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

reply via email to

[Prev in Thread] Current Thread [Next in Thread]