diff options
Diffstat (limited to 'src/cairo-xlib-surface-shm.c')
-rw-r--r-- | src/cairo-xlib-surface-shm.c | 386 |
1 files changed, 324 insertions, 62 deletions
diff --git a/src/cairo-xlib-surface-shm.c b/src/cairo-xlib-surface-shm.c index 08169f2..fa7d3eb 100644 --- a/src/cairo-xlib-surface-shm.c +++ b/src/cairo-xlib-surface-shm.c @@ -41,17 +41,114 @@ #include "cairo-xlib-private.h" #include "cairo-xlib-surface-private.h" -#include "cairo-image-surface-private.h" -#include "cairo-mempool-private.h" + +#if !HAVE_X11_EXTENSIONS_XSHM_H || !(HAVE_X11_EXTENSIONS_SHMPROTO_H || HAVE_X11_EXTENSIONS_SHMSTR_H) +void _cairo_xlib_display_init_shm (cairo_xlib_display_t *display) {} + +cairo_surface_t * +_cairo_xlib_surface_get_shm (cairo_xlib_surface_t *surface, + cairo_bool_t overwrite) +{ + return NULL; +} + +cairo_int_status_t +_cairo_xlib_surface_put_shm (cairo_xlib_surface_t *surface) +{ + assert (!surface->fallback); + return CAIRO_INT_STATUS_SUCCESS; +} + +cairo_surface_t * +_cairo_xlib_surface_create_shm (cairo_xlib_surface_t *other, + pixman_format_code_t format, + int width, int height) +{ + return NULL; +} + +cairo_surface_t * +_cairo_xlib_surface_create_shm__image (cairo_xlib_surface_t *surface, + pixman_format_code_t format, + int width, int height) +{ + return NULL; +} + +cairo_surface_t * +_cairo_xlib_surface_create_similar_shm (void *other, + cairo_format_t format, + int width, int height) +{ + return cairo_image_surface_create (format, width, height); +} + +void +_cairo_xlib_shm_surface_mark_active (cairo_surface_t *_shm) +{ + ASSERT_NOT_REACHED; +} + +void +_cairo_xlib_shm_surface_get_ximage (cairo_surface_t *surface, + XImage *ximage) +{ + ASSERT_NOT_REACHED; +} + +void * +_cairo_xlib_shm_surface_get_obdata (cairo_surface_t *surface) +{ + ASSERT_NOT_REACHED; + return NULL; +} + +Pixmap +_cairo_xlib_shm_surface_get_pixmap (cairo_surface_t *surface) +{ + ASSERT_NOT_REACHED; + return 0; +} + +XRenderPictFormat * +_cairo_xlib_shm_surface_get_xrender_format (cairo_surface_t *surface) +{ + ASSERT_NOT_REACHED; + return NULL; +} + +cairo_bool_t +_cairo_xlib_shm_surface_is_active (cairo_surface_t *surface) +{ + ASSERT_NOT_REACHED; + return FALSE; +} + +cairo_bool_t +_cairo_xlib_shm_surface_is_idle (cairo_surface_t *surface) +{ + ASSERT_NOT_REACHED; + return TRUE; +} + +void _cairo_xlib_display_fini_shm (cairo_xlib_display_t *display) {} + +#else #include "cairo-damage-private.h" #include "cairo-default-context-private.h" +#include "cairo-image-surface-private.h" #include "cairo-list-inline.h" +#include "cairo-mempool-private.h" #include <X11/Xlibint.h> #include <X11/Xproto.h> #include <X11/extensions/XShm.h> +#if HAVE_X11_EXTENSIONS_SHMPROTO_H #include <X11/extensions/shmproto.h> +#elif HAVE_X11_EXTENSIONS_SHMSTR_H +#include <X11/extensions/shmstr.h> +#endif #include <sys/ipc.h> #include <sys/shm.h> @@ -109,6 +206,8 @@ struct _cairo_xlib_shm_display { int event; Window window; + unsigned long last_request; + unsigned long last_event; cairo_list_t surfaces; @@ -119,9 +218,21 @@ struct _cairo_xlib_shm_display { static inline cairo_bool_t seqno_passed (unsigned long a, unsigned long b) { + return (long)(b - a) >= 0; +} + +static inline cairo_bool_t +seqno_before (unsigned long a, unsigned long b) +{ return (long)(b - a) > 0; } +static inline cairo_bool_t +seqno_after (unsigned long a, unsigned long b) +{ + return (long)(a - b) > 0; +} + static inline cairo_status_t _pqueue_init (struct pqueue *pq) { @@ -305,11 +416,6 @@ peek_processed (cairo_device_t *device) return LastKnownRequestProcessed (peek_display(device)); } -static unsigned next_request (cairo_device_t *device) -{ - return NextRequest (peek_display (device)); -} - static void _cairo_xlib_display_shm_pool_destroy (cairo_xlib_display_t *display, cairo_xlib_shm_t *pool) @@ -324,6 +430,43 @@ _cairo_xlib_display_shm_pool_destroy (cairo_xlib_display_t *display, free (pool); } +static void send_event(cairo_xlib_display_t *display, + cairo_xlib_shm_info_t *info, + unsigned long seqno) +{ + XShmCompletionEvent ev; + + if (! seqno_after (seqno, display->shm->last_event)) + return; + + ev.type = display->shm->event; + ev.send_event = 1; /* XXX or lie? */ + ev.serial = NextRequest (display->display); + ev.drawable = display->shm->window; + ev.major_code = display->shm->opcode; + ev.minor_code = X_ShmPutImage; + ev.shmseg = info->pool->shm.shmid; + ev.offset = (char *)info->mem - (char *)info->pool->shm.shmaddr; + + XSendEvent (display->display, ev.drawable, False, 0, (XEvent *)&ev); + + display->shm->last_event = ev.serial; +} + +static void sync (cairo_xlib_display_t *display) +{ + cairo_xlib_shm_info_t *info; + struct pqueue *pq = &display->shm->info; + + XSync (display->display, False); + + while ((info = PQ_TOP(pq))) { + _cairo_mempool_free (&info->pool->mem, info->mem); + _pqueue_pop (&display->shm->info); + free (info); + } +} + static void _cairo_xlib_shm_info_cleanup (cairo_xlib_display_t *display) { @@ -340,8 +483,10 @@ _cairo_xlib_shm_info_cleanup (cairo_xlib_display_t *display) info = PQ_TOP(pq); do { - if (! seqno_passed (info->last_request, processed)) - break; + if (! seqno_passed (info->last_request, processed)) { + send_event (display, info, display->shm->last_request); + return; + } _cairo_mempool_free (&info->pool->mem, info->mem); _pqueue_pop (&display->shm->info); @@ -349,9 +494,9 @@ _cairo_xlib_shm_info_cleanup (cairo_xlib_display_t *display) } while ((info = PQ_TOP(pq))); } -static cairo_xlib_shm_info_t * -_cairo_xlib_shm_info_find (cairo_xlib_display_t *display, - size_t size, unsigned long *last_request) +static cairo_xlib_shm_t * +_cairo_xlib_shm_info_find (cairo_xlib_display_t *display, size_t size, + void **ptr, unsigned long *last_request) { cairo_xlib_shm_info_t *info; struct pqueue *pq = &display->shm->info; @@ -361,14 +506,21 @@ _cairo_xlib_shm_info_find (cairo_xlib_display_t *display, info = PQ_TOP(pq); do { - _pqueue_pop (&display->shm->info); - - if (info->size >= size && size <= 2*info->size) - return info; + cairo_xlib_shm_t *pool = info->pool; *last_request = info->last_request; - _cairo_mempool_free (&info->pool->mem, info->mem); + + _pqueue_pop (&display->shm->info); + _cairo_mempool_free (&pool->mem, info->mem); free (info); + + if (pool->mem.free_bytes >= size) { + void *mem = _cairo_mempool_alloc (&pool->mem, size); + if (mem != NULL) { + *ptr = mem; + return pool; + } + } } while ((info = PQ_TOP(pq))); return NULL; @@ -449,6 +601,9 @@ _cairo_xlib_shm_pool_create(cairo_xlib_display_t *display, pool->attached = NextRequest (dpy); success = XShmAttach (dpy, &pool->shm); +#if !IPC_RMID_DEFERRED_RELEASE + XSync (dpy, FALSE); +#endif shmctl (pool->shm.shmid, IPC_RMID, NULL); if (! success) @@ -482,15 +637,12 @@ _cairo_xlib_shm_info_create (cairo_xlib_display_t *display, unsigned long last_request = 0; void *mem = NULL; - if (will_sync) { - info = _cairo_xlib_shm_info_find (display, size, &last_request); - if (info) - return info; - } - _cairo_xlib_shm_info_cleanup (display); pool = _cairo_xlib_shm_pool_find (display, size, &mem); _cairo_xlib_shm_pool_cleanup (display); + + if (pool == NULL && will_sync) + pool = _cairo_xlib_shm_info_find (display, size, &mem, &last_request); if (pool == NULL) pool = _cairo_xlib_shm_pool_create (display, size, &mem); if (pool == NULL) @@ -517,6 +669,7 @@ _cairo_xlib_shm_surface_flush (void *abstract_surface, unsigned flags) { cairo_xlib_shm_surface_t *shm = abstract_surface; cairo_xlib_display_t *display; + Display *dpy; cairo_status_t status; if (shm->active == 0) @@ -534,10 +687,15 @@ _cairo_xlib_shm_surface_flush (void *abstract_surface, unsigned flags) if (unlikely (status)) return status; - XEventsQueued (display->display, QueuedAfterReading); - if (!seqno_passed (shm->active, - LastKnownRequestProcessed (display->display))) - XSync (display->display, False); + send_event (display, shm->info, shm->active); + + dpy = display->display; + XEventsQueued (dpy, QueuedAfterReading); + while (! seqno_passed (shm->active, LastKnownRequestProcessed (dpy))) { + LockDisplay(dpy); + _XReadEvents(dpy); + UnlockDisplay(dpy); + } cairo_device_release (&display->base); shm->active = 0; @@ -549,7 +707,7 @@ static inline cairo_bool_t active (cairo_xlib_shm_surface_t *shm, Display *dpy) { return (shm->active && - !seqno_passed (shm->active, LastKnownRequestProcessed (dpy))); + ! seqno_passed (shm->active, LastKnownRequestProcessed (dpy))); } static cairo_status_t @@ -559,6 +717,11 @@ _cairo_xlib_shm_surface_finish (void *abstract_surface) cairo_xlib_display_t *display; cairo_status_t status; + if (shm->image.base.damage) { + _cairo_damage_destroy (shm->image.base.damage); + shm->image.base.damage = _cairo_damage_create_in_error (CAIRO_STATUS_SURFACE_FINISHED); + } + status = _cairo_xlib_display_acquire (shm->image.base.device, &display); if (unlikely (status)) return status; @@ -569,6 +732,8 @@ _cairo_xlib_shm_surface_finish (void *abstract_surface) if (active (shm, display->display)) { shm->info->last_request = shm->active; _pqueue_push (&display->shm->info, shm->info); + if (seqno_before (display->shm->last_request, shm->active)) + display->shm->last_request = shm->active; } else { _cairo_mempool_free (&shm->info->pool->mem, shm->info->mem); free (shm->info); @@ -579,7 +744,7 @@ _cairo_xlib_shm_surface_finish (void *abstract_surface) cairo_list_del (&shm->link); cairo_device_release (&display->base); - return CAIRO_STATUS_SUCCESS; + return _cairo_image_surface_finish (abstract_surface); } static const cairo_surface_backend_t cairo_xlib_shm_surface_backend = { @@ -720,6 +885,13 @@ _cairo_xlib_surface_update_shm (cairo_xlib_surface_t *surface) if (_cairo_xlib_surface_get_gc (display, surface, &gc)) goto cleanup_display; + if (! surface->owns_pixmap) { + XGCValues gcv; + + gcv.subwindow_mode = IncludeInferiors; + XChangeGC (display->display, gc, GCSubwindowMode, &gcv); + } + if (damage->region) { XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (sizeof (XRectangle))]; XRectangle *rects = stack_rects; @@ -770,7 +942,14 @@ _cairo_xlib_surface_update_shm (cairo_xlib_surface_t *surface) 0, 0); } - XSync (display->display, False); + if (! surface->owns_pixmap) { + XGCValues gcv; + + gcv.subwindow_mode = ClipByChildren; + XChangeGC (display->display, gc, GCSubwindowMode, &gcv); + } + + sync (display); shm->active = 0; shm->idle--; @@ -899,10 +1078,6 @@ _cairo_xlib_surface_put_shm (cairo_xlib_surface_t *surface) damage = _cairo_damage_reduce (shm->image.base.damage); shm->image.base.damage = _cairo_damage_create (); - status = _cairo_xlib_surface_get_gc (display, surface, &gc); - if (unlikely (status)) - goto out; - TRACE ((stderr, "%s: flushing damage x %d\n", __FUNCTION__, damage->region ? cairo_region_num_rectangles (damage->region) : 0)); if (damage->status == CAIRO_STATUS_SUCCESS && damage->region) { @@ -912,9 +1087,16 @@ _cairo_xlib_surface_put_shm (cairo_xlib_surface_t *surface) int n_rects, i; n_rects = cairo_region_num_rectangles (damage->region); - if (n_rects == 0) { - } else if (n_rects == 1) { + if (n_rects == 0) + goto out; + + status = _cairo_xlib_surface_get_gc (display, surface, &gc); + if (unlikely (status)) + goto out; + + if (n_rects == 1) { cairo_region_get_rectangle (damage->region, 0, &r); + _cairo_xlib_shm_surface_mark_active (surface->shm); XCopyArea (display->display, shm->pixmap, surface->drawable, gc, r.x, r.y, @@ -925,6 +1107,7 @@ _cairo_xlib_surface_put_shm (cairo_xlib_surface_t *surface) rects = _cairo_malloc_ab (n_rects, sizeof (XRectangle)); if (unlikely (rects == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + _cairo_xlib_surface_put_gc (display, surface, gc); goto out; } } @@ -938,6 +1121,7 @@ _cairo_xlib_surface_put_shm (cairo_xlib_surface_t *surface) } XSetClipRectangles (display->display, gc, 0, 0, rects, i, YXBanded); + _cairo_xlib_shm_surface_mark_active (surface->shm); XCopyArea (display->display, shm->pixmap, surface->drawable, gc, 0, 0, @@ -947,12 +1131,12 @@ _cairo_xlib_surface_put_shm (cairo_xlib_surface_t *surface) if (damage->status == CAIRO_STATUS_SUCCESS && damage->region) XSetClipMask (display->display, gc, None); } + + _cairo_xlib_surface_put_gc (display, surface, gc); } - _cairo_damage_destroy (damage); - _cairo_xlib_shm_surface_mark_active (surface->shm); - _cairo_xlib_surface_put_gc (display, surface, gc); out: + _cairo_damage_destroy (damage); cairo_device_release (&display->base); } @@ -968,9 +1152,8 @@ _cairo_xlib_surface_create_shm (cairo_xlib_surface_t *other, surface = NULL; if (has_shm (other)) - surface = &_cairo_xlib_shm_surface_create (other, format, - width, height, FALSE, - has_shm_pixmaps (other))->image.base; + surface = &_cairo_xlib_shm_surface_create (other, format, width, height, + FALSE, has_shm_pixmaps (other))->image.base; return surface; } @@ -983,9 +1166,8 @@ _cairo_xlib_surface_create_shm__image (cairo_xlib_surface_t *surface, if (! has_shm(surface)) return NULL; - return &_cairo_xlib_shm_surface_create (surface, format, - surface->width, surface->height, - TRUE, 0)->image.base; + return &_cairo_xlib_shm_surface_create (surface, format, width, height, + FALSE, 0)->image.base; } cairo_surface_t * @@ -1016,17 +1198,8 @@ _cairo_xlib_shm_surface_mark_active (cairo_surface_t *_shm) { cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) _shm; cairo_xlib_display_t *display = (cairo_xlib_display_t *) _shm->device; - XShmCompletionEvent ev; - - ev.type = display->shm->event; - ev.drawable = display->shm->window; - ev.major_code = display->shm->opcode; - ev.minor_code = X_ShmPutImage; - ev.shmseg = shm->info->pool->shm.shmid; - ev.offset = (char *)shm->info->mem - (char *)shm->info->pool->shm.shmaddr; shm->active = NextRequest (display->display); - XSendEvent (display->display, ev.drawable, False, 0, (XEvent *)&ev); } void @@ -1065,10 +1238,10 @@ _cairo_xlib_shm_surface_get_ximage (cairo_surface_t *surface, void * _cairo_xlib_shm_surface_get_obdata (cairo_surface_t *surface) { - cairo_xlib_shm_surface_t *shm; + cairo_xlib_display_t *display = (cairo_xlib_display_t *) surface->device; + cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) surface; - shm = (cairo_xlib_shm_surface_t *) surface; - shm->active = next_request (surface->device); + display->shm->last_event = shm->active = NextRequest (display->display); return &shm->info->pool->shm; } @@ -1121,6 +1294,85 @@ _cairo_xlib_shm_surface_is_idle (cairo_surface_t *surface) return shm->idle > 0; } +#define XORG_VERSION_ENCODE(major,minor,patch,snap) \ + (((major) * 10000000) + ((minor) * 100000) + ((patch) * 1000) + snap) + +static cairo_bool_t +has_broken_send_shm_event (cairo_xlib_display_t *display, + cairo_xlib_shm_display_t *shm) +{ + Display *dpy = display->display; + int (*old_handler) (Display *display, XErrorEvent *event); + XShmCompletionEvent ev; + XShmSegmentInfo info; + + info.shmid = shmget (IPC_PRIVATE, 0x1000, IPC_CREAT | 0600); + if (info.shmid == -1) + return TRUE; + + info.readOnly = FALSE; + info.shmaddr = shmat (info.shmid, NULL, 0); + if (info.shmaddr == (char *) -1) { + shmctl (info.shmid, IPC_RMID, NULL); + return TRUE; + } + + ev.type = shm->event; + ev.send_event = 1; + ev.serial = 1; + ev.drawable = shm->window; + ev.major_code = shm->opcode; + ev.minor_code = X_ShmPutImage; + + ev.shmseg = info.shmid; + ev.offset = 0; + + assert (CAIRO_MUTEX_IS_LOCKED (_cairo_xlib_display_mutex)); + _x_error_occurred = FALSE; + + XLockDisplay (dpy); + XSync (dpy, False); + old_handler = XSetErrorHandler (_check_error_handler); + + XShmAttach (dpy, &info); + XSendEvent (dpy, ev.drawable, False, 0, (XEvent *)&ev); + XShmDetach (dpy, &info); + + XSync (dpy, False); + XSetErrorHandler (old_handler); + XUnlockDisplay (dpy); + + shmctl (info.shmid, IPC_RMID, NULL); + shmdt (info.shmaddr); + + return _x_error_occurred; +} + +static cairo_bool_t +xorg_has_buggy_send_shm_completion_event(cairo_xlib_display_t *display, + cairo_xlib_shm_display_t *shm) +{ + Display *dpy = display->display; + + /* As libXext sets the SEND_EVENT bit in the ShmCompletionEvent, + * the Xserver may crash if it does not take care when processing + * the event type. For instance versions of Xorg prior to 1.11.1 + * exhibited this bug, and was fixed by: + * + * commit 2d2dce558d24eeea0eb011ec9ebaa6c5c2273c39 + * Author: Sam Spilsbury <sam.spilsbury@canonical.com> + * Date: Wed Sep 14 09:58:34 2011 +0800 + * + * Remove the SendEvent bit (0x80) before doing range checks on event type. + */ + if (_cairo_xlib_vendor_is_xorg (dpy) && + VendorRelease (dpy) < XORG_VERSION_ENCODE(1,11,0,1)) + return TRUE; + + /* For everyone else check that no error is generated */ + return has_broken_send_shm_event (display, shm); +} + void _cairo_xlib_display_init_shm (cairo_xlib_display_t *display) { @@ -1138,6 +1390,15 @@ _cairo_xlib_display_init_shm (cairo_xlib_display_t *display) if (unlikely (shm == NULL)) return; + codes = XInitExtension (display->display, SHMNAME); + if (codes == NULL) { + free (shm); + return; + } + + shm->opcode = codes ->major_opcode; + shm->event = codes->first_event; + if (unlikely (_pqueue_init (&shm->info))) { free (shm); return; @@ -1152,14 +1413,15 @@ _cairo_xlib_display_init_shm (cairo_xlib_display_t *display) InputOutput, DefaultVisual (display->display, scr), CWOverrideRedirect, &attr); + shm->last_event = 0; + shm->last_request = 0; + + if (xorg_has_buggy_send_shm_completion_event(display, shm)) + has_pixmap = 0; shm->has_pixmaps = has_pixmap ? MIN_PIXMAP_SIZE : 0; cairo_list_init (&shm->pool); - codes = XInitExtension (display->display, SHMNAME); - shm->opcode = codes ->major_opcode; - shm->event = codes->first_event; - cairo_list_init (&shm->surfaces); display->shm = shm; @@ -1193,5 +1455,5 @@ _cairo_xlib_display_fini_shm (cairo_xlib_display_t *display) free (shm); display->shm = NULL; } - +#endif #endif |