| /* |
| 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 <limits.h> |
| #include <assert.h> |
| #include <pthread.h> |
| |
| #include <fcntl.h> |
| #include <sys/mman.h> |
| #include <unistd.h> |
| |
| // Get the newer glext.h first |
| #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" |
| |
| #include "util/debug.h" |
| |
| static pthread_mutex_t context_lock = PTHREAD_MUTEX_INITIALIZER; |
| |
| /* |
| * This should be locked on creation and destruction of the |
| * apple_glx_contexts. |
| * |
| * It's also locked when the surface_notify_handler is searching |
| * for a uid associated with a surface. |
| */ |
| static struct apple_glx_context *context_list = NULL; |
| |
| /* This guards the context_list above. */ |
| 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; |
| } |
| |
| /* This creates an apple_private_context struct. |
| * |
| * It's typically called to save the struct in a GLXContext. |
| * |
| * This is also where the CGLContextObj is created, and the CGLPixelFormatObj. |
| */ |
| 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; |
| free(ac); |
| 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, |
| /*offscreen */ 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; |
| } |
| |
| DebugMessageF("error: %s\n", apple_cgl.error_string(error)); |
| |
| return true; |
| } |
| |
| /* The context creation succeeded, so we can link in the new context. */ |
| 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(); |
| } |
| } |
| |
| /* Remove ac from the context_list as soon as possible. */ |
| 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(); |
| } |
| |
| /* |
| * This potentially causes surface_notify_handler to be called in |
| * apple_glx.c... |
| * We can NOT have a lock held at this point. It would result in |
| * an abort due to an attempted deadlock. This is why we earlier |
| * removed the ac pointer from the double-linked list. |
| */ |
| 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); |
| } |
| |
| |
| /* Return true if an error occurred. */ |
| 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 |
| |
| /* This a common path for GLUT and other apps, so special case it. */ |
| if (ac && ac->drawable && ac->drawable->drawable == drawable) { |
| same_drawable = true; |
| |
| if (ac->is_current) |
| return false; |
| } |
| |
| /* Reset the is_current state of the old context, if non-NULL. */ |
| if (oldac && (ac != oldac)) |
| oldac->is_current = false; |
| |
| if (NULL == ac) { |
| /*Clear the current context for this thread. */ |
| apple_cgl.set_current_context(NULL); |
| |
| if (oldac) { |
| oldac->is_current = false; |
| |
| if (oldac->drawable) { |
| oldac->drawable->destroy(oldac->drawable); |
| oldac->drawable = NULL; |
| } |
| |
| /* Invalidate this to prevent surface recreation. */ |
| oldac->last_surface_window = None; |
| } |
| |
| return false; |
| } |
| |
| if (None == drawable) { |
| bool error = false; |
| |
| /* Clear the current drawable for this context_obj. */ |
| |
| 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; |
| } |
| |
| /* Invalidate this to prevent surface recreation. */ |
| ac->last_surface_window = None; |
| |
| apple_glx_diagnostic("%s: drawable is None, error is: %d\n", |
| __func__, error); |
| |
| return error; |
| } |
| |
| /* This is an optimisation to avoid searching for the current drawable. */ |
| if (ac->drawable && ac->drawable->drawable == drawable) { |
| newagd = ac->drawable; |
| } |
| else { |
| /* Find the drawable if possible, and retain a reference to it. */ |
| newagd = |
| apple_glx_drawable_find(drawable, APPLE_GLX_DRAWABLE_REFERENCE); |
| } |
| |
| /* |
| * Try to destroy the old drawable, so long as the new one |
| * isn't the old. |
| */ |
| 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; |
| |
| /* The drawable is referenced once by apple_glx_surface_create. */ |
| |
| /* |
| * FIXME: We actually need 2 references to prevent premature surface |
| * destruction. The problem is that the surface gets destroyed in |
| * the case of the context being reused for another window, and |
| * we then lose the surface contents. Wait for destruction of a |
| * window to destroy a surface. |
| * |
| * Note: this may leave around surfaces we don't want around, if |
| * say we are using X for raster drawing after OpenGL rendering, |
| * but it will be compatible with the old libGL's behavior. |
| * |
| * Someday the X11 and OpenGL rendering must be unified at some |
| * layer. I suspect we can do that via shared memory and |
| * multiple threads in the X server (1 for each context created |
| * by a client). This would also allow users to render from |
| * multiple clients to the same OpenGL surface. In fact it could |
| * all be OpenGL. |
| * |
| */ |
| newagd->reference(newagd); |
| |
| /* Save the new drawable with the context structure. */ |
| ac->drawable = newagd; |
| } |
| else { |
| /* We are reusing an existing drawable structure. */ |
| |
| if (same_drawable) { |
| assert(ac->drawable == newagd); |
| /* The drawable_find above retained a reference for us. */ |
| } |
| else { |
| ac->drawable = newagd; |
| } |
| } |
| |
| /* |
| * Avoid this costly path if this is the same drawable and the |
| * context is already current. |
| */ |
| |
| if (same_drawable && ac->is_current) { |
| apple_glx_diagnostic("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(); |
| |
| /* This will be set if the pending_destroy code indicates it should be: */ |
| 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 srcptr is the current context then we should do an implicit glFlush. |
| */ |
| 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; |
| } |
| |
| /* |
| * The value returned is the total number of contexts set to update. |
| * It's meant for debugging/introspection. |
| */ |
| 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; |
| |
| /* Attempt to recreate the surface for a destroyed drawable. */ |
| 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; |
| |
| /* |
| * This will destroy the surface drawable if there are |
| * no references to it. |
| * It also subtracts 1 from the reference_count. |
| * If there are references to it, then it's probably made |
| * current in another context. |
| */ |
| d->destroy(d); |
| } |
| } |
| } |
| |
| bool |
| apple_glx_context_uses_stereo(void *ptr) |
| { |
| struct apple_glx_context *ac = ptr; |
| |
| return ac->uses_stereo; |
| } |