0,0 → 1,553 |
/* |
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; |
} |