ENOSUCHBLOG

Programming, philosophy, pedaling.


Who Needs Toolkits When You Have Xlib?

Mar 14, 2015     Tags: programming    

This post is at least a year old.

Disclaimer: You shouldn’t write large graphical programs for X without a toolkit. This post will go over some of the smaller things that can be done via Xlib with a minimal amount of pain and confusion. For large programs, working directly with Xlib becomes hairy and painful very quickly.

Dating back to 1984, the X Window System venerable member of the UNIX world, predating just about every popular UNIX or UNIX-like system in common use today. Already well established and frozen as version 11 (hence “X11”) well before the initial release of Linux in 1991, X is a relic of an earlier time in computing, complete with a codebase widely panned for doing too much, too little, and failing to do anything well in the process.

A fair deal of this criticism is deserved; X wasn’t designed with personal single-user desktops in mind and, as a result, it has accumulated quite a bit of cruft over the years in the form of various confusing standards that attempt to bolt modern paradigms onto an aging model. Of course there are other things that are pretty neat about X, like its network-agnostic client-server model and the fact that a modern platform can seamlessly switch between X window managers in a matter of seconds per the user’s wishes.

But I digress.

I’m not writing this to discuss X’s pros and cons, as those have been covered in great detail over its 30+ years of existence. Instead, I am here to cover a very specific situation that I (and others, no doubt) often find myself in: making the X server do <thing> in my program without using a massive toolkit or framework.

To that end, I’m going to outline (and provide examples) for the following common graphical operations, all with mostly simple solutions in plain old Xlib-backed C:

Why? Because we shouldn’t need to use large frameworks for simple things (even when they aren’t so simple, thanks to X), and because it’s good to be comfortable with the underlying library that controls your entire graphical experience.

Creating a basic X Window

Before we take a look at the source code, let’s run through what it actually takes to draw a plain, white window with Xlib: In order:

  1. Open a connection to the X server with XOpenDisplay and obtain a Display *.
  2. Get the default screen of the opened display with DefaultScreen.
  3. Actually create the Window structure with a call to XCreateSimpleWindow.
  4. Map the Window to the Display and flush it all for displaying.
  5. Use an event loop or sleep to keep the program alive, and clean up before exiting.

That’s a lot of steps for a pretty simple task, and we haven’t even included proper error checking or any actual content!

Here’s the actual code:

When compiled as follows:

1
$ gcc basic_window.c -o basic_window -lX11

We should get a binary, basic_window, that produces this when executed:

A plain Window.

Impressive, huh?

Constraining an X Window

Our plain window is fine and dandy, but what happens if a user tries to resize it beyond the size of our (non-resizable) window contents?

Luckily, Xlib provides the XSizeHints structure and XSetWMNormalHints function, allowing us to set the maximum, minimum, and default sizes in pixels of any Window. With a few small modifications, our basic_window.c from before is now properly constrained:

Compiled just as above, this produces them same plain window, but with dimensions no smaller than 200x200 and no larger than 800x800 after resizing.

These gists are starting to get pretty long, so let’s wrap our window creation code into one big function that takes a Display * and all other necessary arguments and returns a usable, flushed Window. You can find this function here.

Changing the name of an X Window

Now that we have a create_window function that does all the ugly Window creation and constraint stuff for us, we can think about actually naming the window (another prime example of something that should be done inside the XCreateSimpleWindow function).

Luckily, setting a Window’s name is a relatively simple matter, and can be isolated in a function we’ll call name_window:

Sticking an X Window to the front of the screen

For some programs, it makes sense for a given window to be constantly visible and never covered up by new windows from other programs. Urgent dialog boxes are the most common example of this, but the principle can be applied to just about any window that needs to be visible (nearly) 100% of the time.

Because the concept of window “frontness” is purely decided by the window manager and not the X server itself, Xlib must rely on standards like the Extended Window Manager Hints to suggest changes in visibility to the window manager running above it. Luckily for us, just about all of the common window managers implement EWMH, allowing us to construct the following function:

On non-EWMH compliant window managers, therefore, this should fail gracefully.

Tying it all together

This post quickly exploded in size thanks to the large examples, so I decided to cut a large part of it out. Specifically, the topics of graphic contexts and bitmaps were removed entirely. To see a relatively clean example of everything in this post, as well as X BitMaps and basic graphics, please take a look at xnelson, which uses all the the principles above (as well as X BitMaps) to stick a simple window to the front of the screen with nothing but Xlib.

Conclusions

So, what have we accomplished?

We’ve proven that it’s possible, with some pain, to perform common graphical operations with raw Xlib routines. Of course, many of these routines aren’t useful by themselves - we need to add images, text, menus, and so forth. For that, I highly recommend some kind of library, even if it’s just Xaw or the more full-featured Qt and GTK+.

In the meantime, I’ll be thinking up new ways of masochistically implementing more simple tasks with as few dependencies as possible.

- William