Mar 14, 2015 Tags: programming
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:
Window
Window
to a fixed size or range of dimensionsWindow
Window
to the front of the screenWhy? 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.
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:
XOpenDisplay
and obtain a Display *
.DefaultScreen
.Window
structure with a call to XCreateSimpleWindow
.Window
to the Display
and flush it all for displaying.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:
Impressive, huh?
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.
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
:
Window
to the front of the screenFor 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.
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
.
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