Explanations

Play, don't show

Tunnel vision

Writing this series is fairly difficult. Not just with cool demos and code, that's actually the easy bit. Picking my topics requires care, and to be quite honest, I've mostly covered a lot of the stuff that can be written about, standalone, without a corresponding history lesson and footnotes by the dozens.

Some stories aren't linear; sometimes an architecture meshes together so well that all of the pieces are required to understand it all, and that makes it difficult to explain any one piece. Sometimes we decide that the future hasn't worked out the way we envisioned it, and so what was initially a beautiful design starts crumbling down over time because our core design assumptions just aren't true.

For the last little while, I've wanted to build up to an article on window managers, and so I write a bunch of accurate JavaScript which implements a window manager, and I write xclock and xlogo and a panel for people to play around with. I talk about the ICCCM, and window reparenting, and grab semantics, but, well, it just never comes out good. It becomes a bunch of tedious minutae, because, honestly, it just kind of is.

And so I switched gears, and talked about the region data structure, for instance. And I was able to write that in a day or two. I want to write more, but it's easy to get discouraged when I have a topic but can't find a way to make it feel good.

I focused too heavily on "window managers", struggling to fit the pieces, that I forgot about my goal for this series; why I want to write this. I want to teach people about the hard problems that exist when building any sort of complex windowing system, about the obscure pitfalls we've encountered, the clever solutions we've designed together, and how we've been able to modernize a protocol that has truly, not withstood the test of time.

Today's article is not about window managers.

Today, we're going to learn about building a dropdown menu. In X11. If that doesn't sound hard to you, please, read on. Together, we'll learn a lot about grabs in the X11 input model, about the one fatal flaw in the X11 subwindow model, and maybe, just maybe, learn a bit about Wayland as well.

Sounds like fun? I sure hope so.

The window manager

As I've mentioned in the past, X11 has a concept of "mechanism, not policy". For anything that seems like it's a user interface problem, or the domain of the desktop environment, X11 doesn't like to make assumptions or enforce a certain way of doing things. It leaves this up to a special client known as a "window manager". We've talked about such a manager client before when we talked about the COMPOSITE extension, which could draw windows with transparency in the middle.

The window manager has a large range of responsibilities to client applications. Most obviously, it serves as a graphical user interface allowing for moving around, resizing, and restacking windows on the desktop. Less obviously, it manages a lot of peculiarities of modern window managers, like constraints to prevent windows from being resized too small or too big, or accidentally being pushed under your taskbar or resting on a monitor that has now been unplugged. And, although we won't get into the details in this article, it serves as an API of sorts for other applications on the system that might want to interact with the desktop.

This API, along with the general protocol that window managers are supposed to follow is described in the Inter-Client Communications Conventions Manual, which goes over this sort of client-to-window-manager communication in extreme detail. We won't be going over too much of the ICCCM today — it's a large, complex topic with its own detailed and rich history. Today, we'll simply be going over the basics of how a simple, non-ICCCM-compliant window manager works.

As a quick refresher, if you want to know when someone presses a button or clicks a key, you can use XSelectInput to "select" for these kinds of events on your window. These applications aren't simply limited to input events, however. If you want to know when a window is mapped, you can select for the MapNotify event. In fact, you don't even have to own the window in question — you can listen to events on "someone else's" window just fine.

When the X11 developers added hooks so people could write window managers, they did so in the form of a special event type you could select with XSelectInput. Namely, they added a way of getting a notification when someone tries to create a new subwindow. They called it SubstructureNotify. Whenever anyone tries to map a new toplevel window, a MapNotify event will be sent to anybody who has SubstructureNotify selected. A variety of these events are sent out whenever someone tries to map, unmap, resize, or reparent windows, allowing interested clients to monitor and perhaps even override behavior.

Given this primitive, I imagine it seems relatively straightforward to write a simple window manager. When getting a MapNotify for a new window, create a frame window, reparent the window into the frame. If you want to make a tiling window manager, for instance, start moving all the frames around.

As one extra convenience, instead of simply being "notified" that new windows have showed up or a window tried to move itself, you can in fact stop such behavior entirely. If you select for the SubstructureRedirect event mask instead, you won't get just MapNotify events, you'll get MapRequest events. Now, whenever a new window tries to map itself, the X server stops, says "whoa, hold on a minute", and redirects the request over to you, and you can decide the fate of the window. You can decide whether to map it, or leave it invisible forever. The power is in your hands.

The X11 developers did leave one tiny bit of wiggle room, though. They thought that perhaps there might perchance be a case where the application does know better, and left an escape hatch, in the form of an attribute called "override-redirect". If a window sets this, it will override the SubstructureRedirect behavior, and not allow the window manager to intercept its events.

Now, while that does mean that your window is free-spirited and independent from the shackles of a window manager, it also means you don't get any of its help, either. You don't get a frame where the user can move or resize your window. The window manager won't even restack you when users click on other windows. Those fancy APIs I talked about earlier? If window managers don't know about your window, because you nicely asked X11 not to tell the window manager about it, then you don't get to use them. The ICCCM dictates that the window manager shouldn't respond to any window it doesn't know about.

But for a window like a dropdown that 1. should always be on top anyway, 2. shouldn't be able to be moved or resized anyway, 3. doesn't need any of those fancy APIs, the override-redirect attribute is exactly what we want. So let's go ahead and set it.

And, as a quick aside, if you are ever unfortunate enough to run into an X11 window manager developer, like me, we'll tell you that they know about your tricks anyway. While we don't get the MapRedirect, we'll certainly get the MapNotify event. In fact, since we're often writing a compositing manager that has to composite all of the windows, we have plenty of code to detect override-redirect windows. We have to be very careful with such "OR windows", since the rulebook says we really aren't supposed to know about them. We make sure never to restack them, never ever to move or resize them, and god forbid try putting a frame around them.

*ahem* sorry about that. Let's go ahead and set override-redirect on our dropdown menu, shall we?