Tizen 2.0 Release
[framework/graphics/cairo.git] / src / cairo-xlib-surface-shm.c
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
3  *
4  * Copyright © 2012 Intel Corporation
5  *
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.
13  *
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
19  *
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/
24  *
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.
28  *
29  * The Original Code is the cairo graphics library.
30  *
31  * The Initial Developer of the Original Code is University of Southern
32  * California.
33  *
34  * Contributor(s):
35  *      Chris Wilson <chris@chris-wilson.co.uk>
36  */
37
38 #include "cairoint.h"
39
40 #if !CAIRO_HAS_XLIB_XCB_FUNCTIONS
41
42 #include "cairo-xlib-private.h"
43 #include "cairo-xlib-surface-private.h"
44
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) {}
47
48 cairo_surface_t *
49 _cairo_xlib_surface_get_shm (cairo_xlib_surface_t *surface,
50                              cairo_bool_t overwrite)
51 {
52     return NULL;
53 }
54
55 cairo_int_status_t
56 _cairo_xlib_surface_put_shm (cairo_xlib_surface_t *surface)
57 {
58     assert (!surface->fallback);
59     return CAIRO_INT_STATUS_SUCCESS;
60 }
61
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)
66 {
67     return NULL;
68 }
69
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)
74 {
75     return NULL;
76 }
77
78 cairo_surface_t *
79 _cairo_xlib_surface_create_similar_shm (void *other,
80                                         cairo_format_t format,
81                                         int width, int height)
82 {
83     return cairo_image_surface_create (format, width, height);
84 }
85
86 void
87 _cairo_xlib_shm_surface_mark_active (cairo_surface_t *_shm)
88 {
89     ASSERT_NOT_REACHED;
90 }
91
92 void
93 _cairo_xlib_shm_surface_get_ximage (cairo_surface_t *surface,
94                                     XImage *ximage)
95 {
96     ASSERT_NOT_REACHED;
97 }
98
99 void *
100 _cairo_xlib_shm_surface_get_obdata (cairo_surface_t *surface)
101 {
102     ASSERT_NOT_REACHED;
103     return NULL;
104 }
105
106 Pixmap
107 _cairo_xlib_shm_surface_get_pixmap (cairo_surface_t *surface)
108 {
109     ASSERT_NOT_REACHED;
110     return 0;
111 }
112
113 XRenderPictFormat *
114 _cairo_xlib_shm_surface_get_xrender_format (cairo_surface_t *surface)
115 {
116     ASSERT_NOT_REACHED;
117     return NULL;
118 }
119
120 cairo_bool_t
121 _cairo_xlib_shm_surface_is_active (cairo_surface_t *surface)
122 {
123     ASSERT_NOT_REACHED;
124     return FALSE;
125 }
126
127 cairo_bool_t
128 _cairo_xlib_shm_surface_is_idle (cairo_surface_t *surface)
129 {
130     ASSERT_NOT_REACHED;
131     return TRUE;
132 }
133
134 void _cairo_xlib_display_fini_shm (cairo_xlib_display_t *display) {}
135
136 #else
137
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"
143
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>
154
155 #define MIN_PIXMAP_SIZE 4096
156
157 #define MIN_BITS 8
158 #define MIN_SIZE (1<<(MIN_BITS-1))
159
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;
163
164 struct _cairo_xlib_shm {
165     cairo_mempool_t mem;
166
167     XShmSegmentInfo shm;
168     unsigned long attached;
169     cairo_list_t link;
170 };
171
172 struct _cairo_xlib_shm_info {
173     unsigned long last_request;
174     void *mem;
175     size_t size;
176     cairo_xlib_shm_t *pool;
177 };
178
179 struct _cairo_xlib_shm_surface {
180     cairo_image_surface_t image;
181
182     cairo_list_t link;
183     cairo_xlib_shm_info_t *info;
184     Pixmap pixmap;
185     unsigned long active;
186     int idle;
187 };
188
189 /* the parent is always given by index/2 */
190 #define PQ_PARENT_INDEX(i) ((i) >> 1)
191 #define PQ_FIRST_ENTRY 1
192
193 /* left and right children are index * 2 and (index * 2) +1 respectively */
194 #define PQ_LEFT_CHILD_INDEX(i) ((i) << 1)
195
196 #define PQ_TOP(pq) ((pq)->elements[PQ_FIRST_ENTRY])
197
198 struct pqueue {
199     int size, max_size;
200     cairo_xlib_shm_info_t **elements;
201 };
202
203 struct _cairo_xlib_shm_display {
204     int has_pixmaps;
205     int opcode;
206     int event;
207
208     Window window;
209
210     cairo_list_t surfaces;
211
212     cairo_list_t pool;
213     struct pqueue info;
214 };
215
216 static inline cairo_bool_t
217 seqno_passed (unsigned long a, unsigned long b)
218 {
219     return (long)(b - a) > 0;
220 }
221
222 static inline cairo_status_t
223 _pqueue_init (struct pqueue *pq)
224 {
225     pq->max_size = 32;
226     pq->size = 0;
227
228     pq->elements = _cairo_malloc_ab (pq->max_size,
229                                      sizeof (cairo_xlib_shm_info_t *));
230     if (unlikely (pq->elements == NULL))
231         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
232
233     PQ_TOP(pq) = NULL;
234     return CAIRO_STATUS_SUCCESS;
235 }
236
237 static inline void
238 _pqueue_fini (struct pqueue *pq)
239 {
240     free (pq->elements);
241 }
242
243 static cairo_status_t
244 _pqueue_grow (struct pqueue *pq)
245 {
246     cairo_xlib_shm_info_t **new_elements;
247
248     new_elements = _cairo_realloc_ab (pq->elements,
249                                       2 * pq->max_size,
250                                       sizeof (cairo_xlib_shm_info_t *));
251     if (unlikely (new_elements == NULL))
252         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
253
254     pq->elements = new_elements;
255     pq->max_size *= 2;
256     return CAIRO_STATUS_SUCCESS;
257 }
258
259 static void
260 _pqueue_shrink (struct pqueue *pq, int min_size)
261 {
262     cairo_xlib_shm_info_t **new_elements;
263
264     if (min_size > pq->max_size)
265         return;
266
267     new_elements = _cairo_realloc_ab (pq->elements,
268                                       min_size,
269                                       sizeof (cairo_xlib_shm_info_t *));
270     if (unlikely (new_elements == NULL))
271         return;
272
273     pq->elements = new_elements;
274     pq->max_size = min_size;
275 }
276
277 static inline cairo_status_t
278 _pqueue_push (struct pqueue *pq, cairo_xlib_shm_info_t *info)
279 {
280     cairo_xlib_shm_info_t **elements;
281     int i, parent;
282
283     if (unlikely (pq->size + 1 == pq->max_size)) {
284         cairo_status_t status;
285
286         status = _pqueue_grow (pq);
287         if (unlikely (status))
288             return status;
289     }
290
291     elements = pq->elements;
292
293     for (i = ++pq->size;
294          i != PQ_FIRST_ENTRY &&
295          info->last_request < elements[parent = PQ_PARENT_INDEX (i)]->last_request;
296          i = parent)
297     {
298         elements[i] = elements[parent];
299     }
300
301     elements[i] = info;
302
303     return CAIRO_STATUS_SUCCESS;
304 }
305
306 static inline void
307 _pqueue_pop (struct pqueue *pq)
308 {
309     cairo_xlib_shm_info_t **elements = pq->elements;
310     cairo_xlib_shm_info_t *tail;
311     int child, i;
312
313     tail = elements[pq->size--];
314     if (pq->size == 0) {
315         elements[PQ_FIRST_ENTRY] = NULL;
316         _pqueue_shrink (pq, 32);
317         return;
318     }
319
320     for (i = PQ_FIRST_ENTRY;
321          (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size;
322          i = child)
323     {
324         if (child != pq->size &&
325             elements[child+1]->last_request < elements[child]->last_request)
326         {
327             child++;
328         }
329
330         if (elements[child]->last_request >= tail->last_request)
331             break;
332
333         elements[i] = elements[child];
334     }
335     elements[i] = tail;
336 }
337
338 static cairo_bool_t _x_error_occurred;
339
340 static int
341 _check_error_handler (Display     *display,
342                      XErrorEvent *event)
343 {
344     _x_error_occurred = TRUE;
345     return False; /* ignored */
346 }
347
348 static cairo_bool_t
349 can_use_shm (Display *dpy, int *has_pixmap)
350 {
351     XShmSegmentInfo shm;
352     int (*old_handler) (Display *display, XErrorEvent *event);
353     Status success;
354     int major, minor;
355
356     if (! XShmQueryExtension (dpy))
357         return FALSE;
358
359     XShmQueryVersion (dpy, &major, &minor, has_pixmap);
360
361     shm.shmid = shmget (IPC_PRIVATE, 0x1000, IPC_CREAT | 0600);
362     if (shm.shmid == -1)
363         return FALSE;
364
365     shm.readOnly = FALSE;
366     shm.shmaddr = shmat (shm.shmid, NULL, 0);
367     if (shm.shmaddr == (char *) -1) {
368         shmctl (shm.shmid, IPC_RMID, NULL);
369         return FALSE;
370     }
371
372     assert (CAIRO_MUTEX_IS_LOCKED (_cairo_xlib_display_mutex));
373     _x_error_occurred = FALSE;
374
375     XLockDisplay (dpy);
376     XSync (dpy, False);
377     old_handler = XSetErrorHandler (_check_error_handler);
378
379     success = XShmAttach (dpy, &shm);
380     if (success)
381         XShmDetach (dpy, &shm);
382
383     XSync (dpy, False);
384     XSetErrorHandler (old_handler);
385     XUnlockDisplay (dpy);
386
387     shmctl (shm.shmid, IPC_RMID, NULL);
388     shmdt (shm.shmaddr);
389
390     return success && ! _x_error_occurred;
391 }
392
393 static inline Display *
394 peek_display (cairo_device_t *device)
395 {
396     return ((cairo_xlib_display_t *)device)->display;
397 }
398
399 static inline unsigned long
400 peek_processed (cairo_device_t *device)
401 {
402     return LastKnownRequestProcessed (peek_display(device));
403 }
404
405 static unsigned next_request (cairo_device_t *device)
406 {
407     return NextRequest (peek_display (device));
408 }
409
410 static void
411 _cairo_xlib_display_shm_pool_destroy (cairo_xlib_display_t *display,
412                                       cairo_xlib_shm_t *pool)
413 {
414     shmdt (pool->shm.shmaddr);
415     if (display->display) /* may be called after CloseDisplay */
416         XShmDetach (display->display, &pool->shm);
417
418     _cairo_mempool_fini (&pool->mem);
419
420     cairo_list_del (&pool->link);
421     free (pool);
422 }
423
424 static void
425 _cairo_xlib_shm_info_cleanup (cairo_xlib_display_t *display)
426 {
427     cairo_xlib_shm_info_t *info;
428     Display *dpy = display->display;
429     struct pqueue *pq = &display->shm->info;
430     unsigned long processed;
431
432     if (PQ_TOP(pq) == NULL)
433         return;
434
435     XEventsQueued (dpy, QueuedAfterReading);
436     processed = LastKnownRequestProcessed (dpy);
437
438     info = PQ_TOP(pq);
439     do {
440         if (! seqno_passed (info->last_request, processed))
441             break;
442
443         _cairo_mempool_free (&info->pool->mem, info->mem);
444         _pqueue_pop (&display->shm->info);
445         free (info);
446     } while ((info = PQ_TOP(pq)));
447 }
448
449 static cairo_xlib_shm_info_t *
450 _cairo_xlib_shm_info_find (cairo_xlib_display_t *display,
451                            size_t size, unsigned long *last_request)
452 {
453     cairo_xlib_shm_info_t *info;
454     struct pqueue *pq = &display->shm->info;
455
456     if (PQ_TOP(pq) == NULL)
457         return NULL;
458
459     info = PQ_TOP(pq);
460     do {
461         _pqueue_pop (&display->shm->info);
462
463         if (info->size >= size && size <= 2*info->size)
464             return info;
465
466         *last_request = info->last_request;
467         _cairo_mempool_free (&info->pool->mem, info->mem);
468         free (info);
469     } while ((info = PQ_TOP(pq)));
470
471     return NULL;
472 }
473
474 static cairo_xlib_shm_t *
475 _cairo_xlib_shm_pool_find (cairo_xlib_display_t *display,
476                            size_t size,
477                            void **ptr)
478 {
479     cairo_xlib_shm_t *pool;
480
481     cairo_list_foreach_entry (pool, cairo_xlib_shm_t, &display->shm->pool, link) {
482         if (pool->mem.free_bytes >= size) {
483             void *mem = _cairo_mempool_alloc (&pool->mem, size);
484             if (mem != NULL) {
485                 *ptr = mem;
486                 return pool;
487             }
488         }
489     }
490
491     return NULL;
492 }
493
494 static void
495 _cairo_xlib_shm_pool_cleanup (cairo_xlib_display_t *display)
496 {
497     cairo_xlib_shm_t *pool, *next;
498     unsigned long processed;
499
500     processed = LastKnownRequestProcessed (display->display);
501
502     cairo_list_foreach_entry_safe (pool, next, cairo_xlib_shm_t,
503                                    &display->shm->pool, link) {
504         if (! seqno_passed (pool->attached, processed))
505             break;
506
507         if (pool->mem.free_bytes == pool->mem.max_bytes)
508             _cairo_xlib_display_shm_pool_destroy (display, pool);
509     }
510 }
511
512 static cairo_xlib_shm_t *
513 _cairo_xlib_shm_pool_create(cairo_xlib_display_t *display,
514                             size_t size, void **ptr)
515 {
516     Display *dpy = display->display;
517     cairo_xlib_shm_t *pool;
518     size_t bytes, maxbits = 16, minbits = MIN_BITS;
519     Status success;
520
521     pool = malloc (sizeof (cairo_xlib_shm_t));
522     if (pool == NULL)
523         return NULL;
524
525     bytes = 1 << maxbits;
526     while (bytes <= size)
527         bytes <<= 1, maxbits++;
528     bytes <<= 3;
529
530     minbits += (maxbits - 16) / 2;
531
532     pool->shm.shmid = shmget (IPC_PRIVATE, bytes, IPC_CREAT | 0600);
533     while (pool->shm.shmid == -1 && bytes >= 2*size) {
534         bytes >>= 1;
535         pool->shm.shmid = shmget (IPC_PRIVATE, bytes, IPC_CREAT | 0600);
536     }
537     if (pool->shm.shmid == -1)
538         goto cleanup;
539
540     pool->shm.readOnly = FALSE;
541     pool->shm.shmaddr = shmat (pool->shm.shmid, NULL, 0);
542     if (pool->shm.shmaddr == (char *) -1) {
543         shmctl (pool->shm.shmid, IPC_RMID, NULL);
544         goto cleanup;
545     }
546
547     pool->attached = NextRequest (dpy);
548     success = XShmAttach (dpy, &pool->shm);
549 #if !IPC_RMID_DEFERRED_RELEASE
550     XSync (dpy, FALSE);
551 #endif
552     shmctl (pool->shm.shmid, IPC_RMID, NULL);
553
554     if (! success)
555         goto cleanup_shm;
556
557     if (_cairo_mempool_init (&pool->mem, pool->shm.shmaddr, bytes,
558                              minbits, maxbits - minbits + 1))
559         goto cleanup_detach;
560
561     cairo_list_add (&pool->link, &display->shm->pool);
562
563     *ptr = _cairo_mempool_alloc (&pool->mem, size);
564     assert (*ptr != NULL);
565     return pool;
566
567 cleanup_detach:
568     XShmDetach (dpy, &pool->shm);
569 cleanup_shm:
570     shmdt (pool->shm.shmaddr);
571 cleanup:
572     free (pool);
573     return NULL;
574 }
575
576 static cairo_xlib_shm_info_t *
577 _cairo_xlib_shm_info_create (cairo_xlib_display_t *display,
578                              size_t size, cairo_bool_t will_sync)
579 {
580     cairo_xlib_shm_info_t *info;
581     cairo_xlib_shm_t *pool;
582     unsigned long last_request = 0;
583     void *mem = NULL;
584
585     if (will_sync) {
586         info = _cairo_xlib_shm_info_find (display, size, &last_request);
587         if (info)
588             return info;
589     }
590
591     _cairo_xlib_shm_info_cleanup (display);
592     pool = _cairo_xlib_shm_pool_find (display, size, &mem);
593     _cairo_xlib_shm_pool_cleanup (display);
594     if (pool == NULL)
595         pool = _cairo_xlib_shm_pool_create (display, size, &mem);
596     if (pool == NULL)
597         return NULL;
598
599     assert (mem != NULL);
600
601     info = malloc (sizeof (*info));
602     if (info == NULL) {
603         _cairo_mempool_free (&pool->mem, mem);
604         return NULL;
605     }
606
607     info->pool = pool;
608     info->mem = mem;
609     info->size = size;
610     info->last_request = last_request;
611
612     return info;
613 }
614
615 static cairo_status_t
616 _cairo_xlib_shm_surface_flush (void *abstract_surface, unsigned flags)
617 {
618     cairo_xlib_shm_surface_t *shm = abstract_surface;
619     cairo_xlib_display_t *display;
620     cairo_status_t status;
621
622     if (shm->active == 0)
623         return CAIRO_STATUS_SUCCESS;
624
625     if (shm->image.base._finishing)
626         return CAIRO_STATUS_SUCCESS;
627
628     if (seqno_passed (shm->active, peek_processed (shm->image.base.device))) {
629         shm->active = 0;
630         return CAIRO_STATUS_SUCCESS;
631     }
632
633     status = _cairo_xlib_display_acquire (shm->image.base.device, &display);
634     if (unlikely (status))
635         return status;
636
637     XEventsQueued (display->display, QueuedAfterReading);
638     if (!seqno_passed (shm->active,
639                        LastKnownRequestProcessed (display->display)))
640         XSync (display->display, False);
641
642     cairo_device_release (&display->base);
643     shm->active = 0;
644
645     return CAIRO_STATUS_SUCCESS;
646 }
647
648 static inline cairo_bool_t
649 active (cairo_xlib_shm_surface_t *shm, Display *dpy)
650 {
651     return (shm->active &&
652             !seqno_passed (shm->active, LastKnownRequestProcessed (dpy)));
653 }
654
655 static cairo_status_t
656 _cairo_xlib_shm_surface_finish (void *abstract_surface)
657 {
658     cairo_xlib_shm_surface_t *shm = abstract_surface;
659     cairo_xlib_display_t *display;
660     cairo_status_t status;
661
662     status = _cairo_xlib_display_acquire (shm->image.base.device, &display);
663     if (unlikely (status))
664         return status;
665
666     if (shm->pixmap)
667         XFreePixmap (display->display, shm->pixmap);
668
669     if (active (shm, display->display)) {
670         shm->info->last_request = shm->active;
671         _pqueue_push (&display->shm->info, shm->info);
672     } else {
673         _cairo_mempool_free (&shm->info->pool->mem, shm->info->mem);
674         free (shm->info);
675
676         _cairo_xlib_shm_pool_cleanup (display);
677     }
678
679     cairo_list_del (&shm->link);
680
681     cairo_device_release (&display->base);
682     return CAIRO_STATUS_SUCCESS;
683 }
684
685 static const cairo_surface_backend_t cairo_xlib_shm_surface_backend = {
686     CAIRO_SURFACE_TYPE_IMAGE,
687     _cairo_xlib_shm_surface_finish,
688
689     _cairo_default_context_create,
690
691     _cairo_image_surface_create_similar,
692     NULL, /* create similar image */
693     _cairo_image_surface_map_to_image,
694     _cairo_image_surface_unmap_image,
695
696     _cairo_image_surface_source,
697     _cairo_image_surface_acquire_source_image,
698     _cairo_image_surface_release_source_image,
699     _cairo_image_surface_snapshot,
700
701     NULL, /* copy_page */
702     NULL, /* show_page */
703
704     _cairo_image_surface_get_extents,
705     _cairo_image_surface_get_font_options,
706
707     _cairo_xlib_shm_surface_flush,
708     NULL,
709
710     _cairo_image_surface_paint,
711     _cairo_image_surface_mask,
712     _cairo_image_surface_stroke,
713     _cairo_image_surface_fill,
714     NULL, /* fill-stroke */
715     _cairo_image_surface_glyphs,
716 };
717
718 static cairo_bool_t
719 has_shm (cairo_xlib_surface_t *surface)
720 {
721     cairo_xlib_display_t *display = (cairo_xlib_display_t *)surface->base.device;
722     return display->shm != NULL;
723 }
724
725 static int
726 has_shm_pixmaps (cairo_xlib_surface_t *surface)
727 {
728     cairo_xlib_display_t *display = (cairo_xlib_display_t *)surface->base.device;
729     if (!display->shm)
730         return 0;
731
732     return display->shm->has_pixmaps;
733 }
734
735 static cairo_xlib_shm_surface_t *
736 _cairo_xlib_shm_surface_create (cairo_xlib_surface_t *other,
737                                 pixman_format_code_t format,
738                                 int width, int height,
739                                 cairo_bool_t will_sync,
740                                 int create_pixmap)
741 {
742     cairo_xlib_shm_surface_t *shm;
743     cairo_xlib_display_t *display;
744     pixman_image_t *image;
745     int stride, size;
746
747     stride = CAIRO_STRIDE_FOR_WIDTH_BPP (width, PIXMAN_FORMAT_BPP(format));
748     size = stride * height;
749     if (size < MIN_SIZE)
750         return NULL;
751
752     shm = malloc (sizeof (*shm));
753     if (unlikely (shm == NULL))
754         return (cairo_xlib_shm_surface_t *)_cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
755
756     _cairo_surface_init (&shm->image.base,
757                          &cairo_xlib_shm_surface_backend,
758                          other->base.device,
759                          _cairo_content_from_pixman_format (format));
760
761     if (_cairo_xlib_display_acquire (other->base.device, &display))
762         goto cleanup_shm;
763
764     shm->info = _cairo_xlib_shm_info_create (display, size, will_sync);
765     if (shm->info == NULL)
766         goto cleanup_display;
767
768     image = pixman_image_create_bits (format, width, height,
769                                       (uint32_t *) shm->info->mem, stride);
770     if (image == NULL)
771         goto cleanup_info;
772
773     _cairo_image_surface_init (&shm->image, image, format);
774
775     shm->pixmap = 0;
776     if (create_pixmap && size >= create_pixmap) {
777         shm->pixmap = XShmCreatePixmap (display->display,
778                                         other->drawable,
779                                         shm->info->mem,
780                                         &shm->info->pool->shm,
781                                         shm->image.width,
782                                         shm->image.height,
783                                         shm->image.depth);
784     }
785     shm->active = shm->info->last_request;
786     shm->idle = -5;
787
788     assert (shm->active == 0 || will_sync);
789
790     cairo_list_add (&shm->link, &display->shm->surfaces);
791
792     cairo_device_release (&display->base);
793
794     return shm;
795
796 cleanup_info:
797     _cairo_mempool_free (&shm->info->pool->mem, shm->info->mem);
798     free(shm->info);
799 cleanup_display:
800     cairo_device_release (&display->base);
801 cleanup_shm:
802     free (shm);
803     return NULL;
804 }
805
806 static void
807 _cairo_xlib_surface_update_shm (cairo_xlib_surface_t *surface)
808 {
809     cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *)surface->shm;
810     cairo_xlib_display_t *display;
811     cairo_damage_t *damage;
812     GC gc;
813
814     damage = _cairo_damage_reduce (surface->base.damage);
815     surface->base.damage = _cairo_damage_create();
816
817     if (_cairo_xlib_display_acquire (surface->base.device, &display))
818         goto cleanup_damage;
819
820     if (_cairo_xlib_surface_get_gc (display, surface, &gc))
821         goto cleanup_display;
822
823     if (! surface->owns_pixmap) {
824         XGCValues gcv;
825
826         gcv.subwindow_mode = IncludeInferiors;
827         XChangeGC (display->display, gc, GCSubwindowMode, &gcv);
828     }
829
830     if (damage->region) {
831         XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (sizeof (XRectangle))];
832         XRectangle *rects = stack_rects;
833         cairo_rectangle_int_t r;
834         int n_rects, i;
835
836         n_rects = cairo_region_num_rectangles (damage->region);
837         if (n_rects == 0) {
838         } else if (n_rects == 1) {
839             cairo_region_get_rectangle (damage->region, 0, &r);
840             XCopyArea (display->display,
841                        surface->drawable, shm->pixmap, gc,
842                        r.x, r.y,
843                        r.width, r.height,
844                        r.x, r.y);
845         } else {
846             if (n_rects > ARRAY_LENGTH (stack_rects)) {
847                 rects = _cairo_malloc_ab (n_rects, sizeof (XRectangle));
848                 if (unlikely (rects == NULL)) {
849                     rects = stack_rects;
850                     n_rects = ARRAY_LENGTH (stack_rects);
851                 }
852             }
853             for (i = 0; i < n_rects; i++) {
854                 cairo_region_get_rectangle (damage->region, i, &r);
855
856                 rects[i].x = r.x;
857                 rects[i].y = r.y;
858                 rects[i].width  = r.width;
859                 rects[i].height = r.height;
860             }
861             XSetClipRectangles (display->display, gc, 0, 0, rects, i, YXBanded);
862
863             XCopyArea (display->display,
864                        surface->drawable, shm->pixmap, gc,
865                        0, 0,
866                        shm->image.width, shm->image.height,
867                        0, 0);
868
869             if (damage->status == CAIRO_STATUS_SUCCESS && damage->region)
870                 XSetClipMask (display->display, gc, None);
871         }
872     } else {
873         XCopyArea (display->display,
874                    surface->drawable, shm->pixmap, gc,
875                    0, 0,
876                    shm->image.width, shm->image.height,
877                    0, 0);
878     }
879
880     if (! surface->owns_pixmap) {
881         XGCValues gcv;
882
883         gcv.subwindow_mode = ClipByChildren;
884         XChangeGC (display->display, gc, GCSubwindowMode, &gcv);
885     }
886
887     XSync (display->display, False);
888     shm->active = 0;
889     shm->idle--;
890
891     _cairo_xlib_surface_put_gc (display, surface, gc);
892 cleanup_display:
893     cairo_device_release (&display->base);
894 cleanup_damage:
895     _cairo_damage_destroy (damage);
896 }
897
898 static void
899 _cairo_xlib_surface_clear_shm (cairo_xlib_surface_t *surface)
900 {
901     cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *)surface->shm;
902
903     assert (shm->active == 0);
904
905     _cairo_damage_destroy (surface->base.damage);
906     surface->base.damage = _cairo_damage_create();
907
908     memset (shm->image.data, 0, shm->image.stride * shm->image.height);
909     shm->image.base.is_clear = TRUE;
910 }
911
912 static void inc_idle (cairo_surface_t *surface)
913 {
914     cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *)surface;
915     shm->idle++;
916 }
917
918 static void dec_idle (cairo_surface_t *surface)
919 {
920     cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *)surface;
921     shm->idle--;
922 }
923
924 cairo_surface_t *
925 _cairo_xlib_surface_get_shm (cairo_xlib_surface_t *surface,
926                              cairo_bool_t overwrite)
927 {
928     if (surface->fallback) {
929         assert (surface->base.damage);
930         assert (surface->shm);
931         assert (surface->shm->damage);
932         goto done;
933     }
934
935     if (surface->shm == NULL) {
936         pixman_format_code_t pixman_format;
937         cairo_bool_t will_sync;
938
939         if (! has_shm_pixmaps (surface))
940             return NULL;
941
942         if ((surface->width | surface->height) < 32)
943             return NULL;
944
945         pixman_format = _pixman_format_for_xlib_surface (surface);
946         if (pixman_format == 0)
947             return NULL;
948
949         will_sync = !surface->base.is_clear && !overwrite;
950
951         surface->shm =
952             &_cairo_xlib_shm_surface_create (surface, pixman_format,
953                                              surface->width, surface->height,
954                                              will_sync, 1)->image.base;
955         if (surface->shm == NULL)
956             return NULL;
957
958         assert (surface->base.damage == NULL);
959         if (surface->base.serial || !surface->owns_pixmap) {
960             cairo_rectangle_int_t rect;
961
962             rect.x = rect.y = 0;
963             rect.width = surface->width;
964             rect.height = surface->height;
965
966             surface->base.damage =
967                 _cairo_damage_add_rectangle (NULL, &rect);
968         } else
969             surface->base.damage = _cairo_damage_create ();
970
971         surface->shm->damage = _cairo_damage_create ();
972     }
973
974     if (overwrite) {
975         _cairo_damage_destroy (surface->base.damage);
976         surface->base.damage = _cairo_damage_create ();
977     }
978
979     if (!surface->base.is_clear && surface->base.damage->dirty)
980         _cairo_xlib_surface_update_shm (surface);
981
982     _cairo_xlib_shm_surface_flush (surface->shm, 1);
983
984     if (surface->base.is_clear && surface->base.damage->dirty)
985         _cairo_xlib_surface_clear_shm (surface);
986
987 done:
988     dec_idle(surface->shm);
989     return surface->shm;
990 }
991
992 cairo_int_status_t
993 _cairo_xlib_surface_put_shm (cairo_xlib_surface_t *surface)
994 {
995     cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
996
997     if (!surface->fallback) {
998         if (surface->shm)
999             inc_idle (surface->shm);
1000         return CAIRO_INT_STATUS_SUCCESS;
1001     }
1002
1003     if (surface->shm->damage->dirty) {
1004         cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) surface->shm;
1005         cairo_xlib_display_t *display;
1006         cairo_damage_t *damage;
1007         GC gc;
1008
1009         status = _cairo_xlib_display_acquire (surface->base.device, &display);
1010         if (unlikely (status))
1011             return status;
1012
1013         damage = _cairo_damage_reduce (shm->image.base.damage);
1014         shm->image.base.damage = _cairo_damage_create ();
1015
1016         status = _cairo_xlib_surface_get_gc (display, surface, &gc);
1017         if (unlikely (status))
1018             goto out;
1019
1020         TRACE ((stderr, "%s: flushing damage x %d\n", __FUNCTION__,
1021                 damage->region ? cairo_region_num_rectangles (damage->region) : 0));
1022         if (damage->status == CAIRO_STATUS_SUCCESS && damage->region) {
1023             XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (sizeof (XRectangle))];
1024             XRectangle *rects = stack_rects;
1025             cairo_rectangle_int_t r;
1026             int n_rects, i;
1027
1028             n_rects = cairo_region_num_rectangles (damage->region);
1029             if (n_rects == 0) {
1030             } else if (n_rects == 1) {
1031                 cairo_region_get_rectangle (damage->region, 0, &r);
1032                 XCopyArea (display->display,
1033                            shm->pixmap, surface->drawable, gc,
1034                            r.x, r.y,
1035                            r.width, r.height,
1036                            r.x, r.y);
1037             } else {
1038                 if (n_rects > ARRAY_LENGTH (stack_rects)) {
1039                     rects = _cairo_malloc_ab (n_rects, sizeof (XRectangle));
1040                     if (unlikely (rects == NULL)) {
1041                         status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
1042                         goto out;
1043                     }
1044                 }
1045                 for (i = 0; i < n_rects; i++) {
1046                     cairo_region_get_rectangle (damage->region, i, &r);
1047
1048                     rects[i].x = r.x;
1049                     rects[i].y = r.y;
1050                     rects[i].width  = r.width;
1051                     rects[i].height = r.height;
1052                 }
1053                 XSetClipRectangles (display->display, gc, 0, 0, rects, i, YXBanded);
1054
1055                 XCopyArea (display->display,
1056                            shm->pixmap, surface->drawable, gc,
1057                            0, 0,
1058                            shm->image.width, shm->image.height,
1059                            0, 0);
1060
1061                 if (damage->status == CAIRO_STATUS_SUCCESS && damage->region)
1062                     XSetClipMask (display->display, gc, None);
1063             }
1064         }
1065         _cairo_damage_destroy (damage);
1066
1067         _cairo_xlib_shm_surface_mark_active (surface->shm);
1068         _cairo_xlib_surface_put_gc (display, surface, gc);
1069 out:
1070         cairo_device_release (&display->base);
1071     }
1072
1073     return status;
1074 }
1075
1076 cairo_surface_t *
1077 _cairo_xlib_surface_create_shm (cairo_xlib_surface_t *other,
1078                                 pixman_format_code_t format,
1079                                 int width, int height)
1080 {
1081     cairo_surface_t *surface;
1082
1083     surface = NULL;
1084     if (has_shm (other))
1085         surface = &_cairo_xlib_shm_surface_create (other, format,
1086                                                    width, height, FALSE,
1087                                                    has_shm_pixmaps (other))->image.base;
1088
1089     return surface;
1090 }
1091
1092 cairo_surface_t *
1093 _cairo_xlib_surface_create_shm__image (cairo_xlib_surface_t *surface,
1094                                        pixman_format_code_t format,
1095                                        int width, int height)
1096 {
1097     if (! has_shm(surface))
1098         return NULL;
1099
1100     return &_cairo_xlib_shm_surface_create (surface, format,
1101                                             surface->width, surface->height,
1102                                             TRUE, 0)->image.base;
1103 }
1104
1105 cairo_surface_t *
1106 _cairo_xlib_surface_create_similar_shm (void *other,
1107                                         cairo_format_t format,
1108                                         int width, int height)
1109 {
1110     cairo_surface_t *surface;
1111
1112     surface = _cairo_xlib_surface_create_shm (other,
1113                                               _cairo_format_to_pixman_format_code (format),
1114                                               width, height);
1115     if (surface) {
1116         if (! surface->is_clear) {
1117             cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) surface;
1118             assert (shm->active == 0);
1119             memset (shm->image.data, 0, shm->image.stride * shm->image.height);
1120             shm->image.base.is_clear = TRUE;
1121         }
1122     } else
1123         surface = cairo_image_surface_create (format, width, height);
1124
1125     return surface;
1126 }
1127
1128 void
1129 _cairo_xlib_shm_surface_mark_active (cairo_surface_t *_shm)
1130 {
1131     cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) _shm;
1132     cairo_xlib_display_t *display = (cairo_xlib_display_t *) _shm->device;
1133     XShmCompletionEvent ev;
1134
1135     ev.type = display->shm->event;
1136     ev.drawable = display->shm->window;
1137     ev.major_code = display->shm->opcode;
1138     ev.minor_code = X_ShmPutImage;
1139     ev.shmseg = shm->info->pool->shm.shmid;
1140     ev.offset = (char *)shm->info->mem - (char *)shm->info->pool->shm.shmaddr;
1141
1142     shm->active = NextRequest (display->display);
1143     XSendEvent (display->display, ev.drawable, False, 0, (XEvent *)&ev);
1144 }
1145
1146 void
1147 _cairo_xlib_shm_surface_get_ximage (cairo_surface_t *surface,
1148                                     XImage *ximage)
1149 {
1150     cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) surface;
1151     int native_byte_order = _cairo_is_little_endian () ? LSBFirst : MSBFirst;
1152     cairo_format_masks_t image_masks;
1153     int ret;
1154
1155     ret = _pixman_format_to_masks (shm->image.pixman_format, &image_masks);
1156     assert (ret);
1157
1158     ximage->width = shm->image.width;
1159     ximage->height = shm->image.height;
1160     ximage->format = ZPixmap;
1161     ximage->data = (char *) shm->image.data;
1162     ximage->obdata = (char *)&shm->info->pool->shm;
1163     ximage->byte_order = native_byte_order;
1164     ximage->bitmap_unit = 32;   /* always for libpixman */
1165     ximage->bitmap_bit_order = native_byte_order;
1166     ximage->bitmap_pad = 32;    /* always for libpixman */
1167     ximage->depth = shm->image.depth;
1168     ximage->bytes_per_line = shm->image.stride;
1169     ximage->bits_per_pixel = image_masks.bpp;
1170     ximage->red_mask = image_masks.red_mask;
1171     ximage->green_mask = image_masks.green_mask;
1172     ximage->blue_mask = image_masks.blue_mask;
1173     ximage->xoffset = 0;
1174
1175     ret = XInitImage (ximage);
1176     assert (ret != 0);
1177 }
1178
1179 void *
1180 _cairo_xlib_shm_surface_get_obdata (cairo_surface_t *surface)
1181 {
1182     cairo_xlib_shm_surface_t *shm;
1183
1184     shm = (cairo_xlib_shm_surface_t *) surface;
1185     shm->active = next_request (surface->device);
1186     return &shm->info->pool->shm;
1187 }
1188
1189 Pixmap
1190 _cairo_xlib_shm_surface_get_pixmap (cairo_surface_t *surface)
1191 {
1192     cairo_xlib_shm_surface_t *shm;
1193
1194     shm = (cairo_xlib_shm_surface_t *) surface;
1195     return shm->pixmap;
1196 }
1197
1198 XRenderPictFormat *
1199 _cairo_xlib_shm_surface_get_xrender_format (cairo_surface_t *surface)
1200 {
1201     cairo_xlib_shm_surface_t *shm;
1202
1203     shm = (cairo_xlib_shm_surface_t *) surface;
1204     if (shm->image.format != CAIRO_FORMAT_INVALID)
1205         return _cairo_xlib_display_get_xrender_format ((cairo_xlib_display_t *)surface->device,
1206                                                        shm->image.format);
1207
1208     return _cairo_xlib_display_get_xrender_format_for_pixman((cairo_xlib_display_t *)surface->device,
1209                                                              shm->image.pixman_format);
1210 }
1211
1212 cairo_bool_t
1213 _cairo_xlib_shm_surface_is_active (cairo_surface_t *surface)
1214 {
1215     cairo_xlib_shm_surface_t *shm;
1216
1217     shm = (cairo_xlib_shm_surface_t *) surface;
1218     if (shm->active == 0)
1219         return FALSE;
1220
1221     if (seqno_passed (shm->active, peek_processed (shm->image.base.device))) {
1222         shm->active = 0;
1223         return FALSE;
1224     }
1225
1226     return TRUE;
1227 }
1228
1229 cairo_bool_t
1230 _cairo_xlib_shm_surface_is_idle (cairo_surface_t *surface)
1231 {
1232     cairo_xlib_shm_surface_t *shm;
1233
1234     shm = (cairo_xlib_shm_surface_t *) surface;
1235     return shm->idle > 0;
1236 }
1237
1238 #define XORG_VERSION_ENCODE(major,minor,patch,snap) \
1239     (((major) * 10000000) + ((minor) * 100000) + ((patch) * 1000) + snap)
1240
1241 static cairo_bool_t
1242 has_broken_send_shm_event (cairo_xlib_display_t *display,
1243                            cairo_xlib_shm_display_t *shm)
1244 {
1245     Display *dpy = display->display;
1246     int (*old_handler) (Display *display, XErrorEvent *event);
1247     XShmCompletionEvent ev;
1248     XShmSegmentInfo info;
1249
1250     info.shmid = shmget (IPC_PRIVATE, 0x1000, IPC_CREAT | 0600);
1251     if (info.shmid == -1)
1252         return TRUE;
1253
1254     info.readOnly = FALSE;
1255     info.shmaddr = shmat (info.shmid, NULL, 0);
1256     if (info.shmaddr == (char *) -1) {
1257         shmctl (info.shmid, IPC_RMID, NULL);
1258         return TRUE;
1259     }
1260
1261     ev.type = shm->event;
1262     ev.drawable = shm->window;
1263     ev.major_code = shm->opcode;
1264     ev.minor_code = X_ShmPutImage;
1265
1266     ev.shmseg = info.shmid;
1267     ev.offset = 0;
1268
1269     assert (CAIRO_MUTEX_IS_LOCKED (_cairo_xlib_display_mutex));
1270     _x_error_occurred = FALSE;
1271
1272     XLockDisplay (dpy);
1273     XSync (dpy, False);
1274     old_handler = XSetErrorHandler (_check_error_handler);
1275
1276     XShmAttach (dpy, &info);
1277     XSendEvent (dpy, ev.drawable, False, 0, (XEvent *)&ev);
1278     XShmDetach (dpy, &info);
1279
1280     XSync (dpy, False);
1281     XSetErrorHandler (old_handler);
1282     XUnlockDisplay (dpy);
1283
1284     shmctl (info.shmid, IPC_RMID, NULL);
1285     shmdt (info.shmaddr);
1286
1287     return _x_error_occurred;
1288 }
1289
1290 static cairo_bool_t
1291 xorg_has_buggy_send_shm_completion_event(cairo_xlib_display_t *display,
1292                                          cairo_xlib_shm_display_t *shm)
1293 {
1294     Display *dpy = display->display;
1295
1296     /* As libXext sets the SEND_EVENT bit in the ShmCompletionEvent,
1297      * the Xserver may crash if it does not take care when processing
1298      * the event type. For instance versions of Xorg prior to 1.11.1
1299      * exhibited this bug, and was fixed by:
1300      *
1301      * commit 2d2dce558d24eeea0eb011ec9ebaa6c5c2273c39
1302      * Author: Sam Spilsbury <sam.spilsbury@canonical.com>
1303      * Date:   Wed Sep 14 09:58:34 2011 +0800
1304      *
1305      * Remove the SendEvent bit (0x80) before doing range checks on event type.
1306      */
1307     if (_cairo_xlib_vendor_is_xorg (dpy) &&
1308         VendorRelease (dpy) < XORG_VERSION_ENCODE(1,11,0,1))
1309         return TRUE;
1310
1311     /* For everyone else check that no error is generated */
1312     return has_broken_send_shm_event (display, shm);
1313 }
1314
1315 void
1316 _cairo_xlib_display_init_shm (cairo_xlib_display_t *display)
1317 {
1318     cairo_xlib_shm_display_t *shm;
1319     XSetWindowAttributes attr;
1320     XExtCodes *codes;
1321     int has_pixmap, scr;
1322
1323     display->shm = NULL;
1324
1325     if (!can_use_shm (display->display, &has_pixmap))
1326         return;
1327
1328     shm = malloc (sizeof (*shm));
1329     if (unlikely (shm == NULL))
1330         return;
1331
1332     codes = XInitExtension (display->display, SHMNAME);
1333     if (codes == NULL) {
1334         free (shm);
1335         return;
1336     }
1337
1338     shm->opcode = codes ->major_opcode;
1339     shm->event = codes->first_event;
1340
1341     if (unlikely (_pqueue_init (&shm->info))) {
1342         free (shm);
1343         return;
1344     }
1345
1346     scr = DefaultScreen (display->display);
1347     attr.override_redirect = 1;
1348     shm->window = XCreateWindow (display->display,
1349                                  DefaultRootWindow (display->display), -1, -1,
1350                                  1, 1, 0,
1351                                  DefaultDepth (display->display, scr),
1352                                  InputOutput,
1353                                  DefaultVisual (display->display, scr),
1354                                  CWOverrideRedirect, &attr);
1355
1356     if (xorg_has_buggy_send_shm_completion_event(display, shm))
1357         has_pixmap = 0;
1358
1359     shm->has_pixmaps = has_pixmap ? MIN_PIXMAP_SIZE : 0;
1360     cairo_list_init (&shm->pool);
1361
1362     cairo_list_init (&shm->surfaces);
1363
1364     display->shm = shm;
1365 }
1366
1367 void
1368 _cairo_xlib_display_fini_shm (cairo_xlib_display_t *display)
1369 {
1370     cairo_xlib_shm_display_t *shm = display->shm;
1371
1372     if (shm == NULL)
1373         return;
1374
1375     while (!cairo_list_is_empty (&shm->surfaces))
1376         cairo_surface_finish (&cairo_list_first_entry (&shm->surfaces,
1377                                                        cairo_xlib_shm_surface_t,
1378                                                        link)->image.base);
1379
1380     _pqueue_fini (&shm->info);
1381
1382     while (!cairo_list_is_empty (&shm->pool)) {
1383         cairo_xlib_shm_t *pool;
1384
1385         pool = cairo_list_first_entry (&shm->pool, cairo_xlib_shm_t, link);
1386         _cairo_xlib_display_shm_pool_destroy (display, pool);
1387     }
1388
1389     if (display->display)
1390         XDestroyWindow (display->display, shm->window);
1391
1392     free (shm);
1393     display->shm = NULL;
1394 }
1395 #endif
1396 #endif