mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-04-30 23:47:59 +02:00
Cleanup the code somewhat by passing cairo_xlib_display_t around internally as opposed to a Display and then having to lookup the corresponding cairo_xlib_display_t each time. [To get a cairo_xlib_display_t from a Display is a list traversal under mutex (though the element we're looking for is most likely at the start), but to get the Display is just a lockless pointer dereference.]
496 lines
14 KiB
C
496 lines
14 KiB
C
/* 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., 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 Chris Wilson.
|
|
*
|
|
* Contributor(s):
|
|
* Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
|
|
*/
|
|
|
|
#include "cairoint.h"
|
|
|
|
#include "cairo-xlib-private.h"
|
|
#include "cairo-xlib-xrender-private.h"
|
|
|
|
#include <fontconfig/fontconfig.h>
|
|
|
|
#include <X11/Xlibint.h> /* For XESetCloseDisplay */
|
|
|
|
typedef int (*cairo_xlib_error_func_t) (Display *display,
|
|
XErrorEvent *event);
|
|
|
|
struct _cairo_xlib_job {
|
|
cairo_xlib_job_t *next;
|
|
enum {
|
|
RESOURCE,
|
|
WORK
|
|
} type;
|
|
union {
|
|
struct {
|
|
cairo_xlib_notify_resource_func notify;
|
|
XID xid;
|
|
} resource;
|
|
struct {
|
|
cairo_xlib_notify_func notify;
|
|
void *data;
|
|
void (*destroy) (void *);
|
|
} work;
|
|
} func;
|
|
};
|
|
|
|
static cairo_xlib_display_t *_cairo_xlib_display_list;
|
|
|
|
static void
|
|
_cairo_xlib_remove_close_display_hook_internal (cairo_xlib_display_t *display,
|
|
cairo_xlib_hook_t *hook);
|
|
|
|
static void
|
|
_cairo_xlib_call_close_display_hooks (cairo_xlib_display_t *display)
|
|
{
|
|
cairo_xlib_screen_info_t *screen;
|
|
cairo_xlib_hook_t *hook;
|
|
|
|
/* call all registered shutdown routines */
|
|
CAIRO_MUTEX_LOCK (display->mutex);
|
|
|
|
for (screen = display->screens; screen != NULL; screen = screen->next)
|
|
_cairo_xlib_screen_info_close_display (screen);
|
|
|
|
while (TRUE) {
|
|
hook = display->close_display_hooks;
|
|
if (hook == NULL)
|
|
break;
|
|
|
|
_cairo_xlib_remove_close_display_hook_internal (display, hook);
|
|
|
|
CAIRO_MUTEX_UNLOCK (display->mutex);
|
|
hook->func (display, hook);
|
|
CAIRO_MUTEX_LOCK (display->mutex);
|
|
}
|
|
display->closed = TRUE;
|
|
|
|
CAIRO_MUTEX_UNLOCK (display->mutex);
|
|
}
|
|
|
|
static void
|
|
_cairo_xlib_display_discard_screens (cairo_xlib_display_t *display)
|
|
{
|
|
cairo_xlib_screen_info_t *screens;
|
|
|
|
CAIRO_MUTEX_LOCK (display->mutex);
|
|
screens = display->screens;
|
|
display->screens = NULL;
|
|
CAIRO_MUTEX_UNLOCK (display->mutex);
|
|
|
|
while (screens != NULL) {
|
|
cairo_xlib_screen_info_t *screen = screens;
|
|
screens = screen->next;
|
|
|
|
_cairo_xlib_screen_info_destroy (screen);
|
|
}
|
|
}
|
|
|
|
cairo_xlib_display_t *
|
|
_cairo_xlib_display_reference (cairo_xlib_display_t *display)
|
|
{
|
|
assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&display->ref_count));
|
|
|
|
_cairo_reference_count_inc (&display->ref_count);
|
|
|
|
return display;
|
|
}
|
|
|
|
void
|
|
_cairo_xlib_display_destroy (cairo_xlib_display_t *display)
|
|
{
|
|
assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&display->ref_count));
|
|
|
|
if (! _cairo_reference_count_dec_and_test (&display->ref_count))
|
|
return;
|
|
|
|
/* destroy all outstanding notifies */
|
|
while (display->workqueue != NULL) {
|
|
cairo_xlib_job_t *job = display->workqueue;
|
|
display->workqueue = job->next;
|
|
|
|
if (job->type == WORK && job->func.work.destroy != NULL)
|
|
job->func.work.destroy (job->func.work.data);
|
|
|
|
_cairo_freelist_free (&display->wq_freelist, job);
|
|
}
|
|
_cairo_freelist_fini (&display->wq_freelist);
|
|
|
|
CAIRO_MUTEX_FINI (display->mutex);
|
|
|
|
free (display);
|
|
}
|
|
|
|
static int
|
|
_noop_error_handler (Display *display,
|
|
XErrorEvent *event)
|
|
{
|
|
return False; /* return value is ignored */
|
|
}
|
|
static int
|
|
_cairo_xlib_close_display (Display *dpy, XExtCodes *codes)
|
|
{
|
|
cairo_xlib_display_t *display, **prev, *next;
|
|
cairo_xlib_error_func_t old_handler;
|
|
|
|
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;
|
|
|
|
/* protect the notifies from triggering XErrors */
|
|
XSync (dpy, False);
|
|
old_handler = XSetErrorHandler (_noop_error_handler);
|
|
|
|
_cairo_xlib_display_notify (display);
|
|
_cairo_xlib_call_close_display_hooks (display);
|
|
_cairo_xlib_display_discard_screens (display);
|
|
|
|
/* catch any that arrived before marking the display as closed */
|
|
_cairo_xlib_display_notify (display);
|
|
|
|
XSync (dpy, False);
|
|
XSetErrorHandler (old_handler);
|
|
|
|
/*
|
|
* 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);
|
|
|
|
assert (display != NULL);
|
|
_cairo_xlib_display_destroy (display);
|
|
|
|
/* Return value in accordance with requirements of
|
|
* XESetCloseDisplay */
|
|
return 0;
|
|
}
|
|
|
|
cairo_xlib_display_t *
|
|
_cairo_xlib_display_get (Display *dpy)
|
|
{
|
|
cairo_xlib_display_t *display;
|
|
cairo_xlib_display_t **prev;
|
|
XExtCodes *codes;
|
|
int major_unused, minor_unused;
|
|
|
|
/* 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;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (display != NULL) {
|
|
display = _cairo_xlib_display_reference (display);
|
|
goto UNLOCK;
|
|
}
|
|
|
|
display = malloc (sizeof (cairo_xlib_display_t));
|
|
if (display == NULL) {
|
|
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
|
|
goto UNLOCK;
|
|
}
|
|
|
|
/* 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.
|
|
*/
|
|
XRenderQueryVersion (dpy, &major_unused, &minor_unused);
|
|
|
|
codes = XAddExtension (dpy);
|
|
if (codes == NULL) {
|
|
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
|
|
free (display);
|
|
display = NULL;
|
|
goto UNLOCK;
|
|
}
|
|
|
|
XESetCloseDisplay (dpy, codes->extension, _cairo_xlib_close_display);
|
|
|
|
_cairo_freelist_init (&display->wq_freelist, sizeof (cairo_xlib_job_t));
|
|
|
|
CAIRO_REFERENCE_COUNT_INIT (&display->ref_count, 2); /* add one for the CloseDisplay */
|
|
CAIRO_MUTEX_INIT (display->mutex);
|
|
display->display = dpy;
|
|
display->screens = NULL;
|
|
display->workqueue = NULL;
|
|
display->close_display_hooks = NULL;
|
|
display->closed = FALSE;
|
|
|
|
memset (display->cached_xrender_formats, 0,
|
|
sizeof (display->cached_xrender_formats));
|
|
|
|
display->buggy_repeat = FALSE;
|
|
if (strstr (ServerVendor (dpy), "X.Org") != NULL) {
|
|
/* When modularized, the X.Org server VendorRelease was
|
|
* bogusly reset to a very small number, without any change in
|
|
* the ServerVendor string. We avoid considering the new
|
|
* servers with the small number as buggy by restricting the
|
|
* test to known bad releases. But there could be a problem
|
|
* again in the future if X.Org server versions ever climb
|
|
* back up to 6.7 or 6.8. */
|
|
if (VendorRelease (dpy) >= 60700000 && VendorRelease (dpy) <= 60802000)
|
|
display->buggy_repeat = TRUE;
|
|
|
|
/* But even the new modular server has bugs, (bad enough to
|
|
* crash the X server), that it so happens we can avoid with
|
|
* the exact same buggy_repeat workaround. We've verified that
|
|
* this bug exists as least as late as version 1.3.0.0, (which
|
|
* is in Fedora 8), and is gone again in version 1.4.99.901
|
|
* (from a Fedora 9 Beta). Versions between those are still
|
|
* unknown, but until we learn more, we'll assume that any 1.3
|
|
* version is buggy. */
|
|
if (VendorRelease (dpy) < 10400000)
|
|
display->buggy_repeat = TRUE;
|
|
} else if (strstr (ServerVendor (dpy), "XFree86") != NULL) {
|
|
if (VendorRelease (dpy) <= 40500000)
|
|
display->buggy_repeat = TRUE;
|
|
}
|
|
|
|
display->next = _cairo_xlib_display_list;
|
|
_cairo_xlib_display_list = display;
|
|
|
|
UNLOCK:
|
|
CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
|
|
return display;
|
|
}
|
|
|
|
void
|
|
_cairo_xlib_add_close_display_hook (cairo_xlib_display_t *display,
|
|
cairo_xlib_hook_t *hook)
|
|
{
|
|
CAIRO_MUTEX_LOCK (display->mutex);
|
|
hook->prev = NULL;
|
|
hook->next = display->close_display_hooks;
|
|
if (hook->next != NULL)
|
|
hook->next->prev = hook;
|
|
display->close_display_hooks = hook;
|
|
CAIRO_MUTEX_UNLOCK (display->mutex);
|
|
}
|
|
|
|
/* display->mutex must be held */
|
|
static void
|
|
_cairo_xlib_remove_close_display_hook_internal (cairo_xlib_display_t *display,
|
|
cairo_xlib_hook_t *hook)
|
|
{
|
|
if (display->close_display_hooks == hook)
|
|
display->close_display_hooks = hook->next;
|
|
else if (hook->prev != NULL)
|
|
hook->prev->next = hook->next;
|
|
|
|
if (hook->next != NULL)
|
|
hook->next->prev = hook->prev;
|
|
|
|
hook->prev = NULL;
|
|
hook->next = NULL;
|
|
}
|
|
|
|
void
|
|
_cairo_xlib_remove_close_display_hook (cairo_xlib_display_t *display,
|
|
cairo_xlib_hook_t *hook)
|
|
{
|
|
CAIRO_MUTEX_LOCK (display->mutex);
|
|
_cairo_xlib_remove_close_display_hook_internal (display, hook);
|
|
CAIRO_MUTEX_UNLOCK (display->mutex);
|
|
}
|
|
|
|
cairo_status_t
|
|
_cairo_xlib_display_queue_resource (cairo_xlib_display_t *display,
|
|
cairo_xlib_notify_resource_func notify,
|
|
XID xid)
|
|
{
|
|
cairo_xlib_job_t *job;
|
|
cairo_status_t status = CAIRO_STATUS_NO_MEMORY;
|
|
|
|
CAIRO_MUTEX_LOCK (display->mutex);
|
|
if (display->closed == FALSE) {
|
|
job = _cairo_freelist_alloc (&display->wq_freelist);
|
|
if (job != NULL) {
|
|
job->type = RESOURCE;
|
|
job->func.resource.xid = xid;
|
|
job->func.resource.notify = notify;
|
|
|
|
job->next = display->workqueue;
|
|
display->workqueue = job;
|
|
|
|
status = CAIRO_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
CAIRO_MUTEX_UNLOCK (display->mutex);
|
|
|
|
return status;
|
|
}
|
|
|
|
cairo_status_t
|
|
_cairo_xlib_display_queue_work (cairo_xlib_display_t *display,
|
|
cairo_xlib_notify_func notify,
|
|
void *data,
|
|
void (*destroy) (void *))
|
|
{
|
|
cairo_xlib_job_t *job;
|
|
cairo_status_t status = CAIRO_STATUS_NO_MEMORY;
|
|
|
|
CAIRO_MUTEX_LOCK (display->mutex);
|
|
if (display->closed == FALSE) {
|
|
job = _cairo_freelist_alloc (&display->wq_freelist);
|
|
if (job != NULL) {
|
|
job->type = WORK;
|
|
job->func.work.data = data;
|
|
job->func.work.notify = notify;
|
|
job->func.work.destroy = destroy;
|
|
|
|
job->next = display->workqueue;
|
|
display->workqueue = job;
|
|
|
|
status = CAIRO_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
CAIRO_MUTEX_UNLOCK (display->mutex);
|
|
|
|
return status;
|
|
}
|
|
|
|
void
|
|
_cairo_xlib_display_notify (cairo_xlib_display_t *display)
|
|
{
|
|
cairo_xlib_job_t *jobs, *job, *freelist;
|
|
Display *dpy = display->display;
|
|
|
|
CAIRO_MUTEX_LOCK (display->mutex);
|
|
jobs = display->workqueue;
|
|
while (jobs != NULL) {
|
|
display->workqueue = NULL;
|
|
CAIRO_MUTEX_UNLOCK (display->mutex);
|
|
|
|
/* reverse the list to obtain FIFO order */
|
|
job = NULL;
|
|
do {
|
|
cairo_xlib_job_t *next = jobs->next;
|
|
jobs->next = job;
|
|
job = jobs;
|
|
jobs = next;
|
|
} while (jobs != NULL);
|
|
freelist = jobs = job;
|
|
|
|
do {
|
|
job = jobs;
|
|
jobs = job->next;
|
|
|
|
switch (job->type){
|
|
case WORK:
|
|
job->func.work.notify (dpy, job->func.work.data);
|
|
if (job->func.work.destroy != NULL)
|
|
job->func.work.destroy (job->func.work.data);
|
|
break;
|
|
|
|
case RESOURCE:
|
|
job->func.resource.notify (dpy, job->func.resource.xid);
|
|
break;
|
|
}
|
|
} while (jobs != NULL);
|
|
|
|
CAIRO_MUTEX_LOCK (display->mutex);
|
|
do {
|
|
job = freelist;
|
|
freelist = job->next;
|
|
_cairo_freelist_free (&display->wq_freelist, job);
|
|
} while (freelist != NULL);
|
|
|
|
jobs = display->workqueue;
|
|
}
|
|
CAIRO_MUTEX_UNLOCK (display->mutex);
|
|
}
|
|
|
|
XRenderPictFormat *
|
|
_cairo_xlib_display_get_xrender_format (cairo_xlib_display_t *display,
|
|
cairo_format_t format)
|
|
{
|
|
XRenderPictFormat *xrender_format;
|
|
|
|
CAIRO_MUTEX_LOCK (display->mutex);
|
|
xrender_format = display->cached_xrender_formats[format];
|
|
if (xrender_format == NULL) {
|
|
int pict_format;
|
|
|
|
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;
|
|
default:
|
|
ASSERT_NOT_REACHED;
|
|
case CAIRO_FORMAT_ARGB32:
|
|
pict_format = PictStandardARGB32; break;
|
|
}
|
|
xrender_format = XRenderFindStandardFormat (display->display,
|
|
pict_format);
|
|
display->cached_xrender_formats[format] = xrender_format;
|
|
}
|
|
CAIRO_MUTEX_UNLOCK (display->mutex);
|
|
|
|
return xrender_format;
|
|
}
|