apple_glx_context.c [plain text]
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <assert.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <GL/gl.h>
#include <GL/glext.h>
#include <OpenGL/CGLTypes.h>
#include <OpenGL/CGLCurrent.h>
#include <OpenGL/OpenGL.h>
#include "glxclient.h"
#include "apple_glx.h"
#include "apple_glx_context.h"
#include "appledri.h"
#include "apple_visual.h"
#include "apple_cgl.h"
#include "apple_glx_drawable.h"
static pthread_mutex_t context_lock = PTHREAD_MUTEX_INITIALIZER;
static struct apple_glx_context *context_list = NULL;
static void
lock_context_list(void)
{
int err;
err = pthread_mutex_lock(&context_lock);
if (err) {
fprintf(stderr, "pthread_mutex_lock failure in %s: %d\n",
__func__, err);
abort();
}
}
static void
unlock_context_list(void)
{
int err;
err = pthread_mutex_unlock(&context_lock);
if (err) {
fprintf(stderr, "pthread_mutex_unlock failure in %s: %d\n",
__func__, err);
abort();
}
}
static bool
is_context_valid(struct apple_glx_context *ac)
{
struct apple_glx_context *i;
lock_context_list();
for (i = context_list; i; i = i->next) {
if (ac == i) {
unlock_context_list();
return true;
}
}
unlock_context_list();
return false;
}
bool
apple_glx_create_context(void **ptr, Display * dpy, int screen,
const void *mode, void *sharedContext,
int *errorptr, bool * x11errorptr)
{
struct apple_glx_context *ac;
struct apple_glx_context *sharedac = sharedContext;
CGLError error;
*ptr = NULL;
ac = malloc(sizeof *ac);
if (NULL == ac) {
*errorptr = BadAlloc;
*x11errorptr = true;
return true;
}
if (sharedac && !is_context_valid(sharedac)) {
*errorptr = GLXBadContext;
*x11errorptr = false;
return true;
}
ac->context_obj = NULL;
ac->pixel_format_obj = NULL;
ac->drawable = NULL;
ac->thread_id = pthread_self();
ac->screen = screen;
ac->double_buffered = false;
ac->uses_stereo = false;
ac->need_update = false;
ac->is_current = false;
ac->made_current = false;
ac->last_surface_window = None;
apple_visual_create_pfobj(&ac->pixel_format_obj, mode,
&ac->double_buffered, &ac->uses_stereo,
false);
error = apple_cgl.create_context(ac->pixel_format_obj,
sharedac ? sharedac->context_obj : NULL,
&ac->context_obj);
if (error) {
(void) apple_cgl.destroy_pixel_format(ac->pixel_format_obj);
free(ac);
if (kCGLBadMatch == error) {
*errorptr = BadMatch;
*x11errorptr = true;
}
else {
*errorptr = GLXBadContext;
*x11errorptr = false;
}
if (getenv("LIBGL_DIAGNOSTIC"))
fprintf(stderr, "error: %s\n", apple_cgl.error_string(error));
return true;
}
lock_context_list();
if (context_list)
context_list->previous = ac;
ac->previous = NULL;
ac->next = context_list;
context_list = ac;
*ptr = ac;
apple_glx_diagnostic("%s: ac %p ac->context_obj %p\n",
__func__, (void *) ac, (void *) ac->context_obj);
unlock_context_list();
return false;
}
void
apple_glx_destroy_context(void **ptr, Display * dpy)
{
struct apple_glx_context *ac = *ptr;
if (NULL == ac)
return;
apple_glx_diagnostic("%s: ac %p ac->context_obj %p\n",
__func__, (void *) ac, (void *) ac->context_obj);
if (apple_cgl.get_current_context() == ac->context_obj) {
apple_glx_diagnostic("%s: context ac->context_obj %p "
"is still current!\n", __func__,
(void *) ac->context_obj);
if (apple_cgl.set_current_context(NULL)) {
abort();
}
}
lock_context_list();
if (ac->previous) {
ac->previous->next = ac->next;
}
else {
context_list = ac->next;
}
if (ac->next) {
ac->next->previous = ac->previous;
}
unlock_context_list();
if (apple_cgl.clear_drawable(ac->context_obj)) {
fprintf(stderr, "error: while clearing drawable!\n");
abort();
}
if (ac->drawable) {
ac->drawable->destroy(ac->drawable);
}
if (apple_cgl.destroy_pixel_format(ac->pixel_format_obj)) {
fprintf(stderr, "error: destroying pixel format in %s\n", __func__);
abort();
}
if (apple_cgl.destroy_context(ac->context_obj)) {
fprintf(stderr, "error: destroying context_obj in %s\n", __func__);
abort();
}
free(ac);
*ptr = NULL;
apple_glx_garbage_collect_drawables(dpy);
}
bool
apple_glx_make_current_context(Display * dpy, void *oldptr, void *ptr,
GLXDrawable drawable)
{
struct apple_glx_context *oldac = oldptr;
struct apple_glx_context *ac = ptr;
struct apple_glx_drawable *newagd = NULL;
CGLError cglerr;
bool same_drawable = false;
#if 0
apple_glx_diagnostic("%s: oldac %p ac %p drawable 0x%lx\n",
__func__, (void *) oldac, (void *) ac, drawable);
apple_glx_diagnostic("%s: oldac->context_obj %p ac->context_obj %p\n",
__func__,
(void *) (oldac ? oldac->context_obj : NULL),
(void *) (ac ? ac->context_obj : NULL));
#endif
if (ac && ac->drawable && ac->drawable->drawable == drawable) {
same_drawable = true;
if (ac->is_current)
return false;
}
if (oldac && (ac != oldac))
oldac->is_current = false;
if (NULL == ac) {
apple_cgl.set_current_context(NULL);
if (oldac) {
oldac->is_current = false;
if (oldac->drawable) {
oldac->drawable->destroy(oldac->drawable);
oldac->drawable = NULL;
}
oldac->last_surface_window = None;
}
return false;
}
if (None == drawable) {
bool error = false;
if (apple_cgl.set_current_context(ac->context_obj))
error = true;
if (apple_cgl.clear_drawable(ac->context_obj))
error = true;
if (ac->drawable) {
ac->drawable->destroy(ac->drawable);
ac->drawable = NULL;
}
ac->last_surface_window = None;
apple_glx_diagnostic("%s: drawable is None, error is: %d\n",
__func__, error);
return error;
}
if (ac->drawable && ac->drawable->drawable == drawable) {
newagd = ac->drawable;
}
else {
newagd =
apple_glx_drawable_find(drawable, APPLE_GLX_DRAWABLE_REFERENCE);
}
if (ac->drawable && !same_drawable) {
ac->drawable->destroy(ac->drawable);
ac->drawable = NULL;
}
if (NULL == newagd) {
if (apple_glx_surface_create(dpy, ac->screen, drawable, &newagd))
return true;
newagd->reference(newagd);
ac->drawable = newagd;
}
else {
if (same_drawable) {
assert(ac->drawable == newagd);
}
else {
ac->drawable = newagd;
}
}
if (same_drawable && ac->is_current) {
apple_glx_diagnostic("%s: same_drawable and ac->is_current\n");
return false;
}
cglerr = apple_cgl.set_current_context(ac->context_obj);
if (kCGLNoError != cglerr) {
fprintf(stderr, "set current error: %s\n",
apple_cgl.error_string(cglerr));
return true;
}
ac->is_current = true;
assert(NULL != ac->context_obj);
assert(NULL != ac->drawable);
ac->thread_id = pthread_self();
ac->last_surface_window = None;
switch (ac->drawable->type) {
case APPLE_GLX_DRAWABLE_PBUFFER:
case APPLE_GLX_DRAWABLE_SURFACE:
case APPLE_GLX_DRAWABLE_PIXMAP:
if (ac->drawable->callbacks.make_current) {
if (ac->drawable->callbacks.make_current(ac, ac->drawable))
return true;
}
break;
default:
fprintf(stderr, "internal error: invalid drawable type: %d\n",
ac->drawable->type);
abort();
}
return false;
}
bool
apple_glx_is_current_drawable(Display * dpy, void *ptr, GLXDrawable drawable)
{
struct apple_glx_context *ac = ptr;
if (ac->drawable && ac->drawable->drawable == drawable) {
return true;
}
else if (NULL == ac->drawable && None != ac->last_surface_window) {
apple_glx_context_update(dpy, ac);
return (ac->drawable && ac->drawable->drawable == drawable);
}
return false;
}
bool
apple_glx_copy_context(void *currentptr, void *srcptr, void *destptr,
unsigned long mask, int *errorptr, bool * x11errorptr)
{
struct apple_glx_context *src, *dest;
CGLError err;
src = srcptr;
dest = destptr;
if (src->screen != dest->screen) {
*errorptr = BadMatch;
*x11errorptr = true;
return true;
}
if (dest == currentptr || dest->is_current) {
*errorptr = BadAccess;
*x11errorptr = true;
return true;
}
if (currentptr == srcptr)
glFlush();
err = apple_cgl.copy_context(src->context_obj, dest->context_obj,
(GLbitfield) mask);
if (kCGLNoError != err) {
*errorptr = GLXBadContext;
*x11errorptr = false;
return true;
}
return false;
}
int
apple_glx_context_surface_changed(unsigned int uid, pthread_t caller)
{
struct apple_glx_context *ac;
int updated = 0;
lock_context_list();
for (ac = context_list; ac; ac = ac->next) {
if (ac->drawable && APPLE_GLX_DRAWABLE_SURFACE == ac->drawable->type
&& ac->drawable->types.surface.uid == uid) {
if (caller == ac->thread_id) {
apple_glx_diagnostic("caller is the same thread for uid %u\n",
uid);
xp_update_gl_context(ac->context_obj);
}
else {
ac->need_update = true;
++updated;
}
}
}
unlock_context_list();
return updated;
}
void
apple_glx_context_update(Display * dpy, void *ptr)
{
struct apple_glx_context *ac = ptr;
if (NULL == ac->drawable && None != ac->last_surface_window) {
bool failed;
failed =
apple_glx_make_current_context(dpy, ac, ac, ac->last_surface_window);
apple_glx_diagnostic("%s: surface recreation failed? %s\n", __func__,
failed ? "YES" : "NO");
}
if (ac->need_update) {
xp_update_gl_context(ac->context_obj);
ac->need_update = false;
apple_glx_diagnostic("%s: updating context %p\n", __func__, ptr);
}
if (ac->drawable && APPLE_GLX_DRAWABLE_SURFACE == ac->drawable->type
&& ac->drawable->types.surface.pending_destroy) {
apple_glx_diagnostic("%s: clearing drawable %p\n", __func__, ptr);
apple_cgl.clear_drawable(ac->context_obj);
if (ac->drawable) {
struct apple_glx_drawable *d;
apple_glx_diagnostic("%s: attempting to destroy drawable %p\n",
__func__, ptr);
apple_glx_diagnostic("%s: ac->drawable->drawable is 0x%lx\n",
__func__, ac->drawable->drawable);
d = ac->drawable;
ac->last_surface_window = d->drawable;
ac->drawable = NULL;
d->destroy(d);
}
}
}
bool
apple_glx_context_uses_stereo(void *ptr)
{
struct apple_glx_context *ac = ptr;
return ac->uses_stereo;
}