/* Cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Red Hat, Inc. * * Partially on code from xftdpy.c * * Copyright © 2000 Keith Packard * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Keith Packard not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Keith Packard makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include "cairoint.h" #include "cairo-xlib-private.h" #include "cairo-xlib-xrender-private.h" #include "cairo-xlib-surface-private.h" #include "cairo-error-private.h" #if CAIRO_HAS_FC_FONT #include #endif static int parse_boolean (const char *v) { char c0, c1; c0 = *v; if (c0 == 't' || c0 == 'T' || c0 == 'y' || c0 == 'Y' || c0 == '1') return 1; if (c0 == 'f' || c0 == 'F' || c0 == 'n' || c0 == 'N' || c0 == '0') return 0; if (c0 == 'o') { c1 = v[1]; if (c1 == 'n' || c1 == 'N') return 1; if (c1 == 'f' || c1 == 'F') return 0; } return -1; } static cairo_bool_t get_boolean_default (Display *dpy, const char *option, cairo_bool_t *value) { char *v; int i; v = XGetDefault (dpy, "Xft", option); if (v) { i = parse_boolean (v); if (i >= 0) { *value = i; return TRUE; } } return FALSE; } static cairo_bool_t get_integer_default (Display *dpy, const char *option, int *value) { char *v, *e; v = XGetDefault (dpy, "Xft", option); if (v) { #if CAIRO_HAS_FC_FONT if (FcNameConstant ((FcChar8 *) v, value)) return TRUE; #endif *value = strtol (v, &e, 0); if (e != v) return TRUE; } return FALSE; } #ifndef FC_RGBA_UNKNOWN #define FC_RGBA_UNKNOWN 0 #define FC_RGBA_RGB 1 #define FC_RGBA_BGR 2 #define FC_RGBA_VRGB 3 #define FC_RGBA_VBGR 4 #define FC_RGBA_NONE 5 #endif #ifndef FC_HINT_NONE #define FC_HINT_NONE 0 #define FC_HINT_SLIGHT 1 #define FC_HINT_MEDIUM 2 #define FC_HINT_FULL 3 #endif static void _cairo_xlib_init_screen_font_options (Display *dpy, cairo_xlib_screen_t *info) { cairo_bool_t xft_hinting; cairo_bool_t xft_antialias; int xft_hintstyle; int xft_rgba; cairo_antialias_t antialias; cairo_subpixel_order_t subpixel_order; cairo_hint_style_t hint_style; if (!get_boolean_default (dpy, "antialias", &xft_antialias)) xft_antialias = TRUE; if (!get_boolean_default (dpy, "hinting", &xft_hinting)) xft_hinting = TRUE; if (!get_integer_default (dpy, "hintstyle", &xft_hintstyle)) xft_hintstyle = FC_HINT_FULL; if (!get_integer_default (dpy, "rgba", &xft_rgba)) { xft_rgba = FC_RGBA_UNKNOWN; #if RENDER_MAJOR > 0 || RENDER_MINOR >= 6 if (info->has_render) { int render_order = XRenderQuerySubpixelOrder (dpy, XScreenNumberOfScreen (info->screen)); switch (render_order) { default: case SubPixelUnknown: xft_rgba = FC_RGBA_UNKNOWN; break; case SubPixelHorizontalRGB: xft_rgba = FC_RGBA_RGB; break; case SubPixelHorizontalBGR: xft_rgba = FC_RGBA_BGR; break; case SubPixelVerticalRGB: xft_rgba = FC_RGBA_VRGB; break; case SubPixelVerticalBGR: xft_rgba = FC_RGBA_VBGR; break; case SubPixelNone: xft_rgba = FC_RGBA_NONE; break; } } #endif } if (xft_hinting) { switch (xft_hintstyle) { case FC_HINT_NONE: hint_style = CAIRO_HINT_STYLE_NONE; break; case FC_HINT_SLIGHT: hint_style = CAIRO_HINT_STYLE_SLIGHT; break; case FC_HINT_MEDIUM: hint_style = CAIRO_HINT_STYLE_MEDIUM; break; case FC_HINT_FULL: hint_style = CAIRO_HINT_STYLE_FULL; break; default: hint_style = CAIRO_HINT_STYLE_DEFAULT; } } else { hint_style = CAIRO_HINT_STYLE_NONE; } switch (xft_rgba) { case FC_RGBA_RGB: subpixel_order = CAIRO_SUBPIXEL_ORDER_RGB; break; case FC_RGBA_BGR: subpixel_order = CAIRO_SUBPIXEL_ORDER_BGR; break; case FC_RGBA_VRGB: subpixel_order = CAIRO_SUBPIXEL_ORDER_VRGB; break; case FC_RGBA_VBGR: subpixel_order = CAIRO_SUBPIXEL_ORDER_VBGR; break; case FC_RGBA_UNKNOWN: case FC_RGBA_NONE: default: subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT; } if (xft_antialias) { if (subpixel_order == CAIRO_SUBPIXEL_ORDER_DEFAULT) antialias = CAIRO_ANTIALIAS_GRAY; else antialias = CAIRO_ANTIALIAS_SUBPIXEL; } else { antialias = CAIRO_ANTIALIAS_NONE; } cairo_font_options_set_hint_style (&info->font_options, hint_style); cairo_font_options_set_antialias (&info->font_options, antialias); cairo_font_options_set_subpixel_order (&info->font_options, subpixel_order); cairo_font_options_set_hint_metrics (&info->font_options, CAIRO_HINT_METRICS_ON); } cairo_xlib_screen_t * _cairo_xlib_screen_reference (cairo_xlib_screen_t *info) { assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&info->ref_count)); _cairo_reference_count_inc (&info->ref_count); return info; } void _cairo_xlib_screen_close_display (cairo_xlib_screen_t *info) { cairo_xlib_visual_info_t **visuals; Display *dpy; cairo_atomic_int_t old; int i; CAIRO_MUTEX_LOCK (info->mutex); dpy = _cairo_xlib_display_get_dpy (info->display); #if HAS_ATOMIC_OPS do { old = _cairo_atomic_int_get (&info->gc_depths); } while (_cairo_atomic_int_cmpxchg (&info->gc_depths, old, 0) != old); #else old = info->gc_depths; info->gc_depths = 0; #endif for (i = 0; i < ARRAY_LENGTH (info->gc); i++) { if ((old >> (8*i)) & 0xff) XFreeGC (dpy, info->gc[i]); } visuals = _cairo_array_index (&info->visuals, 0); for (i = 0; i < _cairo_array_num_elements (&info->visuals); i++) _cairo_xlib_visual_info_destroy (dpy, visuals[i]); _cairo_array_truncate (&info->visuals, 0); CAIRO_MUTEX_UNLOCK (info->mutex); } void _cairo_xlib_screen_destroy (cairo_xlib_screen_t *info) { assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&info->ref_count)); if (! _cairo_reference_count_dec_and_test (&info->ref_count)) return; _cairo_xlib_display_remove_screen (info->display, info); _cairo_xlib_screen_close_display (info); _cairo_xlib_display_destroy (info->display); _cairo_array_fini (&info->visuals); CAIRO_MUTEX_FINI (info->mutex); free (info); } cairo_status_t _cairo_xlib_screen_get (Display *dpy, Screen *screen, cairo_xlib_screen_t **out) { cairo_xlib_display_t *display; cairo_xlib_screen_t *info; cairo_status_t status; status = _cairo_xlib_display_get (dpy, &display); if (likely (status == CAIRO_STATUS_SUCCESS)) status = _cairo_xlib_display_get_screen (display, screen, &info); if (unlikely (status)) goto CLEANUP_DISPLAY; if (info != NULL) { *out = _cairo_xlib_screen_reference (info); goto CLEANUP_DISPLAY; } info = malloc (sizeof (cairo_xlib_screen_t)); if (unlikely (info == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_DISPLAY; } CAIRO_REFERENCE_COUNT_INIT (&info->ref_count, 2); /* Add one for display cache */ CAIRO_MUTEX_INIT (info->mutex); info->display = display; info->screen = screen; info->has_render = FALSE; info->has_font_options = FALSE; info->gc_depths = 0; memset (info->gc, 0, sizeof (info->gc)); _cairo_array_init (&info->visuals, sizeof (cairo_xlib_visual_info_t*)); if (screen) { int event_base, error_base; info->has_render = XRenderQueryExtension (dpy, &event_base, &error_base) && (XRenderFindVisualFormat (dpy, DefaultVisual (dpy, DefaultScreen (dpy))) != 0); } /* Small window of opportunity for two screen infos for the same * Screen - just wastes a little bit of memory but should not cause * any corruption. */ _cairo_xlib_display_add_screen (display, info); *out = info; return CAIRO_STATUS_SUCCESS; CLEANUP_DISPLAY: _cairo_xlib_display_destroy (display); return status; } #if HAS_ATOMIC_OPS GC _cairo_xlib_screen_get_gc (cairo_xlib_screen_t *info, int depth, Drawable drawable) { XGCValues gcv; cairo_atomic_int_t old, new; int i; GC gc; do { gc = NULL; old = info->gc_depths; if (old == 0) break; if (((old >> 0) & 0xff) == depth) i = 0; else if (((old >> 8) & 0xff) == depth) i = 1; else if (((old >> 16) & 0xff) == depth) i = 2; else if (((old >> 24) & 0xff) == depth) i = 3; else break; gc = info->gc[i]; new = old & ~(0xff << (8*i)); } while (_cairo_atomic_int_cmpxchg (&info->gc_depths, old, new) != old); if (likely (gc != NULL)) { (void) _cairo_atomic_ptr_cmpxchg (&info->gc[i], gc, NULL); return gc; } gcv.graphics_exposures = False; gcv.fill_style = FillTiled; return XCreateGC (_cairo_xlib_display_get_dpy (info->display), drawable, GCGraphicsExposures | GCFillStyle, &gcv); } void _cairo_xlib_screen_put_gc (cairo_xlib_screen_t *info, int depth, GC gc) { int i, old, new; do { do { i = -1; old = info->gc_depths; if (((old >> 0) & 0xff) == 0) i = 0; else if (((old >> 8) & 0xff) == 0) i = 1; else if (((old >> 16) & 0xff) == 0) i = 2; else if (((old >> 24) & 0xff) == 0) i = 3; else goto out; new = old | (depth << (8*i)); } while (_cairo_atomic_ptr_cmpxchg (&info->gc[i], NULL, gc) != NULL); } while (_cairo_atomic_int_cmpxchg (&info->gc_depths, old, new) != old); return; out: if (unlikely (_cairo_xlib_display_queue_work (info->display, (cairo_xlib_notify_func) XFreeGC, gc, NULL))) { /* leak the server side resource... */ XFree ((char *) gc); } } #else GC _cairo_xlib_screen_get_gc (cairo_xlib_screen_t *info, int depth, Drawable drawable) { GC gc = NULL; int i; CAIRO_MUTEX_LOCK (info->mutex); for (i = 0; i < ARRAY_LENGTH (info->gc); i++) { if (((info->gc_depths >> (8*i)) & 0xff) == depth) { info->gc_depths &= ~(0xff << (8*i)); gc = info->gc[i]; break; } } CAIRO_MUTEX_UNLOCK (info->mutex); if (gc == NULL) { XGCValues gcv; gcv.graphics_exposures = False; gcv.fill_style = FillTiled; gc = XCreateGC (_cairo_xlib_display_get_dpy (info->display), drawable, GCGraphicsExposures | GCFillStyle, &gcv); } return gc; } void _cairo_xlib_screen_put_gc (cairo_xlib_screen_t *info, int depth, GC gc) { int i; CAIRO_MUTEX_LOCK (info->mutex); for (i = 0; i < ARRAY_LENGTH (info->gc); i++) { if (((info->gc_depths >> (8*i)) & 0xff) == 0) break; } if (i == ARRAY_LENGTH (info->gc)) { cairo_status_t status; /* perform random substitution to ensure fair caching over depths */ i = rand () % ARRAY_LENGTH (info->gc); status = _cairo_xlib_display_queue_work (info->display, (cairo_xlib_notify_func) XFreeGC, info->gc[i], NULL); if (unlikely (status)) { /* leak the server side resource... */ XFree ((char *) info->gc[i]); } } info->gc[i] = gc; info->gc_depths &= ~(0xff << (8*i)); info->gc_depths |= depth << (8*i); CAIRO_MUTEX_UNLOCK (info->mutex); } #endif cairo_status_t _cairo_xlib_screen_get_visual_info (cairo_xlib_screen_t *info, Visual *visual, cairo_xlib_visual_info_t **out) { Display *dpy = _cairo_xlib_display_get_dpy (info->display); cairo_xlib_visual_info_t **visuals, *ret = NULL; cairo_status_t status; int i, n_visuals; CAIRO_MUTEX_LOCK (info->mutex); visuals = _cairo_array_index (&info->visuals, 0); n_visuals = _cairo_array_num_elements (&info->visuals); for (i = 0; i < n_visuals; i++) { if (visuals[i]->visualid == visual->visualid) { ret = visuals[i]; break; } } CAIRO_MUTEX_UNLOCK (info->mutex); if (ret != NULL) { *out = ret; return CAIRO_STATUS_SUCCESS; } status = _cairo_xlib_visual_info_create (dpy, XScreenNumberOfScreen (info->screen), visual->visualid, &ret); if (unlikely (status)) return status; CAIRO_MUTEX_LOCK (info->mutex); if (n_visuals != _cairo_array_num_elements (&info->visuals)) { /* check that another thread has not added our visual */ int new_visuals = _cairo_array_num_elements (&info->visuals); visuals = _cairo_array_index (&info->visuals, 0); for (i = n_visuals; i < new_visuals; i++) { if (visuals[i]->visualid == visual->visualid) { _cairo_xlib_visual_info_destroy (dpy, ret); ret = visuals[i]; break; } } if (i == new_visuals) status = _cairo_array_append (&info->visuals, &ret); } else status = _cairo_array_append (&info->visuals, &ret); CAIRO_MUTEX_UNLOCK (info->mutex); if (unlikely (status)) { _cairo_xlib_visual_info_destroy (dpy, ret); return status; } *out = ret; return CAIRO_STATUS_SUCCESS; } cairo_font_options_t * _cairo_xlib_screen_get_font_options (cairo_xlib_screen_t *info) { if (info->has_font_options) return &info->font_options; CAIRO_MUTEX_LOCK (info->mutex); if (! info->has_font_options) { _cairo_font_options_init_default (&info->font_options); if (info->screen != NULL) { _cairo_xlib_init_screen_font_options (_cairo_xlib_display_get_dpy (info->display), info); } info->has_font_options = TRUE; } CAIRO_MUTEX_UNLOCK (info->mutex); return &info->font_options; }