Spook is an X11 Window Manager, focusing on modularity, and clean, well designed, object-oriented code. It is written in C++.
Spook is licensed under an unrestrictive X/MIT license. Basically, you just have to retain the license text.
The idea design- and implementation-wise is basically to take the time to think things through, instead of tossing together what works and saying I'll clean it up later. Also, I think it's important to avoid monoliths -- monolithic code blocks, monolithic files, monolithic libraries and applications. In fact, probably most of my time spent on Spook has been outside of Spook itself. :)
There will be no prejudice against keyboard or mouse, and for the most part, the behavior of the window manager with regard to input will be completely defined outside of the core of the window manager.
Callbacks are used extensively. You can put your own callbacks into the main loop itself. You can put callbacks into the X event handler for arbitrary X event types. The X event handler itself is simply a callback registered with the main loop. This stuff works right now. And of course there are a ton of ideas in my head of where to go.
Modules will be used, somewhat similar to gaim's plugins (or those of many other apps). Basically, compiled dynamic objects which the window manager will load and then communicate with. It is through this interface that you can register your callbacks and do whatever else you want. The window manager itself will have no "configuration files" or other such end-user-ish things; they will be implemented as modules. There could be a module which reads a text configuration file and registers key and mouse bindings based on what it parses. There could be modules which wrap scripting language interpreters so that you could script things in your favorite scripting language, just as you can do in Kahakai (python & ruby) and Sawfish (lisp). This is still being hashed out -- it works, but I still want to do some more reworking.
Me, Nick Welch, aka mackstann. A computer nerd in Iowa, US.
Spook is yet another spinoff of aewm, and Decklin Foster wrote that.
WIR! Basically - you definitely don't want to use it right now.
The thing runs and works with few crashes/etc., but it's not even worthy of calling alpha. Slow and steady is the key... I plan on this being a long-term project.
You can browse viewarch here, though it is often out of sync with my local copy. The arch archive itself is here (not always online).
I stripped out all of the Xlib drawing stuff that aewm had, and have not replaced it with anything, so right now you just get a random-colored titlebar with no text or buttons. :P Left mouse button moves the window, and right mouse button resizes it. Eventually I'll implement decor.
One thing I want to absolutely avoid in Spook is global variables, and the general habit of "grabbing things out of the sky," as I call it. It just feels wrong to me. I feel that it can make encapsulation a lot more difficult. Something I've noticed a lot in C++ (window managers specifically) is the usage of singletons as syntactically-sugared up global variables. If a global variable is an F, then this is probably a D, IMO. I realize that singletons have their uses; I think they're possibly overused.
The problem with X event handling comes in when you consider that you essentially have to handle X errors, and they're called via a function pointer that you pass to xlib. This means that you can't really access the running program data from inside of the error handler, unless you have some global variables, singletons, or functions in the sky to use to grab onto the rest of the running program.
So after quite a bit of thinking (I may be a bit slow), and after seeing
Metacity's error handling scheme, I realized that it was pretty simple, and
allowed me to handle X errors without resorting to global variables, and in
fact, presented a solution somewhat similar to the try/catch
pattern for exception handling used in many languages. How it works is simple:
when you expect an X error may occur, you instantiate an
xerror_catch
. You then do whatever it is you think might cause an
error, and afterwards, you check your xerror_catch
instance and
see if it caught anything during that time. You can then retrieve the error
and do whatever is appropriate with it. You can nest them, and you can also
use a traditional X error handling function on the "outside" to catch
unexpected X errors.
A somewhat similar problem is handling of unix signals. Normally you do this
by registering handlers for whichever signal(s) you want to catch, and then
they're called, with no access to anything but the signal number, and any
global data you have. Well, if we want no global data, then that's not very
helpful. Signal handlers also have significant restrictions on what they can
do. You can only call a small number of C standard library functions
(re-entrancy problems), and you are only "supposed" to modify
sig_atomic_t
s. You have no idea when your signal handler will be
called, so to be safe, you have to resort to things like blocking signals at
various parts of your code. A lot of people just ignore those restrictions
(it'll usually compile and run fine), which can cause extremely sporadic and
hard to detect bugs.
The solution is also similar to xerror_catch
, but a little
different. Basically you set up a super simple signal handler that just
records the fact that that signal was received, and then you poll that
indicator at regular intervals, and if it is set, you take appropriate
action and then clear it. I don't want to explain it too much, because it
could change, so see the source. (libspook/signal_check.{cc,hh})
Ok, as often happens, I found out that my "solution" was flawed. My new "perfect" (the old solution was perfect, until I found out it wasn't, so now this one is perfect ;) solution is to block signals with sigprocmask, and then check them with sigwaitinfo/sigtimedwait. I split this off into its own library called sigh. And of course, nothing is ever final.
At first I thought that using boost::python was the best idea for making Spook extensible, but I soon realized that basically any scripting interface is a total pain in the ass to deal with. After a lot of pondering, I settled on callbacks and modules, right in C++.
Modules are just dynamically loaded objects, like linux kernel modules, or the modules that Gaim, XMMS, The Golem WM, and many other apps use. They will be implemented using Glib::Module, which is a convenience wrapper around dlopen(), dlsym(), etc.
Callbacks are implemented using the boost signal/bind/function libraries. Essentially, the whole inner core of the window manager will just be a bunch of signals (events) that you connect slots (callback functions) to. This can be a platform not only for the default window manager behavior, but for (almost) any behavior a person would want to implement.
For any non-trivial app, I really think a good logging system is a good
idea. I initially started using log4cplus, but I soon found it to be
extremely hard to work with. I really have a hard time articulating why I
didn't like it; it just felt..obtuse. It seemed to be the only decent C++
logging library around, so I just decided to write my own (boo! hiss!).
It's pretty simple. It has various log levels, and you register ostreams
with it. Each ostream has a log level associated with it, so you could
have your log file only log warnings, but get debug logging on
stderr
. There are three formatter callbacks (one for a
prefix, one for formatting the actual log message, and one for a suffix),
which can be pointed at any function so that you can override them with
your own formatting. And that's about it. Simple and easy to wrap your
brain around, yet decently flexible.
I think this is still a while off. The main problem I am having with my mental assessment of it, is having flexible decor layouts. I guess I'd lay it out similar to Metacity, but I want the label and buttons to be totally flexible. They should be able to go anywhere on the decor. As for how to do this in a sane way, I'm not yet sure. Themes would undoubtedly have to be written in C++ (hah! suck on that, englightenment), or more likely, theme engines would be written in C++. It could go either way. A theme would just be another module, that can execute whatever code it wants. If someone wants to make a theme engine, they could just have their theme module have some sort of parser that reads a theme file and acts accordingly. The interaction with cairo (or whatever other drawing system, if I change my mind) is, well, something I'm not too sure about yet.
Update: And the answer is... don't worry about it. This stuff (drawing) doesn't need to be in the core anyways. But the other part of the decor problem is how to organize the different areas of the decor, i.e. buttons, labels, handles, grips, etc. I think I'll basically give each decor object a container which contains sub-windows, each representing a thing like a button, label, etc. Each would have no specific meaning to the window manager; only to your module (or others that are aware of it). You'd create, say, a button decor piece, make it a certain size, set it to be at certain coordinates within the decor, give it drawing logic, and then register it with the core. You can then write modules that implement decor with different shapes, different drawing libraries, or whatever. The only restrictions within Spook will be that you can't nest decor pieces (I don't think that would be very useful, and it would be much much hairier), and, well.. that's it, I think. Oh, and geometry. There needs to be some way to tell Spook to put N pixels of padding on each of the 4 sides. And I'm not sure about shape extension support (for non-rectangular decor), but I'll probably do it eventually. I also suppose the client window should be at the bottom of the internal stacking of the whole frame window, so that decor can have little parts that hang over it if they want, for gee-whiz effects.