diff options
author | Chris Wilson <chris@chris-wilson.co.uk> | 2011-06-28 19:58:41 +0100 |
---|---|---|
committer | Chris Wilson <chris@chris-wilson.co.uk> | 2011-06-28 19:58:41 +0100 |
commit | e95232ddab6826dd7ea8306495aa122ea64829c9 (patch) | |
tree | 8d59679790cb03b7dcbe709a7460075108e8b1c1 |
A simple demonstration of the ability of various backends
-rw-r--r-- | Makefile | 43 | ||||
-rw-r--r-- | demo.h | 33 | ||||
-rw-r--r-- | glx.c | 108 | ||||
-rw-r--r-- | poppler-demo.c | 195 | ||||
-rw-r--r-- | spiral-demo.c | 363 | ||||
-rw-r--r-- | xcb.c | 122 | ||||
-rw-r--r-- | ximage.c | 184 | ||||
-rw-r--r-- | xlib.c | 99 |
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 + @@ -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); @@ -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; +} @@ -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; +} @@ -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; +} |