1cdc920a0Smrg/*
2cdc920a0Smrg * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
3cdc920a0Smrg * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
4cdc920a0Smrg *
5cdc920a0Smrg * Permission is hereby granted, free of charge, to any person obtaining a
6cdc920a0Smrg * copy of this software and associated documentation files (the "Software"),
7cdc920a0Smrg * to deal in the Software without restriction, including without limitation
8cdc920a0Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9cdc920a0Smrg * and/or sell copies of the Software, and to permit persons to whom the
10cdc920a0Smrg * Software is furnished to do so, subject to the following conditions:
11cdc920a0Smrg *
12cdc920a0Smrg * The above copyright notice including the dates of first publication and
13cdc920a0Smrg * either this permission notice or a reference to
14cdc920a0Smrg * http://oss.sgi.com/projects/FreeB/
15cdc920a0Smrg * shall be included in all copies or substantial portions of the Software.
16cdc920a0Smrg *
17cdc920a0Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18cdc920a0Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19cdc920a0Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20cdc920a0Smrg * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21cdc920a0Smrg * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
22cdc920a0Smrg * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23cdc920a0Smrg * SOFTWARE.
24cdc920a0Smrg *
25cdc920a0Smrg * Except as contained in this notice, the name of Silicon Graphics, Inc.
26cdc920a0Smrg * shall not be used in advertising or otherwise to promote the sale, use or
27cdc920a0Smrg * other dealings in this Software without prior written authorization from
28cdc920a0Smrg * Silicon Graphics, Inc.
29cdc920a0Smrg */
30cdc920a0Smrg
31cdc920a0Smrg/**
32cdc920a0Smrg * \file glxcurrent.c
33cdc920a0Smrg * Client-side GLX interface for current context management.
34cdc920a0Smrg */
35cdc920a0Smrg
36cdc920a0Smrg#include <pthread.h>
37cdc920a0Smrg
38cdc920a0Smrg#include "glxclient.h"
393464ebd5Sriastradh#include "glapi.h"
4001e04c3fSmrg#include "glx_error.h"
413464ebd5Sriastradh
42cdc920a0Smrg/*
43cdc920a0Smrg** We setup some dummy structures here so that the API can be used
44cdc920a0Smrg** even if no context is current.
45cdc920a0Smrg*/
46cdc920a0Smrg
47cdc920a0Smrgstatic GLubyte dummyBuffer[__GLX_BUFFER_LIMIT_SIZE];
483464ebd5Sriastradhstatic struct glx_context_vtable dummyVtable;
49cdc920a0Smrg/*
50cdc920a0Smrg** Dummy context used by small commands when there is no current context.
51cdc920a0Smrg** All the
52cdc920a0Smrg** gl and glx entry points are designed to operate as nop's when using
53cdc920a0Smrg** the dummy context structure.
54cdc920a0Smrg*/
553464ebd5Sriastradhstruct glx_context dummyContext = {
56cdc920a0Smrg   &dummyBuffer[0],
57cdc920a0Smrg   &dummyBuffer[0],
58cdc920a0Smrg   &dummyBuffer[0],
59cdc920a0Smrg   &dummyBuffer[__GLX_BUFFER_LIMIT_SIZE],
60cdc920a0Smrg   sizeof(dummyBuffer),
613464ebd5Sriastradh   &dummyVtable
62cdc920a0Smrg};
63cdc920a0Smrg
64cdc920a0Smrg/*
65cdc920a0Smrg * Current context management and locking
66cdc920a0Smrg */
67cdc920a0Smrg
68cdc920a0Smrg_X_HIDDEN pthread_mutex_t __glXmutex = PTHREAD_MUTEX_INITIALIZER;
69cdc920a0Smrg
701463c08dSmrg# if defined( USE_ELF_TLS )
71cdc920a0Smrg
72cdc920a0Smrg/**
73cdc920a0Smrg * Per-thread GLX context pointer.
74cdc920a0Smrg *
75cdc920a0Smrg * \c __glXSetCurrentContext is written is such a way that this pointer can
76cdc920a0Smrg * \b never be \c NULL.  This is important!  Because of this
77cdc920a0Smrg * \c __glXGetCurrentContext can be implemented as trivial macro.
78cdc920a0Smrg */
791463c08dSmrg__THREAD_INITIAL_EXEC void *__glX_tls_Context
80e81a0f50Smaya#if defined(__NetBSD__)
81e81a0f50Smaya   = NULL; /* non-zero initializers not supported with dlopen */
82e81a0f50Smaya#else
83cdc920a0Smrg   = &dummyContext;
84e81a0f50Smaya#endif
85cdc920a0Smrg
86cdc920a0Smrg_X_HIDDEN void
873464ebd5Sriastradh__glXSetCurrentContext(struct glx_context * c)
88cdc920a0Smrg{
89cdc920a0Smrg   __glX_tls_Context = (c != NULL) ? c : &dummyContext;
90cdc920a0Smrg}
91cdc920a0Smrg
92cdc920a0Smrg# else
93cdc920a0Smrg
94cdc920a0Smrgstatic pthread_once_t once_control = PTHREAD_ONCE_INIT;
95cdc920a0Smrg
96cdc920a0Smrg/**
97cdc920a0Smrg * Per-thread data key.
98cdc920a0Smrg *
99cdc920a0Smrg * Once \c init_thread_data has been called, the per-thread data key will
100cdc920a0Smrg * take a value of \c NULL.  As each new thread is created the default
101cdc920a0Smrg * value, in that thread, will be \c NULL.
102cdc920a0Smrg */
103cdc920a0Smrgstatic pthread_key_t ContextTSD;
104cdc920a0Smrg
105cdc920a0Smrg/**
106cdc920a0Smrg * Initialize the per-thread data key.
107cdc920a0Smrg *
108cdc920a0Smrg * This function is called \b exactly once per-process (not per-thread!) to
109cdc920a0Smrg * initialize the per-thread data key.  This is ideally done using the
110cdc920a0Smrg * \c pthread_once mechanism.
111cdc920a0Smrg */
112cdc920a0Smrgstatic void
113cdc920a0Smrginit_thread_data(void)
114cdc920a0Smrg{
115cdc920a0Smrg   if (pthread_key_create(&ContextTSD, NULL) != 0) {
116cdc920a0Smrg      perror("pthread_key_create");
117cdc920a0Smrg      exit(-1);
118cdc920a0Smrg   }
119cdc920a0Smrg}
120cdc920a0Smrg
121cdc920a0Smrg_X_HIDDEN void
1223464ebd5Sriastradh__glXSetCurrentContext(struct glx_context * c)
123cdc920a0Smrg{
124cdc920a0Smrg   pthread_once(&once_control, init_thread_data);
125cdc920a0Smrg   pthread_setspecific(ContextTSD, c);
126cdc920a0Smrg}
127cdc920a0Smrg
1283464ebd5Sriastradh_X_HIDDEN struct glx_context *
129cdc920a0Smrg__glXGetCurrentContext(void)
130cdc920a0Smrg{
131cdc920a0Smrg   void *v;
132cdc920a0Smrg
133cdc920a0Smrg   pthread_once(&once_control, init_thread_data);
134cdc920a0Smrg
135cdc920a0Smrg   v = pthread_getspecific(ContextTSD);
1363464ebd5Sriastradh   return (v == NULL) ? &dummyContext : (struct glx_context *) v;
137cdc920a0Smrg}
138cdc920a0Smrg
1391463c08dSmrg# endif /* defined( USE_ELF_TLS ) */
140cdc920a0Smrg
141cdc920a0Smrg
142cdc920a0Smrg_X_HIDDEN void
143cdc920a0Smrg__glXSetCurrentContextNull(void)
144cdc920a0Smrg{
145cdc920a0Smrg   __glXSetCurrentContext(&dummyContext);
1463464ebd5Sriastradh#if defined(GLX_DIRECT_RENDERING)
147cdc920a0Smrg   _glapi_set_dispatch(NULL);   /* no-op functions */
148cdc920a0Smrg   _glapi_set_context(NULL);
149cdc920a0Smrg#endif
150cdc920a0Smrg}
151cdc920a0Smrg
15201e04c3fSmrg_GLX_PUBLIC GLXContext
153cdc920a0SmrgglXGetCurrentContext(void)
154cdc920a0Smrg{
1553464ebd5Sriastradh   struct glx_context *cx = __glXGetCurrentContext();
156cdc920a0Smrg
157cdc920a0Smrg   if (cx == &dummyContext) {
158cdc920a0Smrg      return NULL;
159cdc920a0Smrg   }
160cdc920a0Smrg   else {
1613464ebd5Sriastradh      return (GLXContext) cx;
162cdc920a0Smrg   }
163cdc920a0Smrg}
164cdc920a0Smrg
16501e04c3fSmrg_GLX_PUBLIC GLXDrawable
166cdc920a0SmrgglXGetCurrentDrawable(void)
167cdc920a0Smrg{
1683464ebd5Sriastradh   struct glx_context *gc = __glXGetCurrentContext();
169cdc920a0Smrg   return gc->currentDrawable;
170cdc920a0Smrg}
171cdc920a0Smrg
172cdc920a0Smrg/**
173cdc920a0Smrg * Make a particular context current.
174cdc920a0Smrg *
175cdc920a0Smrg * \note This is in this file so that it can access dummyContext.
176cdc920a0Smrg */
177cdc920a0Smrgstatic Bool
178cdc920a0SmrgMakeContextCurrent(Display * dpy, GLXDrawable draw,
1791463c08dSmrg                   GLXDrawable read, GLXContext gc_user,
1801463c08dSmrg                   int opcode)
181cdc920a0Smrg{
1823464ebd5Sriastradh   struct glx_context *gc = (struct glx_context *) gc_user;
1833464ebd5Sriastradh   struct glx_context *oldGC = __glXGetCurrentContext();
1843464ebd5Sriastradh
185cdc920a0Smrg   /* Make sure that the new context has a nonzero ID.  In the request,
186cdc920a0Smrg    * a zero context ID is used only to mean that we bind to no current
187cdc920a0Smrg    * context.
188cdc920a0Smrg    */
189cdc920a0Smrg   if ((gc != NULL) && (gc->xid == None)) {
190cdc920a0Smrg      return GL_FALSE;
191cdc920a0Smrg   }
192cdc920a0Smrg
193cdc920a0Smrg   _glapi_check_multithread();
194cdc920a0Smrg
1953464ebd5Sriastradh   __glXLock();
1963464ebd5Sriastradh   if (oldGC == gc &&
1973464ebd5Sriastradh       gc->currentDrawable == draw && gc->currentReadable == read) {
1983464ebd5Sriastradh      __glXUnlock();
1993464ebd5Sriastradh      return True;
200cdc920a0Smrg   }
201cdc920a0Smrg
20201e04c3fSmrg   /* can't have only one be 0 */
20301e04c3fSmrg   if (!!draw != !!read) {
20401e04c3fSmrg      __glXUnlock();
2051463c08dSmrg      __glXSendError(dpy, BadMatch, None, opcode, True);
20601e04c3fSmrg      return False;
20701e04c3fSmrg   }
20801e04c3fSmrg
2093464ebd5Sriastradh   if (oldGC != &dummyContext) {
2103464ebd5Sriastradh      if (--oldGC->thread_refcount == 0) {
2113464ebd5Sriastradh	 oldGC->vtable->unbind(oldGC, gc);
2123464ebd5Sriastradh	 oldGC->currentDpy = 0;
213cdc920a0Smrg      }
214cdc920a0Smrg   }
215cdc920a0Smrg
2163464ebd5Sriastradh   if (gc) {
2173464ebd5Sriastradh      /* Attempt to bind the context.  We do this before mucking with
2183464ebd5Sriastradh       * gc and __glXSetCurrentContext to properly handle our state in
2193464ebd5Sriastradh       * case of an error.
2203464ebd5Sriastradh       *
2213464ebd5Sriastradh       * If an error occurs, set the Null context since we've already
2223464ebd5Sriastradh       * blown away our old context.  The caller is responsible for
2233464ebd5Sriastradh       * figuring out how to handle setting a valid context.
224cdc920a0Smrg       */
2253464ebd5Sriastradh      if (gc->vtable->bind(gc, oldGC, draw, read) != Success) {
2263464ebd5Sriastradh         __glXSetCurrentContextNull();
2273464ebd5Sriastradh         __glXUnlock();
2281463c08dSmrg         __glXSendError(dpy, GLXBadContext, None, opcode, False);
2293464ebd5Sriastradh         return GL_FALSE;
230cdc920a0Smrg      }
231cdc920a0Smrg
2323464ebd5Sriastradh      if (gc->thread_refcount == 0) {
233cdc920a0Smrg         gc->currentDpy = dpy;
234cdc920a0Smrg         gc->currentDrawable = draw;
235cdc920a0Smrg         gc->currentReadable = read;
236cdc920a0Smrg      }
2373464ebd5Sriastradh      gc->thread_refcount++;
2383464ebd5Sriastradh      __glXSetCurrentContext(gc);
2393464ebd5Sriastradh   } else {
2403464ebd5Sriastradh      __glXSetCurrentContextNull();
2413464ebd5Sriastradh   }
2423464ebd5Sriastradh
2433464ebd5Sriastradh   if (oldGC->thread_refcount == 0 && oldGC != &dummyContext && oldGC->xid == None) {
2443464ebd5Sriastradh      /* We are switching away from a context that was
2453464ebd5Sriastradh       * previously destroyed, so we need to free the memory
2463464ebd5Sriastradh       * for the old handle. */
2473464ebd5Sriastradh      oldGC->vtable->destroy(oldGC);
248cdc920a0Smrg   }
2493464ebd5Sriastradh
250cdc920a0Smrg   __glXUnlock();
2513464ebd5Sriastradh
252cdc920a0Smrg   return GL_TRUE;
253cdc920a0Smrg}
254cdc920a0Smrg
25501e04c3fSmrg_GLX_PUBLIC Bool
256cdc920a0SmrgglXMakeCurrent(Display * dpy, GLXDrawable draw, GLXContext gc)
257cdc920a0Smrg{
2581463c08dSmrg   return MakeContextCurrent(dpy, draw, draw, gc, X_GLXMakeCurrent);
259cdc920a0Smrg}
260cdc920a0Smrg
2611463c08dSmrg_GLX_PUBLIC Bool
2621463c08dSmrgglXMakeContextCurrent(Display *dpy, GLXDrawable d, GLXDrawable r,
2631463c08dSmrg                      GLXContext ctx)
2641463c08dSmrg{
2651463c08dSmrg   return MakeContextCurrent(dpy, d, r, ctx, X_GLXMakeContextCurrent);
2661463c08dSmrg}
267cdc920a0Smrg
2681463c08dSmrg_GLX_PUBLIC Bool
2691463c08dSmrgglXMakeCurrentReadSGI(Display *dpy, GLXDrawable d, GLXDrawable r,
2701463c08dSmrg                      GLXContext ctx)
2711463c08dSmrg{
2721463c08dSmrg   return MakeContextCurrent(dpy, d, r, ctx, X_GLXvop_MakeCurrentReadSGI);
2731463c08dSmrg}
274