- SCWM: The Scheme Constraints Window Manager
- Introduction
- Background
- Features of SCWM
- Architecture and Implementation
- User Interface for Constraints
- Summary
Architecture and Implementation
The current implementation of SCWM contains roughly 32,500 noncomment, nonblank lines of C code; 800 lines of C++ code; and 25,000 lines of Scheme code. The Guile/Scheme system has about 44,000 lines of C code and 11,500 lines of Scheme code. Finally, the Cassowary constraint-solving toolkit has about 9,500 lines of C++ code in its core, and about 1,400 lines of C++ code in the Guile wrapper.
Basic Philosophy
Our first version of SCWM was a simple derivative of its predecessor, FVWM2, with the ad-hoc configuration language that was replaced by Guile/Scheme. Like FVWM2, SCWM reads a startup file containing all of the commands to initialize the settings of various options. Most FVWM2 commands have reasonably straightforward translations to SCWM sentential expressions. For example, take a look at these FVWM2 configuration lines:
Style "*" ForeColor black Style "*" BackColor grey76 HilightColor white navyblue AddToFunc Raise-and-Stick + "I" Raise + "I" Stick Key s WT CSM Function Raise-and-Stick
The lines are rewritten here for SCWM in Guile/Scheme:
(window-style "*" #:fg "black" #:bg "grey76") (set-highlight-foreground! "white") (set-highlight-background! "navyblue") (define* (raise-and-stick #&optional (win (get-window))) (raise-window win) (stick win)) (bind-key '(window title) "C-S-M-s" raise-and-stick)
This simpler and more regular syntax is convenient for the end user. An even greater advantage of using a real programming language rather than a static configuration language stems from the capability to extend the set of commands (either by writing C or Scheme code) and to combine those new procedures arbitrarily.
Adding a new SCWM primitive can be done easily by writing a new C function that registers itself with the Guile interpreter. For example, we implement the "X-property-get" primitive in C:
SCWM_PROC( X_property_get, "X-property-get", 2, 1, 0, (SCM win, SCM name, SCM consume_p)) /* Get X property NAME of window WIN. */ #define FUNC_NAME s_X_property_get { SCM answer; VALIDARG_WIN_ROOTSYM_OR_NUM_COPY(1,win,w); VALIDARG_STRING_COPY(2,name,aprop); VALIDARG_BOOL_COPY_USE_F(3,consume_p,del); ... XGetWindowProperty(...); ... answer = ...; return answer; } #undef FUNC_NAME }
This primitive, along with the related X-property-set!, opens up a whole range of capabilities for SCWM. For example, we can write a new procedure to report a window's class, which is just the value of its "WM_CLASS" property:
(define*-public (window-class #&optional (win (get-window))) "Return the class of window WIN." (X-property-get win "WM_CLASS"))
Then we can use that window-class procedure interactively by writing the following:
(bind-key 'all "C-S-M-f" (lambda () (let* ((win (window-with-focus)) (class (window-class win))) (if (string=? class "Emacs") (resize-window 500 700 win) (resize-window 400 300 win)))))
When evaluated in SCWM's interpreter, these expressions will make the user's Control+Shift+Meta+f keystroke resize the window to either 500 × 700 pixels, if the currently focused window is an Emacs application window; or 400 × 300 pixels otherwise.
The advantages of SCWM's extensible architecture are even more recognizable in the presence of independently developed Guile extensions, which are accessible to the window manager. Via standard Guile modules, SCWM can read and parse Web pages, download files via ftp, and do regular expression matching, among many more actions. In fact, nearly all of the user-interface elements in SCWM are built using guile-gtk, a Guile wrapper of the GTK+ toolkit.
Binary Modules
Because each user needs only a subset of the full functionality that SCWM provides, it is important that users pay only for the features that they require (in terms of size of the process image). Unlike Emacs Lisp, Guile allows new primitives to be defined by dynamically loadable binary modules. Without this feature, all primitives would need to be contained in the SCWM core, thus complicating the source code and increasing the size of the resulting monolithic system.
The voice-recognition module, which is based on IBM's ViaVoice software, is an excellent example of the benefits of dynamically loaded extensions. Some users do not use that feature—perhaps because the library is not available on their platform, or maybe because they do not have an audio input device. Those users will never have the module's code loaded. In addition, if ViaVoice does not exist at compile time, the voice-recognition module will not even be built.
Implementing the module was also straightforward. After getting a sample program from IBM's ViaVoice voice-recognition engine to work, it required fewer than six hours of development to wrap the core functionality of the engine with a Scheme interface. A grammar describes the various utterances that SCWM understands, and the C code asynchronously invokes a Scheme procedure when a phrase is recognized. Because those action procedures are written in Scheme, the responses to phrases can be easily modified and extended without even restarting SCWM.