summaryrefslogtreecommitdiff
path: root/src/cairo-xlib-surface-shm.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cairo-xlib-surface-shm.c')
-rwxr-xr-xsrc/cairo-xlib-surface-shm.c1459
1 files changed, 1459 insertions, 0 deletions
diff --git a/src/cairo-xlib-surface-shm.c b/src/cairo-xlib-surface-shm.c
new file mode 100755
index 000000000..fa7d3eb9b
--- /dev/null
+++ b/src/cairo-xlib-surface-shm.c
@@ -0,0 +1,1459 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2012 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS
+
+#include "cairo-xlib-private.h"
+#include "cairo-xlib-surface-private.h"
+
+#if !HAVE_X11_EXTENSIONS_XSHM_H || !(HAVE_X11_EXTENSIONS_SHMPROTO_H || HAVE_X11_EXTENSIONS_SHMSTR_H)
+void _cairo_xlib_display_init_shm (cairo_xlib_display_t *display) {}
+
+cairo_surface_t *
+_cairo_xlib_surface_get_shm (cairo_xlib_surface_t *surface,
+ cairo_bool_t overwrite)
+{
+ return NULL;
+}
+
+cairo_int_status_t
+_cairo_xlib_surface_put_shm (cairo_xlib_surface_t *surface)
+{
+ assert (!surface->fallback);
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+cairo_surface_t *
+_cairo_xlib_surface_create_shm (cairo_xlib_surface_t *other,
+ pixman_format_code_t format,
+ int width, int height)
+{
+ return NULL;
+}
+
+cairo_surface_t *
+_cairo_xlib_surface_create_shm__image (cairo_xlib_surface_t *surface,
+ pixman_format_code_t format,
+ int width, int height)
+{
+ return NULL;
+}
+
+cairo_surface_t *
+_cairo_xlib_surface_create_similar_shm (void *other,
+ cairo_format_t format,
+ int width, int height)
+{
+ return cairo_image_surface_create (format, width, height);
+}
+
+void
+_cairo_xlib_shm_surface_mark_active (cairo_surface_t *_shm)
+{
+ ASSERT_NOT_REACHED;
+}
+
+void
+_cairo_xlib_shm_surface_get_ximage (cairo_surface_t *surface,
+ XImage *ximage)
+{
+ ASSERT_NOT_REACHED;
+}
+
+void *
+_cairo_xlib_shm_surface_get_obdata (cairo_surface_t *surface)
+{
+ ASSERT_NOT_REACHED;
+ return NULL;
+}
+
+Pixmap
+_cairo_xlib_shm_surface_get_pixmap (cairo_surface_t *surface)
+{
+ ASSERT_NOT_REACHED;
+ return 0;
+}
+
+XRenderPictFormat *
+_cairo_xlib_shm_surface_get_xrender_format (cairo_surface_t *surface)
+{
+ ASSERT_NOT_REACHED;
+ return NULL;
+}
+
+cairo_bool_t
+_cairo_xlib_shm_surface_is_active (cairo_surface_t *surface)
+{
+ ASSERT_NOT_REACHED;
+ return FALSE;
+}
+
+cairo_bool_t
+_cairo_xlib_shm_surface_is_idle (cairo_surface_t *surface)
+{
+ ASSERT_NOT_REACHED;
+ return TRUE;
+}
+
+void _cairo_xlib_display_fini_shm (cairo_xlib_display_t *display) {}
+
+#else
+
+#include "cairo-damage-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-list-inline.h"
+#include "cairo-mempool-private.h"
+
+#include <X11/Xlibint.h>
+#include <X11/Xproto.h>
+#include <X11/extensions/XShm.h>
+#if HAVE_X11_EXTENSIONS_SHMPROTO_H
+#include <X11/extensions/shmproto.h>
+#elif HAVE_X11_EXTENSIONS_SHMSTR_H
+#include <X11/extensions/shmstr.h>
+#endif
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+#define MIN_PIXMAP_SIZE 4096
+
+#define MIN_BITS 8
+#define MIN_SIZE (1<<(MIN_BITS-1))
+
+typedef struct _cairo_xlib_shm cairo_xlib_shm_t;
+typedef struct _cairo_xlib_shm_info cairo_xlib_shm_info_t;
+typedef struct _cairo_xlib_shm_surface cairo_xlib_shm_surface_t;
+
+struct _cairo_xlib_shm {
+ cairo_mempool_t mem;
+
+ XShmSegmentInfo shm;
+ unsigned long attached;
+ cairo_list_t link;
+};
+
+struct _cairo_xlib_shm_info {
+ unsigned long last_request;
+ void *mem;
+ size_t size;
+ cairo_xlib_shm_t *pool;
+};
+
+struct _cairo_xlib_shm_surface {
+ cairo_image_surface_t image;
+
+ cairo_list_t link;
+ cairo_xlib_shm_info_t *info;
+ Pixmap pixmap;
+ unsigned long active;
+ int idle;
+};
+
+/* the parent is always given by index/2 */
+#define PQ_PARENT_INDEX(i) ((i) >> 1)
+#define PQ_FIRST_ENTRY 1
+
+/* left and right children are index * 2 and (index * 2) +1 respectively */
+#define PQ_LEFT_CHILD_INDEX(i) ((i) << 1)
+
+#define PQ_TOP(pq) ((pq)->elements[PQ_FIRST_ENTRY])
+
+struct pqueue {
+ int size, max_size;
+ cairo_xlib_shm_info_t **elements;
+};
+
+struct _cairo_xlib_shm_display {
+ int has_pixmaps;
+ int opcode;
+ int event;
+
+ Window window;
+ unsigned long last_request;
+ unsigned long last_event;
+
+ cairo_list_t surfaces;
+
+ cairo_list_t pool;
+ struct pqueue info;
+};
+
+static inline cairo_bool_t
+seqno_passed (unsigned long a, unsigned long b)
+{
+ return (long)(b - a) >= 0;
+}
+
+static inline cairo_bool_t
+seqno_before (unsigned long a, unsigned long b)
+{
+ return (long)(b - a) > 0;
+}
+
+static inline cairo_bool_t
+seqno_after (unsigned long a, unsigned long b)
+{
+ return (long)(a - b) > 0;
+}
+
+static inline cairo_status_t
+_pqueue_init (struct pqueue *pq)
+{
+ pq->max_size = 32;
+ pq->size = 0;
+
+ pq->elements = _cairo_malloc_ab (pq->max_size,
+ sizeof (cairo_xlib_shm_info_t *));
+ if (unlikely (pq->elements == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ PQ_TOP(pq) = NULL;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static inline void
+_pqueue_fini (struct pqueue *pq)
+{
+ free (pq->elements);
+}
+
+static cairo_status_t
+_pqueue_grow (struct pqueue *pq)
+{
+ cairo_xlib_shm_info_t **new_elements;
+
+ new_elements = _cairo_realloc_ab (pq->elements,
+ 2 * pq->max_size,
+ sizeof (cairo_xlib_shm_info_t *));
+ if (unlikely (new_elements == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ pq->elements = new_elements;
+ pq->max_size *= 2;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_pqueue_shrink (struct pqueue *pq, int min_size)
+{
+ cairo_xlib_shm_info_t **new_elements;
+
+ if (min_size > pq->max_size)
+ return;
+
+ new_elements = _cairo_realloc_ab (pq->elements,
+ min_size,
+ sizeof (cairo_xlib_shm_info_t *));
+ if (unlikely (new_elements == NULL))
+ return;
+
+ pq->elements = new_elements;
+ pq->max_size = min_size;
+}
+
+static inline cairo_status_t
+_pqueue_push (struct pqueue *pq, cairo_xlib_shm_info_t *info)
+{
+ cairo_xlib_shm_info_t **elements;
+ int i, parent;
+
+ if (unlikely (pq->size + 1 == pq->max_size)) {
+ cairo_status_t status;
+
+ status = _pqueue_grow (pq);
+ if (unlikely (status))
+ return status;
+ }
+
+ elements = pq->elements;
+
+ for (i = ++pq->size;
+ i != PQ_FIRST_ENTRY &&
+ info->last_request < elements[parent = PQ_PARENT_INDEX (i)]->last_request;
+ i = parent)
+ {
+ elements[i] = elements[parent];
+ }
+
+ elements[i] = info;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static inline void
+_pqueue_pop (struct pqueue *pq)
+{
+ cairo_xlib_shm_info_t **elements = pq->elements;
+ cairo_xlib_shm_info_t *tail;
+ int child, i;
+
+ tail = elements[pq->size--];
+ if (pq->size == 0) {
+ elements[PQ_FIRST_ENTRY] = NULL;
+ _pqueue_shrink (pq, 32);
+ return;
+ }
+
+ for (i = PQ_FIRST_ENTRY;
+ (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size;
+ i = child)
+ {
+ if (child != pq->size &&
+ elements[child+1]->last_request < elements[child]->last_request)
+ {
+ child++;
+ }
+
+ if (elements[child]->last_request >= tail->last_request)
+ break;
+
+ elements[i] = elements[child];
+ }
+ elements[i] = tail;
+}
+
+static cairo_bool_t _x_error_occurred;
+
+static int
+_check_error_handler (Display *display,
+ XErrorEvent *event)
+{
+ _x_error_occurred = TRUE;
+ return False; /* ignored */
+}
+
+static cairo_bool_t
+can_use_shm (Display *dpy, int *has_pixmap)
+{
+ XShmSegmentInfo shm;
+ int (*old_handler) (Display *display, XErrorEvent *event);
+ Status success;
+ int major, minor;
+
+ if (! XShmQueryExtension (dpy))
+ return FALSE;
+
+ XShmQueryVersion (dpy, &major, &minor, has_pixmap);
+
+ shm.shmid = shmget (IPC_PRIVATE, 0x1000, IPC_CREAT | 0600);
+ if (shm.shmid == -1)
+ return FALSE;
+
+ shm.readOnly = FALSE;
+ shm.shmaddr = shmat (shm.shmid, NULL, 0);
+ if (shm.shmaddr == (char *) -1) {
+ shmctl (shm.shmid, IPC_RMID, NULL);
+ return FALSE;
+ }
+
+ assert (CAIRO_MUTEX_IS_LOCKED (_cairo_xlib_display_mutex));
+ _x_error_occurred = FALSE;
+
+ XLockDisplay (dpy);
+ XSync (dpy, False);
+ old_handler = XSetErrorHandler (_check_error_handler);
+
+ success = XShmAttach (dpy, &shm);
+ if (success)
+ XShmDetach (dpy, &shm);
+
+ XSync (dpy, False);
+ XSetErrorHandler (old_handler);
+ XUnlockDisplay (dpy);
+
+ shmctl (shm.shmid, IPC_RMID, NULL);
+ shmdt (shm.shmaddr);
+
+ return success && ! _x_error_occurred;
+}
+
+static inline Display *
+peek_display (cairo_device_t *device)
+{
+ return ((cairo_xlib_display_t *)device)->display;
+}
+
+static inline unsigned long
+peek_processed (cairo_device_t *device)
+{
+ return LastKnownRequestProcessed (peek_display(device));
+}
+
+static void
+_cairo_xlib_display_shm_pool_destroy (cairo_xlib_display_t *display,
+ cairo_xlib_shm_t *pool)
+{
+ shmdt (pool->shm.shmaddr);
+ if (display->display) /* may be called after CloseDisplay */
+ XShmDetach (display->display, &pool->shm);
+
+ _cairo_mempool_fini (&pool->mem);
+
+ cairo_list_del (&pool->link);
+ free (pool);
+}
+
+static void send_event(cairo_xlib_display_t *display,
+ cairo_xlib_shm_info_t *info,
+ unsigned long seqno)
+{
+ XShmCompletionEvent ev;
+
+ if (! seqno_after (seqno, display->shm->last_event))
+ return;
+
+ ev.type = display->shm->event;
+ ev.send_event = 1; /* XXX or lie? */
+ ev.serial = NextRequest (display->display);
+ ev.drawable = display->shm->window;
+ ev.major_code = display->shm->opcode;
+ ev.minor_code = X_ShmPutImage;
+ ev.shmseg = info->pool->shm.shmid;
+ ev.offset = (char *)info->mem - (char *)info->pool->shm.shmaddr;
+
+ XSendEvent (display->display, ev.drawable, False, 0, (XEvent *)&ev);
+
+ display->shm->last_event = ev.serial;
+}
+
+static void sync (cairo_xlib_display_t *display)
+{
+ cairo_xlib_shm_info_t *info;
+ struct pqueue *pq = &display->shm->info;
+
+ XSync (display->display, False);
+
+ while ((info = PQ_TOP(pq))) {
+ _cairo_mempool_free (&info->pool->mem, info->mem);
+ _pqueue_pop (&display->shm->info);
+ free (info);
+ }
+}
+
+static void
+_cairo_xlib_shm_info_cleanup (cairo_xlib_display_t *display)
+{
+ cairo_xlib_shm_info_t *info;
+ Display *dpy = display->display;
+ struct pqueue *pq = &display->shm->info;
+ unsigned long processed;
+
+ if (PQ_TOP(pq) == NULL)
+ return;
+
+ XEventsQueued (dpy, QueuedAfterReading);
+ processed = LastKnownRequestProcessed (dpy);
+
+ info = PQ_TOP(pq);
+ do {
+ if (! seqno_passed (info->last_request, processed)) {
+ send_event (display, info, display->shm->last_request);
+ return;
+ }
+
+ _cairo_mempool_free (&info->pool->mem, info->mem);
+ _pqueue_pop (&display->shm->info);
+ free (info);
+ } while ((info = PQ_TOP(pq)));
+}
+
+static cairo_xlib_shm_t *
+_cairo_xlib_shm_info_find (cairo_xlib_display_t *display, size_t size,
+ void **ptr, unsigned long *last_request)
+{
+ cairo_xlib_shm_info_t *info;
+ struct pqueue *pq = &display->shm->info;
+
+ if (PQ_TOP(pq) == NULL)
+ return NULL;
+
+ info = PQ_TOP(pq);
+ do {
+ cairo_xlib_shm_t *pool = info->pool;
+
+ *last_request = info->last_request;
+
+ _pqueue_pop (&display->shm->info);
+ _cairo_mempool_free (&pool->mem, info->mem);
+ free (info);
+
+ if (pool->mem.free_bytes >= size) {
+ void *mem = _cairo_mempool_alloc (&pool->mem, size);
+ if (mem != NULL) {
+ *ptr = mem;
+ return pool;
+ }
+ }
+ } while ((info = PQ_TOP(pq)));
+
+ return NULL;
+}
+
+static cairo_xlib_shm_t *
+_cairo_xlib_shm_pool_find (cairo_xlib_display_t *display,
+ size_t size,
+ void **ptr)
+{
+ cairo_xlib_shm_t *pool;
+
+ cairo_list_foreach_entry (pool, cairo_xlib_shm_t, &display->shm->pool, link) {
+ if (pool->mem.free_bytes >= size) {
+ void *mem = _cairo_mempool_alloc (&pool->mem, size);
+ if (mem != NULL) {
+ *ptr = mem;
+ return pool;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static void
+_cairo_xlib_shm_pool_cleanup (cairo_xlib_display_t *display)
+{
+ cairo_xlib_shm_t *pool, *next;
+ unsigned long processed;
+
+ processed = LastKnownRequestProcessed (display->display);
+
+ cairo_list_foreach_entry_safe (pool, next, cairo_xlib_shm_t,
+ &display->shm->pool, link) {
+ if (! seqno_passed (pool->attached, processed))
+ break;
+
+ if (pool->mem.free_bytes == pool->mem.max_bytes)
+ _cairo_xlib_display_shm_pool_destroy (display, pool);
+ }
+}
+
+static cairo_xlib_shm_t *
+_cairo_xlib_shm_pool_create(cairo_xlib_display_t *display,
+ size_t size, void **ptr)
+{
+ Display *dpy = display->display;
+ cairo_xlib_shm_t *pool;
+ size_t bytes, maxbits = 16, minbits = MIN_BITS;
+ Status success;
+
+ pool = malloc (sizeof (cairo_xlib_shm_t));
+ if (pool == NULL)
+ return NULL;
+
+ bytes = 1 << maxbits;
+ while (bytes <= size)
+ bytes <<= 1, maxbits++;
+ bytes <<= 3;
+
+ minbits += (maxbits - 16) / 2;
+
+ pool->shm.shmid = shmget (IPC_PRIVATE, bytes, IPC_CREAT | 0600);
+ while (pool->shm.shmid == -1 && bytes >= 2*size) {
+ bytes >>= 1;
+ pool->shm.shmid = shmget (IPC_PRIVATE, bytes, IPC_CREAT | 0600);
+ }
+ if (pool->shm.shmid == -1)
+ goto cleanup;
+
+ pool->shm.readOnly = FALSE;
+ pool->shm.shmaddr = shmat (pool->shm.shmid, NULL, 0);
+ if (pool->shm.shmaddr == (char *) -1) {
+ shmctl (pool->shm.shmid, IPC_RMID, NULL);
+ goto cleanup;
+ }
+
+ pool->attached = NextRequest (dpy);
+ success = XShmAttach (dpy, &pool->shm);
+#if !IPC_RMID_DEFERRED_RELEASE
+ XSync (dpy, FALSE);
+#endif
+ shmctl (pool->shm.shmid, IPC_RMID, NULL);
+
+ if (! success)
+ goto cleanup_shm;
+
+ if (_cairo_mempool_init (&pool->mem, pool->shm.shmaddr, bytes,
+ minbits, maxbits - minbits + 1))
+ goto cleanup_detach;
+
+ cairo_list_add (&pool->link, &display->shm->pool);
+
+ *ptr = _cairo_mempool_alloc (&pool->mem, size);
+ assert (*ptr != NULL);
+ return pool;
+
+cleanup_detach:
+ XShmDetach (dpy, &pool->shm);
+cleanup_shm:
+ shmdt (pool->shm.shmaddr);
+cleanup:
+ free (pool);
+ return NULL;
+}
+
+static cairo_xlib_shm_info_t *
+_cairo_xlib_shm_info_create (cairo_xlib_display_t *display,
+ size_t size, cairo_bool_t will_sync)
+{
+ cairo_xlib_shm_info_t *info;
+ cairo_xlib_shm_t *pool;
+ unsigned long last_request = 0;
+ void *mem = NULL;
+
+ _cairo_xlib_shm_info_cleanup (display);
+ pool = _cairo_xlib_shm_pool_find (display, size, &mem);
+ _cairo_xlib_shm_pool_cleanup (display);
+
+ if (pool == NULL && will_sync)
+ pool = _cairo_xlib_shm_info_find (display, size, &mem, &last_request);
+ if (pool == NULL)
+ pool = _cairo_xlib_shm_pool_create (display, size, &mem);
+ if (pool == NULL)
+ return NULL;
+
+ assert (mem != NULL);
+
+ info = malloc (sizeof (*info));
+ if (info == NULL) {
+ _cairo_mempool_free (&pool->mem, mem);
+ return NULL;
+ }
+
+ info->pool = pool;
+ info->mem = mem;
+ info->size = size;
+ info->last_request = last_request;
+
+ return info;
+}
+
+static cairo_status_t
+_cairo_xlib_shm_surface_flush (void *abstract_surface, unsigned flags)
+{
+ cairo_xlib_shm_surface_t *shm = abstract_surface;
+ cairo_xlib_display_t *display;
+ Display *dpy;
+ cairo_status_t status;
+
+ if (shm->active == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (shm->image.base._finishing)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (seqno_passed (shm->active, peek_processed (shm->image.base.device))) {
+ shm->active = 0;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ status = _cairo_xlib_display_acquire (shm->image.base.device, &display);
+ if (unlikely (status))
+ return status;
+
+ send_event (display, shm->info, shm->active);
+
+ dpy = display->display;
+ XEventsQueued (dpy, QueuedAfterReading);
+ while (! seqno_passed (shm->active, LastKnownRequestProcessed (dpy))) {
+ LockDisplay(dpy);
+ _XReadEvents(dpy);
+ UnlockDisplay(dpy);
+ }
+
+ cairo_device_release (&display->base);
+ shm->active = 0;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static inline cairo_bool_t
+active (cairo_xlib_shm_surface_t *shm, Display *dpy)
+{
+ return (shm->active &&
+ ! seqno_passed (shm->active, LastKnownRequestProcessed (dpy)));
+}
+
+static cairo_status_t
+_cairo_xlib_shm_surface_finish (void *abstract_surface)
+{
+ cairo_xlib_shm_surface_t *shm = abstract_surface;
+ cairo_xlib_display_t *display;
+ cairo_status_t status;
+
+ if (shm->image.base.damage) {
+ _cairo_damage_destroy (shm->image.base.damage);
+ shm->image.base.damage = _cairo_damage_create_in_error (CAIRO_STATUS_SURFACE_FINISHED);
+ }
+
+ status = _cairo_xlib_display_acquire (shm->image.base.device, &display);
+ if (unlikely (status))
+ return status;
+
+ if (shm->pixmap)
+ XFreePixmap (display->display, shm->pixmap);
+
+ if (active (shm, display->display)) {
+ shm->info->last_request = shm->active;
+ _pqueue_push (&display->shm->info, shm->info);
+ if (seqno_before (display->shm->last_request, shm->active))
+ display->shm->last_request = shm->active;
+ } else {
+ _cairo_mempool_free (&shm->info->pool->mem, shm->info->mem);
+ free (shm->info);
+
+ _cairo_xlib_shm_pool_cleanup (display);
+ }
+
+ cairo_list_del (&shm->link);
+
+ cairo_device_release (&display->base);
+ return _cairo_image_surface_finish (abstract_surface);
+}
+
+static const cairo_surface_backend_t cairo_xlib_shm_surface_backend = {
+ CAIRO_SURFACE_TYPE_IMAGE,
+ _cairo_xlib_shm_surface_finish,
+
+ _cairo_default_context_create,
+
+ _cairo_image_surface_create_similar,
+ NULL, /* create similar image */
+ _cairo_image_surface_map_to_image,
+ _cairo_image_surface_unmap_image,
+
+ _cairo_image_surface_source,
+ _cairo_image_surface_acquire_source_image,
+ _cairo_image_surface_release_source_image,
+ _cairo_image_surface_snapshot,
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+
+ _cairo_image_surface_get_extents,
+ _cairo_image_surface_get_font_options,
+
+ _cairo_xlib_shm_surface_flush,
+ NULL,
+
+ _cairo_image_surface_paint,
+ _cairo_image_surface_mask,
+ _cairo_image_surface_stroke,
+ _cairo_image_surface_fill,
+ NULL, /* fill-stroke */
+ _cairo_image_surface_glyphs,
+};
+
+static cairo_bool_t
+has_shm (cairo_xlib_surface_t *surface)
+{
+ cairo_xlib_display_t *display = (cairo_xlib_display_t *)surface->base.device;
+ return display->shm != NULL;
+}
+
+static int
+has_shm_pixmaps (cairo_xlib_surface_t *surface)
+{
+ cairo_xlib_display_t *display = (cairo_xlib_display_t *)surface->base.device;
+ if (!display->shm)
+ return 0;
+
+ return display->shm->has_pixmaps;
+}
+
+static cairo_xlib_shm_surface_t *
+_cairo_xlib_shm_surface_create (cairo_xlib_surface_t *other,
+ pixman_format_code_t format,
+ int width, int height,
+ cairo_bool_t will_sync,
+ int create_pixmap)
+{
+ cairo_xlib_shm_surface_t *shm;
+ cairo_xlib_display_t *display;
+ pixman_image_t *image;
+ int stride, size;
+
+ stride = CAIRO_STRIDE_FOR_WIDTH_BPP (width, PIXMAN_FORMAT_BPP(format));
+ size = stride * height;
+ if (size < MIN_SIZE)
+ return NULL;
+
+ shm = malloc (sizeof (*shm));
+ if (unlikely (shm == NULL))
+ return (cairo_xlib_shm_surface_t *)_cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+ _cairo_surface_init (&shm->image.base,
+ &cairo_xlib_shm_surface_backend,
+ other->base.device,
+ _cairo_content_from_pixman_format (format));
+
+ if (_cairo_xlib_display_acquire (other->base.device, &display))
+ goto cleanup_shm;
+
+ shm->info = _cairo_xlib_shm_info_create (display, size, will_sync);
+ if (shm->info == NULL)
+ goto cleanup_display;
+
+ image = pixman_image_create_bits (format, width, height,
+ (uint32_t *) shm->info->mem, stride);
+ if (image == NULL)
+ goto cleanup_info;
+
+ _cairo_image_surface_init (&shm->image, image, format);
+
+ shm->pixmap = 0;
+ if (create_pixmap && size >= create_pixmap) {
+ shm->pixmap = XShmCreatePixmap (display->display,
+ other->drawable,
+ shm->info->mem,
+ &shm->info->pool->shm,
+ shm->image.width,
+ shm->image.height,
+ shm->image.depth);
+ }
+ shm->active = shm->info->last_request;
+ shm->idle = -5;
+
+ assert (shm->active == 0 || will_sync);
+
+ cairo_list_add (&shm->link, &display->shm->surfaces);
+
+ cairo_device_release (&display->base);
+
+ return shm;
+
+cleanup_info:
+ _cairo_mempool_free (&shm->info->pool->mem, shm->info->mem);
+ free(shm->info);
+cleanup_display:
+ cairo_device_release (&display->base);
+cleanup_shm:
+ free (shm);
+ return NULL;
+}
+
+static void
+_cairo_xlib_surface_update_shm (cairo_xlib_surface_t *surface)
+{
+ cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *)surface->shm;
+ cairo_xlib_display_t *display;
+ cairo_damage_t *damage;
+ GC gc;
+
+ damage = _cairo_damage_reduce (surface->base.damage);
+ surface->base.damage = _cairo_damage_create();
+
+ if (_cairo_xlib_display_acquire (surface->base.device, &display))
+ goto cleanup_damage;
+
+ if (_cairo_xlib_surface_get_gc (display, surface, &gc))
+ goto cleanup_display;
+
+ if (! surface->owns_pixmap) {
+ XGCValues gcv;
+
+ gcv.subwindow_mode = IncludeInferiors;
+ XChangeGC (display->display, gc, GCSubwindowMode, &gcv);
+ }
+
+ if (damage->region) {
+ XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (sizeof (XRectangle))];
+ XRectangle *rects = stack_rects;
+ cairo_rectangle_int_t r;
+ int n_rects, i;
+
+ n_rects = cairo_region_num_rectangles (damage->region);
+ if (n_rects == 0) {
+ } else if (n_rects == 1) {
+ cairo_region_get_rectangle (damage->region, 0, &r);
+ XCopyArea (display->display,
+ surface->drawable, shm->pixmap, gc,
+ r.x, r.y,
+ r.width, r.height,
+ r.x, r.y);
+ } else {
+ if (n_rects > ARRAY_LENGTH (stack_rects)) {
+ rects = _cairo_malloc_ab (n_rects, sizeof (XRectangle));
+ if (unlikely (rects == NULL)) {
+ rects = stack_rects;
+ n_rects = ARRAY_LENGTH (stack_rects);
+ }
+ }
+ for (i = 0; i < n_rects; i++) {
+ cairo_region_get_rectangle (damage->region, i, &r);
+
+ rects[i].x = r.x;
+ rects[i].y = r.y;
+ rects[i].width = r.width;
+ rects[i].height = r.height;
+ }
+ XSetClipRectangles (display->display, gc, 0, 0, rects, i, YXBanded);
+
+ XCopyArea (display->display,
+ surface->drawable, shm->pixmap, gc,
+ 0, 0,
+ shm->image.width, shm->image.height,
+ 0, 0);
+
+ if (damage->status == CAIRO_STATUS_SUCCESS && damage->region)
+ XSetClipMask (display->display, gc, None);
+ }
+ } else {
+ XCopyArea (display->display,
+ surface->drawable, shm->pixmap, gc,
+ 0, 0,
+ shm->image.width, shm->image.height,
+ 0, 0);
+ }
+
+ if (! surface->owns_pixmap) {
+ XGCValues gcv;
+
+ gcv.subwindow_mode = ClipByChildren;
+ XChangeGC (display->display, gc, GCSubwindowMode, &gcv);
+ }
+
+ sync (display);
+ shm->active = 0;
+ shm->idle--;
+
+ _cairo_xlib_surface_put_gc (display, surface, gc);
+cleanup_display:
+ cairo_device_release (&display->base);
+cleanup_damage:
+ _cairo_damage_destroy (damage);
+}
+
+static void
+_cairo_xlib_surface_clear_shm (cairo_xlib_surface_t *surface)
+{
+ cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *)surface->shm;
+
+ assert (shm->active == 0);
+
+ _cairo_damage_destroy (surface->base.damage);
+ surface->base.damage = _cairo_damage_create();
+
+ memset (shm->image.data, 0, shm->image.stride * shm->image.height);
+ shm->image.base.is_clear = TRUE;
+}
+
+static void inc_idle (cairo_surface_t *surface)
+{
+ cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *)surface;
+ shm->idle++;
+}
+
+static void dec_idle (cairo_surface_t *surface)
+{
+ cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *)surface;
+ shm->idle--;
+}
+
+cairo_surface_t *
+_cairo_xlib_surface_get_shm (cairo_xlib_surface_t *surface,
+ cairo_bool_t overwrite)
+{
+ if (surface->fallback) {
+ assert (surface->base.damage);
+ assert (surface->shm);
+ assert (surface->shm->damage);
+ goto done;
+ }
+
+ if (surface->shm == NULL) {
+ pixman_format_code_t pixman_format;
+ cairo_bool_t will_sync;
+
+ if (! has_shm_pixmaps (surface))
+ return NULL;
+
+ if ((surface->width | surface->height) < 32)
+ return NULL;
+
+ pixman_format = _pixman_format_for_xlib_surface (surface);
+ if (pixman_format == 0)
+ return NULL;
+
+ will_sync = !surface->base.is_clear && !overwrite;
+
+ surface->shm =
+ &_cairo_xlib_shm_surface_create (surface, pixman_format,
+ surface->width, surface->height,
+ will_sync, 1)->image.base;
+ if (surface->shm == NULL)
+ return NULL;
+
+ assert (surface->base.damage == NULL);
+ if (surface->base.serial || !surface->owns_pixmap) {
+ cairo_rectangle_int_t rect;
+
+ rect.x = rect.y = 0;
+ rect.width = surface->width;
+ rect.height = surface->height;
+
+ surface->base.damage =
+ _cairo_damage_add_rectangle (NULL, &rect);
+ } else
+ surface->base.damage = _cairo_damage_create ();
+
+ surface->shm->damage = _cairo_damage_create ();
+ }
+
+ if (overwrite) {
+ _cairo_damage_destroy (surface->base.damage);
+ surface->base.damage = _cairo_damage_create ();
+ }
+
+ if (!surface->base.is_clear && surface->base.damage->dirty)
+ _cairo_xlib_surface_update_shm (surface);
+
+ _cairo_xlib_shm_surface_flush (surface->shm, 1);
+
+ if (surface->base.is_clear && surface->base.damage->dirty)
+ _cairo_xlib_surface_clear_shm (surface);
+
+done:
+ dec_idle(surface->shm);
+ return surface->shm;
+}
+
+cairo_int_status_t
+_cairo_xlib_surface_put_shm (cairo_xlib_surface_t *surface)
+{
+ cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
+
+ if (!surface->fallback) {
+ if (surface->shm)
+ inc_idle (surface->shm);
+ return CAIRO_INT_STATUS_SUCCESS;
+ }
+
+ if (surface->shm->damage->dirty) {
+ cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) surface->shm;
+ cairo_xlib_display_t *display;
+ cairo_damage_t *damage;
+ GC gc;
+
+ status = _cairo_xlib_display_acquire (surface->base.device, &display);
+ if (unlikely (status))
+ return status;
+
+ damage = _cairo_damage_reduce (shm->image.base.damage);
+ shm->image.base.damage = _cairo_damage_create ();
+
+ TRACE ((stderr, "%s: flushing damage x %d\n", __FUNCTION__,
+ damage->region ? cairo_region_num_rectangles (damage->region) : 0));
+ if (damage->status == CAIRO_STATUS_SUCCESS && damage->region) {
+ XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (sizeof (XRectangle))];
+ XRectangle *rects = stack_rects;
+ cairo_rectangle_int_t r;
+ int n_rects, i;
+
+ n_rects = cairo_region_num_rectangles (damage->region);
+ if (n_rects == 0)
+ goto out;
+
+ status = _cairo_xlib_surface_get_gc (display, surface, &gc);
+ if (unlikely (status))
+ goto out;
+
+ if (n_rects == 1) {
+ cairo_region_get_rectangle (damage->region, 0, &r);
+ _cairo_xlib_shm_surface_mark_active (surface->shm);
+ XCopyArea (display->display,
+ shm->pixmap, surface->drawable, gc,
+ r.x, r.y,
+ r.width, r.height,
+ r.x, r.y);
+ } else {
+ if (n_rects > ARRAY_LENGTH (stack_rects)) {
+ rects = _cairo_malloc_ab (n_rects, sizeof (XRectangle));
+ if (unlikely (rects == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ _cairo_xlib_surface_put_gc (display, surface, gc);
+ goto out;
+ }
+ }
+ for (i = 0; i < n_rects; i++) {
+ cairo_region_get_rectangle (damage->region, i, &r);
+
+ rects[i].x = r.x;
+ rects[i].y = r.y;
+ rects[i].width = r.width;
+ rects[i].height = r.height;
+ }
+ XSetClipRectangles (display->display, gc, 0, 0, rects, i, YXBanded);
+
+ _cairo_xlib_shm_surface_mark_active (surface->shm);
+ XCopyArea (display->display,
+ shm->pixmap, surface->drawable, gc,
+ 0, 0,
+ shm->image.width, shm->image.height,
+ 0, 0);
+
+ if (damage->status == CAIRO_STATUS_SUCCESS && damage->region)
+ XSetClipMask (display->display, gc, None);
+ }
+
+ _cairo_xlib_surface_put_gc (display, surface, gc);
+ }
+
+out:
+ _cairo_damage_destroy (damage);
+ cairo_device_release (&display->base);
+ }
+
+ return status;
+}
+
+cairo_surface_t *
+_cairo_xlib_surface_create_shm (cairo_xlib_surface_t *other,
+ pixman_format_code_t format,
+ int width, int height)
+{
+ cairo_surface_t *surface;
+
+ surface = NULL;
+ if (has_shm (other))
+ surface = &_cairo_xlib_shm_surface_create (other, format, width, height,
+ FALSE, has_shm_pixmaps (other))->image.base;
+
+ return surface;
+}
+
+cairo_surface_t *
+_cairo_xlib_surface_create_shm__image (cairo_xlib_surface_t *surface,
+ pixman_format_code_t format,
+ int width, int height)
+{
+ if (! has_shm(surface))
+ return NULL;
+
+ return &_cairo_xlib_shm_surface_create (surface, format, width, height,
+ FALSE, 0)->image.base;
+}
+
+cairo_surface_t *
+_cairo_xlib_surface_create_similar_shm (void *other,
+ cairo_format_t format,
+ int width, int height)
+{
+ cairo_surface_t *surface;
+
+ surface = _cairo_xlib_surface_create_shm (other,
+ _cairo_format_to_pixman_format_code (format),
+ width, height);
+ if (surface) {
+ if (! surface->is_clear) {
+ cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) surface;
+ assert (shm->active == 0);
+ memset (shm->image.data, 0, shm->image.stride * shm->image.height);
+ shm->image.base.is_clear = TRUE;
+ }
+ } else
+ surface = cairo_image_surface_create (format, width, height);
+
+ return surface;
+}
+
+void
+_cairo_xlib_shm_surface_mark_active (cairo_surface_t *_shm)
+{
+ cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) _shm;
+ cairo_xlib_display_t *display = (cairo_xlib_display_t *) _shm->device;
+
+ shm->active = NextRequest (display->display);
+}
+
+void
+_cairo_xlib_shm_surface_get_ximage (cairo_surface_t *surface,
+ XImage *ximage)
+{
+ cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) surface;
+ int native_byte_order = _cairo_is_little_endian () ? LSBFirst : MSBFirst;
+ cairo_format_masks_t image_masks;
+ int ret;
+
+ ret = _pixman_format_to_masks (shm->image.pixman_format, &image_masks);
+ assert (ret);
+
+ ximage->width = shm->image.width;
+ ximage->height = shm->image.height;
+ ximage->format = ZPixmap;
+ ximage->data = (char *) shm->image.data;
+ ximage->obdata = (char *)&shm->info->pool->shm;
+ 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 = shm->image.depth;
+ ximage->bytes_per_line = shm->image.stride;
+ ximage->bits_per_pixel = image_masks.bpp;
+ ximage->red_mask = image_masks.red_mask;
+ ximage->green_mask = image_masks.green_mask;
+ ximage->blue_mask = image_masks.blue_mask;
+ ximage->xoffset = 0;
+
+ ret = XInitImage (ximage);
+ assert (ret != 0);
+}
+
+void *
+_cairo_xlib_shm_surface_get_obdata (cairo_surface_t *surface)
+{
+ cairo_xlib_display_t *display = (cairo_xlib_display_t *) surface->device;
+ cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) surface;
+
+ display->shm->last_event = shm->active = NextRequest (display->display);
+ return &shm->info->pool->shm;
+}
+
+Pixmap
+_cairo_xlib_shm_surface_get_pixmap (cairo_surface_t *surface)
+{
+ cairo_xlib_shm_surface_t *shm;
+
+ shm = (cairo_xlib_shm_surface_t *) surface;
+ return shm->pixmap;
+}
+
+XRenderPictFormat *
+_cairo_xlib_shm_surface_get_xrender_format (cairo_surface_t *surface)
+{
+ cairo_xlib_shm_surface_t *shm;
+
+ shm = (cairo_xlib_shm_surface_t *) surface;
+ if (shm->image.format != CAIRO_FORMAT_INVALID)
+ return _cairo_xlib_display_get_xrender_format ((cairo_xlib_display_t *)surface->device,
+ shm->image.format);
+
+ return _cairo_xlib_display_get_xrender_format_for_pixman((cairo_xlib_display_t *)surface->device,
+ shm->image.pixman_format);
+}
+
+cairo_bool_t
+_cairo_xlib_shm_surface_is_active (cairo_surface_t *surface)
+{
+ cairo_xlib_shm_surface_t *shm;
+
+ shm = (cairo_xlib_shm_surface_t *) surface;
+ if (shm->active == 0)
+ return FALSE;
+
+ if (seqno_passed (shm->active, peek_processed (shm->image.base.device))) {
+ shm->active = 0;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+cairo_bool_t
+_cairo_xlib_shm_surface_is_idle (cairo_surface_t *surface)
+{
+ cairo_xlib_shm_surface_t *shm;
+
+ shm = (cairo_xlib_shm_surface_t *) surface;
+ return shm->idle > 0;
+}
+
+#define XORG_VERSION_ENCODE(major,minor,patch,snap) \
+ (((major) * 10000000) + ((minor) * 100000) + ((patch) * 1000) + snap)
+
+static cairo_bool_t
+has_broken_send_shm_event (cairo_xlib_display_t *display,
+ cairo_xlib_shm_display_t *shm)
+{
+ Display *dpy = display->display;
+ int (*old_handler) (Display *display, XErrorEvent *event);
+ XShmCompletionEvent ev;
+ XShmSegmentInfo info;
+
+ info.shmid = shmget (IPC_PRIVATE, 0x1000, IPC_CREAT | 0600);
+ if (info.shmid == -1)
+ return TRUE;
+
+ info.readOnly = FALSE;
+ info.shmaddr = shmat (info.shmid, NULL, 0);
+ if (info.shmaddr == (char *) -1) {
+ shmctl (info.shmid, IPC_RMID, NULL);
+ return TRUE;
+ }
+
+ ev.type = shm->event;
+ ev.send_event = 1;
+ ev.serial = 1;
+ ev.drawable = shm->window;
+ ev.major_code = shm->opcode;
+ ev.minor_code = X_ShmPutImage;
+
+ ev.shmseg = info.shmid;
+ ev.offset = 0;
+
+ assert (CAIRO_MUTEX_IS_LOCKED (_cairo_xlib_display_mutex));
+ _x_error_occurred = FALSE;
+
+ XLockDisplay (dpy);
+ XSync (dpy, False);
+ old_handler = XSetErrorHandler (_check_error_handler);
+
+ XShmAttach (dpy, &info);
+ XSendEvent (dpy, ev.drawable, False, 0, (XEvent *)&ev);
+ XShmDetach (dpy, &info);
+
+ XSync (dpy, False);
+ XSetErrorHandler (old_handler);
+ XUnlockDisplay (dpy);
+
+ shmctl (info.shmid, IPC_RMID, NULL);
+ shmdt (info.shmaddr);
+
+ return _x_error_occurred;
+}
+
+static cairo_bool_t
+xorg_has_buggy_send_shm_completion_event(cairo_xlib_display_t *display,
+ cairo_xlib_shm_display_t *shm)
+{
+ Display *dpy = display->display;
+
+ /* As libXext sets the SEND_EVENT bit in the ShmCompletionEvent,
+ * the Xserver may crash if it does not take care when processing
+ * the event type. For instance versions of Xorg prior to 1.11.1
+ * exhibited this bug, and was fixed by:
+ *
+ * commit 2d2dce558d24eeea0eb011ec9ebaa6c5c2273c39
+ * Author: Sam Spilsbury <sam.spilsbury@canonical.com>
+ * Date: Wed Sep 14 09:58:34 2011 +0800
+ *
+ * Remove the SendEvent bit (0x80) before doing range checks on event type.
+ */
+ if (_cairo_xlib_vendor_is_xorg (dpy) &&
+ VendorRelease (dpy) < XORG_VERSION_ENCODE(1,11,0,1))
+ return TRUE;
+
+ /* For everyone else check that no error is generated */
+ return has_broken_send_shm_event (display, shm);
+}
+
+void
+_cairo_xlib_display_init_shm (cairo_xlib_display_t *display)
+{
+ cairo_xlib_shm_display_t *shm;
+ XSetWindowAttributes attr;
+ XExtCodes *codes;
+ int has_pixmap, scr;
+
+ display->shm = NULL;
+
+ if (!can_use_shm (display->display, &has_pixmap))
+ return;
+
+ shm = malloc (sizeof (*shm));
+ if (unlikely (shm == NULL))
+ return;
+
+ codes = XInitExtension (display->display, SHMNAME);
+ if (codes == NULL) {
+ free (shm);
+ return;
+ }
+
+ shm->opcode = codes ->major_opcode;
+ shm->event = codes->first_event;
+
+ if (unlikely (_pqueue_init (&shm->info))) {
+ free (shm);
+ return;
+ }
+
+ scr = DefaultScreen (display->display);
+ attr.override_redirect = 1;
+ shm->window = XCreateWindow (display->display,
+ DefaultRootWindow (display->display), -1, -1,
+ 1, 1, 0,
+ DefaultDepth (display->display, scr),
+ InputOutput,
+ DefaultVisual (display->display, scr),
+ CWOverrideRedirect, &attr);
+ shm->last_event = 0;
+ shm->last_request = 0;
+
+ if (xorg_has_buggy_send_shm_completion_event(display, shm))
+ has_pixmap = 0;
+
+ shm->has_pixmaps = has_pixmap ? MIN_PIXMAP_SIZE : 0;
+ cairo_list_init (&shm->pool);
+
+ cairo_list_init (&shm->surfaces);
+
+ display->shm = shm;
+}
+
+void
+_cairo_xlib_display_fini_shm (cairo_xlib_display_t *display)
+{
+ cairo_xlib_shm_display_t *shm = display->shm;
+
+ if (shm == NULL)
+ return;
+
+ while (!cairo_list_is_empty (&shm->surfaces))
+ cairo_surface_finish (&cairo_list_first_entry (&shm->surfaces,
+ cairo_xlib_shm_surface_t,
+ link)->image.base);
+
+ _pqueue_fini (&shm->info);
+
+ while (!cairo_list_is_empty (&shm->pool)) {
+ cairo_xlib_shm_t *pool;
+
+ pool = cairo_list_first_entry (&shm->pool, cairo_xlib_shm_t, link);
+ _cairo_xlib_display_shm_pool_destroy (display, pool);
+ }
+
+ if (display->display)
+ XDestroyWindow (display->display, shm->window);
+
+ free (shm);
+ display->shm = NULL;
+}
+#endif
+#endif