/* ** License Applicability. Except to the extent portions of this file are ** made subject to an alternative license as permitted in the SGI Free ** Software License B, Version 1.1 (the "License"), the contents of this ** file are subject only to the provisions of the License. You may not use ** this file except in compliance with the License. You may obtain a copy ** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 ** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: ** ** http://oss.sgi.com/projects/FreeB ** ** Note that, as provided in the License, the Software is distributed on an ** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS ** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND ** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A ** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. ** ** Original Code. The Original Code is: OpenGL Sample Implementation, ** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, ** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. ** Copyright in any portions created by third parties is as indicated ** elsewhere herein. All Rights Reserved. ** ** Additional Notice Provisions: The application programming interfaces ** established by SGI in conjunction with the Original Code are The ** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released ** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version ** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X ** Window System(R) (Version 1.3), released October 19, 1998. This software ** was created using the OpenGL(R) version 1.2.1 Sample Implementation ** published by SGI, but has not been independently verified as being ** compliant with the OpenGL(R) version 1.2.1 Specification. ** */ /** * \file glxcurrent.c * Client-side GLX interface for current context management. */ #include "glxclient.h" #include "glapi.h" #include "glheader.h" #include "indirect_init.h" /* ** We setup some dummy structures here so that the API can be used ** even if no context is current. */ static GLubyte dummyBuffer[__GLX_BUFFER_LIMIT_SIZE]; /* ** Dummy context used by small commands when there is no current context. ** All the ** gl and glx entry points are designed to operate as nop's when using ** the dummy context structure. */ static __GLXcontext dummyContext = { &dummyBuffer[0], &dummyBuffer[0], &dummyBuffer[0], &dummyBuffer[__GLX_BUFFER_LIMIT_SIZE], sizeof(dummyBuffer), }; /* ** All indirect rendering contexts will share the same indirect dispatch table. */ static __GLapi *IndirectAPI = NULL; /* * Current context management and locking */ #if defined( USE_XTHREADS ) /* thread safe */ static GLboolean TSDinitialized = GL_FALSE; static xthread_key_t ContextTSD; __GLXcontext *__glXGetCurrentContext(void) { if (!TSDinitialized) { xthread_key_create(&ContextTSD, NULL); TSDinitialized = GL_TRUE; return &dummyContext; } else { void *p; xthread_get_specific(ContextTSD, &p); if (!p) return &dummyContext; else return (__GLXcontext *) p; } } void __glXSetCurrentContext(__GLXcontext *c) { if (!TSDinitialized) { xthread_key_create(&ContextTSD, NULL); TSDinitialized = GL_TRUE; } xthread_set_specific(ContextTSD, c); } /* Used by the __glXLock() and __glXUnlock() macros */ xmutex_rec __glXmutex; #elif defined( PTHREADS ) pthread_mutex_t __glXmutex = PTHREAD_MUTEX_INITIALIZER; # if defined( GLX_USE_TLS ) /** * Per-thread GLX context pointer. * * \c __glXSetCurrentContext is written is such a way that this pointer can * \b never be \c NULL. This is important! Because of this * \c __glXGetCurrentContext can be implemented as trivial macro. */ __thread void * __glX_tls_Context __attribute__((tls_model("initial-exec"))) = &dummyContext; void __glXSetCurrentContext( __GLXcontext * c ) { __glX_tls_Context = (c != NULL) ? c : &dummyContext; } # else static pthread_once_t once_control = PTHREAD_ONCE_INIT; /** * Per-thread data key. * * Once \c init_thread_data has been called, the per-thread data key will * take a value of \c NULL. As each new thread is created the default * value, in that thread, will be \c NULL. */ static pthread_key_t ContextTSD; /** * Initialize the per-thread data key. * * This function is called \b exactly once per-process (not per-thread!) to * initialize the per-thread data key. This is ideally done using the * \c pthread_once mechanism. */ static void init_thread_data( void ) { if ( pthread_key_create( & ContextTSD, NULL ) != 0 ) { perror( "pthread_key_create" ); exit( -1 ); } } void __glXSetCurrentContext( __GLXcontext * c ) { pthread_once( & once_control, init_thread_data ); pthread_setspecific( ContextTSD, c ); } __GLXcontext * __glXGetCurrentContext( void ) { void * v; pthread_once( & once_control, init_thread_data ); v = pthread_getspecific( ContextTSD ); return (v == NULL) ? & dummyContext : (__GLXcontext *) v; } # endif /* defined( GLX_USE_TLS ) */ #elif defined( THREADS ) #error Unknown threading method specified. #else /* not thread safe */ __GLXcontext *__glXcurrentContext = &dummyContext; #endif void __glXSetCurrentContextNull(Display *dpy) { GLXContext gc; gc = __glXGetCurrentContext(); if (dpy == gc->currentDpy) { __glXSetCurrentContext(&dummyContext); #ifdef GLX_DIRECT_RENDERING _glapi_set_dispatch(NULL); /* no-op functions */ #endif __glXFreeContext(gc); } } /************************************************************************/ GLXContext glXGetCurrentContext(void) { GLXContext cx = __glXGetCurrentContext(); if (cx == &dummyContext) { return NULL; } else { return cx; } } GLXDrawable glXGetCurrentDrawable(void) { GLXContext gc = __glXGetCurrentContext(); return gc->currentDrawable; } /************************************************************************/ /** * Sends a GLX protocol message to the specified display to make the context * and the drawables current. * * \param dpy Display to send the message to. * \param opcode Major opcode value for the display. * \param gc_id Context tag for the context to be made current. * \param draw Drawable ID for the "draw" drawable. * \param read Drawable ID for the "read" drawable. * \param reply Space to store the X-server's reply. * * \warning * This function assumes that \c dpy is locked with \c LockDisplay on entry. */ static Bool SendMakeCurrentRequest( Display *dpy, CARD8 opcode, GLXContextID gc_id, GLXContextTag gc_tag, GLXDrawable draw, GLXDrawable read, xGLXMakeCurrentReply * reply ) { if ( draw == read ) { xGLXMakeCurrentReq *req; GetReq(GLXMakeCurrent,req); req->reqType = opcode; req->glxCode = X_GLXMakeCurrent; req->drawable = draw; req->context = gc_id; req->oldContextTag = gc_tag; } else { __GLXdisplayPrivate *priv = __glXInitialize(dpy); /* If the server can support the GLX 1.3 version, we should * perfer that. Not only that, some servers support GLX 1.3 but * not the SGI extension. */ if ( (priv->majorVersion > 1) || (priv->minorVersion >= 3) ) { xGLXMakeContextCurrentReq *req; GetReq(GLXMakeContextCurrent,req); req->reqType = opcode; req->glxCode = X_GLXMakeContextCurrent; req->drawable = draw; req->readdrawable = read; req->context = gc_id; req->oldContextTag = gc_tag; } else { xGLXVendorPrivateWithReplyReq *vpreq; xGLXMakeCurrentReadSGIReq *req; GetReqExtra(GLXVendorPrivateWithReply, sz_xGLXMakeCurrentReadSGIReq-sz_xGLXVendorPrivateWithReplyReq,vpreq); req = (xGLXMakeCurrentReadSGIReq *)vpreq; req->reqType = opcode; req->glxCode = X_GLXVendorPrivateWithReply; req->vendorCode = X_GLXvop_MakeCurrentReadSGI; req->drawable = draw; req->readable = read; req->context = gc_id; req->oldContextTag = gc_tag; } } return _XReply(dpy, (xReply*) reply, 0, False); } #ifdef GLX_DIRECT_RENDERING static Bool BindContextWrapper( Display *dpy, GLXContext gc, GLXDrawable draw, GLXDrawable read ) { return (*gc->driContext.bindContext)(dpy, gc->screen, draw, read, & gc->driContext); } static Bool UnbindContextWrapper( GLXContext gc ) { return (*gc->driContext.unbindContext)(gc->currentDpy, gc->screen, gc->currentDrawable, gc->currentReadable, & gc->driContext ); } #endif /* GLX_DIRECT_RENDERING */ /* ** Make a particular context current. ** NOTE: this is in this file so that it can access dummyContext. */ USED static Bool MakeContextCurrent(Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext gc) { xGLXMakeCurrentReply reply; GLXContext oldGC; CARD8 opcode, oldOpcode; Bool sentRequestToOldDpy = False; Bool bindReturnValue = True; opcode = __glXSetupForCommand(dpy); if (!opcode) { return GL_FALSE; } /* ** Make sure that the new context has a nonzero ID. In the request, ** a zero context ID is used only to mean that we bind to no current ** context. */ if ((gc != NULL) && (gc->xid == None)) { return GL_FALSE; } oldGC = __glXGetCurrentContext(); oldOpcode = (gc == oldGC) ? opcode : __glXSetupForCommand(dpy); if (!oldOpcode) { return GL_FALSE; } if ((dpy != oldGC->currentDpy || (gc && gc->isDirect)) && !oldGC->isDirect && oldGC != &dummyContext) { /* ** We are either switching from one dpy to another and have to ** send a request to the previous dpy to unbind the previous ** context, or we are switching away from a indirect context to ** a direct context and have to send a request to the dpy to ** unbind the previous context. */ sentRequestToOldDpy = True; LockDisplay(oldGC->currentDpy); if ( ! SendMakeCurrentRequest( oldGC->currentDpy, oldOpcode, None, oldGC->currentContextTag, None, None, &reply ) ) { /* The make current failed. Just return GL_FALSE. */ UnlockDisplay(oldGC->currentDpy); SyncHandle(); return GL_FALSE; } oldGC->currentContextTag = 0; } _glapi_check_multithread(); #ifdef GLX_DIRECT_RENDERING /* Unbind the old direct rendering context */ if (oldGC->isDirect) { if (oldGC->driContext.private) { if (! UnbindContextWrapper( oldGC )) { /* The make current failed. Just return GL_FALSE. */ return GL_FALSE; } } oldGC->currentContextTag = 0; } /* Bind the direct rendering context to the drawable */ if (gc && gc->isDirect) { if (gc->driContext.private) { bindReturnValue = BindContextWrapper( dpy, gc, draw, read ); } } else { #endif /* Send a glXMakeCurrent request to bind the new context. */ LockDisplay(dpy); bindReturnValue = SendMakeCurrentRequest( dpy, opcode, gc ? gc->xid : None, oldGC->currentContextTag, draw, read, &reply ); UnlockDisplay(dpy); #ifdef GLX_DIRECT_RENDERING } #endif if (!bindReturnValue) { /* The make current failed. */ if (gc && !gc->isDirect) { SyncHandle(); } #ifdef GLX_DIRECT_RENDERING /* If the old context was direct rendering, then re-bind to it. */ if (oldGC->isDirect) { if (oldGC->driContext.private) { if (! BindContextWrapper( oldGC->currentDpy, oldGC, oldGC->currentDrawable, oldGC->currentReadable )) { /* ** The request failed; this cannot happen with the ** current API. If in the future the API is ** extended to allow context sharing between ** clients, then this may fail (because another ** client may have grabbed the context); in that ** case, we cannot undo the previous request, and ** cannot adhere to the "no-op" behavior. */ } } } else #endif /* ** If we had just sent a request to a previous dpy, we have to ** undo that request (because if a command fails, it should act ** like a no-op) by making current to the previous context and ** drawable. */ if (sentRequestToOldDpy) { if ( !SendMakeCurrentRequest( oldGC->currentDpy, oldOpcode, oldGC->xid, 0, oldGC->currentDrawable, oldGC->currentReadable, &reply ) ) { UnlockDisplay(oldGC->currentDpy); SyncHandle(); /* ** The request failed; this cannot happen with the ** current API. If in the future the API is extended to ** allow context sharing between clients, then this may ** fail (because another client may have grabbed the ** context); in that case, we cannot undo the previous ** request, and cannot adhere to the "no-op" behavior. */ } else { UnlockDisplay(oldGC->currentDpy); } oldGC->currentContextTag = reply.contextTag; } return GL_FALSE; } /* Update our notion of what is current */ __glXLock(); if (gc == oldGC) { /* ** Even though the contexts are the same the drawable might have ** changed. Note that gc cannot be the dummy, and that oldGC ** cannot be NULL, therefore if they are the same, gc is not ** NULL and not the dummy. */ gc->currentDrawable = draw; gc->currentReadable = read; } else { if (oldGC != &dummyContext) { /* Old current context is no longer current to anybody */ oldGC->currentDpy = 0; oldGC->currentDrawable = None; oldGC->currentReadable = None; oldGC->currentContextTag = 0; if (oldGC->xid == None) { /* ** We are switching away from a context that was ** previously destroyed, so we need to free the memory ** for the old handle. */ #ifdef GLX_DIRECT_RENDERING /* Destroy the old direct rendering context */ if (oldGC->isDirect) { if (oldGC->driContext.private) { (*oldGC->driContext.destroyContext) (dpy, oldGC->screen, oldGC->driContext.private); oldGC->driContext.private = NULL; } } #endif __glXFreeContext(oldGC); } } if (gc) { __glXSetCurrentContext(gc); #ifdef GLX_DIRECT_RENDERING if (!gc->isDirect) { if (!IndirectAPI) IndirectAPI = __glXNewIndirectAPI(); _glapi_set_dispatch(IndirectAPI); # ifdef GLX_USE_APPLEGL do { extern void XAppleDRIUseIndirectDispatch(void); XAppleDRIUseIndirectDispatch(); } while (0); # endif } #else /* if not direct rendering, always need indirect dispatch */ if (!IndirectAPI) IndirectAPI = __glXNewIndirectAPI(); _glapi_set_dispatch(IndirectAPI); #endif gc->currentDpy = dpy; gc->currentDrawable = draw; gc->currentReadable = read; if ( ! gc->isDirect ) { __GLXattribute * state = (__GLXattribute *)(gc->client_state_private); gc->currentContextTag = reply.contextTag; if ( state->array_state == NULL ) { (void) glGetString( GL_EXTENSIONS ); (void) glGetString( GL_VERSION ); __glXInitVertexArrayState(gc); } } else { gc->currentContextTag = -1; } } else { __glXSetCurrentContext(&dummyContext); #ifdef GLX_DIRECT_RENDERING _glapi_set_dispatch(NULL); /* no-op functions */ #endif } } __glXUnlock(); return GL_TRUE; } PUBLIC Bool glXMakeCurrent(Display *dpy, GLXDrawable draw, GLXContext gc) { return MakeContextCurrent( dpy, draw, draw, gc ); } PUBLIC GLX_ALIAS(Bool, glXMakeCurrentReadSGI, (Display *dpy, GLXDrawable d, GLXDrawable r, GLXContext ctx), (dpy, d, r, ctx), MakeContextCurrent) PUBLIC GLX_ALIAS(Bool, glXMakeContextCurrent, (Display *dpy, GLXDrawable d, GLXDrawable r, GLXContext ctx), (dpy, d, r, ctx), MakeContextCurrent)