CINXE.COM
Using Cairo with X11/Xlib
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Using Cairo with X11/Xlib</title> <link rel="stylesheet" href="../style.css" type="text/css" /> <link rel="stylesheet" href="../local.css" type="text/css" /> </head> <body> <div class="header"> <div id="logo"> <a href="/"><img alt="cairo" src="/cairo-banner.png" /></a> </div> </div> <div id="navcontainer"> <ul id="navlist"> <li><a href="/news/">News</a></li> <li><a href="/download/">Download</a></li> <li><a href="/documentation/">Documentation</a></li> <li><a href="/contact/">Contact</a></li> <li><a href="/examples/">Examples</a></li> </ul> </div> <div id="title"> Using Cairo with X11/Xlib </div> <div id="content"> <p>Cairo is a graphics library which offers common drawing primitives independently of the actual backend. It comes with a set of backends, such as PNG or PDF. One of these backends allows to use libcairo for drawing on X11 windows. Although there are very powerful libraries such as <a href="http://qt-project.org/">Qt</a> or <a href="http://www.gtk.org/">GTK</a>, they are often far too complex for simple applications.</p> <p>Following I explain how to open an X11 window using Xlib and show how to create graphics output with Cairo graphics.</p> <h1>Opening a Window</h1> <p>X11 is probably the most flexible graphical interface which makes it a little bit complicated, at least at a first sight. To open a window, you need to do the following steps:</p> <ol> <li><p> Connect to the X sever: <code>XOpenDisplay(3)</code>.</p></li> <li><p> Select the output screen: <code>DefaultScreen(3)</code>.</p></li> <li><p> Create a window: <code>XCreateSimpleWindow(3)</code>.</p></li> <li><p> Choose input events: <code>XSelectInput(3)</code>. Please note that this is not mandatory for opening a window but typically you'd like to receive events such as mouse clicks and key board input.</p></li> <li><p> Display the window: <code>XMapWindow(3)</code>.</p></li> </ol> <p>After the window is ready, it is mapped to a cairo Xlib surface. The following function shows how to do it.</p> <pre><code>cairo_surface_t *cairo_create_x11_surface0(int x, int y) { Display *dsp; Drawable da; int screen; cairo_surface_t *sfc; if ((dsp = XOpenDisplay(NULL)) == NULL) exit(1); screen = DefaultScreen(dsp); da = XCreateSimpleWindow(dsp, DefaultRootWindow(dsp), 0, 0, x, y, 0, 0, 0); XSelectInput(dsp, da, ButtonPressMask | KeyPressMask); XMapWindow(dsp, da); sfc = cairo_xlib_surface_create(dsp, da, DefaultVisual(dsp, screen), x, y); cairo_xlib_surface_set_size(sfc, x, y); return sfc; } </code></pre> <h1>Receiving Events</h1> <p>The next task is to receive an event. The function from above configured the window to receive mouse button and keyboard events. The function <code>XNextEvent(3)</code> returns the next event of the X server's event queue and it blocks if the queue is empty. If blocking is not an option for your tool because you have permanent interaction -- such as in e.g. computer games -- you have to check if there are events in the queue before retrieving it with <code>XNextEvent(3)</code> to avoid blocking. This is done with <code>XPending(3)</code>. The function immediately returns the number of events in the queue. Thus, it returns 0 if there are no events.</p> <p><code>XNextEvent(3)</code> returns an <code>XEvent</code> which actually is a union of all different kinds of X events. The type field distinguishes between them. The <code>XKeyEvent</code> receives key codes which have to be translated with <code>XLookupString(3)</code> to symbols. All symbols are defined in <code>X11/keysyndef.h</code>. The following function shows how to do it.</p> <pre><code>int cairo_check_event(cairo_surface_t *sfc, int block) { char keybuf[8]; KeySym key; XEvent e; for (;;) { if (block || XPending(cairo_xlib_surface_get_display(sfc))) XNextEvent(cairo_xlib_surface_get_display(sfc), &e); else return 0; switch (e.type) { case ButtonPress: return -e.xbutton.button; case KeyPress: XLookupString(&e.xkey, keybuf, sizeof(keybuf), &key, NULL); return key; default: fprintf(stderr, "Dropping unhandled XEevent.type = %d.\n", e.type); } } } </code></pre> <p>Putting all this together creates a first simple window example. Download the <a href="https://www.cypherpunk.at/files/2014/11/cairo_xlib_simple.c">cairo_xlib_simple.c</a> source file.</p> <h1>Animations and Full Screen</h1> <p>To create animations you simple repaint the image in a loop. The problem is that you basically have no control about the timing when the X server actually updates the screen. All graphic operations are queued and at some time the queue is processed by the X server. This creates the problem that the animation might flicker. The solution is to push all operations to a group (<code>cairo_push_group()</code>) instead of directly drawing to the surface. Finally the group is popped to the Cairo drawing source (<code>cairo_pop_group_to_source()</code>) and painted (<code>cairo_paint()</code>) all at once. Finally we force the X server to flush its queue (<code>cairo_surface_flush()</code>). Although this produces good results you should be aware that this (Cairo + Xlib) is not the method of choice if you intend to write a high speed graphics intensive computer game. If this is the case you should start to learn <a href="https://www.opengl.org/">OpenGL</a> or <a href="https://www.libsdl.org/">SDL</a>.</p> <p>Making the window fullscreen seems to be a well protected X11 secret. Fullscreen is a specific property of a window, such as "maximized", "minimized", and similar ones. The property <code>_NET_WM_STATE_FULLSCREEN</code> is set with the function <code>XChangeProperty(3)</code>.</p> <p>Putting all this together leads to this second final example <a href="https://www.cypherpunk.at/files/2014/11/cairo_xlib.c">cairo_xlib.c</a>.</p> <p>A last note: since the size of a window can change you have to react accordingly. The window change event is sent to the event queue as an <code>XConfigureEvent</code>. It contains the new width and height which has to be passed to the Cairo surface with <code>cairo_xlib_surface_set_size()</code>.</p> </div> <div id="footer"> <div id="pageinfo"> <div id="backlinks"> Links: <a href="../documentation/">documentation</a> <a href="../examples/">examples</a> <a href="../">index</a> </div> <div class="pagedate"> Last edited <span class="date">Wed Nov 19 17:29:49 2014</span> </div> </div> <!-- from cairographics.org --> </div> </body> </html>