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