[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