Hello World (C, Xlib)

From LiteratePrograms

Jump to: navigation, search
Other implementations: Ada | ALGOL 68 | Alice ML | Amiga E | Applescript | AspectJ | Assembly Intel x86 Linux | Assembly Intel x86 NetBSD | AWK | bash | BASIC | Batch files | C | C, Cairo | C, Xlib | Candle | Clojure | C++ | C# | Delphi | Dylan | E | Eiffel | Erlang | Forth | FORTRAN | Fortress | Go | Groovy | Haskell | Hume | IBM PC bootstrap | Inform 7 | Java | Java, Swing | JavaScript | LaTeX | Lisp | Logo | Lua | Maple | MATLAB | Mercury | OCaml/F Sharp | occam | Oz | Pascal | Perl | PHP | Pic | PIR | PLI | PostScript | Prolog | Python | Rexx | Ruby | Scala | Scheme | Seed7 | sh | Smalltalk | SQL | Standard ML | SVG | Tcl | Tcl Tk | Visual Basic | Visual Basic .NET | XSL

In this article we describe how to write "Hello World!" in an X window, using Xlib. While this can look like a simple operation, there are lots of formalities to deal with. Most applications use higher level libraries to interact with the X server, but there are situattions when we need to go into more detail, and use a low level library like Xlib.

To get declarations for the Xlib functions, we include X11/Xlib.

<<hello.c>>=
#include<X11/Xlib.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
	Display *dpy;
	Window rootwin;
	Window win;
	Colormap cmap;
	XEvent e;
	int scr;
	GC gc;

Setup

All X programs need a connection to the X server. In Xlib, XOpenDisplay() is used for that. The only argument is display_name, a string indicating which X server to connect to. When we provide NULL, the DISPLAY environment variable is used.

<<hello.c>>=
	if(!(dpy=XOpenDisplay(NULL))) {
		fprintf(stderr, "ERROR: could not open display\n");
		exit(1);
	}

An X server can have multiple screens, and many functions expect a screen number as argument. The screen number of the default screen is retrieved with the DefaultScreen() macro. We store this number for later use.

When creating a window, we must provide the parent, which, in our case is the root window. The RootWindow() macro provides this.

The DefaultColormap() macro returns a color map that is appropriate for the current graphic mode and palette.

<<hello.c>>=
	scr = DefaultScreen(dpy);
	rootwin = RootWindow(dpy, scr);
	cmap = DefaultColormap(dpy, scr);

Creating a window in Xlib can be done in 2 ways. The XCreateWindow() function has a lot of arguments and allows us to specify detailed information aboout the window. The much simpler XCreateSimpleWindow() doesn't allow such detail, but is good enough for our use.

For the first 2 arguments, display and parent window, we use the values stored earlier.

The next 4 arguments are position and size of the window. These are only hints to the window manager, which is free to use any values.

We set the next argument, border_width, to 0, to indicate we do not want an extra border around the window.

The last 2 arguments are border color and background color. Xlib defines 2 macros, BlackPixel() and WhitePixel(), to get the pixel values of black and white on a screen. To get other colors, we can use XAllocNamedColor().

<<hello.c>>=
	win=XCreateSimpleWindow(dpy, rootwin, 1, 1, 100, 50, 0, 
			BlackPixel(dpy, scr), BlackPixel(dpy, scr));

We use XStoreName() to give the window manager a hint of what to show in the title bar.

<<hello.c>>=
	XStoreName(dpy, win, "hello");

All drawing in X is done with a graphic context (GC), which can be used to set color, font, etc. We use the default font, and use XSetForeground() to set a white drawing color.

<<hello.c>>=
	gc=XCreateGC(dpy, win, 0, NULL);
	XSetForeground(dpy, gc, WhitePixel(dpy, scr));

X is uses events to communicate with an application. To select which events to receive, we use XSelectInput() with an event mask as the third argument. The ExposureMask makes sure we are informed when the X server wants a redraw. We also want to know when the user clicks on a mouse button inside the window. This is done with the ButtonPressMask.

<<hello.c>>=
	XSelectInput(dpy, win, ExposureMask|ButtonPressMask);

The created window is not initially visible on the screen. To make the X server show it, we use XMapWindow().

<<hello.c>>=
	XMapWindow(dpy, win);

Event loop

Most of the life-time of an X application is normally spent in the event loop.

The XNextEvent() blocks until a new event is received from the X server, in which case it stores event details in an XEvent union. All members of the union are structs, and have a type member, indicating which event type it is. We are only interested in Expose and ButtonPress.

The Expose event has a field named count, which indicates how many more Expose events are in the queue. To avoid unnecessary redraws we only draw if there are no more such events in the queue.

The XDrawString() is quite simple. The third argument is the graphic context to use, the fourth and fifth arguments is the drawing position, and the last two are the text and the length.

We handle all ButtonPress' events by terminating the event loop.

<<hello.c>>=
	while(1) {
		XNextEvent(dpy, &e);
		if(e.type==Expose && e.xexpose.count<1)
			XDrawString(dpy, win, gc, 10, 10, "Hello World!", 12);
		else if(e.type==ButtonPress) break;
	}

The last action of an X application is to close the connection to the X server. This will also close the window.

<<hello.c>>=
	XCloseDisplay(dpy);
	return 0;
}

Building

The Xlib library is in "/usr/X11R6/lib". This path is not normally automatically used by the linker, so we specify it with the -L option. We must also specify the use of the library. This is done with the -lX11 option.

Similarly, the Xlib.h file is in /usr/X11R6/include, which the preprocessor does not search automatically. We use the -I option for this.

<<Makefile>>=
all: hello

hello: hello.o
	cc -o hello -Wall -L/usr/X11R6/lib -lX11 hello.o

hello.o: hello.c
	cc -o hello.o -Wall -I/usr/X11R6/include -c hello.c
Download code
Views
Personal tools