| /* Cairo - a vector graphics library with display and print output |
| * |
| * Copyright © 2007 Chris Wilson |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it either under the terms of the GNU Lesser General Public |
| * License version 2.1 as published by the Free Software Foundation |
| * (the "LGPL") or, at your option, under the terms of the Mozilla |
| * Public License Version 1.1 (the "MPL"). If you do not alter this |
| * notice, a recipient may use your version of this file under either |
| * the MPL or the LGPL. |
| * |
| * You should have received a copy of the LGPL along with this library |
| * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA |
| * You should have received a copy of the MPL along with this library |
| * in the file COPYING-MPL-1.1 |
| * |
| * The contents of this file are subject to the Mozilla Public License |
| * Version 1.1 (the "License"); you may not use this file except in |
| * compliance with the License. You may obtain a copy of the License at |
| * http://www.mozilla.org/MPL/ |
| * |
| * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
| * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
| * the specific language governing rights and limitations. |
| * |
| * The Original Code is the cairo graphics library. |
| * |
| * The Initial Developer of the Original Code is Chris Wilson. |
| * |
| * Contributor(s): |
| * Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation |
| */ |
| |
| #include "cairoint.h" |
| |
| #if !CAIRO_HAS_XLIB_XCB_FUNCTIONS |
| |
| #include "cairo-xlib-private.h" |
| #include "cairo-xlib-xrender-private.h" |
| #include "cairo-freelist-private.h" |
| #include "cairo-error-private.h" |
| #include "cairo-list-inline.h" |
| |
| #include <X11/Xlibint.h> /* For XESetCloseDisplay */ |
| |
| typedef int (*cairo_xlib_error_func_t) (Display *display, |
| XErrorEvent *event); |
| |
| static cairo_xlib_display_t *_cairo_xlib_display_list; |
| |
| static int |
| _noop_error_handler (Display *display, |
| XErrorEvent *event) |
| { |
| return False; /* return value is ignored */ |
| } |
| |
| static void |
| _cairo_xlib_display_finish (void *abstract_display) |
| { |
| cairo_xlib_display_t *display = abstract_display; |
| Display *dpy = display->display; |
| |
| _cairo_xlib_display_fini_shm (display); |
| |
| if (! cairo_device_acquire (&display->base)) { |
| cairo_xlib_error_func_t old_handler; |
| |
| /* protect the notifies from triggering XErrors */ |
| XSync (dpy, False); |
| old_handler = XSetErrorHandler (_noop_error_handler); |
| |
| while (! cairo_list_is_empty (&display->fonts)) { |
| _cairo_xlib_font_close (cairo_list_first_entry (&display->fonts, |
| cairo_xlib_font_t, |
| link)); |
| } |
| |
| while (! cairo_list_is_empty (&display->screens)) { |
| _cairo_xlib_screen_destroy (display, |
| cairo_list_first_entry (&display->screens, |
| cairo_xlib_screen_t, |
| link)); |
| } |
| |
| XSync (dpy, False); |
| XSetErrorHandler (old_handler); |
| |
| cairo_device_release (&display->base); |
| } |
| } |
| |
| static void |
| _cairo_xlib_display_destroy (void *abstract_display) |
| { |
| cairo_xlib_display_t *display = abstract_display; |
| |
| free (display); |
| } |
| |
| static int |
| _cairo_xlib_close_display (Display *dpy, XExtCodes *codes) |
| { |
| cairo_xlib_display_t *display, **prev, *next; |
| |
| CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex); |
| for (display = _cairo_xlib_display_list; display; display = display->next) |
| if (display->display == dpy) |
| break; |
| CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex); |
| if (display == NULL) |
| return 0; |
| |
| cairo_device_finish (&display->base); |
| |
| /* |
| * Unhook from the global list |
| */ |
| CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex); |
| prev = &_cairo_xlib_display_list; |
| for (display = _cairo_xlib_display_list; display; display = next) { |
| next = display->next; |
| if (display->display == dpy) { |
| *prev = next; |
| break; |
| } else |
| prev = &display->next; |
| } |
| CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex); |
| |
| display->display = NULL; /* catch any later invalid access */ |
| cairo_device_destroy (&display->base); |
| |
| /* Return value in accordance with requirements of |
| * XESetCloseDisplay */ |
| return 0; |
| } |
| |
| static const cairo_device_backend_t _cairo_xlib_device_backend = { |
| CAIRO_DEVICE_TYPE_XLIB, |
| |
| NULL, |
| NULL, |
| |
| NULL, /* flush */ |
| _cairo_xlib_display_finish, |
| _cairo_xlib_display_destroy, |
| }; |
| |
| static void _cairo_xlib_display_select_compositor (cairo_xlib_display_t *display) |
| { |
| #if 1 |
| if (display->render_major > 0 || display->render_minor >= 4) |
| display->compositor = _cairo_xlib_traps_compositor_get (); |
| else if (display->render_major > 0 || display->render_minor >= 0) |
| display->compositor = _cairo_xlib_mask_compositor_get (); |
| else |
| display->compositor = _cairo_xlib_core_compositor_get (); |
| #else |
| display->compositor = _cairo_xlib_fallback_compositor_get (); |
| #endif |
| } |
| |
| /** |
| * _cairo_xlib_device_create: |
| * @dpy: the display to create the device for |
| * |
| * Gets the device belonging to @dpy, or creates it if it doesn't exist yet. |
| * |
| * Returns: the device belonging to @dpy |
| **/ |
| cairo_device_t * |
| _cairo_xlib_device_create (Display *dpy) |
| { |
| cairo_xlib_display_t *display; |
| cairo_xlib_display_t **prev; |
| cairo_device_t *device; |
| XExtCodes *codes; |
| const char *env; |
| |
| CAIRO_MUTEX_INITIALIZE (); |
| |
| /* There is an apparent deadlock between this mutex and the |
| * mutex for the display, but it's actually safe. For the |
| * app to call XCloseDisplay() while any other thread is |
| * inside this function would be an error in the logic |
| * app, and the CloseDisplay hook is the only other place we |
| * acquire this mutex. |
| */ |
| CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex); |
| |
| for (prev = &_cairo_xlib_display_list; (display = *prev); prev = &(*prev)->next) |
| { |
| if (display->display == dpy) { |
| /* |
| * MRU the list |
| */ |
| if (prev != &_cairo_xlib_display_list) { |
| *prev = display->next; |
| display->next = _cairo_xlib_display_list; |
| _cairo_xlib_display_list = display; |
| } |
| device = cairo_device_reference (&display->base); |
| goto UNLOCK; |
| } |
| } |
| |
| display = malloc (sizeof (cairo_xlib_display_t)); |
| if (unlikely (display == NULL)) { |
| device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); |
| goto UNLOCK; |
| } |
| |
| _cairo_device_init (&display->base, &_cairo_xlib_device_backend); |
| |
| display->display = dpy; |
| cairo_list_init (&display->screens); |
| cairo_list_init (&display->fonts); |
| display->closed = FALSE; |
| |
| /* Xlib calls out to the extension close_display hooks in LIFO |
| * order. So we have to ensure that all extensions that we depend |
| * on in our close_display hook are properly initialized before we |
| * add our hook. For now, that means Render, so we call into its |
| * QueryVersion function to ensure it gets initialized. |
| */ |
| display->render_major = display->render_minor = -1; |
| XRenderQueryVersion (dpy, &display->render_major, &display->render_minor); |
| env = getenv ("CAIRO_DEBUG"); |
| if (env != NULL && (env = strstr (env, "xrender-version=")) != NULL) { |
| int max_render_major, max_render_minor; |
| |
| env += sizeof ("xrender-version=") - 1; |
| if (sscanf (env, "%d.%d", &max_render_major, &max_render_minor) != 2) |
| max_render_major = max_render_minor = -1; |
| |
| if (max_render_major < display->render_major || |
| (max_render_major == display->render_major && |
| max_render_minor < display->render_minor)) |
| { |
| display->render_major = max_render_major; |
| display->render_minor = max_render_minor; |
| } |
| } |
| |
| _cairo_xlib_display_select_compositor (display); |
| |
| display->white = NULL; |
| memset (display->alpha, 0, sizeof (display->alpha)); |
| memset (display->solid, 0, sizeof (display->solid)); |
| memset (display->solid_cache, 0, sizeof (display->solid_cache)); |
| memset (display->last_solid_cache, 0, sizeof (display->last_solid_cache)); |
| |
| memset (display->cached_xrender_formats, 0, |
| sizeof (display->cached_xrender_formats)); |
| |
| display->force_precision = -1; |
| |
| _cairo_xlib_display_init_shm (display); |
| |
| /* Prior to Render 0.10, there is no protocol support for gradients and |
| * we call function stubs instead, which would silently consume the drawing. |
| */ |
| #if RENDER_MAJOR == 0 && RENDER_MINOR < 10 |
| display->buggy_gradients = TRUE; |
| #else |
| display->buggy_gradients = FALSE; |
| #endif |
| display->buggy_pad_reflect = FALSE; |
| display->buggy_repeat = FALSE; |
| |
| /* This buggy_repeat condition is very complicated because there |
| * are multiple X server code bases (with multiple versioning |
| * schemes within a code base), and multiple bugs. |
| * |
| * The X servers: |
| * |
| * 1. The Vendor=="XFree86" code base with release numbers such |
| * as 4.7.0 (VendorRelease==40700000). |
| * |
| * 2. The Vendor=="X.Org" code base (a descendant of the |
| * XFree86 code base). It originally had things like |
| * VendorRelease==60700000 for release 6.7.0 but then changed |
| * its versioning scheme so that, for example, |
| * VendorRelease==10400000 for the 1.4.0 X server within the |
| * X.Org 7.3 release. |
| * |
| * The bugs: |
| * |
| * 1. The original bug that led to the buggy_repeat |
| * workaround. This was a bug that Owen Taylor investigated, |
| * understood well, and characterized against various X |
| * servers. Confirmed X servers with this bug include: |
| * |
| * "XFree86" <= 40500000 |
| * "X.Org" <= 60802000 (only with old numbering >= 60700000) |
| * |
| * 2. A separate bug resulting in a crash of the X server when |
| * using cairo's extend-reflect test case, (which, surprisingly |
| * enough was not passing RepeatReflect to the X server, but |
| * instead using RepeatNormal in a workaround). Nobody to date |
| * has understood the bug well, but it appears to be gone as of |
| * the X.Org 1.4.0 server. This bug is coincidentally avoided |
| * by using the same buggy_repeat workaround. Confirmed X |
| * servers with this bug include: |
| * |
| * "X.org" == 60900000 (old versioning scheme) |
| * "X.org" < 10400000 (new numbering scheme) |
| * |
| * For the old-versioning-scheme X servers we don't know |
| * exactly when second the bug started, but since bug 1 is |
| * present through 6.8.2 and bug 2 is present in 6.9.0 it seems |
| * safest to just blacklist all old-versioning-scheme X servers, |
| * (just using VendorRelease < 70000000), as buggy_repeat=TRUE. |
| */ |
| if (_cairo_xlib_vendor_is_xorg (dpy)) { |
| if (VendorRelease (dpy) >= 60700000) { |
| if (VendorRelease (dpy) < 70000000) |
| display->buggy_repeat = TRUE; |
| |
| /* We know that gradients simply do not work in early Xorg servers */ |
| if (VendorRelease (dpy) < 70200000) |
| display->buggy_gradients = TRUE; |
| |
| /* And the extended repeat modes were not fixed until much later */ |
| display->buggy_pad_reflect = TRUE; |
| } else { |
| if (VendorRelease (dpy) < 10400000) |
| display->buggy_repeat = TRUE; |
| |
| /* Too many bugs in the early drivers */ |
| if (VendorRelease (dpy) < 10699000) |
| display->buggy_pad_reflect = TRUE; |
| } |
| } else if (strstr (ServerVendor (dpy), "XFree86") != NULL) { |
| if (VendorRelease (dpy) <= 40500000) |
| display->buggy_repeat = TRUE; |
| |
| display->buggy_gradients = TRUE; |
| display->buggy_pad_reflect = TRUE; |
| } |
| |
| codes = XAddExtension (dpy); |
| if (unlikely (codes == NULL)) { |
| device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); |
| free (display); |
| goto UNLOCK; |
| } |
| |
| XESetCloseDisplay (dpy, codes->extension, _cairo_xlib_close_display); |
| cairo_device_reference (&display->base); /* add one for the CloseDisplay */ |
| |
| display->next = _cairo_xlib_display_list; |
| _cairo_xlib_display_list = display; |
| |
| device = &display->base; |
| |
| UNLOCK: |
| CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex); |
| return device; |
| } |
| |
| cairo_status_t |
| _cairo_xlib_display_acquire (cairo_device_t *device, cairo_xlib_display_t **display) |
| { |
| cairo_status_t status; |
| |
| status = cairo_device_acquire (device); |
| if (status) |
| return status; |
| |
| *display = (cairo_xlib_display_t *) device; |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| XRenderPictFormat * |
| _cairo_xlib_display_get_xrender_format_for_pixman(cairo_xlib_display_t *display, |
| pixman_format_code_t format) |
| { |
| Display *dpy = display->display; |
| XRenderPictFormat tmpl; |
| int mask; |
| |
| #define MASK(x) ((1<<(x))-1) |
| |
| tmpl.depth = PIXMAN_FORMAT_DEPTH(format); |
| mask = PictFormatType | PictFormatDepth; |
| |
| switch (PIXMAN_FORMAT_TYPE(format)) { |
| case PIXMAN_TYPE_ARGB: |
| tmpl.type = PictTypeDirect; |
| |
| tmpl.direct.alphaMask = MASK(PIXMAN_FORMAT_A(format)); |
| if (PIXMAN_FORMAT_A(format)) |
| tmpl.direct.alpha = (PIXMAN_FORMAT_R(format) + |
| PIXMAN_FORMAT_G(format) + |
| PIXMAN_FORMAT_B(format)); |
| |
| tmpl.direct.redMask = MASK(PIXMAN_FORMAT_R(format)); |
| tmpl.direct.red = (PIXMAN_FORMAT_G(format) + |
| PIXMAN_FORMAT_B(format)); |
| |
| tmpl.direct.greenMask = MASK(PIXMAN_FORMAT_G(format)); |
| tmpl.direct.green = PIXMAN_FORMAT_B(format); |
| |
| tmpl.direct.blueMask = MASK(PIXMAN_FORMAT_B(format)); |
| tmpl.direct.blue = 0; |
| |
| mask |= PictFormatRed | PictFormatRedMask; |
| mask |= PictFormatGreen | PictFormatGreenMask; |
| mask |= PictFormatBlue | PictFormatBlueMask; |
| mask |= PictFormatAlpha | PictFormatAlphaMask; |
| break; |
| |
| case PIXMAN_TYPE_ABGR: |
| tmpl.type = PictTypeDirect; |
| |
| tmpl.direct.alphaMask = MASK(PIXMAN_FORMAT_A(format)); |
| if (tmpl.direct.alphaMask) |
| tmpl.direct.alpha = (PIXMAN_FORMAT_B(format) + |
| PIXMAN_FORMAT_G(format) + |
| PIXMAN_FORMAT_R(format)); |
| |
| tmpl.direct.blueMask = MASK(PIXMAN_FORMAT_B(format)); |
| tmpl.direct.blue = (PIXMAN_FORMAT_G(format) + |
| PIXMAN_FORMAT_R(format)); |
| |
| tmpl.direct.greenMask = MASK(PIXMAN_FORMAT_G(format)); |
| tmpl.direct.green = PIXMAN_FORMAT_R(format); |
| |
| tmpl.direct.redMask = MASK(PIXMAN_FORMAT_R(format)); |
| tmpl.direct.red = 0; |
| |
| mask |= PictFormatRed | PictFormatRedMask; |
| mask |= PictFormatGreen | PictFormatGreenMask; |
| mask |= PictFormatBlue | PictFormatBlueMask; |
| mask |= PictFormatAlpha | PictFormatAlphaMask; |
| break; |
| |
| case PIXMAN_TYPE_BGRA: |
| tmpl.type = PictTypeDirect; |
| |
| tmpl.direct.blueMask = MASK(PIXMAN_FORMAT_B(format)); |
| tmpl.direct.blue = (PIXMAN_FORMAT_BPP(format) - PIXMAN_FORMAT_B(format)); |
| |
| tmpl.direct.greenMask = MASK(PIXMAN_FORMAT_G(format)); |
| tmpl.direct.green = (PIXMAN_FORMAT_BPP(format) - PIXMAN_FORMAT_B(format) - |
| PIXMAN_FORMAT_G(format)); |
| |
| tmpl.direct.redMask = MASK(PIXMAN_FORMAT_R(format)); |
| tmpl.direct.red = (PIXMAN_FORMAT_BPP(format) - PIXMAN_FORMAT_B(format) - |
| PIXMAN_FORMAT_G(format) - PIXMAN_FORMAT_R(format)); |
| |
| tmpl.direct.alphaMask = MASK(PIXMAN_FORMAT_A(format)); |
| tmpl.direct.alpha = 0; |
| |
| mask |= PictFormatRed | PictFormatRedMask; |
| mask |= PictFormatGreen | PictFormatGreenMask; |
| mask |= PictFormatBlue | PictFormatBlueMask; |
| mask |= PictFormatAlpha | PictFormatAlphaMask; |
| break; |
| |
| case PIXMAN_TYPE_A: |
| tmpl.type = PictTypeDirect; |
| |
| tmpl.direct.alpha = 0; |
| tmpl.direct.alphaMask = MASK(PIXMAN_FORMAT_A(format)); |
| |
| mask |= PictFormatAlpha | PictFormatAlphaMask; |
| break; |
| |
| case PIXMAN_TYPE_COLOR: |
| case PIXMAN_TYPE_GRAY: |
| /* XXX Find matching visual/colormap */ |
| tmpl.type = PictTypeIndexed; |
| //tmpl.colormap = screen->visuals[PIXMAN_FORMAT_VIS(format)].vid; |
| //mask |= PictFormatColormap; |
| return NULL; |
| } |
| #undef MASK |
| |
| /* XXX caching? */ |
| return XRenderFindFormat(dpy, mask, &tmpl, 0); |
| } |
| |
| XRenderPictFormat * |
| _cairo_xlib_display_get_xrender_format (cairo_xlib_display_t *display, |
| cairo_format_t format) |
| { |
| XRenderPictFormat *xrender_format; |
| |
| xrender_format = display->cached_xrender_formats[format]; |
| if (xrender_format == NULL) { |
| int pict_format = PictStandardNUM; |
| |
| switch (format) { |
| case CAIRO_FORMAT_A1: |
| pict_format = PictStandardA1; break; |
| case CAIRO_FORMAT_A8: |
| pict_format = PictStandardA8; break; |
| case CAIRO_FORMAT_RGB24: |
| pict_format = PictStandardRGB24; break; |
| case CAIRO_FORMAT_RGB16_565: |
| xrender_format = _cairo_xlib_display_get_xrender_format_for_pixman(display, |
| PIXMAN_r5g6b5); |
| break; |
| case CAIRO_FORMAT_RGB30: |
| xrender_format = _cairo_xlib_display_get_xrender_format_for_pixman(display, |
| PIXMAN_x2r10g10b10); |
| break; |
| case CAIRO_FORMAT_INVALID: |
| default: |
| ASSERT_NOT_REACHED; |
| case CAIRO_FORMAT_ARGB32: |
| pict_format = PictStandardARGB32; break; |
| } |
| if (pict_format != PictStandardNUM) |
| xrender_format = |
| XRenderFindStandardFormat (display->display, pict_format); |
| display->cached_xrender_formats[format] = xrender_format; |
| } |
| |
| return xrender_format; |
| } |
| |
| cairo_xlib_screen_t * |
| _cairo_xlib_display_get_screen (cairo_xlib_display_t *display, |
| Screen *screen) |
| { |
| cairo_xlib_screen_t *info; |
| |
| cairo_list_foreach_entry (info, cairo_xlib_screen_t, &display->screens, link) { |
| if (info->screen == screen) { |
| if (display->screens.next != &info->link) |
| cairo_list_move (&info->link, &display->screens); |
| return info; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| cairo_bool_t |
| _cairo_xlib_display_has_repeat (cairo_device_t *device) |
| { |
| return ! ((cairo_xlib_display_t *) device)->buggy_repeat; |
| } |
| |
| cairo_bool_t |
| _cairo_xlib_display_has_reflect (cairo_device_t *device) |
| { |
| return ! ((cairo_xlib_display_t *) device)->buggy_pad_reflect; |
| } |
| |
| cairo_bool_t |
| _cairo_xlib_display_has_gradients (cairo_device_t *device) |
| { |
| return ! ((cairo_xlib_display_t *) device)->buggy_gradients; |
| } |
| |
| /** |
| * cairo_xlib_device_debug_cap_xrender_version: |
| * @device: a #cairo_device_t for the Xlib backend |
| * @major_version: major version to restrict to |
| * @minor_version: minor version to restrict to |
| * |
| * Restricts all future Xlib surfaces for this devices to the specified version |
| * of the RENDER extension. This function exists solely for debugging purpose. |
| * It lets you find out how cairo would behave with an older version of |
| * the RENDER extension. |
| * |
| * Use the special values -1 and -1 for disabling the RENDER extension. |
| * |
| * Since: 1.12 |
| **/ |
| void |
| cairo_xlib_device_debug_cap_xrender_version (cairo_device_t *device, |
| int major_version, |
| int minor_version) |
| { |
| cairo_xlib_display_t *display = (cairo_xlib_display_t *) device; |
| |
| if (device == NULL || device->status) |
| return; |
| |
| if (device->backend->type != CAIRO_DEVICE_TYPE_XLIB) |
| return; |
| |
| if (major_version < display->render_major || |
| (major_version == display->render_major && |
| minor_version < display->render_minor)) |
| { |
| display->render_major = major_version; |
| display->render_minor = minor_version; |
| } |
| |
| _cairo_xlib_display_select_compositor (display); |
| } |
| |
| /** |
| * cairo_xlib_device_debug_set_precision: |
| * @device: a #cairo_device_t for the Xlib backend |
| * @precision: the precision to use |
| * |
| * Render supports two modes of precision when rendering trapezoids. Set |
| * the precision to the desired mode. |
| * |
| * Since: 1.12 |
| **/ |
| void |
| cairo_xlib_device_debug_set_precision (cairo_device_t *device, |
| int precision) |
| { |
| if (device == NULL || device->status) |
| return; |
| if (device->backend->type != CAIRO_DEVICE_TYPE_XLIB) { |
| cairo_status_t status; |
| |
| status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH); |
| (void) status; |
| return; |
| } |
| |
| ((cairo_xlib_display_t *) device)->force_precision = precision; |
| } |
| |
| /** |
| * cairo_xlib_device_debug_get_precision: |
| * @device: a #cairo_device_t for the Xlib backend |
| * |
| * Get the Xrender precision mode. |
| * |
| * Returns: the render precision mode |
| * |
| * Since: 1.12 |
| **/ |
| int |
| cairo_xlib_device_debug_get_precision (cairo_device_t *device) |
| { |
| if (device == NULL || device->status) |
| return -1; |
| if (device->backend->type != CAIRO_DEVICE_TYPE_XLIB) { |
| cairo_status_t status; |
| |
| status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH); |
| (void) status; |
| return -1; |
| } |
| |
| return ((cairo_xlib_display_t *) device)->force_precision; |
| } |
| |
| #endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */ |