[cairo-commit] [cairo-www] src/Xlib.mdwn

Bryce Harrington bryce at freedesktop.org
Wed Nov 19 17:28:50 PST 2014


 src/Xlib.mdwn |  137 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 137 insertions(+)

New commits:
commit 2217d0f6f0742ebf1fd0983a17068ce91d283275
Author: Bryce Harrington <bryce at osg.samsung.com>
Date:   Wed Nov 19 17:27:58 2014 -0800

    Add a Cairo + Xlib tutorial
    
    Based on blog originally posted to cairo-devel@ list.
    Thanks go to "Bernhard R. Fischer" <bf at abenteuerland.at>

diff --git a/src/Xlib.mdwn b/src/Xlib.mdwn
new file mode 100644
index 0000000..8075a81
--- /dev/null
+++ b/src/Xlib.mdwn
@@ -0,0 +1,137 @@
+[[!meta title="Using Cairo with X11/Xlib"]]
+
+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
+[Qt](http://qt-project.org/) or [GTK](http://www.gtk.org/), they are often far
+too complex for simple applications.
+
+Following I explain how to open an X11 window using Xlib and show how to create
+graphics output with Cairo graphics.
+
+# Opening a Window
+
+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:
+
+  1. Connect to the X sever: `XOpenDisplay(3)`.
+
+  2. Select the output screen: `DefaultScreen(3)`.
+
+  3. Create a window: `XCreateSimpleWindow(3)`.
+
+  4. Choose input events: `XSelectInput(3)`. 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.
+
+  5. Display the window: `XMapWindow(3)`.
+
+After the window is ready, it is mapped to a cairo Xlib surface. The following
+function shows how to do it.
+
+```C
+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;
+}
+```
+
+# Receiving Events
+
+The next task is to receive an event. The function from above configured the
+window to receive mouse button and keyboard events. The function
+`XNextEvent(3)` 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
+`XNextEvent(3)` to avoid blocking. This is done with `XPending(3)`. The
+function immediately returns the number of events in the queue. Thus, it
+returns 0 if there are no events.
+
+`XNextEvent(3)` returns an `XEvent` which actually is a union of all different
+kinds of X events. The type field distinguishes between them. The `XKeyEvent`
+receives key codes which have to be translated with `XLookupString(3)` to
+symbols. All symbols are defined in `X11/keysyndef.h`. The following function
+shows how to do it.
+
+```C
+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);
+      }
+   }
+}
+```
+
+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.
+
+# Animations and Full Screen
+
+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
+(`cairo_push_group()`) instead of directly drawing to the surface. Finally the
+group is popped to the Cairo drawing source (`cairo_pop_group_to_source()`) and
+painted (`cairo_paint()`) all at once. Finally we force the X server to flush
+its queue (`cairo_surface_flush()`). 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 [OpenGL](https://www.opengl.org/) or
+[SDL](https://www.libsdl.org/).
+
+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 `_NET_WM_STATE_FULLSCREEN` is set
+with the function `XChangeProperty(3)`.
+
+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>.
+
+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
+`XConfigureEvent`. It contains the new width and height which has to be passed
+to the Cairo surface with `cairo_xlib_surface_set_size()`.
+


More information about the cairo-commit mailing list