forked from KolibriOS/kolibrios
754f9336f0
git-svn-id: svn://kolibrios.org@4349 a494cfbc-eb01-0410-851d-a64ba20cac60
978 lines
28 KiB
C
978 lines
28 KiB
C
/* Cairo - a vector graphics library with display and print output
|
|
*
|
|
* Copyright © 2009 Intel Corporation
|
|
*
|
|
* 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.
|
|
*
|
|
* Authors:
|
|
* Chris Wilson <chris@chris-wilson.co.uk>
|
|
*/
|
|
|
|
|
|
#include "cairoint.h"
|
|
|
|
#include "cairo-xcb-private.h"
|
|
#include "cairo-hash-private.h"
|
|
#include "cairo-freelist-private.h"
|
|
#include "cairo-list-inline.h"
|
|
|
|
#include <xcb/xcbext.h>
|
|
#include <xcb/bigreq.h>
|
|
#include <errno.h>
|
|
|
|
#if CAIRO_HAS_XCB_SHM_FUNCTIONS
|
|
#include <sys/ipc.h>
|
|
#include <sys/shm.h>
|
|
#include <xcb/shm.h>
|
|
#endif
|
|
|
|
typedef struct _cairo_xcb_xrender_format {
|
|
cairo_hash_entry_t key;
|
|
xcb_render_pictformat_t xrender_format;
|
|
} cairo_xcb_xrender_format_t;
|
|
|
|
typedef struct _cairo_xcb_xid {
|
|
cairo_list_t link;
|
|
uint32_t xid;
|
|
} cairo_xcb_xid_t;
|
|
|
|
#define XCB_RENDER_AT_LEAST(V, major, minor) \
|
|
(((V)->major_version > major) || \
|
|
(((V)->major_version == major) && ((V)->minor_version >= minor)))
|
|
|
|
#define XCB_RENDER_HAS_CREATE_PICTURE(surface) XCB_RENDER_AT_LEAST((surface), 0, 0)
|
|
#define XCB_RENDER_HAS_COMPOSITE(surface) XCB_RENDER_AT_LEAST((surface), 0, 0)
|
|
#define XCB_RENDER_HAS_COMPOSITE_TEXT(surface) XCB_RENDER_AT_LEAST((surface), 0, 0)
|
|
|
|
#define XCB_RENDER_HAS_FILL_RECTANGLES(surface) XCB_RENDER_AT_LEAST((surface), 0, 1)
|
|
|
|
#define XCB_RENDER_HAS_DISJOINT(surface) XCB_RENDER_AT_LEAST((surface), 0, 2)
|
|
#define XCB_RENDER_HAS_CONJOINT(surface) XCB_RENDER_AT_LEAST((surface), 0, 2)
|
|
|
|
#define XCB_RENDER_HAS_TRAPEZOIDS(surface) XCB_RENDER_AT_LEAST((surface), 0, 4)
|
|
#define XCB_RENDER_HAS_TRIANGLES(surface) XCB_RENDER_AT_LEAST((surface), 0, 4)
|
|
#define XCB_RENDER_HAS_TRISTRIP(surface) XCB_RENDER_AT_LEAST((surface), 0, 4)
|
|
#define XCB_RENDER_HAS_TRIFAN(surface) XCB_RENDER_AT_LEAST((surface), 0, 4)
|
|
|
|
#define XCB_RENDER_HAS_PICTURE_TRANSFORM(surface) XCB_RENDER_AT_LEAST((surface), 0, 6)
|
|
#define XCB_RENDER_HAS_FILTERS(surface) XCB_RENDER_AT_LEAST((surface), 0, 6)
|
|
|
|
#define XCB_RENDER_HAS_EXTENDED_REPEAT(surface) XCB_RENDER_AT_LEAST((surface), 0, 10)
|
|
#define XCB_RENDER_HAS_GRADIENTS(surface) XCB_RENDER_AT_LEAST((surface), 0, 10)
|
|
|
|
#define XCB_RENDER_HAS_PDF_OPERATORS(surface) XCB_RENDER_AT_LEAST((surface), 0, 11)
|
|
|
|
static cairo_list_t connections;
|
|
|
|
static cairo_status_t
|
|
_cairo_xcb_connection_find_visual_formats (cairo_xcb_connection_t *connection,
|
|
const xcb_render_query_pict_formats_reply_t *formats)
|
|
{
|
|
xcb_render_pictscreen_iterator_t screens;
|
|
xcb_render_pictdepth_iterator_t depths;
|
|
xcb_render_pictvisual_iterator_t visuals;
|
|
|
|
for (screens = xcb_render_query_pict_formats_screens_iterator (formats);
|
|
screens.rem;
|
|
xcb_render_pictscreen_next (&screens))
|
|
{
|
|
for (depths = xcb_render_pictscreen_depths_iterator (screens.data);
|
|
depths.rem;
|
|
xcb_render_pictdepth_next (&depths))
|
|
{
|
|
for (visuals = xcb_render_pictdepth_visuals_iterator (depths.data);
|
|
visuals.rem;
|
|
xcb_render_pictvisual_next (&visuals))
|
|
{
|
|
cairo_xcb_xrender_format_t *f;
|
|
cairo_status_t status;
|
|
|
|
f = malloc (sizeof (cairo_xcb_xrender_format_t));
|
|
if (unlikely (f == NULL))
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
f->key.hash = visuals.data->visual;
|
|
f->xrender_format = visuals.data->format;
|
|
status = _cairo_hash_table_insert (connection->visual_to_xrender_format,
|
|
&f->key);
|
|
if (unlikely (status))
|
|
return status;
|
|
}
|
|
}
|
|
}
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
#if 0
|
|
static xcb_format_t *
|
|
find_format_for_depth (const xcb_setup_t *setup, uint8_t depth)
|
|
{
|
|
xcb_format_t *fmt = xcb_setup_pixmap_formats (setup);
|
|
xcb_format_t *fmtend = fmt + xcb_setup_pixmap_formats_length (setup);
|
|
|
|
for (; fmt != fmtend; ++fmt)
|
|
if (fmt->depth == depth)
|
|
return fmt;
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static cairo_status_t
|
|
_cairo_xcb_connection_parse_xrender_formats (cairo_xcb_connection_t *connection,
|
|
const xcb_render_query_pict_formats_reply_t *formats)
|
|
{
|
|
xcb_render_pictforminfo_iterator_t i;
|
|
cairo_status_t status;
|
|
|
|
for (i = xcb_render_query_pict_formats_formats_iterator (formats);
|
|
i.rem;
|
|
xcb_render_pictforminfo_next (&i))
|
|
{
|
|
cairo_format_masks_t masks;
|
|
pixman_format_code_t pixman_format;
|
|
|
|
if (i.data->type != XCB_RENDER_PICT_TYPE_DIRECT)
|
|
continue;
|
|
|
|
masks.alpha_mask =
|
|
(unsigned long) i.data->direct.alpha_mask << i.data->direct.alpha_shift;
|
|
masks.red_mask =
|
|
(unsigned long) i.data->direct.red_mask << i.data->direct.red_shift;
|
|
masks.green_mask =
|
|
(unsigned long) i.data->direct.green_mask << i.data->direct.green_shift;
|
|
masks.blue_mask =
|
|
(unsigned long) i.data->direct.blue_mask << i.data->direct.blue_shift;
|
|
masks.bpp = i.data->depth;
|
|
|
|
if (_pixman_format_from_masks (&masks, &pixman_format)) {
|
|
cairo_hash_entry_t key;
|
|
|
|
key.hash = pixman_format;
|
|
if (! _cairo_hash_table_lookup (connection->xrender_formats, &key)) {
|
|
cairo_xcb_xrender_format_t *f;
|
|
|
|
f = malloc (sizeof (cairo_xcb_xrender_format_t));
|
|
if (unlikely (f == NULL))
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
f->key.hash = pixman_format;
|
|
f->xrender_format = i.data->id;
|
|
status = _cairo_hash_table_insert (connection->xrender_formats,
|
|
&f->key);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
#if 0
|
|
printf ("xrender %x -> (%lx, %lx, %lx, %lx, %d) %x [%d, %d]\n",
|
|
i.data->id,
|
|
masks.alpha_mask,
|
|
masks.red_mask,
|
|
masks.green_mask,
|
|
masks.blue_mask,
|
|
masks.bpp,
|
|
pixman_format,
|
|
PIXMAN_FORMAT_DEPTH(pixman_format),
|
|
PIXMAN_FORMAT_BPP(pixman_format));
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
status = _cairo_xcb_connection_find_visual_formats (connection, formats);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
connection->standard_formats[CAIRO_FORMAT_A1] =
|
|
_cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a1);
|
|
|
|
connection->standard_formats[CAIRO_FORMAT_A8] =
|
|
_cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a8);
|
|
|
|
connection->standard_formats[CAIRO_FORMAT_RGB24] =
|
|
_cairo_xcb_connection_get_xrender_format (connection,
|
|
PIXMAN_FORMAT (24,
|
|
PIXMAN_TYPE_ARGB,
|
|
0, 8, 8, 8));
|
|
if (connection->standard_formats[CAIRO_FORMAT_RGB24] == XCB_NONE) {
|
|
connection->standard_formats[CAIRO_FORMAT_RGB24] =
|
|
_cairo_xcb_connection_get_xrender_format (connection,
|
|
PIXMAN_FORMAT (24, PIXMAN_TYPE_ABGR,
|
|
0, 8, 8, 8));
|
|
}
|
|
|
|
connection->standard_formats[CAIRO_FORMAT_ARGB32] =
|
|
_cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a8r8g8b8);
|
|
if (connection->standard_formats[CAIRO_FORMAT_ARGB32] == XCB_NONE) {
|
|
connection->standard_formats[CAIRO_FORMAT_ARGB32] =
|
|
_cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a8b8g8r8);
|
|
}
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* We require support for depth 1, 8, 24 and 32 pixmaps
|
|
*/
|
|
#define DEPTH_MASK(d) (1 << ((d) - 1))
|
|
#define REQUIRED_DEPTHS (DEPTH_MASK(1) | \
|
|
DEPTH_MASK(8) | \
|
|
DEPTH_MASK(24) | \
|
|
DEPTH_MASK(32))
|
|
static cairo_bool_t
|
|
pixmap_depths_usable (cairo_xcb_connection_t *connection,
|
|
uint32_t missing,
|
|
xcb_drawable_t root)
|
|
{
|
|
xcb_connection_t *c = connection->xcb_connection;
|
|
xcb_void_cookie_t create_cookie[32];
|
|
xcb_pixmap_t pixmap;
|
|
cairo_bool_t success = TRUE;
|
|
int depth, i, j;
|
|
|
|
pixmap = _cairo_xcb_connection_get_xid (connection);
|
|
|
|
for (depth = 1, i = 0; depth <= 32; depth++) {
|
|
if (missing & DEPTH_MASK(depth)) {
|
|
create_cookie[i] = xcb_create_pixmap_checked (c, depth, pixmap, root, 1, 1);
|
|
xcb_free_pixmap (c, pixmap);
|
|
if (!create_cookie[i].sequence) {
|
|
success = FALSE;
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
|
|
for (j = 0; j < i; j++) {
|
|
xcb_generic_error_t *create_error = xcb_request_check (c, create_cookie[j]);
|
|
success &= create_error == NULL;
|
|
free (create_error);
|
|
}
|
|
|
|
_cairo_xcb_connection_put_xid (connection, pixmap);
|
|
|
|
return success;
|
|
}
|
|
|
|
static cairo_bool_t
|
|
has_required_depths (cairo_xcb_connection_t *connection)
|
|
{
|
|
xcb_screen_iterator_t screens;
|
|
|
|
for (screens = xcb_setup_roots_iterator (connection->root);
|
|
screens.rem;
|
|
xcb_screen_next (&screens))
|
|
{
|
|
xcb_depth_iterator_t depths;
|
|
uint32_t missing = REQUIRED_DEPTHS;
|
|
|
|
for (depths = xcb_screen_allowed_depths_iterator (screens.data);
|
|
depths.rem;
|
|
xcb_depth_next (&depths))
|
|
{
|
|
missing &= ~DEPTH_MASK (depths.data->depth);
|
|
}
|
|
if (missing == 0)
|
|
continue;
|
|
|
|
/*
|
|
* Ok, this is ugly. It should be sufficient at this
|
|
* point to just return false, but Xinerama is broken at
|
|
* this point and only advertises depths which have an
|
|
* associated visual. Of course, the other depths still
|
|
* work, but the only way to find out is to try them.
|
|
*/
|
|
if (! pixmap_depths_usable (connection, missing, screens.data->root))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static xcb_render_query_version_reply_t *
|
|
_render_restrict_env(xcb_render_query_version_reply_t *version)
|
|
{
|
|
const char *env;
|
|
|
|
if (version == NULL)
|
|
return NULL;
|
|
|
|
env = getenv ("CAIRO_DEBUG");
|
|
if (env != NULL)
|
|
env = strstr (env, "xrender-version=");
|
|
if (env != 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 < 0 || max_render_minor < 0) {
|
|
free (version);
|
|
return NULL;
|
|
}
|
|
|
|
if (max_render_major < (int) version->major_version ||
|
|
(max_render_major == (int) version->major_version &&
|
|
max_render_minor < (int) version->minor_version))
|
|
{
|
|
version->major_version = max_render_major;
|
|
version->minor_version = max_render_minor;
|
|
}
|
|
}
|
|
|
|
return version;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xcb_connection_query_render (cairo_xcb_connection_t *connection)
|
|
{
|
|
xcb_connection_t *c = connection->xcb_connection;
|
|
xcb_render_query_version_cookie_t version_cookie;
|
|
xcb_render_query_pict_formats_cookie_t formats_cookie;
|
|
xcb_render_query_version_reply_t *version;
|
|
xcb_render_query_pict_formats_reply_t *formats;
|
|
cairo_status_t status;
|
|
cairo_bool_t present;
|
|
|
|
version_cookie = xcb_render_query_version (c, XCB_RENDER_MAJOR_VERSION, XCB_RENDER_MINOR_VERSION);
|
|
formats_cookie = xcb_render_query_pict_formats (c);
|
|
|
|
present = has_required_depths (connection);
|
|
version = xcb_render_query_version_reply (c, version_cookie, 0);
|
|
formats = xcb_render_query_pict_formats_reply (c, formats_cookie, 0);
|
|
|
|
version = _render_restrict_env (version);
|
|
|
|
if (! present || version == NULL || formats == NULL) {
|
|
free (version);
|
|
free (formats);
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
/* always true if the extension is present (i.e. >= 0.0) */
|
|
connection->flags |= CAIRO_XCB_HAS_RENDER;
|
|
connection->flags |= CAIRO_XCB_RENDER_HAS_COMPOSITE;
|
|
connection->flags |= CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS;
|
|
|
|
if (XCB_RENDER_HAS_FILL_RECTANGLES (version))
|
|
connection->flags |= CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES;
|
|
|
|
if (XCB_RENDER_HAS_TRAPEZOIDS (version))
|
|
connection->flags |= CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS;
|
|
|
|
if (XCB_RENDER_HAS_PICTURE_TRANSFORM (version))
|
|
connection->flags |= CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM;
|
|
|
|
if (XCB_RENDER_HAS_FILTERS (version))
|
|
connection->flags |= CAIRO_XCB_RENDER_HAS_FILTERS;
|
|
|
|
if (XCB_RENDER_HAS_PDF_OPERATORS (version))
|
|
connection->flags |= CAIRO_XCB_RENDER_HAS_PDF_OPERATORS;
|
|
|
|
if (XCB_RENDER_HAS_EXTENDED_REPEAT (version))
|
|
connection->flags |= CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT;
|
|
|
|
if (XCB_RENDER_HAS_GRADIENTS (version))
|
|
connection->flags |= CAIRO_XCB_RENDER_HAS_GRADIENTS;
|
|
|
|
free (version);
|
|
|
|
status = _cairo_xcb_connection_parse_xrender_formats (connection, formats);
|
|
free (formats);
|
|
|
|
return status;
|
|
}
|
|
|
|
#if 0
|
|
static void
|
|
_cairo_xcb_connection_query_cairo (cairo_xcb_connection_t *connection)
|
|
{
|
|
xcb_connection_t *c = connection->xcb_connection;
|
|
xcb_cairo_query_version_reply_t *version;
|
|
|
|
version = xcb_cairo_query_version_reply (c,
|
|
xcb_cairo_query_version (c, 0, 0),
|
|
0);
|
|
|
|
free (version);
|
|
}
|
|
#endif
|
|
|
|
#if CAIRO_HAS_XCB_SHM_FUNCTIONS
|
|
static cairo_bool_t
|
|
can_use_shm (cairo_xcb_connection_t *connection)
|
|
{
|
|
cairo_bool_t success = TRUE;
|
|
xcb_connection_t *c = connection->xcb_connection;
|
|
xcb_void_cookie_t cookie[2];
|
|
xcb_generic_error_t *error;
|
|
int shmid;
|
|
uint32_t shmseg;
|
|
void *ptr;
|
|
|
|
shmid = shmget (IPC_PRIVATE, 0x1000, IPC_CREAT | 0600);
|
|
if (shmid == -1)
|
|
return FALSE;
|
|
|
|
ptr = shmat (shmid, NULL, 0);
|
|
if (ptr == (char *) -1) {
|
|
shmctl (shmid, IPC_RMID, NULL);
|
|
return FALSE;
|
|
}
|
|
|
|
shmseg = _cairo_xcb_connection_get_xid (connection);
|
|
cookie[0] = xcb_shm_attach_checked (c, shmseg, shmid, FALSE);
|
|
cookie[1] = xcb_shm_detach_checked (c, shmseg);
|
|
_cairo_xcb_connection_put_xid (connection, shmseg);
|
|
|
|
error = xcb_request_check (c, cookie[0]);
|
|
if (error != NULL)
|
|
success = FALSE;
|
|
|
|
error = xcb_request_check (c, cookie[1]);
|
|
if (error != NULL)
|
|
success = FALSE;
|
|
|
|
shmctl (shmid, IPC_RMID, NULL);
|
|
shmdt (ptr);
|
|
|
|
return success;
|
|
}
|
|
|
|
static void
|
|
_cairo_xcb_connection_query_shm (cairo_xcb_connection_t *connection)
|
|
{
|
|
xcb_connection_t *c = connection->xcb_connection;
|
|
xcb_shm_query_version_reply_t *version;
|
|
|
|
version = xcb_shm_query_version_reply (c, xcb_shm_query_version (c), 0);
|
|
if (version == NULL)
|
|
return;
|
|
|
|
free (version);
|
|
|
|
if (can_use_shm (connection))
|
|
connection->flags |= CAIRO_XCB_HAS_SHM;
|
|
}
|
|
#endif
|
|
|
|
static cairo_status_t
|
|
_device_flush (void *device)
|
|
{
|
|
cairo_xcb_connection_t *connection = device;
|
|
cairo_status_t status;
|
|
|
|
status = cairo_device_acquire (&connection->device);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
#if CAIRO_HAS_XCB_SHM_FUNCTIONS
|
|
_cairo_xcb_connection_shm_mem_pools_flush (connection);
|
|
#endif
|
|
|
|
xcb_flush (connection->xcb_connection);
|
|
|
|
cairo_device_release (&connection->device);
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
_pluck_xrender_format (void *entry,
|
|
void *closure)
|
|
{
|
|
_cairo_hash_table_remove (closure, entry);
|
|
free (entry);
|
|
}
|
|
|
|
static void
|
|
_device_finish (void *device)
|
|
{
|
|
cairo_xcb_connection_t *connection = device;
|
|
cairo_bool_t was_cached = FALSE;
|
|
|
|
if (! cairo_list_is_empty (&connection->link)) {
|
|
CAIRO_MUTEX_LOCK (_cairo_xcb_connections_mutex);
|
|
cairo_list_del (&connection->link);
|
|
CAIRO_MUTEX_UNLOCK (_cairo_xcb_connections_mutex);
|
|
was_cached = TRUE;
|
|
}
|
|
|
|
while (! cairo_list_is_empty (&connection->fonts)) {
|
|
cairo_xcb_font_t *font;
|
|
|
|
font = cairo_list_first_entry (&connection->fonts,
|
|
cairo_xcb_font_t,
|
|
link);
|
|
_cairo_xcb_font_close (font);
|
|
}
|
|
|
|
while (! cairo_list_is_empty (&connection->screens)) {
|
|
cairo_xcb_screen_t *screen;
|
|
|
|
screen = cairo_list_first_entry (&connection->screens,
|
|
cairo_xcb_screen_t,
|
|
link);
|
|
_cairo_xcb_screen_finish (screen);
|
|
}
|
|
|
|
#if CAIRO_HAS_XCB_SHM_FUNCTIONS
|
|
/* _cairo_xcb_screen_finish finishes surfaces. If any of those surfaces had
|
|
* a fallback image, we might have done a SHM PutImage. */
|
|
_cairo_xcb_connection_shm_mem_pools_flush (connection);
|
|
#endif
|
|
|
|
if (was_cached)
|
|
cairo_device_destroy (device);
|
|
}
|
|
|
|
static void
|
|
_device_destroy (void *device)
|
|
{
|
|
cairo_xcb_connection_t *connection = device;
|
|
|
|
_cairo_hash_table_foreach (connection->xrender_formats,
|
|
_pluck_xrender_format, connection->xrender_formats);
|
|
_cairo_hash_table_destroy (connection->xrender_formats);
|
|
|
|
_cairo_hash_table_foreach (connection->visual_to_xrender_format,
|
|
_pluck_xrender_format,
|
|
connection->visual_to_xrender_format);
|
|
_cairo_hash_table_destroy (connection->visual_to_xrender_format);
|
|
|
|
#if CAIRO_HAS_XCB_SHM_FUNCTIONS
|
|
_cairo_xcb_connection_shm_mem_pools_fini (connection);
|
|
#endif
|
|
_cairo_freepool_fini (&connection->shm_info_freelist);
|
|
|
|
_cairo_freepool_fini (&connection->xid_pool);
|
|
|
|
CAIRO_MUTEX_FINI (connection->shm_mutex);
|
|
CAIRO_MUTEX_FINI (connection->screens_mutex);
|
|
|
|
free (connection);
|
|
}
|
|
|
|
static const cairo_device_backend_t _cairo_xcb_device_backend = {
|
|
CAIRO_DEVICE_TYPE_XCB,
|
|
|
|
NULL, NULL, /* lock, unlock */
|
|
|
|
_device_flush,
|
|
_device_finish,
|
|
_device_destroy,
|
|
};
|
|
|
|
cairo_xcb_connection_t *
|
|
_cairo_xcb_connection_get (xcb_connection_t *xcb_connection)
|
|
{
|
|
cairo_xcb_connection_t *connection;
|
|
const xcb_query_extension_reply_t *ext;
|
|
cairo_status_t status;
|
|
|
|
CAIRO_MUTEX_INITIALIZE ();
|
|
|
|
CAIRO_MUTEX_LOCK (_cairo_xcb_connections_mutex);
|
|
if (connections.next == NULL) {
|
|
/* XXX _cairo_init () */
|
|
cairo_list_init (&connections);
|
|
}
|
|
|
|
cairo_list_foreach_entry (connection,
|
|
cairo_xcb_connection_t,
|
|
&connections,
|
|
link)
|
|
{
|
|
if (connection->xcb_connection == xcb_connection) {
|
|
/* Maintain MRU order. */
|
|
if (connections.next != &connection->link)
|
|
cairo_list_move (&connection->link, &connections);
|
|
|
|
goto unlock;
|
|
}
|
|
}
|
|
|
|
connection = malloc (sizeof (cairo_xcb_connection_t));
|
|
if (unlikely (connection == NULL))
|
|
goto unlock;
|
|
|
|
_cairo_device_init (&connection->device, &_cairo_xcb_device_backend);
|
|
|
|
connection->xcb_connection = xcb_connection;
|
|
|
|
cairo_list_init (&connection->fonts);
|
|
cairo_list_init (&connection->screens);
|
|
cairo_list_init (&connection->link);
|
|
connection->xrender_formats = _cairo_hash_table_create (NULL);
|
|
if (connection->xrender_formats == NULL) {
|
|
CAIRO_MUTEX_FINI (connection->device.mutex);
|
|
free (connection);
|
|
connection = NULL;
|
|
goto unlock;
|
|
}
|
|
|
|
connection->visual_to_xrender_format = _cairo_hash_table_create (NULL);
|
|
if (connection->visual_to_xrender_format == NULL) {
|
|
_cairo_hash_table_destroy (connection->xrender_formats);
|
|
CAIRO_MUTEX_FINI (connection->device.mutex);
|
|
free (connection);
|
|
connection = NULL;
|
|
goto unlock;
|
|
}
|
|
|
|
cairo_list_init (&connection->free_xids);
|
|
_cairo_freepool_init (&connection->xid_pool,
|
|
sizeof (cairo_xcb_xid_t));
|
|
|
|
cairo_list_init (&connection->shm_pools);
|
|
cairo_list_init (&connection->shm_pending);
|
|
_cairo_freepool_init (&connection->shm_info_freelist,
|
|
sizeof (cairo_xcb_shm_info_t));
|
|
|
|
connection->maximum_request_length =
|
|
xcb_get_maximum_request_length (xcb_connection);
|
|
|
|
CAIRO_MUTEX_INIT (connection->shm_mutex);
|
|
CAIRO_MUTEX_INIT (connection->screens_mutex);
|
|
|
|
CAIRO_MUTEX_LOCK (connection->device.mutex);
|
|
|
|
connection->flags = 0;
|
|
connection->force_precision = -1;
|
|
|
|
xcb_prefetch_extension_data (xcb_connection, &xcb_big_requests_id);
|
|
xcb_prefetch_extension_data (xcb_connection, &xcb_render_id);
|
|
#if CAIRO_HAS_XCB_SHM_FUNCTIONS
|
|
xcb_prefetch_extension_data (xcb_connection, &xcb_shm_id);
|
|
#endif
|
|
#if 0
|
|
xcb_prefetch_extension_data (xcb_connection, &xcb_cairo_id);
|
|
#endif
|
|
|
|
xcb_prefetch_maximum_request_length (xcb_connection);
|
|
|
|
connection->root = xcb_get_setup (xcb_connection);
|
|
connection->render = NULL;
|
|
ext = xcb_get_extension_data (xcb_connection, &xcb_render_id);
|
|
if (ext != NULL && ext->present) {
|
|
status = _cairo_xcb_connection_query_render (connection);
|
|
if (unlikely (status)) {
|
|
CAIRO_MUTEX_UNLOCK (connection->device.mutex);
|
|
_cairo_xcb_connection_destroy (connection);
|
|
connection = NULL;
|
|
goto unlock;
|
|
}
|
|
|
|
connection->render = ext;
|
|
}
|
|
|
|
#if 0
|
|
ext = xcb_get_extension_data (connection, &xcb_cairo_id);
|
|
if (ext != NULL && ext->present)
|
|
_cairo_xcb_connection_query_cairo (connection);
|
|
#endif
|
|
|
|
connection->shm = NULL;
|
|
#if CAIRO_HAS_XCB_SHM_FUNCTIONS
|
|
ext = xcb_get_extension_data (xcb_connection, &xcb_shm_id);
|
|
if (ext != NULL && ext->present) {
|
|
_cairo_xcb_connection_query_shm (connection);
|
|
connection->shm = ext;
|
|
}
|
|
#endif
|
|
|
|
connection->original_flags = connection->flags;
|
|
|
|
CAIRO_MUTEX_UNLOCK (connection->device.mutex);
|
|
|
|
cairo_list_add (&connection->link, &connections);
|
|
unlock:
|
|
CAIRO_MUTEX_UNLOCK (_cairo_xcb_connections_mutex);
|
|
|
|
return connection;
|
|
}
|
|
|
|
xcb_render_pictformat_t
|
|
_cairo_xcb_connection_get_xrender_format (cairo_xcb_connection_t *connection,
|
|
pixman_format_code_t pixman_format)
|
|
{
|
|
cairo_hash_entry_t key;
|
|
cairo_xcb_xrender_format_t *format;
|
|
|
|
key.hash = pixman_format;
|
|
format = _cairo_hash_table_lookup (connection->xrender_formats, &key);
|
|
return format ? format->xrender_format : XCB_NONE;
|
|
}
|
|
|
|
xcb_render_pictformat_t
|
|
_cairo_xcb_connection_get_xrender_format_for_visual (cairo_xcb_connection_t *connection,
|
|
const xcb_visualid_t visual)
|
|
{
|
|
cairo_hash_entry_t key;
|
|
cairo_xcb_xrender_format_t *format;
|
|
|
|
key.hash = visual;
|
|
format = _cairo_hash_table_lookup (connection->visual_to_xrender_format, &key);
|
|
return format ? format->xrender_format : XCB_NONE;
|
|
}
|
|
|
|
void
|
|
_cairo_xcb_connection_put_xid (cairo_xcb_connection_t *connection,
|
|
uint32_t xid)
|
|
{
|
|
cairo_xcb_xid_t *cache;
|
|
|
|
assert (CAIRO_MUTEX_IS_LOCKED (connection->device.mutex));
|
|
cache = _cairo_freepool_alloc (&connection->xid_pool);
|
|
if (likely (cache != NULL)) {
|
|
cache->xid = xid;
|
|
cairo_list_add (&cache->link, &connection->free_xids);
|
|
}
|
|
}
|
|
|
|
uint32_t
|
|
_cairo_xcb_connection_get_xid (cairo_xcb_connection_t *connection)
|
|
{
|
|
uint32_t xid;
|
|
|
|
assert (CAIRO_MUTEX_IS_LOCKED (connection->device.mutex));
|
|
if (! cairo_list_is_empty (&connection->free_xids)) {
|
|
cairo_xcb_xid_t *cache;
|
|
|
|
cache = cairo_list_first_entry (&connection->free_xids,
|
|
cairo_xcb_xid_t,
|
|
link);
|
|
xid = cache->xid;
|
|
|
|
cairo_list_del (&cache->link);
|
|
_cairo_freepool_free (&connection->xid_pool, cache);
|
|
} else {
|
|
xid = xcb_generate_id (connection->xcb_connection);
|
|
}
|
|
|
|
return xid;
|
|
}
|
|
|
|
/**
|
|
* cairo_xcb_device_get_connection:
|
|
* @device: a #cairo_device_t for the XCB backend
|
|
*
|
|
* Get the connection for the XCB device.
|
|
*
|
|
* Returns: the #xcb_connection_t for the connection
|
|
*
|
|
* Since: 1.12
|
|
**/
|
|
xcb_connection_t *
|
|
cairo_xcb_device_get_connection (cairo_device_t *device)
|
|
{
|
|
if (device->backend->type != CAIRO_DEVICE_TYPE_XCB)
|
|
return NULL;
|
|
|
|
return ((cairo_xcb_connection_t *)device)->xcb_connection;
|
|
}
|
|
|
|
/* public (debug) interface */
|
|
|
|
/**
|
|
* cairo_xcb_device_debug_cap_xshm_version:
|
|
* @device: a #cairo_device_t for the XCB backend
|
|
* @major_version: major version to restrict to
|
|
* @minor_version: minor version to restrict to
|
|
*
|
|
* Restricts all future XCB surfaces for this devices to the specified version
|
|
* of the SHM extension. This function exists solely for debugging purpose.
|
|
* It let's you find out how cairo would behave with an older version of
|
|
* the SHM extension.
|
|
*
|
|
* Use the special values -1 and -1 for disabling the SHM extension.
|
|
*
|
|
* Since: 1.12
|
|
**/
|
|
void
|
|
cairo_xcb_device_debug_cap_xshm_version (cairo_device_t *device,
|
|
int major_version,
|
|
int minor_version)
|
|
{
|
|
cairo_xcb_connection_t *connection = (cairo_xcb_connection_t *) device;
|
|
|
|
if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) {
|
|
cairo_status_t status;
|
|
|
|
status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
|
|
(void) status;
|
|
return;
|
|
}
|
|
|
|
/* First reset all the SHM flags to their original value. This works
|
|
* because we only ever clear bits after the connection was created.
|
|
*/
|
|
connection->flags |= (connection->original_flags & CAIRO_XCB_SHM_MASK);
|
|
|
|
/* clear any flags that are inappropriate for the desired version */
|
|
if (major_version < 0 && minor_version < 0) {
|
|
connection->flags &= ~(CAIRO_XCB_HAS_SHM);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* cairo_xcb_device_debug_cap_xrender_version:
|
|
* @device: a #cairo_device_t for the XCB backend
|
|
* @major_version: major version to restrict to
|
|
* @minor_version: minor version to restrict to
|
|
*
|
|
* Restricts all future XCB surfaces for this devices to the specified version
|
|
* of the RENDER extension. This function exists solely for debugging purpose.
|
|
* It let's 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_xcb_device_debug_cap_xrender_version (cairo_device_t *device,
|
|
int major_version,
|
|
int minor_version)
|
|
{
|
|
cairo_xcb_connection_t *connection = (cairo_xcb_connection_t *) device;
|
|
|
|
if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) {
|
|
cairo_status_t status;
|
|
|
|
status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
|
|
(void) status;
|
|
return;
|
|
}
|
|
|
|
/* First reset all the RENDER flags to their original value. This works
|
|
* because we only ever clear bits after the connection was created.
|
|
*/
|
|
connection->flags |= (connection->original_flags & CAIRO_XCB_RENDER_MASK);
|
|
|
|
/* clear any flags that are inappropriate for the desired version */
|
|
if (major_version < 0 && minor_version < 0) {
|
|
connection->flags &= ~(CAIRO_XCB_HAS_RENDER |
|
|
CAIRO_XCB_RENDER_HAS_COMPOSITE |
|
|
CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS |
|
|
CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES |
|
|
CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS |
|
|
CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM |
|
|
CAIRO_XCB_RENDER_HAS_FILTERS |
|
|
CAIRO_XCB_RENDER_HAS_PDF_OPERATORS |
|
|
CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT |
|
|
CAIRO_XCB_RENDER_HAS_GRADIENTS);
|
|
} else {
|
|
xcb_render_query_version_reply_t version;
|
|
|
|
version.major_version = major_version;
|
|
version.minor_version = minor_version;
|
|
|
|
if (! XCB_RENDER_HAS_FILL_RECTANGLES (&version))
|
|
connection->flags &= ~CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES;
|
|
|
|
if (! XCB_RENDER_HAS_TRAPEZOIDS (&version))
|
|
connection->flags &= ~CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS;
|
|
|
|
if (! XCB_RENDER_HAS_PICTURE_TRANSFORM (&version))
|
|
connection->flags &= ~CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM;
|
|
|
|
if (! XCB_RENDER_HAS_FILTERS (&version))
|
|
connection->flags &= ~CAIRO_XCB_RENDER_HAS_FILTERS;
|
|
|
|
if (! XCB_RENDER_HAS_PDF_OPERATORS (&version))
|
|
connection->flags &= ~CAIRO_XCB_RENDER_HAS_PDF_OPERATORS;
|
|
|
|
if (! XCB_RENDER_HAS_EXTENDED_REPEAT (&version))
|
|
connection->flags &= ~CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT;
|
|
|
|
if (! XCB_RENDER_HAS_GRADIENTS (&version))
|
|
connection->flags &= ~CAIRO_XCB_RENDER_HAS_GRADIENTS;
|
|
}
|
|
}
|
|
#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
|
|
slim_hidden_def (cairo_xcb_device_debug_cap_xrender_version);
|
|
#endif
|
|
|
|
/**
|
|
* cairo_xcb_device_debug_set_precision:
|
|
* @device: a #cairo_device_t for the XCB 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_xcb_device_debug_set_precision (cairo_device_t *device,
|
|
int precision)
|
|
{
|
|
if (device == NULL || device->status)
|
|
return;
|
|
if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) {
|
|
cairo_status_t status;
|
|
|
|
status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
|
|
(void) status;
|
|
return;
|
|
}
|
|
|
|
((cairo_xcb_connection_t *) device)->force_precision = precision;
|
|
}
|
|
#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
|
|
slim_hidden_def (cairo_xcb_device_debug_set_precision);
|
|
#endif
|
|
|
|
/**
|
|
* cairo_xcb_device_debug_get_precision:
|
|
* @device: a #cairo_device_t for the XCB backend
|
|
*
|
|
* Get the Xrender precision mode.
|
|
*
|
|
* Returns: the render precision mode
|
|
*
|
|
* Since: 1.12
|
|
**/
|
|
int
|
|
cairo_xcb_device_debug_get_precision (cairo_device_t *device)
|
|
{
|
|
if (device == NULL || device->status)
|
|
return -1;
|
|
if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) {
|
|
cairo_status_t status;
|
|
|
|
status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
|
|
(void) status;
|
|
return -1;
|
|
}
|
|
|
|
return ((cairo_xcb_connection_t *) device)->force_precision;
|
|
}
|
|
#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
|
|
slim_hidden_def (cairo_xcb_device_debug_get_precision);
|
|
#endif
|