glxext.c revision 5afdac23
1/*
2 * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
3 * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice including the dates of first publication and
13 * either this permission notice or a reference to
14 * http://oss.sgi.com/projects/FreeB/
15 * shall be included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
22 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 *
25 * Except as contained in this notice, the name of Silicon Graphics, Inc.
26 * shall not be used in advertising or otherwise to promote the sale, use or
27 * other dealings in this Software without prior written authorization from
28 * Silicon Graphics, Inc.
29 */
30
31#ifdef HAVE_DIX_CONFIG_H
32#include <dix-config.h>
33#endif
34
35#include <string.h>
36#include "glxserver.h"
37#include <windowstr.h>
38#include <propertyst.h>
39#include <registry.h>
40#include "privates.h"
41#include <os.h>
42#include "unpack.h"
43#include "glxutil.h"
44#include "glxext.h"
45#include "indirect_table.h"
46#include "indirect_util.h"
47
48/*
49** The last context used by the server.  It is the context that is current
50** from the server's perspective.
51*/
52__GLXcontext *__glXLastContext;
53__GLXcontext *__glXContextList;
54
55/*
56** X resources.
57*/
58RESTYPE __glXContextRes;
59RESTYPE __glXDrawableRes;
60
61/*
62** Reply for most singles.
63*/
64xGLXSingleReply __glXReply;
65
66static DevPrivateKeyRec glxClientPrivateKeyRec;
67#define glxClientPrivateKey (&glxClientPrivateKeyRec)
68
69/*
70** Client that called into GLX dispatch.
71*/
72ClientPtr __pGlxClient;
73
74/*
75** Forward declarations.
76*/
77static int __glXDispatch(ClientPtr);
78
79/*
80** Called when the extension is reset.
81*/
82static void ResetExtension(ExtensionEntry* extEntry)
83{
84    __glXFlushContextCache();
85}
86
87/*
88** Reset state used to keep track of large (multi-request) commands.
89*/
90void __glXResetLargeCommandStatus(__GLXclientState *cl)
91{
92    cl->largeCmdBytesSoFar = 0;
93    cl->largeCmdBytesTotal = 0;
94    cl->largeCmdRequestsSoFar = 0;
95    cl->largeCmdRequestsTotal = 0;
96}
97
98/*
99** This procedure is called when the client who created the context goes
100** away OR when glXDestroyContext is called.  In either case, all we do is
101** flag that the ID is no longer valid, and (maybe) free the context.
102** use.
103*/
104static int ContextGone(__GLXcontext* cx, XID id)
105{
106    cx->idExists = GL_FALSE;
107    if (!cx->isCurrent) {
108	__glXFreeContext(cx);
109    }
110
111    return True;
112}
113
114static __GLXcontext *glxPendingDestroyContexts;
115static __GLXcontext *glxAllContexts;
116static int glxServerLeaveCount;
117static int glxBlockClients;
118
119/*
120** Destroy routine that gets called when a drawable is freed.  A drawable
121** contains the ancillary buffers needed for rendering.
122*/
123static Bool DrawableGone(__GLXdrawable *glxPriv, XID xid)
124{
125    __GLXcontext *c, *next;
126
127    if (glxPriv->type == GLX_DRAWABLE_WINDOW || glxPriv->type == GLX_DRAWABLE_PIXMAP) {
128        /* If this was created by glXCreateWindow, free the matching resource */
129        if (glxPriv->otherId) {
130            XID other = glxPriv->otherId;
131            glxPriv->otherId = 0;
132            if (xid == other)
133                FreeResourceByType(glxPriv->drawId, __glXDrawableRes, TRUE);
134            else
135                FreeResourceByType(other, __glXDrawableRes, TRUE);
136        }
137        /* otherwise this window was implicitly created by MakeCurrent */
138    }
139
140    for (c = glxAllContexts; c; c = next) {
141	next = c->next;
142	if (c->isCurrent && (c->drawPriv == glxPriv || c->readPriv == glxPriv)) {
143	    (*c->loseCurrent)(c);
144	    c->isCurrent = GL_FALSE;
145	    if (c == __glXLastContext)
146		__glXFlushContextCache();
147	}
148	if (c->drawPriv == glxPriv)
149	    c->drawPriv = NULL;
150	if (c->readPriv == glxPriv)
151	    c->readPriv = NULL;
152    }
153
154    /* drop our reference to any backing pixmap */
155    if (glxPriv->type == GLX_DRAWABLE_PIXMAP)
156        glxPriv->pDraw->pScreen->DestroyPixmap((PixmapPtr)glxPriv->pDraw);
157
158    glxPriv->destroy(glxPriv);
159
160    return True;
161}
162
163void __glXAddToContextList(__GLXcontext *cx)
164{
165    cx->next = glxAllContexts;
166    glxAllContexts = cx;
167}
168
169static void __glXRemoveFromContextList(__GLXcontext *cx)
170{
171    __GLXcontext *c, *prev;
172
173    if (cx == glxAllContexts)
174	glxAllContexts = cx->next;
175    else {
176	prev = glxAllContexts;
177	for (c = glxAllContexts; c; c = c->next) {
178	    if (c == cx)
179		prev->next = c->next;
180	    prev = c;
181	}
182    }
183}
184
185/*
186** Free a context.
187*/
188GLboolean __glXFreeContext(__GLXcontext *cx)
189{
190    if (cx->idExists || cx->isCurrent) return GL_FALSE;
191
192    free(cx->feedbackBuf);
193    free(cx->selectBuf);
194    if (cx == __glXLastContext) {
195	__glXFlushContextCache();
196    }
197
198    __glXRemoveFromContextList(cx);
199
200    /* We can get here through both regular dispatching from
201     * __glXDispatch() or as a callback from the resource manager.  In
202     * the latter case we need to lift the DRI lock manually. */
203
204    if (!glxBlockClients) {
205	__glXleaveServer(GL_FALSE);
206	cx->destroy(cx);
207	__glXenterServer(GL_FALSE);
208    } else {
209	cx->next = glxPendingDestroyContexts;
210	glxPendingDestroyContexts = cx;
211    }
212
213    return GL_TRUE;
214}
215
216/************************************************************************/
217
218/*
219** These routines can be used to check whether a particular GL command
220** has caused an error.  Specifically, we use them to check whether a
221** given query has caused an error, in which case a zero-length data
222** reply is sent to the client.
223*/
224
225static GLboolean errorOccured = GL_FALSE;
226
227/*
228** The GL was will call this routine if an error occurs.
229*/
230void __glXErrorCallBack(GLenum code)
231{
232    errorOccured = GL_TRUE;
233}
234
235/*
236** Clear the error flag before calling the GL command.
237*/
238void __glXClearErrorOccured(void)
239{
240    errorOccured = GL_FALSE;
241}
242
243/*
244** Check if the GL command caused an error.
245*/
246GLboolean __glXErrorOccured(void)
247{
248    return errorOccured;
249}
250
251static int __glXErrorBase;
252int __glXEventBase;
253
254int __glXError(int error)
255{
256    return __glXErrorBase + error;
257}
258
259__GLXclientState *
260glxGetClient(ClientPtr pClient)
261{
262    return dixLookupPrivate(&pClient->devPrivates, glxClientPrivateKey);
263}
264
265static void
266glxClientCallback (CallbackListPtr	*list,
267		   pointer		closure,
268		   pointer		data)
269{
270    NewClientInfoRec	*clientinfo = (NewClientInfoRec *) data;
271    ClientPtr		pClient = clientinfo->client;
272    __GLXclientState	*cl = glxGetClient(pClient);
273
274    switch (pClient->clientState) {
275    case ClientStateRunning:
276	/*
277	** By default, assume that the client supports
278	** GLX major version 1 minor version 0 protocol.
279	*/
280	cl->GLClientmajorVersion = 1;
281	cl->GLClientminorVersion = 0;
282	cl->client = pClient;
283	break;
284
285    case ClientStateGone:
286	free(cl->returnBuf);
287	free(cl->largeCmdBuf);
288	free(cl->GLClientextensions);
289	break;
290
291    default:
292	break;
293    }
294}
295
296/************************************************************************/
297
298static __GLXprovider *__glXProviderStack;
299
300void GlxPushProvider(__GLXprovider *provider)
301{
302    provider->next = __glXProviderStack;
303    __glXProviderStack = provider;
304}
305
306/*
307** Initialize the GLX extension.
308*/
309void GlxExtensionInit(void)
310{
311    ExtensionEntry *extEntry;
312    ScreenPtr pScreen;
313    int i;
314    __GLXprovider *p;
315    Bool glx_provided = False;
316
317    __glXContextRes = CreateNewResourceType((DeleteType)ContextGone,
318					    "GLXContext");
319    __glXDrawableRes = CreateNewResourceType((DeleteType)DrawableGone,
320					     "GLXDrawable");
321    if (!__glXContextRes || !__glXDrawableRes)
322	return;
323
324    if (!dixRegisterPrivateKey(&glxClientPrivateKeyRec, PRIVATE_CLIENT, sizeof (__GLXclientState)))
325	return;
326    if (!AddCallback (&ClientStateCallback, glxClientCallback, 0))
327	return;
328
329    for (i = 0; i < screenInfo.numScreens; i++) {
330	pScreen = screenInfo.screens[i];
331
332	for (p = __glXProviderStack; p != NULL; p = p->next) {
333	    __GLXscreen *glxScreen;
334
335	    glxScreen = p->screenProbe(pScreen);
336	    if (glxScreen != NULL) {
337	        if (glxScreen->GLXminor < glxMinorVersion)
338		    glxMinorVersion = glxScreen->GLXminor;
339		LogMessage(X_INFO,
340			   "GLX: Initialized %s GL provider for screen %d\n",
341			   p->name, i);
342		break;
343	    }
344
345	}
346
347	if (!p)
348	    LogMessage(X_INFO,
349		       "GLX: no usable GL providers found for screen %d\n", i);
350	else
351	    glx_provided = True;
352    }
353
354    /* don't register extension if GL is not provided on any screen */
355    if (!glx_provided)
356	return;
357
358    /*
359    ** Add extension to server extensions.
360    */
361    extEntry = AddExtension(GLX_EXTENSION_NAME, __GLX_NUMBER_EVENTS,
362			    __GLX_NUMBER_ERRORS, __glXDispatch,
363			    __glXDispatch, ResetExtension,
364			    StandardMinorOpcode);
365    if (!extEntry) {
366	FatalError("__glXExtensionInit: AddExtensions failed\n");
367	return;
368    }
369    if (!AddExtensionAlias(GLX_EXTENSION_ALIAS, extEntry)) {
370	ErrorF("__glXExtensionInit: AddExtensionAlias failed\n");
371	return;
372    }
373
374    __glXErrorBase = extEntry->errorBase;
375    __glXEventBase = extEntry->eventBase;
376}
377
378/************************************************************************/
379
380void __glXFlushContextCache(void)
381{
382    __glXLastContext = 0;
383}
384
385/*
386** Make a context the current one for the GL (in this implementation, there
387** is only one instance of the GL, and we use it to serve all GL clients by
388** switching it between different contexts).  While we are at it, look up
389** a context by its tag and return its (__GLXcontext *).
390*/
391__GLXcontext *__glXForceCurrent(__GLXclientState *cl, GLXContextTag tag,
392				int *error)
393{
394    __GLXcontext *cx;
395
396    /*
397    ** See if the context tag is legal; it is managed by the extension,
398    ** so if it's invalid, we have an implementation error.
399    */
400    cx = (__GLXcontext *) __glXLookupContextByTag(cl, tag);
401    if (!cx) {
402	cl->client->errorValue = tag;
403	*error = __glXError(GLXBadContextTag);
404	return 0;
405    }
406
407    if (!cx->isDirect) {
408	if (cx->drawPriv == NULL) {
409	    /*
410	    ** The drawable has vanished.  It must be a window, because only
411	    ** windows can be destroyed from under us; GLX pixmaps are
412	    ** refcounted and don't go away until no one is using them.
413	    */
414	    *error = __glXError(GLXBadCurrentWindow);
415	    return 0;
416    	}
417    }
418
419    if (cx->wait && (*cx->wait)(cx, cl, error))
420	return NULL;
421
422    if (cx == __glXLastContext) {
423	/* No need to re-bind */
424	return cx;
425    }
426
427    /* Make this context the current one for the GL. */
428    if (!cx->isDirect) {
429	if (!(*cx->forceCurrent)(cx)) {
430	    /* Bind failed, and set the error code.  Bummer */
431	    cl->client->errorValue = cx->id;
432	    *error = __glXError(GLXBadContextState);
433	    return 0;
434    	}
435    }
436    __glXLastContext = cx;
437    return cx;
438}
439
440/************************************************************************/
441
442void glxSuspendClients(void)
443{
444    int i;
445
446    for (i = 1; i < currentMaxClients; i++) {
447	if (clients[i] && glxGetClient(clients[i])->inUse)
448	    IgnoreClient(clients[i]);
449    }
450
451    glxBlockClients = TRUE;
452}
453
454void glxResumeClients(void)
455{
456    __GLXcontext *cx, *next;
457    int i;
458
459    glxBlockClients = FALSE;
460
461    for (i = 1; i < currentMaxClients; i++) {
462	if (clients[i] && glxGetClient(clients[i])->inUse)
463	    AttendClient(clients[i]);
464    }
465
466    __glXleaveServer(GL_FALSE);
467    for (cx = glxPendingDestroyContexts; cx != NULL; cx = next) {
468	next = cx->next;
469
470	cx->destroy(cx);
471    }
472    glxPendingDestroyContexts = NULL;
473    __glXenterServer(GL_FALSE);
474}
475
476static void
477__glXnopEnterServer(GLboolean rendering)
478{
479}
480
481static void
482__glXnopLeaveServer(GLboolean rendering)
483{
484}
485
486static void (*__glXenterServerFunc)(GLboolean) = __glXnopEnterServer;
487static void (*__glXleaveServerFunc)(GLboolean)  = __glXnopLeaveServer;
488
489void __glXsetEnterLeaveServerFuncs(void (*enter)(GLboolean),
490				   void (*leave)(GLboolean))
491{
492  __glXenterServerFunc = enter;
493  __glXleaveServerFunc = leave;
494}
495
496
497void __glXenterServer(GLboolean rendering)
498{
499  glxServerLeaveCount--;
500
501  if (glxServerLeaveCount == 0)
502    (*__glXenterServerFunc)(rendering);
503}
504
505void __glXleaveServer(GLboolean rendering)
506{
507  if (glxServerLeaveCount == 0)
508    (*__glXleaveServerFunc)(rendering);
509
510  glxServerLeaveCount++;
511}
512
513/*
514** Top level dispatcher; all commands are executed from here down.
515*/
516static int __glXDispatch(ClientPtr client)
517{
518    REQUEST(xGLXSingleReq);
519    CARD8 opcode;
520    __GLXdispatchSingleProcPtr proc;
521    __GLXclientState *cl;
522    int retval;
523
524    opcode = stuff->glxCode;
525    cl = glxGetClient(client);
526    /* Mark it in use so we suspend it on VT switch. */
527    cl->inUse = TRUE;
528
529    /*
530    ** If we're expecting a glXRenderLarge request, this better be one.
531    */
532    if ((cl->largeCmdRequestsSoFar != 0) && (opcode != X_GLXRenderLarge)) {
533	client->errorValue = stuff->glxCode;
534	return __glXError(GLXBadLargeRequest);
535    }
536
537    /* If we're currently blocking GLX clients, just put this guy to
538     * sleep, reset the request and return. */
539    if (glxBlockClients) {
540	ResetCurrentRequest(client);
541	client->sequence--;
542	IgnoreClient(client);
543	return Success;
544    }
545
546    /*
547    ** Use the opcode to index into the procedure table.
548    */
549    proc = (__GLXdispatchSingleProcPtr) __glXGetProtocolDecodeFunction(& Single_dispatch_info,
550								       opcode,
551								       client->swapped);
552    if (proc != NULL) {
553	GLboolean rendering = opcode <= X_GLXRenderLarge;
554	__glXleaveServer(rendering);
555
556	__pGlxClient = client;
557
558	retval = (*proc)(cl, (GLbyte *) stuff);
559
560	__glXenterServer(rendering);
561    }
562    else {
563	retval = BadRequest;
564    }
565
566    return retval;
567}
568