You can write a window manager in Haskell,
Java, or any other language that has X bindings,
i.e. a library for communicating with X servers.
I chose C++ for basic_wm, our example
window manager, mainly because the C libraries for X11 are the best documented.
In addition to books such as the
Programming Manual, documentation can be found in the form of widely available
man pages (e.g., try
XOpenDisplay at a terminal). Example usage and common patterns abound in the
source code of many great window managers written in the past three decades.
We will use C++11 and C++14 features where convenient, so you will need a
compatible compiler (GCC 4.9 or higher, or Clang 3.4 or higher) if you want to
play with the example source code.
A Tale of Two X Libraries
There are two official C libraries for X:
XCB. Xlib, hailing from 1985, was the original
X client library, and was the only official X client library until the
introduction of XCB in 2001. The two libraries have very different philosophies:
whereas Xlib tries to hide the X protocol behind a friendly C API with lots of
bells and whistles, XCB directly exposes the plumbing beneath.
In practice, this different manifests itself most prominently in how the two
libraries handle the fundamental asynchronous nature of X’s client-server
architecture. Xlib attempts to hide the asynchronous X protocol behind a mixed
synchronous and asynchronous API, whereas XCB exposes a fully asynchronous API.
For example, to lookup the attributes (e.g., size and position) of a window, you
would write the following code using Xlib:
XGetWindowAttributes(display, window, &attrs);
// Do stuff.
Under the hood,
sends a request to the X server and blocks until it receives a response; in
other words, it is synchronous. On the other hand, using XCB, you would write
xcb_get_window_attributes_cookie_t cookie =
// Do other stuff while waiting for reply.
xcb_get_window_attributes_reply_t* reply =
connection, cookie, nullptr);
// Do stuff.
xcb_get_window_attributes merely sends the request to the X
server, and returns immediately without waiting for the reply; in other words,
it is asynchronous. The client program must subsequently call
xcb_get_window_attributes_reply to block on the response.
The advantage of the asynchronous approach is obvious if we consider an example
where we need to retrieve the attributes of, say, 5 windows at once. Using XCB,
we can immediately fire off all 5 requests to the X server, and then wait for
all of them to return. With Xlib, we have send one request at a time and wait
for its response to come back before we can send the next request. Therefore,
we’d expect to only block for the duration of one round-trip to the X server
using XCB, compared to 5 with Xlib.
The downside of XCB’s fully asynchronous approach is verbosity and a less
programmer-friendly interface. The Xlib code above looks like your average C
library call; the XCB code above is significantly more involved.
However, it is important to note that Xlib isn’t fully synchronous. Rather,
Xlib has a mixture of synchronous and asynchronous APIs. In general, functions
that do not return values (e.g.,
which changes the size of a window) are asynchronous, while functions that
return values (e.g.,
which return the size and position of a window) are synchronous:
Xlib saves up requests instead of sending them to the server immediately, so
that the client program can continue running instead of waiting to gain access
to the network after every Xlib call. This is possible because most Xlib calls
do not require immediate action by the server. This grouping of requests by the
client before sending them over the network also increases the performance of
most networks, because it makes the network transactions longer and less
numerous, reducing the total overhead involved.
Xlib sends the buffer full of requests to the server under three conditions.
The most common is when an application calls an Xlib routine to wait for an
event but no matching event is currently available on Xlib’s queue. Since, in
this case, the application must wait for an appropriate event anyway, it makes
sense to flush the request buffer.
Second, Xlib calls that get information from the server require a reply before
the program can continue, and therefore, the request buffer is sent and all the
requests acted on before the information is returned.
Third, the client would like to be able to flush the request buffer manually in
situations where no user events and no calls to query the server are expected.
One good example of this third case is an animated game, where the display
changes even when there is no user input.
— Xlib Programming Manual §2.1.2
This is the most confusing aspect of Xlib, and a source of endless frustration
for those new to X programming. One of the major motivations for the creation of
XCB was to eliminate this complexity.
Many popular window managers have already been ported to XCB from Xlib for the
performance benefits. If you are interested, you can read up on how the
window managers were ported to XCB.
I chose to use Xlib for basic_wm, however,
because as a pedagogical example, readability and simplicity is much more
important than performance. In fact, I would recommend starting with Xlib first
for any project and worry about porting to XCB later, as Xlib is much easier to
learn and prototype with.
While an in-depth discussion of the merits of Xlib and XCB is beyond the scope
of this discussion, I do recommend you check out
the official article on Xlib vs. XCB
as it presents a fascinating case study of API design.
Dependencies and Building
Firstly, you will need Xlib development headers in order to compile against
Xlib. They are available on Debian/Ubuntu as
libx11-dev, on Fedora as
libX11-devel, and on Arch Linux as part of
The only additional library used by the example
basic_wm code is
google-glog, Google’s open source C++
logging library. It is available on Debian/Ubuntu as
glog-devel, and on Arch Linux as
The recommended way to build the source code is with
GNU Make: just
make in the source directory. Alternatively,
g++ *.cpp will also do the
trick if you supply all the libraries correctly.
To test the window manager, you will likely need
Xephyr along with a couple of
simple X programs such as