Applying C - Programming X Windows
Written by Harry Fairhead   
Tuesday, 11 November 2025
Article Index
Applying C - Programming X Windows
Device, Screen and Window
An Example
Events

Device, Screen and Window

With all these warnings in place, it is time to get started on a simple example. The first step in any X11 client is to open an X display using:

display=XOpenDisplay(displayname)

Servers can support multiple devices and you can pick which one you want to work with, or you can simply pass NULL, or the X11 macro None, and accept the default. A display can support multiple screens, but generally only one keyboard and mouse.

So to open the default display you would use:

Display *dpy = XOpenDisplay(None);

The function returns None if it cannot open a display and you need to test for this.

Once you have the display there are a large number of functions that you can use to discover its characteristics. For example, you can get the default screen number using:

int screen=DefaultScreen(dpy);

A screen is only guaranteed to support two colors - black and white - and you can get the color codes to be used for these from the display:

int blackColor = BlackPixel(dpy, screen);
int whiteColor = WhitePixel(dpy, screen);

Now we are ready to create a window. This is a two-step process. We first create the data structure that defines the window and then we map it into the display so that it is rendered. The simplest way to create a window is to use:

Window w=XCreateSimpleWindow(dpy, parent, x, y,
width, height,
border_width, border, background)

The window is a child of parent, positioned at x,y, of size width by height and with a border of the specified width and color. The border is additional to the window manager's border and hence rarely used. There is a more complete function to create a window but XCreateSimpleWindow inherits all unspecified attributes from the parent window. To create a top-level window we can use:

DefaultRootWindow(dpy);

to get the root window. So for example:

Window w = XCreateSimpleWindow(dpy, 
DefaultRootWindow(dpy),
x, y, 500, 400, 0, blackColor, blackColor);

creates a 500 by 400 pixel top-level window at x,y with no border and black background. Of course, the window manager will probably override your position values and probably the size values as well.

The window is created in an unmapped, i.e. hidden, state. To make it visible on the screen you have to map it to the display:

XMapWindow(dpy, w);

You can hide the window again using the XUnmapWindow function, which doesn't destroy the window. If you want to destroy a window and all its children then you need to use XDestroyWindow function.

We can't start to use the window until it is fully mapped. The problem is that this can be a relatively slow process. The correct way to wait for the mapping to be complete is to wait on a notification from the server, but for the moment we can ignore notifications or events by simply putting in a delay long enough to ensure that the window is mapped. This is not the normal way of doing the job, but it is simple and for the moment it gets us started.

Graphics Functions

Once the window is mapped, we can ask for a graphics context. This is a fairly standard idea in graphics systems. A graphics context is a set of properties, like foreground color and so on, that allows you to draw on a window. In other graphics systems it would be called a brush or a pen.

You can create multiple graphics contexts per window and use each one to draw in a different style, or you can modify a single graphics context each time you need a new style. You can also use a graphics context with any drawable that is the same sort of graphic as the context was created for. The only real problem is that an X11 graphics context contains a great many attributes you can change. In many cases all you need is a default graphics context that you then use to modify a few simple things like foreground color.

To get a default graphics context you can use:

GC gc = XCreateGC(dpy, w, 0, 0);

you have to specify the display and the window. The final two parameters can be used to modify the graphics context. In this case all we need to do is set the foreground color, i.e. the color used to draw:

XSetForeground(dpy, gc, whiteColor);

notice that in this case you only need the display and the graphics context.

With a foreground color set, we can now draw things using any of the many graphics functions. For example, to draw a point use:

XDrawPoint(dpy, w, gc, x, y);

Other useful drawing functions are:

XDrawLine(dpy, w, gc, x1,y1, x2,y2);
XDrawRectangle(dpy, w, gc, x, y, width, height);
XDrawArc(dpy, w, gc, cx,cy, width,height, 
angle1, angle2); XDrawString(dpy, w, gc, x, y, string, nchars);

There are also Fill versions of the rectangle and arc drawing functions and most drawing functions have a version which will draw multiple items specified in array.

There are many other graphics functions available, consult the man pages and other documentation.



Last Updated ( Tuesday, 11 November 2025 )