summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Wilson <chris@chris-wilson.co.uk>2011-06-28 19:58:41 +0100
committerChris Wilson <chris@chris-wilson.co.uk>2011-06-28 19:58:41 +0100
commite95232ddab6826dd7ea8306495aa122ea64829c9 (patch)
tree8d59679790cb03b7dcbe709a7460075108e8b1c1
A simple demonstration of the ability of various backends
-rw-r--r--Makefile43
-rw-r--r--demo.h33
-rw-r--r--glx.c108
-rw-r--r--poppler-demo.c195
-rw-r--r--spiral-demo.c363
-rw-r--r--xcb.c122
-rw-r--r--ximage.c184
-rw-r--r--xlib.c99
8 files changed, 1147 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..f310712
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,43 @@
+SOURCES:=xlib.c ximage.c
+REQUIRES:=cairo-xlib xext gdk-pixbuf-2.0
+DEFINES:=-DHAVE_XLIB=1 -DHAVE_XIMAGE=1 -DHAVE_GLX=1
+
+DRM:=0
+ifneq ($(DRM),0)
+DEFINES+=-DHAVE_DRM=1
+SOURCES+=drm.c
+REQUIRES+=cairo-drm libdrm
+else
+DEFINES+=-DHAVE_DRM=0
+endif
+
+XCB:=1
+ifneq ($(XCB),0)
+DEFINES+=-DHAVE_XCB=1
+SOURCES+=xcb.c
+REQUIRES+=cairo-xcb
+else
+DEFINES+=-DHAVE_XCB=0
+endif
+
+GLX:=1
+ifneq ($(GLX),0)
+DEFINES+=-DHAVE_GLX=1
+SOURCES+=glx.c
+REQUIRES+=cairo-gl
+else
+DEFINES+=-DHAVE_GLX=0
+endif
+
+all: spiral-demo poppler-demo
+
+CFLAGS:=$(shell pkg-config --cflags $(REQUIRES)) -Wall -g3
+LIBS:=$(shell pkg-config --libs $(REQUIRES))
+
+spiral-demo: spiral-demo.c $(SOURCES) demo.h Makefile
+ $(CC) $(DEFINES) $(CFLAGS) -o $@ spiral-demo.c $(SOURCES) $(LIBS)
+poppler-demo: poppler-demo.c $(SOURCES) demo.h Makefile
+ $(CC) $(DEFINES) $(CFLAGS) $(shell pkg-config --cflags poppler-glib) -o $@ poppler-demo.c $(SOURCES) $(LIBS) $(shell pkg-config --libs poppler-glib)
+clean:
+ rm -f *-demo
+
diff --git a/demo.h b/demo.h
new file mode 100644
index 0000000..f2de9c6
--- /dev/null
+++ b/demo.h
@@ -0,0 +1,33 @@
+#include <cairo.h>
+#include <stdint.h>
+
+struct device;
+struct framebuffer;
+struct slide;
+
+struct framebuffer {
+ struct device *device;
+
+ cairo_surface_t *surface;
+
+ void (*show) (struct framebuffer *);
+ void (*destroy) (struct framebuffer *);
+};
+
+struct device {
+ const char *name;
+ struct framebuffer *(*get_framebuffer) (struct device *);
+ cairo_surface_t *scanout;
+ int width, height;
+};
+
+struct slide {
+ void (*draw) (struct slide *, cairo_t *, int, int);
+};
+
+
+struct device *xcb_open (int argc, char **argv);
+struct device *xlib_open (int argc, char **argv);
+struct device *ximage_open (int argc, char **argv);
+struct device *glx_open (int argc, char **argv);
+struct device *drm_open (int argc, char **argv);
diff --git a/glx.c b/glx.c
new file mode 100644
index 0000000..119af90
--- /dev/null
+++ b/glx.c
@@ -0,0 +1,108 @@
+#include <cairo-gl.h>
+
+#include "demo.h"
+
+#include <stdlib.h>
+#include <X11/X.h>
+
+struct glx_device {
+ struct device base;
+ struct framebuffer fb;
+
+ Display *display;
+ Window drawable;
+ GLXContext ctx;
+ cairo_device_t *device;
+};
+
+static void
+destroy (struct framebuffer *fb)
+{
+}
+
+static void
+show (struct framebuffer *fb)
+{
+ cairo_gl_surface_swapbuffers (fb->surface);
+}
+
+static struct framebuffer *
+get_fb (struct device *abstract_device)
+{
+ struct glx_device *device = (struct glx_device *) abstract_device;
+
+ return &device->fb;
+}
+
+struct device *
+glx_open (int argc, char **argv)
+{
+ int rgba_attribs[] = {
+ GLX_RGBA,
+ GLX_RED_SIZE, 1,
+ GLX_GREEN_SIZE, 1,
+ GLX_BLUE_SIZE, 1,
+ GLX_ALPHA_SIZE, 1,
+ GLX_DOUBLEBUFFER,
+ None };
+ XVisualInfo *vi;
+ struct glx_device *device;
+ Display *dpy;
+ Screen *scr;
+ int screen;
+ XSetWindowAttributes attr;
+
+ dpy = XOpenDisplay (NULL);
+ if (dpy == NULL)
+ return NULL;
+
+ device = malloc(sizeof(struct glx_device));
+ device->base.name = "glx";
+ device->base.get_framebuffer = get_fb;
+ device->display = dpy;
+
+ device->fb.device = &device->base;
+ device->fb.show = show;
+ device->fb.destroy = destroy;
+
+ screen = DefaultScreen (dpy);
+ scr = XScreenOfDisplay (dpy, screen);
+ device->base.width = WidthOfScreen (scr);
+ device->base.height = HeightOfScreen (scr);
+
+ vi = glXChooseVisual (dpy, DefaultScreen (dpy), rgba_attribs);
+ if (vi == NULL) {
+ XCloseDisplay (dpy);
+ free (device);
+ return NULL;
+ }
+
+ attr.colormap = XCreateColormap (dpy,
+ RootWindow (dpy, vi->screen),
+ vi->visual,
+ AllocNone);
+
+ attr.border_pixel = 0;
+ attr.override_redirect = True;
+ device->drawable = XCreateWindow (dpy, DefaultRootWindow (dpy),
+ 0, 0,
+ device->base.width, device->base.height, 0,
+ vi->depth, InputOutput, vi->visual,
+ CWOverrideRedirect | CWBorderPixel | CWColormap,
+ &attr);
+ XMapWindow (dpy, device->drawable);
+
+ device->ctx = glXCreateContext(dpy, vi, NULL, True);
+ XFree(vi);
+
+ device->device = cairo_glx_device_create(dpy, device->ctx);
+
+ device->base.scanout =
+ cairo_gl_surface_create_for_window (device->device,
+ device->drawable,
+ device->base.width,
+ device->base.height);
+ device->fb.surface = device->base.scanout;
+
+ return &device->base;
+}
diff --git a/poppler-demo.c b/poppler-demo.c
new file mode 100644
index 0000000..5f054db
--- /dev/null
+++ b/poppler-demo.c
@@ -0,0 +1,195 @@
+#include "demo.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <glib-object.h>
+#include <poppler.h>
+
+static void
+fps_draw (cairo_t *cr, const char *name, const struct timeval *last, const struct timeval *now)
+{
+#define N_FILTER 25
+ static double filter[25];
+ static int filter_pos;
+ cairo_text_extents_t extents;
+ char buf[80];
+ double fps;
+ int n, max;
+
+ fps = now->tv_sec - last->tv_sec;
+ fps += (now->tv_usec - last->tv_usec) / 1000000.;
+
+ max = N_FILTER;
+ if (filter_pos < max)
+ max = filter_pos;
+ for (n = 0; n < max; n++)
+ fps += filter[n];
+ fps /= max + 1;
+ filter[filter_pos++ % N_FILTER] = fps;
+
+ snprintf (buf, sizeof (buf), "%s: %.1f fps", name, 1. / fps);
+ cairo_set_font_size (cr, 12);
+ cairo_text_extents (cr, buf, &extents);
+ cairo_move_to (cr, 4 - extents.x_bearing, 4 - extents.y_bearing);
+ cairo_set_source_rgb (cr, .05, .05, .05);
+ cairo_show_text (cr, buf);
+}
+
+int
+main (int argc, char **argv)
+{
+ struct device *device;
+ struct timeval last, now;
+ enum {
+ AUTO,
+ XLIB,
+ XIMAGE,
+ XCB,
+ GLX,
+ DRM,
+ } backend = AUTO;
+ const char *filename = NULL;
+ PopplerDocument *document;
+ gchar *absolute, *uri;
+ GError *error = NULL;
+ int n, num_pages;
+
+ g_type_init ();
+
+ for (n = 1; n < argc; n++) {
+ if (strcmp (argv[n], "--xlib") == 0) {
+ backend = XLIB;
+ } else if (strcmp (argv[n], "--xcb") == 0) {
+ backend = XCB;
+ } else if (strcmp (argv[n], "--ximage") == 0) {
+ backend = XIMAGE;
+ } else if (strcmp (argv[n], "--glx") == 0) {
+ backend = GLX;
+ } else if (strcmp (argv[n], "--drm") == 0) {
+ backend = DRM;
+ } else if (strcmp (argv[n], "--filename") == 0) {
+ filename = argv[n+1];
+ n++;
+ }
+ }
+
+ if (filename == NULL) {
+ fprintf (stderr, "Please use --filename <filename> to specify the PDF file to render\n");
+ return 1;
+ }
+
+ if (g_path_is_absolute (filename)) {
+ absolute = g_strdup (filename);
+ } else {
+ gchar *dir = g_get_current_dir ();
+ absolute = g_build_filename (dir, filename, (gchar *) 0);
+ g_free (dir);
+ }
+
+ uri = g_filename_to_uri (absolute, NULL, &error);
+ g_free (absolute);
+ if (uri == NULL)
+ return 1;
+
+ document = poppler_document_new_from_file (uri, NULL, &error);
+ g_free (uri);
+ if (document == NULL)
+ return 1;
+
+ num_pages = poppler_document_get_n_pages (document);
+
+ if (backend == AUTO) {
+ device = NULL;
+ if (device == NULL && HAVE_DRM) {
+ device = drm_open (argc, argv);
+ }
+ if (device == NULL && HAVE_XCB)
+ device = xcb_open (argc, argv);
+ if (device == NULL && HAVE_XLIB)
+ device = xlib_open (argc, argv);
+ if (device == NULL && HAVE_XIMAGE)
+ device = ximage_open (argc, argv);
+ if (device == NULL && HAVE_GLX)
+ device = glx_open (argc, argv);
+ if (device == NULL) {
+ fprintf (stderr, "Failed to open output device.\n");
+ return 1;
+ }
+ } else {
+ switch (backend) {
+ case AUTO:
+ case XLIB:
+#if HAVE_XLIB
+ device = xlib_open (argc, argv);
+#endif
+ break;
+ case XCB:
+#if HAVE_XCB
+ device = xcb_open (argc, argv);
+#endif
+ break;
+ case XIMAGE:
+#if HAVE_XIMAGE
+ device = ximage_open (argc, argv);
+#endif
+ break;
+ case DRM:
+#if HAVE_DRM
+ device = drm_open (argc, argv);
+#endif
+ break;
+ case GLX:
+#if HAVE_GLX
+ device = glx_open (argc, argv);
+#endif
+ break;
+ }
+
+ if (device == NULL) {
+ fprintf (stderr, "Failed to open backend device\n");
+ return 1;
+ }
+ }
+
+ n = 0;
+ gettimeofday (&last, NULL);
+ while (1) {
+ cairo_t *cr;
+ struct framebuffer *fb;
+ PopplerPage *page;
+ double page_width, page_height;
+ double sf_x, sf_y, sf;
+
+ fb = device->get_framebuffer (device);
+
+ page = poppler_document_get_page (document, n++ % num_pages);
+
+ cr = cairo_create (fb->surface);
+
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_paint (cr);
+
+ poppler_page_get_size (page, &page_width, &page_height);
+ sf_x = device->width / page_width;
+ sf_y = device->height / page_height;
+ sf = MIN (sf_x, sf_y);
+ cairo_scale (cr, sf, sf);
+
+ poppler_page_render (page, cr);
+ g_object_unref (page);
+
+ gettimeofday (&now, NULL);
+ fps_draw (cr, device->name, &last, &now);
+ last = now;
+
+ cairo_destroy (cr);
+
+ fb->show (fb);
+ fb->destroy (fb);
+ }
+
+ return 0;
+}
diff --git a/spiral-demo.c b/spiral-demo.c
new file mode 100644
index 0000000..5aaa5c1
--- /dev/null
+++ b/spiral-demo.c
@@ -0,0 +1,363 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <dirent.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include "demo.h"
+
+#define SHOW_PATH 1
+
+struct source{
+ cairo_surface_t *surface;
+ int width, height;
+} *sources;
+int num_sources;
+
+static cairo_surface_t *
+_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *pixbuf)
+{
+ gint width = gdk_pixbuf_get_width (pixbuf);
+ gint height = gdk_pixbuf_get_height (pixbuf);
+ guchar *gdk_pixels = gdk_pixbuf_get_pixels (pixbuf);
+ int gdk_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+ int n_channels = gdk_pixbuf_get_n_channels (pixbuf);
+ int cairo_stride;
+ guchar *cairo_pixels;
+ cairo_format_t format;
+ cairo_surface_t *surface;
+ int j;
+
+ if (n_channels == 3)
+ format = CAIRO_FORMAT_RGB24;
+ else
+ format = CAIRO_FORMAT_ARGB32;
+
+ surface = cairo_image_surface_create(format, width, height);
+ cairo_stride = cairo_image_surface_get_stride (surface);
+ cairo_pixels = cairo_image_surface_get_data(surface);
+
+ for (j = height; j; j--) {
+ guchar *p = gdk_pixels;
+ guchar *q = cairo_pixels;
+
+ if (n_channels == 3) {
+ guchar *end = p + 3 * width;
+
+ while (p < end) {
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ q[0] = p[2];
+ q[1] = p[1];
+ q[2] = p[0];
+#else
+ q[1] = p[0];
+ q[2] = p[1];
+ q[3] = p[2];
+#endif
+ p += 3;
+ q += 4;
+ }
+ } else {
+ guchar *end = p + 4 * width;
+ guint t1,t2,t3;
+
+#define MULT(d,c,a,t) G_STMT_START { t = c * a + 0x7f; d = ((t >> 8) + t) >> 8; } G_STMT_END
+
+ while (p < end) {
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ MULT(q[0], p[2], p[3], t1);
+ MULT(q[1], p[1], p[3], t2);
+ MULT(q[2], p[0], p[3], t3);
+ q[3] = p[3];
+#else
+ q[0] = p[3];
+ MULT(q[1], p[0], p[3], t1);
+ MULT(q[2], p[1], p[3], t2);
+ MULT(q[3], p[2], p[3], t3);
+#endif
+
+ p += 4;
+ q += 4;
+ }
+#undef MULT
+ }
+
+ gdk_pixels += gdk_rowstride;
+ cairo_pixels += cairo_stride;
+ }
+
+ return surface;
+}
+
+static void load_sources(const char *path, cairo_surface_t *target)
+{
+ struct dirent *de;
+ DIR *dir;
+
+ dir = opendir(path);
+ if (dir == NULL)
+ return;
+
+ while ((de = readdir(dir))) {
+ GdkPixbuf *pb;
+ gchar *filename;
+ cairo_surface_t *surface, *image;
+ cairo_status_t status;
+ cairo_t *cr;
+ struct stat st;
+
+ if (de->d_name[0] == '.')
+ continue;
+
+ filename = g_build_filename(path, de->d_name, NULL);
+
+ if (stat(filename, &st)) {
+ g_free(filename);
+ continue;
+ }
+
+ if (S_ISDIR(st.st_mode)) {
+ load_sources(filename, target);
+ g_free(filename);
+ continue;
+ }
+
+ pb = gdk_pixbuf_new_from_file(filename, NULL);
+ g_free(filename);
+
+ if (pb == NULL)
+ continue;
+
+ image = _cairo_image_surface_create_from_pixbuf(pb);
+ g_object_unref(pb);
+
+ surface = cairo_surface_create_similar(target, CAIRO_CONTENT_COLOR_ALPHA,
+ cairo_image_surface_get_width(image),
+ cairo_image_surface_get_height(image));
+
+ cr = cairo_create(surface);
+ cairo_set_source_surface(cr, image, 0, 0);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_paint(cr);
+ status = cairo_status(cr);
+ cairo_destroy(cr);
+
+ if (status == CAIRO_STATUS_SUCCESS) {
+ struct source *s;
+
+ s = realloc(sources,
+ (num_sources+1)*sizeof(struct source));
+ if (s) {
+ s[num_sources].surface =
+ cairo_surface_reference(surface);
+ s[num_sources].width =
+ cairo_image_surface_get_width(image);
+ s[num_sources].height =
+ cairo_image_surface_get_height(image);
+ num_sources++;
+ sources = s;
+ }
+ }
+
+ cairo_surface_destroy(image);
+ cairo_surface_destroy(surface);
+ }
+ closedir(dir);
+
+ printf("Loaded %d images from %s\n", num_sources, path);
+}
+
+int main(int argc, char **argv)
+{
+ struct device *device;
+ enum {
+ AUTO,
+ XLIB,
+ XIMAGE,
+ XCB,
+ GLX,
+ DRM,
+ } backend = AUTO;
+ const char *path = "/usr/share/backgrounds";
+ float sincos_lut[360];
+ struct timeval start, last, now;
+ int theta, frames, n;
+
+ for (n = 1; n < argc; n++) {
+ if (strcmp (argv[n], "--xlib") == 0) {
+ backend = XLIB;
+ } else if (strcmp (argv[n], "--xcb") == 0) {
+ backend = XCB;
+ } else if (strcmp (argv[n], "--ximage") == 0) {
+ backend = XIMAGE;
+ } else if (strcmp (argv[n], "--drm") == 0) {
+ backend = DRM;
+ } else if (strcmp (argv[n], "--glx") == 0) {
+ backend = GLX;
+ } else if (strcmp (argv[n], "--images") == 0) {
+ path = argv[n+1];
+ n++;
+ }
+ }
+
+ if (backend == AUTO) {
+ device = NULL;
+ if (device == NULL && HAVE_DRM) {
+ device = drm_open (argc, argv);
+ }
+ if (device == NULL && HAVE_XCB)
+ device = xcb_open (argc, argv);
+ if (device == NULL && HAVE_XLIB)
+ device = xlib_open (argc, argv);
+ if (device == NULL && HAVE_XLIB)
+ device = glx_open (argc, argv);
+ if (device == NULL && HAVE_XIMAGE)
+ device = ximage_open (argc, argv);
+ if (device == NULL) {
+ fprintf (stderr, "Failed to open output device.\n");
+ return 1;
+ }
+ } else {
+ switch (backend) {
+ case AUTO:
+ case XLIB:
+#if HAVE_XLIB
+ device = xlib_open (argc, argv);
+#endif
+ break;
+ case XCB:
+#if HAVE_XCB
+ device = xcb_open (argc, argv);
+#endif
+ break;
+ case XIMAGE:
+#if HAVE_XIMAGE
+ device = ximage_open (argc, argv);
+#endif
+ break;
+ case GLX:
+#if HAVE_GLX
+ device = glx_open (argc, argv);
+#endif
+ break;
+ case DRM:
+#if HAVE_DRM
+ device = drm_open (argc, argv);
+#endif
+ break;
+ }
+
+ if (device == NULL) {
+ fprintf (stderr, "Failed to open backend device\n");
+ return 1;
+ }
+ }
+ printf("Using backend \"%s\"\n", device->name);
+
+ g_type_init();
+
+ for (theta = 0; theta < 360; theta+= 2) {
+ sincos_lut[theta+0] = sin(theta/180. * M_PI);
+ sincos_lut[theta+1] = cos(theta/180. * M_PI);
+ }
+
+ load_sources(path, device->get_framebuffer(device)->surface);
+
+ gettimeofday(&start, 0); now = last = start;
+ frames = 0;
+ do {
+ struct framebuffer *fb;
+ cairo_t *cr;
+ double delta;
+ int width = device->width;
+ int height = device->height;
+ int rotation;
+
+ fb = device->get_framebuffer (device);
+ cr = cairo_create (fb->surface);
+
+ gettimeofday(&now, 0);
+ delta = now.tv_sec - start.tv_sec;
+ delta += (now.tv_usec - start.tv_usec)*1e-6;
+
+ rotation = floor(50*delta);
+
+#if SINGLE_BUFFERED
+ cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
+ cairo_paint(cr);
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+#else
+ cairo_push_group_with_content(cr, CAIRO_CONTENT_COLOR);
+#endif
+
+ if (SHOW_PATH) {
+ cairo_set_source_rgb(cr, 1, 1, 1);
+ cairo_set_line_width(cr, 5);
+ cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
+
+ cairo_move_to(cr, width/2, height/2);
+ for (theta = 0; theta < 2000; theta += 5) {
+ double dx = theta*sincos_lut[(((rotation+theta)%360)&~1)+0]/2;
+ double dy = theta*sincos_lut[(((rotation+theta)%360)&~1)+1]/2;
+ cairo_line_to(cr, width/2+dx, height/2+dy);
+ }
+ cairo_stroke(cr);
+ }
+
+ if (num_sources) {
+ int step = 50;
+ int image = num_sources;
+ for (theta = 0; theta < 2000; theta += step) {
+ double dx = theta*sincos_lut[(((rotation+theta)%360)&~1)+0]/2;
+ double dy = theta*sincos_lut[(((rotation+theta)%360)&~1)+1]/2;
+ double spin = 500*delta/(step+theta);
+
+ struct source *source = &sources[image++%num_sources];
+ int w = 2*ceil(step/M_SQRT2/5*4 * (1+theta/360.));
+ int h = 2*ceil(step/M_SQRT2/5*3 * (1+theta/360.));
+
+ cairo_save(cr);
+ cairo_translate(cr, width/2+dx, height/2+dy);
+ cairo_rotate(cr, M_PI/2+spin+(rotation+theta)/180.*M_PI);
+ cairo_scale(cr,
+ w/(double)source->width,
+ h/(double)source->height);
+ cairo_set_source_surface(cr, source->surface,
+ -source->width/2,
+ -source->height/2);
+ cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_NONE);
+ cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_BILINEAR);
+ cairo_identity_matrix(cr);
+ cairo_paint(cr);
+ cairo_restore(cr);
+ step += 5;
+ }
+ }
+
+
+#if !SINGLE_BUFFERED
+ cairo_pop_group_to_source(cr);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_paint(cr);
+#endif
+ cairo_destroy(cr);
+
+ fb->show (fb);
+ fb->destroy (fb);
+
+ frames++;
+ delta = now.tv_sec - last.tv_sec;
+ delta += (now.tv_usec - last.tv_usec)*1e-6;
+ if (delta > 5) {
+ printf("%.2f fps\n", frames/delta);
+ last = now;
+ frames = 0;
+ }
+ } while (1);
+
+ return 0;
+}
diff --git a/xcb.c b/xcb.c
new file mode 100644
index 0000000..a0b9dd3
--- /dev/null
+++ b/xcb.c
@@ -0,0 +1,122 @@
+#include <cairo-xcb.h>
+
+#include "demo.h"
+
+#include <stdlib.h>
+
+struct xcb_device {
+ struct device base;
+
+ xcb_connection_t *connection;
+ xcb_window_t drawable;
+};
+
+struct xcb_framebuffer {
+ struct framebuffer base;
+};
+
+static void
+destroy (struct framebuffer *abstract_framebuffer)
+{
+ struct xcb_framebuffer *fb = (struct xcb_framebuffer *) abstract_framebuffer;
+
+ cairo_surface_destroy (fb->base.surface);
+ free (fb);
+}
+
+static void
+show (struct framebuffer *abstract_framebuffer)
+{
+ struct xcb_framebuffer *fb = (struct xcb_framebuffer *) abstract_framebuffer;
+ struct xcb_device *device = (struct xcb_device *) fb->base.device;
+ cairo_t *cr;
+
+ cr = cairo_create (device->base.scanout);
+ cairo_set_source_surface (cr, fb->base.surface, 0, 0);
+ cairo_paint (cr);
+ cairo_destroy (cr);
+
+ xcb_flush (device->connection);
+}
+
+static struct framebuffer *
+get_fb (struct device *abstract_device)
+{
+ struct xcb_device *device = (struct xcb_device *) abstract_device;
+ struct xcb_framebuffer *fb;
+
+ fb = malloc (sizeof (struct xcb_framebuffer));
+ fb->base.device = &device->base;
+ fb->base.show = show;
+ fb->base.destroy = destroy;
+
+ fb->base.surface = cairo_surface_create_similar (device->base.scanout,
+ CAIRO_CONTENT_COLOR,
+ device->base.width,
+ device->base.height);
+
+ return &fb->base;
+}
+
+static xcb_visualtype_t *
+lookup_visual (xcb_screen_t *s, xcb_visualid_t visual)
+{
+ xcb_depth_iterator_t d;
+
+ d = xcb_screen_allowed_depths_iterator (s);
+ for (; d.rem; xcb_depth_next (&d)) {
+ xcb_visualtype_iterator_t v = xcb_depth_visuals_iterator (d.data);
+ for (; v.rem; xcb_visualtype_next (&v)) {
+ if (v.data->visual_id == visual)
+ return v.data;
+ }
+ }
+
+ return 0;
+}
+
+struct device *
+xcb_open (int argc, char **argv)
+{
+ struct xcb_device *device;
+ xcb_connection_t *c;
+ xcb_screen_t *s;
+ xcb_visualtype_t *v;
+ uint32_t values[] = { 1 };
+
+ c = xcb_connect (NULL, NULL);
+ if (c == NULL)
+ return NULL;
+
+ device = malloc (sizeof (struct xcb_device));
+ device->base.name = "xcb";
+ device->base.get_framebuffer = get_fb;
+ device->connection = c;
+ device->drawable = xcb_generate_id (c);
+
+ s = xcb_setup_roots_iterator (xcb_get_setup (c)).data;
+ v = lookup_visual (s, s->root_visual);
+
+ device->base.width = s->width_in_pixels;
+ device->base.height = s->height_in_pixels;
+
+ xcb_create_window (c,
+ s->root_depth,
+ device->drawable,
+ s->root,
+ 0, 0, device->base.width, device->base.height, 0,
+ XCB_WINDOW_CLASS_INPUT_OUTPUT,
+ s->root_visual,
+ XCB_CW_OVERRIDE_REDIRECT,
+ values);
+ xcb_map_window (c, device->drawable);
+
+ device->base.scanout = cairo_xcb_surface_create (c,
+ device->drawable,
+ v,
+ device->base.width,
+ device->base.height);
+
+ return &device->base;
+}
+
diff --git a/ximage.c b/ximage.c
new file mode 100644
index 0000000..6a16c36
--- /dev/null
+++ b/ximage.c
@@ -0,0 +1,184 @@
+#include <cairo-xlib.h>
+
+#include "demo.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <X11/extensions/XShm.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+struct ximage_device {
+ struct device base;
+
+ Display *display;
+ Window drawable;
+
+ int stride;
+ Pixmap pixmap;
+ GC gc;
+ XShmSegmentInfo shm;
+};
+
+struct ximage_framebuffer {
+ struct framebuffer base;
+};
+
+static void
+destroy (struct framebuffer *abstract_framebuffer)
+{
+ struct ximage_framebuffer *fb = (struct ximage_framebuffer *) abstract_framebuffer;
+
+ free (fb);
+}
+
+static cairo_bool_t
+_native_byte_order_lsb (void)
+{
+ int x = 1;
+
+ return *((char *) &x) == 1;
+}
+
+static void
+show (struct framebuffer *abstract_framebuffer)
+{
+ struct ximage_framebuffer *fb = (struct ximage_framebuffer *) abstract_framebuffer;
+ struct ximage_device *device = (struct ximage_device *) fb->base.device;
+
+ if (device->pixmap) {
+ XCopyArea (device->display, device->pixmap, device->drawable, device->gc,
+ 0, 0,
+ device->base.width, device->base.height,
+ 0, 0);
+ } else {
+ XImage ximage;
+ int native_byte_order = _native_byte_order_lsb () ? LSBFirst : MSBFirst;
+
+ ximage.width = device->base.width;
+ ximage.height = device->base.height;
+ ximage.format = ZPixmap;
+ ximage.byte_order = native_byte_order;
+ ximage.bitmap_unit = 32; /* always for libpixman */
+ ximage.bitmap_bit_order = native_byte_order;
+ ximage.bitmap_pad = 32; /* always for libpixman */
+ ximage.depth = 24;
+ ximage.red_mask = 0xff;
+ ximage.green_mask = 0xff00;
+ ximage.blue_mask = 0xff000;
+ ximage.xoffset = 0;
+ ximage.bits_per_pixel = 32;
+ ximage.data = device->shm.shmaddr;
+ ximage.obdata = (char *) &device->shm;
+ ximage.bytes_per_line = device->stride;
+
+ XShmPutImage (device->display, device->drawable, device->gc, &ximage,
+ 0, 0, 0, 0, device->base.width, device->base.height,
+ False);
+ }
+
+ XSync (device->display, True);
+}
+
+static void
+clear(cairo_surface_t *surface)
+{
+ cairo_t *cr = cairo_create (surface);
+ cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+ cairo_paint (cr);
+ cairo_destroy (cr);
+}
+
+static struct framebuffer *
+get_fb (struct device *abstract_device)
+{
+ struct ximage_device *device = (struct ximage_device *) abstract_device;
+ struct ximage_framebuffer *fb;
+
+ fb = malloc (sizeof (struct ximage_framebuffer));
+ fb->base.device = &device->base;
+ fb->base.show = show;
+ fb->base.destroy = destroy;
+
+ fb->base.surface = cairo_surface_reference (device->base.scanout);
+ clear(fb->base.surface);
+
+ return &fb->base;
+}
+
+struct device *
+ximage_open (int argc, char **argv)
+{
+ struct ximage_device *device;
+ Display *dpy;
+ Screen *scr;
+ int screen;
+ XSetWindowAttributes attr;
+ int major, minor, has_pixmap;
+ XGCValues gcv;
+
+ dpy = XOpenDisplay (NULL);
+ if (dpy == NULL)
+ return NULL;
+
+ if (! XShmQueryExtension (dpy)) {
+ XCloseDisplay (dpy);
+ return NULL;
+ }
+
+ device = malloc (sizeof (struct ximage_device));
+ device->base.name = "ximage";
+ device->base.get_framebuffer = get_fb;
+ device->display = dpy;
+
+ screen = DefaultScreen (dpy);
+ scr = XScreenOfDisplay (dpy, screen);
+ device->base.width = WidthOfScreen (scr);
+ device->base.height = HeightOfScreen (scr);
+
+ attr.override_redirect = True;
+ device->drawable = XCreateWindow (dpy, DefaultRootWindow (dpy),
+ 0, 0,
+ device->base.width, device->base.height, 0,
+ DefaultDepth (dpy, screen),
+ InputOutput,
+ DefaultVisual (dpy, screen),
+ CWOverrideRedirect, &attr);
+ XMapWindow (dpy, device->drawable);
+
+ device->stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, device->base.width);
+
+ device->shm.shmid = shmget(IPC_PRIVATE, device->stride * device->base.height, IPC_CREAT | 0777);
+ device->shm.shmaddr = shmat (device->shm.shmid, NULL, 0);
+ device->shm.readOnly = False;
+
+ if (!XShmAttach (dpy, &device->shm))
+ abort ();
+
+ device->base.scanout = cairo_image_surface_create_for_data ((uint8_t *) device->shm.shmaddr,
+ CAIRO_FORMAT_RGB24,
+ device->base.width,
+ device->base.height,
+ device->stride);
+ XShmQueryVersion (dpy, &major, &minor, &has_pixmap);
+
+ if (has_pixmap) {
+ printf ("Using SHM Pixmap\n");
+ device->pixmap = XShmCreatePixmap (dpy,
+ device->drawable,
+ device->shm.shmaddr,
+ &device->shm,
+ device->base.width,
+ device->base.height,
+ 24);
+ } else {
+ printf ("Using SHM PutImage\n");
+ device->pixmap = 0;
+ }
+
+ gcv.graphics_exposures = False;
+ device->gc = XCreateGC (dpy, device->drawable, GCGraphicsExposures, &gcv);
+
+ return &device->base;
+}
diff --git a/xlib.c b/xlib.c
new file mode 100644
index 0000000..ba44a1c
--- /dev/null
+++ b/xlib.c
@@ -0,0 +1,99 @@
+#include <cairo-xlib.h>
+
+#include "demo.h"
+
+#include <stdlib.h>
+
+struct xlib_device {
+ struct device base;
+
+ Display *display;
+ Window drawable;
+};
+
+struct xlib_framebuffer {
+ struct framebuffer base;
+};
+
+static void
+destroy (struct framebuffer *abstract_framebuffer)
+{
+ struct xlib_framebuffer *fb = (struct xlib_framebuffer *) abstract_framebuffer;
+
+ cairo_surface_destroy (fb->base.surface);
+ free (fb);
+}
+
+static void
+show (struct framebuffer *abstract_framebuffer)
+{
+ struct xlib_framebuffer *fb = (struct xlib_framebuffer *) abstract_framebuffer;
+ struct xlib_device *device = (struct xlib_device *) fb->base.device;
+ cairo_t *cr;
+
+ cr = cairo_create (device->base.scanout);
+ cairo_set_source_surface (cr, fb->base.surface, 0, 0);
+ cairo_paint (cr);
+ cairo_destroy (cr);
+
+ XSync (device->display, True);
+}
+
+static struct framebuffer *
+get_fb (struct device *abstract_device)
+{
+ struct xlib_device *device = (struct xlib_device *) abstract_device;
+ struct xlib_framebuffer *fb;
+
+ fb = malloc (sizeof (struct xlib_framebuffer));
+ fb->base.device = &device->base;
+ fb->base.show = show;
+ fb->base.destroy = destroy;
+
+ fb->base.surface = cairo_surface_create_similar (device->base.scanout,
+ CAIRO_CONTENT_COLOR,
+ device->base.width,
+ device->base.height);
+
+ return &fb->base;
+}
+
+struct device *
+xlib_open (int argc, char **argv)
+{
+ struct xlib_device *device;
+ Display *dpy;
+ Screen *scr;
+ int screen;
+ XSetWindowAttributes attr;
+
+ dpy = XOpenDisplay (NULL);
+ if (dpy == NULL)
+ return NULL;
+
+ device = malloc (sizeof (struct xlib_device));
+ device->base.name = "xlib";
+ device->base.get_framebuffer = get_fb;
+ device->display = dpy;
+
+ screen = DefaultScreen (dpy);
+ scr = XScreenOfDisplay (dpy, screen);
+ device->base.width = WidthOfScreen (scr);
+ device->base.height = HeightOfScreen (scr);
+
+ attr.override_redirect = True;
+ device->drawable = XCreateWindow (dpy, DefaultRootWindow (dpy),
+ 0, 0,
+ device->base.width, device->base.height, 0,
+ DefaultDepth (dpy, screen),
+ InputOutput,
+ DefaultVisual (dpy, screen),
+ CWOverrideRedirect, &attr);
+ XMapWindow (dpy, device->drawable);
+
+ device->base.scanout = cairo_xlib_surface_create (dpy, device->drawable,
+ DefaultVisual (dpy, screen),
+ device->base.width, device->base.height);
+
+ return &device->base;
+}