beta-0.89.2
[luatex.git] / source / libs / cairo / cairo-src / src / cairo-xlib-surface-shm.c
blobfb40699dc1d3ddcd339d794691433a6884ae064a
1 /* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
2 /* cairo - a vector graphics library with display and print output
4 * Copyright © 2012 Intel Corporation
6 * This library is free software; you can redistribute it and/or
7 * modify it either under the terms of the GNU Lesser General Public
8 * License version 2.1 as published by the Free Software Foundation
9 * (the "LGPL") or, at your option, under the terms of the Mozilla
10 * Public License Version 1.1 (the "MPL"). If you do not alter this
11 * notice, a recipient may use your version of this file under either
12 * the MPL or the LGPL.
14 * You should have received a copy of the LGPL along with this library
15 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
17 * You should have received a copy of the MPL along with this library
18 * in the file COPYING-MPL-1.1
20 * The contents of this file are subject to the Mozilla Public License
21 * Version 1.1 (the "License"); you may not use this file except in
22 * compliance with the License. You may obtain a copy of the License at
23 * http://www.mozilla.org/MPL/
25 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
26 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
27 * the specific language governing rights and limitations.
29 * The Original Code is the cairo graphics library.
31 * The Initial Developer of the Original Code is University of Southern
32 * California.
34 * Contributor(s):
35 * Chris Wilson <chris@chris-wilson.co.uk>
38 #include "cairoint.h"
40 #if !CAIRO_HAS_XLIB_XCB_FUNCTIONS
42 #include "cairo-xlib-private.h"
43 #include "cairo-xlib-surface-private.h"
45 #if !HAVE_X11_EXTENSIONS_XSHM_H || !(HAVE_X11_EXTENSIONS_SHMPROTO_H || HAVE_X11_EXTENSIONS_SHMSTR_H)
46 void _cairo_xlib_display_init_shm (cairo_xlib_display_t *display) {}
48 cairo_surface_t *
49 _cairo_xlib_surface_get_shm (cairo_xlib_surface_t *surface,
50 cairo_bool_t overwrite)
52 return NULL;
55 cairo_int_status_t
56 _cairo_xlib_surface_put_shm (cairo_xlib_surface_t *surface)
58 assert (!surface->fallback);
59 return CAIRO_INT_STATUS_SUCCESS;
62 cairo_surface_t *
63 _cairo_xlib_surface_create_shm (cairo_xlib_surface_t *other,
64 pixman_format_code_t format,
65 int width, int height)
67 return NULL;
70 cairo_surface_t *
71 _cairo_xlib_surface_create_shm__image (cairo_xlib_surface_t *surface,
72 pixman_format_code_t format,
73 int width, int height)
75 return NULL;
78 cairo_surface_t *
79 _cairo_xlib_surface_create_similar_shm (void *other,
80 cairo_format_t format,
81 int width, int height)
83 return cairo_image_surface_create (format, width, height);
86 void
87 _cairo_xlib_shm_surface_mark_active (cairo_surface_t *_shm)
89 ASSERT_NOT_REACHED;
92 void
93 _cairo_xlib_shm_surface_get_ximage (cairo_surface_t *surface,
94 XImage *ximage)
96 ASSERT_NOT_REACHED;
99 void *
100 _cairo_xlib_shm_surface_get_obdata (cairo_surface_t *surface)
102 ASSERT_NOT_REACHED;
103 return NULL;
106 Pixmap
107 _cairo_xlib_shm_surface_get_pixmap (cairo_surface_t *surface)
109 ASSERT_NOT_REACHED;
110 return 0;
113 XRenderPictFormat *
114 _cairo_xlib_shm_surface_get_xrender_format (cairo_surface_t *surface)
116 ASSERT_NOT_REACHED;
117 return NULL;
120 cairo_bool_t
121 _cairo_xlib_shm_surface_is_active (cairo_surface_t *surface)
123 ASSERT_NOT_REACHED;
124 return FALSE;
127 cairo_bool_t
128 _cairo_xlib_shm_surface_is_idle (cairo_surface_t *surface)
130 ASSERT_NOT_REACHED;
131 return TRUE;
134 void _cairo_xlib_display_fini_shm (cairo_xlib_display_t *display) {}
136 #else
138 #include "cairo-damage-private.h"
139 #include "cairo-default-context-private.h"
140 #include "cairo-image-surface-private.h"
141 #include "cairo-list-inline.h"
142 #include "cairo-mempool-private.h"
144 #include <X11/Xlibint.h>
145 #include <X11/Xproto.h>
146 #include <X11/extensions/XShm.h>
147 #if HAVE_X11_EXTENSIONS_SHMPROTO_H
148 #include <X11/extensions/shmproto.h>
149 #elif HAVE_X11_EXTENSIONS_SHMSTR_H
150 #include <X11/extensions/shmstr.h>
151 #endif
152 #include <sys/ipc.h>
153 #include <sys/shm.h>
155 #define MIN_PIXMAP_SIZE 4096
157 #define MIN_BITS 8
158 #define MIN_SIZE (1<<(MIN_BITS-1))
160 typedef struct _cairo_xlib_shm cairo_xlib_shm_t;
161 typedef struct _cairo_xlib_shm_info cairo_xlib_shm_info_t;
162 typedef struct _cairo_xlib_shm_surface cairo_xlib_shm_surface_t;
164 struct _cairo_xlib_shm {
165 cairo_mempool_t mem;
167 XShmSegmentInfo shm;
168 unsigned long attached;
169 cairo_list_t link;
172 struct _cairo_xlib_shm_info {
173 unsigned long last_request;
174 void *mem;
175 size_t size;
176 cairo_xlib_shm_t *pool;
179 struct _cairo_xlib_shm_surface {
180 cairo_image_surface_t image;
182 cairo_list_t link;
183 cairo_xlib_shm_info_t *info;
184 Pixmap pixmap;
185 unsigned long active;
186 int idle;
189 /* the parent is always given by index/2 */
190 #define PQ_PARENT_INDEX(i) ((i) >> 1)
191 #define PQ_FIRST_ENTRY 1
193 /* left and right children are index * 2 and (index * 2) +1 respectively */
194 #define PQ_LEFT_CHILD_INDEX(i) ((i) << 1)
196 #define PQ_TOP(pq) ((pq)->elements[PQ_FIRST_ENTRY])
198 struct pqueue {
199 int size, max_size;
200 cairo_xlib_shm_info_t **elements;
203 struct _cairo_xlib_shm_display {
204 int has_pixmaps;
205 int opcode;
206 int event;
208 Window window;
209 unsigned long last_request;
210 unsigned long last_event;
212 cairo_list_t surfaces;
214 cairo_list_t pool;
215 struct pqueue info;
218 static inline cairo_bool_t
219 seqno_passed (unsigned long a, unsigned long b)
221 return (long)(b - a) >= 0;
224 static inline cairo_bool_t
225 seqno_before (unsigned long a, unsigned long b)
227 return (long)(b - a) > 0;
230 static inline cairo_bool_t
231 seqno_after (unsigned long a, unsigned long b)
233 return (long)(a - b) > 0;
236 static inline cairo_status_t
237 _pqueue_init (struct pqueue *pq)
239 pq->max_size = 32;
240 pq->size = 0;
242 pq->elements = _cairo_malloc_ab (pq->max_size,
243 sizeof (cairo_xlib_shm_info_t *));
244 if (unlikely (pq->elements == NULL))
245 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
247 PQ_TOP(pq) = NULL;
248 return CAIRO_STATUS_SUCCESS;
251 static inline void
252 _pqueue_fini (struct pqueue *pq)
254 free (pq->elements);
257 static cairo_status_t
258 _pqueue_grow (struct pqueue *pq)
260 cairo_xlib_shm_info_t **new_elements;
262 new_elements = _cairo_realloc_ab (pq->elements,
263 2 * pq->max_size,
264 sizeof (cairo_xlib_shm_info_t *));
265 if (unlikely (new_elements == NULL))
266 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
268 pq->elements = new_elements;
269 pq->max_size *= 2;
270 return CAIRO_STATUS_SUCCESS;
273 static void
274 _pqueue_shrink (struct pqueue *pq, int min_size)
276 cairo_xlib_shm_info_t **new_elements;
278 if (min_size > pq->max_size)
279 return;
281 new_elements = _cairo_realloc_ab (pq->elements,
282 min_size,
283 sizeof (cairo_xlib_shm_info_t *));
284 if (unlikely (new_elements == NULL))
285 return;
287 pq->elements = new_elements;
288 pq->max_size = min_size;
291 static inline cairo_status_t
292 _pqueue_push (struct pqueue *pq, cairo_xlib_shm_info_t *info)
294 cairo_xlib_shm_info_t **elements;
295 int i, parent;
297 if (unlikely (pq->size + 1 == pq->max_size)) {
298 cairo_status_t status;
300 status = _pqueue_grow (pq);
301 if (unlikely (status))
302 return status;
305 elements = pq->elements;
307 for (i = ++pq->size;
308 i != PQ_FIRST_ENTRY &&
309 info->last_request < elements[parent = PQ_PARENT_INDEX (i)]->last_request;
310 i = parent)
312 elements[i] = elements[parent];
315 elements[i] = info;
317 return CAIRO_STATUS_SUCCESS;
320 static inline void
321 _pqueue_pop (struct pqueue *pq)
323 cairo_xlib_shm_info_t **elements = pq->elements;
324 cairo_xlib_shm_info_t *tail;
325 int child, i;
327 tail = elements[pq->size--];
328 if (pq->size == 0) {
329 elements[PQ_FIRST_ENTRY] = NULL;
330 _pqueue_shrink (pq, 32);
331 return;
334 for (i = PQ_FIRST_ENTRY;
335 (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size;
336 i = child)
338 if (child != pq->size &&
339 elements[child+1]->last_request < elements[child]->last_request)
341 child++;
344 if (elements[child]->last_request >= tail->last_request)
345 break;
347 elements[i] = elements[child];
349 elements[i] = tail;
352 static cairo_bool_t _x_error_occurred;
354 static int
355 _check_error_handler (Display *display,
356 XErrorEvent *event)
358 _x_error_occurred = TRUE;
359 return False; /* ignored */
362 static cairo_bool_t
363 can_use_shm (Display *dpy, int *has_pixmap)
365 XShmSegmentInfo shm;
366 int (*old_handler) (Display *display, XErrorEvent *event);
367 Status success;
368 int major, minor;
370 if (! XShmQueryExtension (dpy))
371 return FALSE;
373 XShmQueryVersion (dpy, &major, &minor, has_pixmap);
375 shm.shmid = shmget (IPC_PRIVATE, 0x1000, IPC_CREAT | 0600);
376 if (shm.shmid == -1)
377 return FALSE;
379 shm.readOnly = FALSE;
380 shm.shmaddr = shmat (shm.shmid, NULL, 0);
381 if (shm.shmaddr == (char *) -1) {
382 shmctl (shm.shmid, IPC_RMID, NULL);
383 return FALSE;
386 assert (CAIRO_MUTEX_IS_LOCKED (_cairo_xlib_display_mutex));
387 _x_error_occurred = FALSE;
389 XLockDisplay (dpy);
390 XSync (dpy, False);
391 old_handler = XSetErrorHandler (_check_error_handler);
393 success = XShmAttach (dpy, &shm);
394 if (success)
395 XShmDetach (dpy, &shm);
397 XSync (dpy, False);
398 XSetErrorHandler (old_handler);
399 XUnlockDisplay (dpy);
401 shmctl (shm.shmid, IPC_RMID, NULL);
402 shmdt (shm.shmaddr);
404 return success && ! _x_error_occurred;
407 static inline Display *
408 peek_display (cairo_device_t *device)
410 return ((cairo_xlib_display_t *)device)->display;
413 static inline unsigned long
414 peek_processed (cairo_device_t *device)
416 return LastKnownRequestProcessed (peek_display(device));
419 static void
420 _cairo_xlib_display_shm_pool_destroy (cairo_xlib_display_t *display,
421 cairo_xlib_shm_t *pool)
423 shmdt (pool->shm.shmaddr);
424 if (display->display) /* may be called after CloseDisplay */
425 XShmDetach (display->display, &pool->shm);
427 _cairo_mempool_fini (&pool->mem);
429 cairo_list_del (&pool->link);
430 free (pool);
433 static void send_event(cairo_xlib_display_t *display,
434 cairo_xlib_shm_info_t *info,
435 unsigned long seqno)
437 XShmCompletionEvent ev;
439 if (! seqno_after (seqno, display->shm->last_event))
440 return;
442 ev.type = display->shm->event;
443 ev.send_event = 1; /* XXX or lie? */
444 ev.serial = NextRequest (display->display);
445 ev.drawable = display->shm->window;
446 ev.major_code = display->shm->opcode;
447 ev.minor_code = X_ShmPutImage;
448 ev.shmseg = info->pool->shm.shmid;
449 ev.offset = (char *)info->mem - (char *)info->pool->shm.shmaddr;
451 XSendEvent (display->display, ev.drawable, False, 0, (XEvent *)&ev);
453 display->shm->last_event = ev.serial;
456 static void _cairo_xlib_display_sync (cairo_xlib_display_t *display)
458 cairo_xlib_shm_info_t *info;
459 struct pqueue *pq = &display->shm->info;
461 XSync (display->display, False);
463 while ((info = PQ_TOP(pq))) {
464 _cairo_mempool_free (&info->pool->mem, info->mem);
465 _pqueue_pop (&display->shm->info);
466 free (info);
470 static void
471 _cairo_xlib_shm_info_cleanup (cairo_xlib_display_t *display)
473 cairo_xlib_shm_info_t *info;
474 Display *dpy = display->display;
475 struct pqueue *pq = &display->shm->info;
476 unsigned long processed;
478 if (PQ_TOP(pq) == NULL)
479 return;
481 XEventsQueued (dpy, QueuedAfterReading);
482 processed = LastKnownRequestProcessed (dpy);
484 info = PQ_TOP(pq);
485 do {
486 if (! seqno_passed (info->last_request, processed)) {
487 send_event (display, info, display->shm->last_request);
488 return;
491 _cairo_mempool_free (&info->pool->mem, info->mem);
492 _pqueue_pop (&display->shm->info);
493 free (info);
494 } while ((info = PQ_TOP(pq)));
497 static cairo_xlib_shm_t *
498 _cairo_xlib_shm_info_find (cairo_xlib_display_t *display, size_t size,
499 void **ptr, unsigned long *last_request)
501 cairo_xlib_shm_info_t *info;
502 struct pqueue *pq = &display->shm->info;
504 if (PQ_TOP(pq) == NULL)
505 return NULL;
507 info = PQ_TOP(pq);
508 do {
509 cairo_xlib_shm_t *pool = info->pool;
511 *last_request = info->last_request;
513 _pqueue_pop (&display->shm->info);
514 _cairo_mempool_free (&pool->mem, info->mem);
515 free (info);
517 if (pool->mem.free_bytes >= size) {
518 void *mem = _cairo_mempool_alloc (&pool->mem, size);
519 if (mem != NULL) {
520 *ptr = mem;
521 return pool;
524 } while ((info = PQ_TOP(pq)));
526 return NULL;
529 static cairo_xlib_shm_t *
530 _cairo_xlib_shm_pool_find (cairo_xlib_display_t *display,
531 size_t size,
532 void **ptr)
534 cairo_xlib_shm_t *pool;
536 cairo_list_foreach_entry (pool, cairo_xlib_shm_t, &display->shm->pool, link) {
537 if (pool->mem.free_bytes >= size) {
538 void *mem = _cairo_mempool_alloc (&pool->mem, size);
539 if (mem != NULL) {
540 *ptr = mem;
541 return pool;
546 return NULL;
549 static void
550 _cairo_xlib_shm_pool_cleanup (cairo_xlib_display_t *display)
552 cairo_xlib_shm_t *pool, *next;
553 unsigned long processed;
555 processed = LastKnownRequestProcessed (display->display);
557 cairo_list_foreach_entry_safe (pool, next, cairo_xlib_shm_t,
558 &display->shm->pool, link) {
559 if (! seqno_passed (pool->attached, processed))
560 break;
562 if (pool->mem.free_bytes == pool->mem.max_bytes)
563 _cairo_xlib_display_shm_pool_destroy (display, pool);
567 static cairo_xlib_shm_t *
568 _cairo_xlib_shm_pool_create(cairo_xlib_display_t *display,
569 size_t size, void **ptr)
571 Display *dpy = display->display;
572 cairo_xlib_shm_t *pool;
573 size_t bytes, maxbits = 16, minbits = MIN_BITS;
574 Status success;
576 pool = malloc (sizeof (cairo_xlib_shm_t));
577 if (pool == NULL)
578 return NULL;
580 bytes = 1 << maxbits;
581 while (bytes <= size)
582 bytes <<= 1, maxbits++;
583 bytes <<= 3;
585 minbits += (maxbits - 16) / 2;
587 pool->shm.shmid = shmget (IPC_PRIVATE, bytes, IPC_CREAT | 0600);
588 while (pool->shm.shmid == -1 && bytes >= 2*size) {
589 bytes >>= 1;
590 pool->shm.shmid = shmget (IPC_PRIVATE, bytes, IPC_CREAT | 0600);
592 if (pool->shm.shmid == -1)
593 goto cleanup;
595 pool->shm.readOnly = FALSE;
596 pool->shm.shmaddr = shmat (pool->shm.shmid, NULL, 0);
597 if (pool->shm.shmaddr == (char *) -1) {
598 shmctl (pool->shm.shmid, IPC_RMID, NULL);
599 goto cleanup;
602 pool->attached = NextRequest (dpy);
603 success = XShmAttach (dpy, &pool->shm);
604 #if !IPC_RMID_DEFERRED_RELEASE
605 XSync (dpy, FALSE);
606 #endif
607 shmctl (pool->shm.shmid, IPC_RMID, NULL);
609 if (! success)
610 goto cleanup_shm;
612 if (_cairo_mempool_init (&pool->mem, pool->shm.shmaddr, bytes,
613 minbits, maxbits - minbits + 1))
614 goto cleanup_detach;
616 cairo_list_add (&pool->link, &display->shm->pool);
618 *ptr = _cairo_mempool_alloc (&pool->mem, size);
619 assert (*ptr != NULL);
620 return pool;
622 cleanup_detach:
623 XShmDetach (dpy, &pool->shm);
624 cleanup_shm:
625 shmdt (pool->shm.shmaddr);
626 cleanup:
627 free (pool);
628 return NULL;
631 static cairo_xlib_shm_info_t *
632 _cairo_xlib_shm_info_create (cairo_xlib_display_t *display,
633 size_t size, cairo_bool_t will_sync)
635 cairo_xlib_shm_info_t *info;
636 cairo_xlib_shm_t *pool;
637 unsigned long last_request = 0;
638 void *mem = NULL;
640 _cairo_xlib_shm_info_cleanup (display);
641 pool = _cairo_xlib_shm_pool_find (display, size, &mem);
642 _cairo_xlib_shm_pool_cleanup (display);
644 if (pool == NULL && will_sync)
645 pool = _cairo_xlib_shm_info_find (display, size, &mem, &last_request);
646 if (pool == NULL)
647 pool = _cairo_xlib_shm_pool_create (display, size, &mem);
648 if (pool == NULL)
649 return NULL;
651 assert (mem != NULL);
653 info = malloc (sizeof (*info));
654 if (info == NULL) {
655 _cairo_mempool_free (&pool->mem, mem);
656 return NULL;
659 info->pool = pool;
660 info->mem = mem;
661 info->size = size;
662 info->last_request = last_request;
664 return info;
667 static cairo_status_t
668 _cairo_xlib_shm_surface_flush (void *abstract_surface, unsigned flags)
670 cairo_xlib_shm_surface_t *shm = abstract_surface;
671 cairo_xlib_display_t *display;
672 Display *dpy;
673 cairo_status_t status;
675 if (shm->active == 0)
676 return CAIRO_STATUS_SUCCESS;
678 if (shm->image.base._finishing)
679 return CAIRO_STATUS_SUCCESS;
681 if (seqno_passed (shm->active, peek_processed (shm->image.base.device))) {
682 shm->active = 0;
683 return CAIRO_STATUS_SUCCESS;
686 status = _cairo_xlib_display_acquire (shm->image.base.device, &display);
687 if (unlikely (status))
688 return status;
690 send_event (display, shm->info, shm->active);
692 dpy = display->display;
693 XEventsQueued (dpy, QueuedAfterReading);
694 while (! seqno_passed (shm->active, LastKnownRequestProcessed (dpy))) {
695 LockDisplay(dpy);
696 _XReadEvents(dpy);
697 UnlockDisplay(dpy);
700 cairo_device_release (&display->base);
701 shm->active = 0;
703 return CAIRO_STATUS_SUCCESS;
706 static inline cairo_bool_t
707 active (cairo_xlib_shm_surface_t *shm, Display *dpy)
709 return (shm->active &&
710 ! seqno_passed (shm->active, LastKnownRequestProcessed (dpy)));
713 static cairo_status_t
714 _cairo_xlib_shm_surface_finish (void *abstract_surface)
716 cairo_xlib_shm_surface_t *shm = abstract_surface;
717 cairo_xlib_display_t *display;
718 cairo_status_t status;
720 if (shm->image.base.damage) {
721 _cairo_damage_destroy (shm->image.base.damage);
722 shm->image.base.damage = _cairo_damage_create_in_error (CAIRO_STATUS_SURFACE_FINISHED);
725 status = _cairo_xlib_display_acquire (shm->image.base.device, &display);
726 if (unlikely (status))
727 return status;
729 if (shm->pixmap)
730 XFreePixmap (display->display, shm->pixmap);
732 if (active (shm, display->display)) {
733 shm->info->last_request = shm->active;
734 _pqueue_push (&display->shm->info, shm->info);
735 if (seqno_before (display->shm->last_request, shm->active))
736 display->shm->last_request = shm->active;
737 } else {
738 _cairo_mempool_free (&shm->info->pool->mem, shm->info->mem);
739 free (shm->info);
741 _cairo_xlib_shm_pool_cleanup (display);
744 cairo_list_del (&shm->link);
746 cairo_device_release (&display->base);
747 return _cairo_image_surface_finish (abstract_surface);
750 static const cairo_surface_backend_t cairo_xlib_shm_surface_backend = {
751 CAIRO_SURFACE_TYPE_IMAGE,
752 _cairo_xlib_shm_surface_finish,
754 _cairo_default_context_create,
756 _cairo_image_surface_create_similar,
757 NULL, /* create similar image */
758 _cairo_image_surface_map_to_image,
759 _cairo_image_surface_unmap_image,
761 _cairo_image_surface_source,
762 _cairo_image_surface_acquire_source_image,
763 _cairo_image_surface_release_source_image,
764 _cairo_image_surface_snapshot,
766 NULL, /* copy_page */
767 NULL, /* show_page */
769 _cairo_image_surface_get_extents,
770 _cairo_image_surface_get_font_options,
772 _cairo_xlib_shm_surface_flush,
773 NULL,
775 _cairo_image_surface_paint,
776 _cairo_image_surface_mask,
777 _cairo_image_surface_stroke,
778 _cairo_image_surface_fill,
779 NULL, /* fill-stroke */
780 _cairo_image_surface_glyphs,
783 static cairo_bool_t
784 has_shm (cairo_xlib_surface_t *surface)
786 cairo_xlib_display_t *display = (cairo_xlib_display_t *)surface->base.device;
787 return display->shm != NULL;
790 static int
791 has_shm_pixmaps (cairo_xlib_surface_t *surface)
793 cairo_xlib_display_t *display = (cairo_xlib_display_t *)surface->base.device;
794 if (!display->shm)
795 return 0;
797 return display->shm->has_pixmaps;
800 static cairo_xlib_shm_surface_t *
801 _cairo_xlib_shm_surface_create (cairo_xlib_surface_t *other,
802 pixman_format_code_t format,
803 int width, int height,
804 cairo_bool_t will_sync,
805 int create_pixmap)
807 cairo_xlib_shm_surface_t *shm;
808 cairo_xlib_display_t *display;
809 pixman_image_t *image;
810 int stride, size;
812 stride = CAIRO_STRIDE_FOR_WIDTH_BPP (width, PIXMAN_FORMAT_BPP(format));
813 size = stride * height;
814 if (size < MIN_SIZE)
815 return NULL;
817 shm = malloc (sizeof (*shm));
818 if (unlikely (shm == NULL))
819 return (cairo_xlib_shm_surface_t *)_cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
821 _cairo_surface_init (&shm->image.base,
822 &cairo_xlib_shm_surface_backend,
823 other->base.device,
824 _cairo_content_from_pixman_format (format));
826 if (_cairo_xlib_display_acquire (other->base.device, &display))
827 goto cleanup_shm;
829 shm->info = _cairo_xlib_shm_info_create (display, size, will_sync);
830 if (shm->info == NULL)
831 goto cleanup_display;
833 image = pixman_image_create_bits (format, width, height,
834 (uint32_t *) shm->info->mem, stride);
835 if (image == NULL)
836 goto cleanup_info;
838 _cairo_image_surface_init (&shm->image, image, format);
840 shm->pixmap = 0;
841 if (create_pixmap && size >= create_pixmap) {
842 shm->pixmap = XShmCreatePixmap (display->display,
843 other->drawable,
844 shm->info->mem,
845 &shm->info->pool->shm,
846 shm->image.width,
847 shm->image.height,
848 shm->image.depth);
850 shm->active = shm->info->last_request;
851 shm->idle = -5;
853 assert (shm->active == 0 || will_sync);
855 cairo_list_add (&shm->link, &display->shm->surfaces);
857 cairo_device_release (&display->base);
859 return shm;
861 cleanup_info:
862 _cairo_mempool_free (&shm->info->pool->mem, shm->info->mem);
863 free(shm->info);
864 cleanup_display:
865 cairo_device_release (&display->base);
866 cleanup_shm:
867 free (shm);
868 return NULL;
871 static void
872 _cairo_xlib_surface_update_shm (cairo_xlib_surface_t *surface)
874 cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *)surface->shm;
875 cairo_xlib_display_t *display;
876 cairo_damage_t *damage;
877 GC gc;
879 damage = _cairo_damage_reduce (surface->base.damage);
880 surface->base.damage = _cairo_damage_create();
882 if (_cairo_xlib_display_acquire (surface->base.device, &display))
883 goto cleanup_damage;
885 if (_cairo_xlib_surface_get_gc (display, surface, &gc))
886 goto cleanup_display;
888 if (! surface->owns_pixmap) {
889 XGCValues gcv;
891 gcv.subwindow_mode = IncludeInferiors;
892 XChangeGC (display->display, gc, GCSubwindowMode, &gcv);
895 if (damage->region) {
896 XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)];
897 XRectangle *rects = stack_rects;
898 cairo_rectangle_int_t r;
899 int n_rects, i;
901 n_rects = cairo_region_num_rectangles (damage->region);
902 if (n_rects == 0) {
903 } else if (n_rects == 1) {
904 cairo_region_get_rectangle (damage->region, 0, &r);
905 XCopyArea (display->display,
906 surface->drawable, shm->pixmap, gc,
907 r.x, r.y,
908 r.width, r.height,
909 r.x, r.y);
910 } else {
911 if (n_rects > ARRAY_LENGTH (stack_rects)) {
912 rects = _cairo_malloc_ab (n_rects, sizeof (XRectangle));
913 if (unlikely (rects == NULL)) {
914 rects = stack_rects;
915 n_rects = ARRAY_LENGTH (stack_rects);
918 for (i = 0; i < n_rects; i++) {
919 cairo_region_get_rectangle (damage->region, i, &r);
921 rects[i].x = r.x;
922 rects[i].y = r.y;
923 rects[i].width = r.width;
924 rects[i].height = r.height;
926 XSetClipRectangles (display->display, gc, 0, 0, rects, i, YXBanded);
928 XCopyArea (display->display,
929 surface->drawable, shm->pixmap, gc,
930 0, 0,
931 shm->image.width, shm->image.height,
932 0, 0);
934 if (damage->status == CAIRO_STATUS_SUCCESS && damage->region)
935 XSetClipMask (display->display, gc, None);
937 } else {
938 XCopyArea (display->display,
939 surface->drawable, shm->pixmap, gc,
940 0, 0,
941 shm->image.width, shm->image.height,
942 0, 0);
945 if (! surface->owns_pixmap) {
946 XGCValues gcv;
948 gcv.subwindow_mode = ClipByChildren;
949 XChangeGC (display->display, gc, GCSubwindowMode, &gcv);
952 _cairo_xlib_display_sync (display);
953 shm->active = 0;
954 shm->idle--;
956 _cairo_xlib_surface_put_gc (display, surface, gc);
957 cleanup_display:
958 cairo_device_release (&display->base);
959 cleanup_damage:
960 _cairo_damage_destroy (damage);
963 static void
964 _cairo_xlib_surface_clear_shm (cairo_xlib_surface_t *surface)
966 cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *)surface->shm;
968 assert (shm->active == 0);
970 _cairo_damage_destroy (surface->base.damage);
971 surface->base.damage = _cairo_damage_create();
973 memset (shm->image.data, 0, shm->image.stride * shm->image.height);
974 shm->image.base.is_clear = TRUE;
977 static void inc_idle (cairo_surface_t *surface)
979 cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *)surface;
980 shm->idle++;
983 static void dec_idle (cairo_surface_t *surface)
985 cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *)surface;
986 shm->idle--;
989 cairo_surface_t *
990 _cairo_xlib_surface_get_shm (cairo_xlib_surface_t *surface,
991 cairo_bool_t overwrite)
993 if (surface->fallback) {
994 assert (surface->base.damage);
995 assert (surface->shm);
996 assert (surface->shm->damage);
997 goto done;
1000 if (surface->shm == NULL) {
1001 pixman_format_code_t pixman_format;
1002 cairo_bool_t will_sync;
1004 if (! has_shm_pixmaps (surface))
1005 return NULL;
1007 if ((surface->width | surface->height) < 32)
1008 return NULL;
1010 pixman_format = _pixman_format_for_xlib_surface (surface);
1011 if (pixman_format == 0)
1012 return NULL;
1014 will_sync = !surface->base.is_clear && !overwrite;
1016 surface->shm =
1017 &_cairo_xlib_shm_surface_create (surface, pixman_format,
1018 surface->width, surface->height,
1019 will_sync, 1)->image.base;
1020 if (surface->shm == NULL)
1021 return NULL;
1023 assert (surface->base.damage == NULL);
1024 if (surface->base.serial || !surface->owns_pixmap) {
1025 cairo_rectangle_int_t rect;
1027 rect.x = rect.y = 0;
1028 rect.width = surface->width;
1029 rect.height = surface->height;
1031 surface->base.damage =
1032 _cairo_damage_add_rectangle (NULL, &rect);
1033 } else
1034 surface->base.damage = _cairo_damage_create ();
1036 surface->shm->damage = _cairo_damage_create ();
1039 if (overwrite) {
1040 _cairo_damage_destroy (surface->base.damage);
1041 surface->base.damage = _cairo_damage_create ();
1044 if (!surface->base.is_clear && surface->base.damage->dirty)
1045 _cairo_xlib_surface_update_shm (surface);
1047 _cairo_xlib_shm_surface_flush (surface->shm, 1);
1049 if (surface->base.is_clear && surface->base.damage->dirty)
1050 _cairo_xlib_surface_clear_shm (surface);
1052 done:
1053 dec_idle(surface->shm);
1054 return surface->shm;
1057 cairo_int_status_t
1058 _cairo_xlib_surface_put_shm (cairo_xlib_surface_t *surface)
1060 cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
1062 if (!surface->fallback) {
1063 if (surface->shm)
1064 inc_idle (surface->shm);
1065 return CAIRO_INT_STATUS_SUCCESS;
1068 if (surface->shm->damage->dirty) {
1069 cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) surface->shm;
1070 cairo_xlib_display_t *display;
1071 cairo_damage_t *damage;
1072 GC gc;
1074 status = _cairo_xlib_display_acquire (surface->base.device, &display);
1075 if (unlikely (status))
1076 return status;
1078 damage = _cairo_damage_reduce (shm->image.base.damage);
1079 shm->image.base.damage = _cairo_damage_create ();
1081 TRACE ((stderr, "%s: flushing damage x %d\n", __FUNCTION__,
1082 damage->region ? cairo_region_num_rectangles (damage->region) : 0));
1083 if (damage->status == CAIRO_STATUS_SUCCESS && damage->region) {
1084 XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)];
1085 XRectangle *rects = stack_rects;
1086 cairo_rectangle_int_t r;
1087 int n_rects, i;
1089 n_rects = cairo_region_num_rectangles (damage->region);
1090 if (n_rects == 0)
1091 goto out;
1093 status = _cairo_xlib_surface_get_gc (display, surface, &gc);
1094 if (unlikely (status))
1095 goto out;
1097 if (n_rects == 1) {
1098 cairo_region_get_rectangle (damage->region, 0, &r);
1099 _cairo_xlib_shm_surface_mark_active (surface->shm);
1100 XCopyArea (display->display,
1101 shm->pixmap, surface->drawable, gc,
1102 r.x, r.y,
1103 r.width, r.height,
1104 r.x, r.y);
1105 } else {
1106 if (n_rects > ARRAY_LENGTH (stack_rects)) {
1107 rects = _cairo_malloc_ab (n_rects, sizeof (XRectangle));
1108 if (unlikely (rects == NULL)) {
1109 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
1110 _cairo_xlib_surface_put_gc (display, surface, gc);
1111 goto out;
1114 for (i = 0; i < n_rects; i++) {
1115 cairo_region_get_rectangle (damage->region, i, &r);
1117 rects[i].x = r.x;
1118 rects[i].y = r.y;
1119 rects[i].width = r.width;
1120 rects[i].height = r.height;
1122 XSetClipRectangles (display->display, gc, 0, 0, rects, i, YXBanded);
1124 _cairo_xlib_shm_surface_mark_active (surface->shm);
1125 XCopyArea (display->display,
1126 shm->pixmap, surface->drawable, gc,
1127 0, 0,
1128 shm->image.width, shm->image.height,
1129 0, 0);
1131 if (damage->status == CAIRO_STATUS_SUCCESS && damage->region)
1132 XSetClipMask (display->display, gc, None);
1135 _cairo_xlib_surface_put_gc (display, surface, gc);
1138 out:
1139 _cairo_damage_destroy (damage);
1140 cairo_device_release (&display->base);
1143 return status;
1146 cairo_surface_t *
1147 _cairo_xlib_surface_create_shm (cairo_xlib_surface_t *other,
1148 pixman_format_code_t format,
1149 int width, int height)
1151 cairo_surface_t *surface;
1153 surface = NULL;
1154 if (has_shm (other))
1155 surface = &_cairo_xlib_shm_surface_create (other, format, width, height,
1156 FALSE, has_shm_pixmaps (other))->image.base;
1158 return surface;
1161 cairo_surface_t *
1162 _cairo_xlib_surface_create_shm__image (cairo_xlib_surface_t *surface,
1163 pixman_format_code_t format,
1164 int width, int height)
1166 if (! has_shm(surface))
1167 return NULL;
1169 return &_cairo_xlib_shm_surface_create (surface, format, width, height,
1170 FALSE, 0)->image.base;
1173 cairo_surface_t *
1174 _cairo_xlib_surface_create_similar_shm (void *other,
1175 cairo_format_t format,
1176 int width, int height)
1178 cairo_surface_t *surface;
1180 surface = _cairo_xlib_surface_create_shm (other,
1181 _cairo_format_to_pixman_format_code (format),
1182 width, height);
1183 if (surface) {
1184 if (! surface->is_clear) {
1185 cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) surface;
1186 assert (shm->active == 0);
1187 memset (shm->image.data, 0, shm->image.stride * shm->image.height);
1188 shm->image.base.is_clear = TRUE;
1190 } else
1191 surface = cairo_image_surface_create (format, width, height);
1193 return surface;
1196 void
1197 _cairo_xlib_shm_surface_mark_active (cairo_surface_t *_shm)
1199 cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) _shm;
1200 cairo_xlib_display_t *display = (cairo_xlib_display_t *) _shm->device;
1202 shm->active = NextRequest (display->display);
1205 void
1206 _cairo_xlib_shm_surface_get_ximage (cairo_surface_t *surface,
1207 XImage *ximage)
1209 cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) surface;
1210 int native_byte_order = _cairo_is_little_endian () ? LSBFirst : MSBFirst;
1211 cairo_format_masks_t image_masks;
1212 int ret;
1214 ret = _pixman_format_to_masks (shm->image.pixman_format, &image_masks);
1215 assert (ret);
1217 ximage->width = shm->image.width;
1218 ximage->height = shm->image.height;
1219 ximage->format = ZPixmap;
1220 ximage->data = (char *) shm->image.data;
1221 ximage->obdata = (char *)&shm->info->pool->shm;
1222 ximage->byte_order = native_byte_order;
1223 ximage->bitmap_unit = 32; /* always for libpixman */
1224 ximage->bitmap_bit_order = native_byte_order;
1225 ximage->bitmap_pad = 32; /* always for libpixman */
1226 ximage->depth = shm->image.depth;
1227 ximage->bytes_per_line = shm->image.stride;
1228 ximage->bits_per_pixel = image_masks.bpp;
1229 ximage->red_mask = image_masks.red_mask;
1230 ximage->green_mask = image_masks.green_mask;
1231 ximage->blue_mask = image_masks.blue_mask;
1232 ximage->xoffset = 0;
1234 ret = XInitImage (ximage);
1235 assert (ret != 0);
1238 void *
1239 _cairo_xlib_shm_surface_get_obdata (cairo_surface_t *surface)
1241 cairo_xlib_display_t *display = (cairo_xlib_display_t *) surface->device;
1242 cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) surface;
1244 display->shm->last_event = shm->active = NextRequest (display->display);
1245 return &shm->info->pool->shm;
1248 Pixmap
1249 _cairo_xlib_shm_surface_get_pixmap (cairo_surface_t *surface)
1251 cairo_xlib_shm_surface_t *shm;
1253 shm = (cairo_xlib_shm_surface_t *) surface;
1254 return shm->pixmap;
1257 XRenderPictFormat *
1258 _cairo_xlib_shm_surface_get_xrender_format (cairo_surface_t *surface)
1260 cairo_xlib_shm_surface_t *shm;
1262 shm = (cairo_xlib_shm_surface_t *) surface;
1263 if (shm->image.format != CAIRO_FORMAT_INVALID)
1264 return _cairo_xlib_display_get_xrender_format ((cairo_xlib_display_t *)surface->device,
1265 shm->image.format);
1267 return _cairo_xlib_display_get_xrender_format_for_pixman((cairo_xlib_display_t *)surface->device,
1268 shm->image.pixman_format);
1271 cairo_bool_t
1272 _cairo_xlib_shm_surface_is_active (cairo_surface_t *surface)
1274 cairo_xlib_shm_surface_t *shm;
1276 shm = (cairo_xlib_shm_surface_t *) surface;
1277 if (shm->active == 0)
1278 return FALSE;
1280 if (seqno_passed (shm->active, peek_processed (shm->image.base.device))) {
1281 shm->active = 0;
1282 return FALSE;
1285 return TRUE;
1288 cairo_bool_t
1289 _cairo_xlib_shm_surface_is_idle (cairo_surface_t *surface)
1291 cairo_xlib_shm_surface_t *shm;
1293 shm = (cairo_xlib_shm_surface_t *) surface;
1294 return shm->idle > 0;
1297 #define XORG_VERSION_ENCODE(major,minor,patch,snap) \
1298 (((major) * 10000000) + ((minor) * 100000) + ((patch) * 1000) + snap)
1300 static cairo_bool_t
1301 has_broken_send_shm_event (cairo_xlib_display_t *display,
1302 cairo_xlib_shm_display_t *shm)
1304 Display *dpy = display->display;
1305 int (*old_handler) (Display *display, XErrorEvent *event);
1306 XShmCompletionEvent ev;
1307 XShmSegmentInfo info;
1309 info.shmid = shmget (IPC_PRIVATE, 0x1000, IPC_CREAT | 0600);
1310 if (info.shmid == -1)
1311 return TRUE;
1313 info.readOnly = FALSE;
1314 info.shmaddr = shmat (info.shmid, NULL, 0);
1315 if (info.shmaddr == (char *) -1) {
1316 shmctl (info.shmid, IPC_RMID, NULL);
1317 return TRUE;
1320 ev.type = shm->event;
1321 ev.send_event = 1;
1322 ev.serial = 1;
1323 ev.drawable = shm->window;
1324 ev.major_code = shm->opcode;
1325 ev.minor_code = X_ShmPutImage;
1327 ev.shmseg = info.shmid;
1328 ev.offset = 0;
1330 assert (CAIRO_MUTEX_IS_LOCKED (_cairo_xlib_display_mutex));
1331 _x_error_occurred = FALSE;
1333 XLockDisplay (dpy);
1334 XSync (dpy, False);
1335 old_handler = XSetErrorHandler (_check_error_handler);
1337 XShmAttach (dpy, &info);
1338 XSendEvent (dpy, ev.drawable, False, 0, (XEvent *)&ev);
1339 XShmDetach (dpy, &info);
1341 XSync (dpy, False);
1342 XSetErrorHandler (old_handler);
1343 XUnlockDisplay (dpy);
1345 shmctl (info.shmid, IPC_RMID, NULL);
1346 shmdt (info.shmaddr);
1348 return _x_error_occurred;
1351 static cairo_bool_t
1352 xorg_has_buggy_send_shm_completion_event(cairo_xlib_display_t *display,
1353 cairo_xlib_shm_display_t *shm)
1355 Display *dpy = display->display;
1357 /* As libXext sets the SEND_EVENT bit in the ShmCompletionEvent,
1358 * the Xserver may crash if it does not take care when processing
1359 * the event type. For instance versions of Xorg prior to 1.11.1
1360 * exhibited this bug, and was fixed by:
1362 * commit 2d2dce558d24eeea0eb011ec9ebaa6c5c2273c39
1363 * Author: Sam Spilsbury <sam.spilsbury@canonical.com>
1364 * Date: Wed Sep 14 09:58:34 2011 +0800
1366 * Remove the SendEvent bit (0x80) before doing range checks on event type.
1368 if (_cairo_xlib_vendor_is_xorg (dpy) &&
1369 VendorRelease (dpy) < XORG_VERSION_ENCODE(1,11,0,1))
1370 return TRUE;
1372 /* For everyone else check that no error is generated */
1373 return has_broken_send_shm_event (display, shm);
1376 void
1377 _cairo_xlib_display_init_shm (cairo_xlib_display_t *display)
1379 cairo_xlib_shm_display_t *shm;
1380 XSetWindowAttributes attr;
1381 XExtCodes *codes;
1382 int has_pixmap, scr;
1384 display->shm = NULL;
1386 if (!can_use_shm (display->display, &has_pixmap))
1387 return;
1389 shm = malloc (sizeof (*shm));
1390 if (unlikely (shm == NULL))
1391 return;
1393 codes = XInitExtension (display->display, SHMNAME);
1394 if (codes == NULL) {
1395 free (shm);
1396 return;
1399 shm->opcode = codes ->major_opcode;
1400 shm->event = codes->first_event;
1402 if (unlikely (_pqueue_init (&shm->info))) {
1403 free (shm);
1404 return;
1407 scr = DefaultScreen (display->display);
1408 attr.override_redirect = 1;
1409 shm->window = XCreateWindow (display->display,
1410 DefaultRootWindow (display->display), -1, -1,
1411 1, 1, 0,
1412 DefaultDepth (display->display, scr),
1413 InputOutput,
1414 DefaultVisual (display->display, scr),
1415 CWOverrideRedirect, &attr);
1416 shm->last_event = 0;
1417 shm->last_request = 0;
1419 if (xorg_has_buggy_send_shm_completion_event(display, shm))
1420 has_pixmap = 0;
1422 shm->has_pixmaps = has_pixmap ? MIN_PIXMAP_SIZE : 0;
1423 cairo_list_init (&shm->pool);
1425 cairo_list_init (&shm->surfaces);
1427 display->shm = shm;
1430 void
1431 _cairo_xlib_display_fini_shm (cairo_xlib_display_t *display)
1433 cairo_xlib_shm_display_t *shm = display->shm;
1435 if (shm == NULL)
1436 return;
1438 while (!cairo_list_is_empty (&shm->surfaces))
1439 cairo_surface_finish (&cairo_list_first_entry (&shm->surfaces,
1440 cairo_xlib_shm_surface_t,
1441 link)->image.base);
1443 _pqueue_fini (&shm->info);
1445 while (!cairo_list_is_empty (&shm->pool)) {
1446 cairo_xlib_shm_t *pool;
1448 pool = cairo_list_first_entry (&shm->pool, cairo_xlib_shm_t, link);
1449 _cairo_xlib_display_shm_pool_destroy (display, pool);
1452 if (display->display)
1453 XDestroyWindow (display->display, shm->window);
1455 free (shm);
1456 display->shm = NULL;
1458 #endif
1459 #endif