mirror of https://gitlab.freedesktop.org/mesa/mesa
554 lines
12 KiB
C
554 lines
12 KiB
C
/*
|
|
Copyright (c) 2008, 2009 Apple Inc.
|
|
|
|
Permission is hereby granted, free of charge, to any person
|
|
obtaining a copy of this software and associated documentation files
|
|
(the "Software"), to deal in the Software without restriction,
|
|
including without limitation the rights to use, copy, modify, merge,
|
|
publish, distribute, sublicense, and/or sell copies of the Software,
|
|
and to permit persons to whom the Software is furnished to do so,
|
|
subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be
|
|
included in all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
NONINFRINGEMENT. IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT
|
|
HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
DEALINGS IN THE SOFTWARE.
|
|
|
|
Except as contained in this notice, the name(s) of the above
|
|
copyright holders shall not be used in advertising or otherwise to
|
|
promote the sale, use or other dealings in this Software without
|
|
prior written authorization.
|
|
*/
|
|
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <pthread.h>
|
|
#include <string.h>
|
|
#include "apple_glx.h"
|
|
#include "apple_glx_context.h"
|
|
#include "apple_glx_drawable.h"
|
|
#include "appledri.h"
|
|
|
|
static pthread_mutex_t drawables_lock = PTHREAD_MUTEX_INITIALIZER;
|
|
static struct apple_glx_drawable *drawables_list = NULL;
|
|
|
|
static void
|
|
lock_drawables_list(void)
|
|
{
|
|
int err;
|
|
|
|
err = pthread_mutex_lock(&drawables_lock);
|
|
|
|
if (err) {
|
|
fprintf(stderr, "pthread_mutex_lock failure in %s: %s\n",
|
|
__func__, strerror(err));
|
|
abort();
|
|
}
|
|
}
|
|
|
|
static void
|
|
unlock_drawables_list(void)
|
|
{
|
|
int err;
|
|
|
|
err = pthread_mutex_unlock(&drawables_lock);
|
|
|
|
if (err) {
|
|
fprintf(stderr, "pthread_mutex_unlock failure in %s: %s\n",
|
|
__func__, strerror(err));
|
|
abort();
|
|
}
|
|
}
|
|
|
|
struct apple_glx_drawable *
|
|
apple_glx_find_drawable(Display * dpy, GLXDrawable drawable)
|
|
{
|
|
struct apple_glx_drawable *i, *agd = NULL;
|
|
|
|
lock_drawables_list();
|
|
|
|
for (i = drawables_list; i; i = i->next) {
|
|
if (i->drawable == drawable) {
|
|
agd = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
unlock_drawables_list();
|
|
|
|
return agd;
|
|
}
|
|
|
|
static void
|
|
drawable_lock(struct apple_glx_drawable *agd)
|
|
{
|
|
int err;
|
|
|
|
err = pthread_mutex_lock(&agd->mutex);
|
|
|
|
if (err) {
|
|
fprintf(stderr, "pthread_mutex_lock error: %s\n", strerror(err));
|
|
abort();
|
|
}
|
|
}
|
|
|
|
static void
|
|
drawable_unlock(struct apple_glx_drawable *d)
|
|
{
|
|
int err;
|
|
|
|
err = pthread_mutex_unlock(&d->mutex);
|
|
|
|
if (err) {
|
|
fprintf(stderr, "pthread_mutex_unlock error: %s\n", strerror(err));
|
|
abort();
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
reference_drawable(struct apple_glx_drawable *d)
|
|
{
|
|
d->lock(d);
|
|
d->reference_count++;
|
|
d->unlock(d);
|
|
}
|
|
|
|
static void
|
|
release_drawable(struct apple_glx_drawable *d)
|
|
{
|
|
d->lock(d);
|
|
d->reference_count--;
|
|
d->unlock(d);
|
|
}
|
|
|
|
/* The drawables list must be locked prior to calling this. */
|
|
/* Return true if the drawable was destroyed. */
|
|
static bool
|
|
destroy_drawable(struct apple_glx_drawable *d)
|
|
{
|
|
int err;
|
|
|
|
d->lock(d);
|
|
|
|
if (d->reference_count > 0) {
|
|
d->unlock(d);
|
|
return false;
|
|
}
|
|
|
|
d->unlock(d);
|
|
|
|
if (d->previous) {
|
|
d->previous->next = d->next;
|
|
}
|
|
else {
|
|
/*
|
|
* The item must be at the head of the list, if it
|
|
* has no previous pointer.
|
|
*/
|
|
drawables_list = d->next;
|
|
}
|
|
|
|
if (d->next)
|
|
d->next->previous = d->previous;
|
|
|
|
unlock_drawables_list();
|
|
|
|
if (d->callbacks.destroy) {
|
|
/*
|
|
* Warning: this causes other routines to be called (potentially)
|
|
* from surface_notify_handler. It's probably best to not have
|
|
* any locks at this point locked.
|
|
*/
|
|
d->callbacks.destroy(d->display, d);
|
|
}
|
|
|
|
apple_glx_diagnostic("%s: freeing %p\n", __func__, (void *) d);
|
|
|
|
/* Stupid recursive locks */
|
|
while (pthread_mutex_unlock(&d->mutex) == 0);
|
|
|
|
err = pthread_mutex_destroy(&d->mutex);
|
|
if (err) {
|
|
fprintf(stderr, "pthread_mutex_destroy error: %s\n", strerror(err));
|
|
abort();
|
|
}
|
|
|
|
free(d);
|
|
|
|
/* So that the locks are balanced and the caller correctly unlocks. */
|
|
lock_drawables_list();
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* This is typically called when a context is destroyed or the current
|
|
* drawable is made None.
|
|
*/
|
|
static bool
|
|
destroy_drawable_callback(struct apple_glx_drawable *d)
|
|
{
|
|
bool result;
|
|
|
|
d->lock(d);
|
|
|
|
apple_glx_diagnostic("%s: %p ->reference_count before -- %d\n", __func__,
|
|
(void *) d, d->reference_count);
|
|
|
|
d->reference_count--;
|
|
|
|
if (d->reference_count > 0) {
|
|
d->unlock(d);
|
|
return false;
|
|
}
|
|
|
|
d->unlock(d);
|
|
|
|
lock_drawables_list();
|
|
|
|
result = destroy_drawable(d);
|
|
|
|
unlock_drawables_list();
|
|
|
|
return result;
|
|
}
|
|
|
|
static bool
|
|
is_pbuffer(struct apple_glx_drawable *d)
|
|
{
|
|
return APPLE_GLX_DRAWABLE_PBUFFER == d->type;
|
|
}
|
|
|
|
static bool
|
|
is_pixmap(struct apple_glx_drawable *d)
|
|
{
|
|
return APPLE_GLX_DRAWABLE_PIXMAP == d->type;
|
|
}
|
|
|
|
static void
|
|
common_init(Display * dpy, GLXDrawable drawable, struct apple_glx_drawable *d)
|
|
{
|
|
int err;
|
|
pthread_mutexattr_t attr;
|
|
|
|
d->display = dpy;
|
|
d->reference_count = 0;
|
|
d->drawable = drawable;
|
|
d->type = -1;
|
|
|
|
err = pthread_mutexattr_init(&attr);
|
|
|
|
if (err) {
|
|
fprintf(stderr, "pthread_mutexattr_init error: %s\n", strerror(err));
|
|
abort();
|
|
}
|
|
|
|
/*
|
|
* There are some patterns that require a recursive mutex,
|
|
* when working with locks that protect the apple_glx_drawable,
|
|
* and reference functions like ->reference, and ->release.
|
|
*/
|
|
err = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
|
|
|
if (err) {
|
|
fprintf(stderr, "error: setting pthread mutex type: %s\n", strerror(err));
|
|
abort();
|
|
}
|
|
|
|
err = pthread_mutex_init(&d->mutex, &attr);
|
|
|
|
if (err) {
|
|
fprintf(stderr, "pthread_mutex_init error: %s\n", strerror(err));
|
|
abort();
|
|
}
|
|
|
|
(void) pthread_mutexattr_destroy(&attr);
|
|
|
|
d->lock = drawable_lock;
|
|
d->unlock = drawable_unlock;
|
|
|
|
d->reference = reference_drawable;
|
|
d->release = release_drawable;
|
|
|
|
d->destroy = destroy_drawable_callback;
|
|
|
|
d->is_pbuffer = is_pbuffer;
|
|
d->is_pixmap = is_pixmap;
|
|
|
|
d->width = -1;
|
|
d->height = -1;
|
|
d->row_bytes = 0;
|
|
d->path[0] = '\0';
|
|
d->fd = -1;
|
|
d->buffer = NULL;
|
|
d->buffer_length = 0;
|
|
|
|
d->previous = NULL;
|
|
d->next = NULL;
|
|
}
|
|
|
|
static void
|
|
link_tail(struct apple_glx_drawable *agd)
|
|
{
|
|
lock_drawables_list();
|
|
|
|
/* Link the new drawable into the global list. */
|
|
agd->next = drawables_list;
|
|
|
|
if (drawables_list)
|
|
drawables_list->previous = agd;
|
|
|
|
drawables_list = agd;
|
|
|
|
unlock_drawables_list();
|
|
}
|
|
|
|
/*WARNING: this returns a locked and referenced object. */
|
|
bool
|
|
apple_glx_drawable_create(Display * dpy,
|
|
int screen,
|
|
GLXDrawable drawable,
|
|
struct apple_glx_drawable **agdResult,
|
|
struct apple_glx_drawable_callbacks *callbacks)
|
|
{
|
|
struct apple_glx_drawable *d;
|
|
|
|
d = calloc(1, sizeof *d);
|
|
|
|
if (NULL == d) {
|
|
perror("malloc");
|
|
return true;
|
|
}
|
|
|
|
common_init(dpy, drawable, d);
|
|
d->type = callbacks->type;
|
|
d->callbacks = *callbacks;
|
|
|
|
d->reference(d);
|
|
d->lock(d);
|
|
|
|
link_tail(d);
|
|
|
|
apple_glx_diagnostic("%s: new drawable %p\n", __func__, (void *) d);
|
|
|
|
*agdResult = d;
|
|
|
|
return false;
|
|
}
|
|
|
|
static int error_count = 0;
|
|
|
|
static int
|
|
error_handler(Display * dpy, XErrorEvent * err)
|
|
{
|
|
if (err->error_code == BadWindow) {
|
|
++error_count;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
apple_glx_garbage_collect_drawables(Display * dpy)
|
|
{
|
|
struct apple_glx_drawable *d, *dnext;
|
|
Window root;
|
|
int x, y;
|
|
unsigned int width, height, bd, depth;
|
|
int (*old_handler) (Display *, XErrorEvent *);
|
|
|
|
|
|
if (NULL == drawables_list)
|
|
return;
|
|
|
|
old_handler = XSetErrorHandler(error_handler);
|
|
|
|
XSync(dpy, False);
|
|
|
|
lock_drawables_list();
|
|
|
|
for (d = drawables_list; d;) {
|
|
dnext = d->next;
|
|
|
|
d->lock(d);
|
|
|
|
if (d->reference_count > 0) {
|
|
/*
|
|
* Skip this, because some context still retains a reference
|
|
* to the drawable.
|
|
*/
|
|
d->unlock(d);
|
|
d = dnext;
|
|
continue;
|
|
}
|
|
|
|
d->unlock(d);
|
|
|
|
error_count = 0;
|
|
|
|
/*
|
|
* Mesa uses XGetWindowAttributes, but some of these things are
|
|
* most definitely not Windows, and that's against the rules.
|
|
* XGetGeometry on the other hand is legal with a Pixmap and Window.
|
|
*/
|
|
XGetGeometry(dpy, d->drawable, &root, &x, &y, &width, &height, &bd,
|
|
&depth);
|
|
|
|
if (error_count > 0) {
|
|
/*
|
|
* Note: this may not actually destroy the drawable.
|
|
* If another context retains a reference to the drawable
|
|
* after the reference count test above.
|
|
*/
|
|
(void) destroy_drawable(d);
|
|
error_count = 0;
|
|
}
|
|
|
|
d = dnext;
|
|
}
|
|
|
|
XSetErrorHandler(old_handler);
|
|
|
|
unlock_drawables_list();
|
|
}
|
|
|
|
unsigned int
|
|
apple_glx_get_drawable_count(void)
|
|
{
|
|
unsigned int result = 0;
|
|
struct apple_glx_drawable *d;
|
|
|
|
lock_drawables_list();
|
|
|
|
for (d = drawables_list; d; d = d->next)
|
|
++result;
|
|
|
|
unlock_drawables_list();
|
|
|
|
return result;
|
|
}
|
|
|
|
struct apple_glx_drawable *
|
|
apple_glx_drawable_find_by_type(GLXDrawable drawable, int type, int flags)
|
|
{
|
|
struct apple_glx_drawable *d;
|
|
|
|
lock_drawables_list();
|
|
|
|
for (d = drawables_list; d; d = d->next) {
|
|
if (d->type == type && d->drawable == drawable) {
|
|
if (flags & APPLE_GLX_DRAWABLE_REFERENCE)
|
|
d->reference(d);
|
|
|
|
if (flags & APPLE_GLX_DRAWABLE_LOCK)
|
|
d->lock(d);
|
|
|
|
unlock_drawables_list();
|
|
|
|
return d;
|
|
}
|
|
}
|
|
|
|
unlock_drawables_list();
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct apple_glx_drawable *
|
|
apple_glx_drawable_find(GLXDrawable drawable, int flags)
|
|
{
|
|
struct apple_glx_drawable *d;
|
|
|
|
lock_drawables_list();
|
|
|
|
for (d = drawables_list; d; d = d->next) {
|
|
if (d->drawable == drawable) {
|
|
if (flags & APPLE_GLX_DRAWABLE_REFERENCE)
|
|
d->reference(d);
|
|
|
|
if (flags & APPLE_GLX_DRAWABLE_LOCK)
|
|
d->lock(d);
|
|
|
|
unlock_drawables_list();
|
|
|
|
return d;
|
|
}
|
|
}
|
|
|
|
unlock_drawables_list();
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Return true if the type is valid for the drawable. */
|
|
bool
|
|
apple_glx_drawable_destroy_by_type(Display * dpy,
|
|
GLXDrawable drawable, int type)
|
|
{
|
|
struct apple_glx_drawable *d;
|
|
|
|
lock_drawables_list();
|
|
|
|
for (d = drawables_list; d; d = d->next) {
|
|
if (drawable == d->drawable && type == d->type) {
|
|
/*
|
|
* The user has requested that we destroy this resource.
|
|
* However, there may be references in the contexts to it, so
|
|
* release it, and call destroy_drawable which doesn't destroy
|
|
* if the reference_count is > 0.
|
|
*/
|
|
d->release(d);
|
|
|
|
apple_glx_diagnostic("%s d->reference_count %d\n",
|
|
__func__, d->reference_count);
|
|
|
|
destroy_drawable(d);
|
|
unlock_drawables_list();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
unlock_drawables_list();
|
|
|
|
return false;
|
|
}
|
|
|
|
struct apple_glx_drawable *
|
|
apple_glx_drawable_find_by_uid(unsigned int uid, int flags)
|
|
{
|
|
struct apple_glx_drawable *d;
|
|
|
|
lock_drawables_list();
|
|
|
|
for (d = drawables_list; d; d = d->next) {
|
|
/* Only surfaces have a uid. */
|
|
if (APPLE_GLX_DRAWABLE_SURFACE == d->type) {
|
|
if (d->types.surface.uid == uid) {
|
|
if (flags & APPLE_GLX_DRAWABLE_REFERENCE)
|
|
d->reference(d);
|
|
|
|
if (flags & APPLE_GLX_DRAWABLE_LOCK)
|
|
d->lock(d);
|
|
|
|
unlock_drawables_list();
|
|
|
|
return d;
|
|
}
|
|
}
|
|
}
|
|
|
|
unlock_drawables_list();
|
|
|
|
return NULL;
|
|
}
|