I've decided to write my own X window manager, despite the fact that X is dying and all that. I'm doing it for fun but also to get more better at scheme (CHICKEN) and C interop, I think... Anyway, I'm going to write about my experiences here. You can view the current state of the project at its git repository.
Hi there, it's been a minute -- since uh, January 2. Oh so just like 2 weeks, that's not so bad. ANYWAY.
I've made a good number of changes in the repo. I did that thing where I moved
~/src/acdwm
to ~/src/acdwm_
and re-typed the whole
thing while referring to the original. I reorganized the project from a
one-file pure translation of tinywm.c
to a more modular project
with schemier wrappers for things (I hope). You can check out the code for this
iteration on git
or download
a tarball.
Now, I have three submodules—xlib
, keys
, and
events
—and a utility module (aptly named util
).
(acdwm xlib)
Eventually, I'd like to have only this library do anything with raw X calls.
Until then, I've re-exported CHICKEN's xlib
library so that other
libraries in acdwm only have to
import (acdw
xlib)
.
Since last time, I've written a macro to define event predicates for all the
types of events xlib
defines. This means I can
write (buttonpress? event)
instead
of (= BUTTONPRESS (xevent‑type event))
, which
I find much easier to deal with. I also figured out the Xevent structure's
... structure, and written a procedure-writing macro to convert the union type
to its constituent parts. I suppose I could write a xevent-subtype
procedure to check the type and just convert the thing to that. Though really I
should wrap all these types in an Xevent record type ... anyway that's for next
time I suppose!
Things I really need to work on as I continue include
- Figuring out when I need to
free
structures and when I can get away with not - Better-wrapping Xevents (maybe with a record type—will I need inheritance?)
- Improving symbol<->constant interface. This is mostly just data entry and some wrapper code
(acdwm keys)
A major addition was made in
my key- and
button-handling code, which makes sense because tinywm's is minimal,
to say the least. I added record types for keybinds and mousebinds, set up code
to collect bindings in a list parameter to be grabbed and decided upon later in
execution (so you just have to define a binding once), and keybind-parsing code
so you can define keys using strings like "C-M-r", which I'm actually fairly
proud of. Now,the M-F1
binding from tinywm can be defined simply
as
(bind "M-<F1>" (lambda (ev dpy)
(let ((win (xevent-xkey-subwindow ev)))
(when (some? win)
(xraisewindow dpy win)))))
which, indeed, it is.
I considered writing a macro for bind
that would let me not specify the lambda bit, going straight into the body... but I realized that I'd need to inject the event and display into the definition, or do something really evil like making the current event a parameter. shudder
(acdwm events)
The main differences here are refactoring handle-events
to dispatch
based on the event
and new
dragging code to allow more customizability. The dragging bit was pretty
complicated because of the amount of state held in the tinywm event loop, and
the apparently multiple ways to actually grab the mouse in Xlib. I finally
figured it out by creating a new drag
record type and creating a
parameter to hold an instance of that type. Honestly, I will proably refactor
that again because having a record type with one instance is ... meh.
(acdw util)
My utility
library is trying to be as minimal as possible, but you know how it
is with Scheme (if you don't, projects tend to accrete utility functions). So
far, the most useful is the define-public
macro, stolen from guile,
and dprint
procedure, which prints debugging stuff when needed.
shoutouts
This project is teaching me a lot but I also am learning from good people. These include
addendum
(not really; I haven't even finished writing the post proper but was getting
distracted by the thought of porting to xcb
, a terrible idea for
now)
I just found the guile-xcb project by Mark Witmer, which features a tinywm port. I like the way this code looks in the wm, so I'm defiitely going to be studying the ideas in this codebase for acdwm.