glxext.c revision 17c9e1e9
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 "g_disptab.h"
43#include "unpack.h"
44#include "glxutil.h"
45#include "glxext.h"
46#include "indirect_table.h"
47#include "indirect_util.h"
48
49/*
50** The last context used by the server.  It is the context that is current
51** from the server's perspective.
52*/
53__GLXcontext *__glXLastContext;
54__GLXcontext *__glXContextList;
55
56/*
57** X resources.
58*/
59RESTYPE __glXContextRes;
60RESTYPE __glXDrawableRes;
61RESTYPE __glXSwapBarrierRes;
62
63/*
64** Reply for most singles.
65*/
66xGLXSingleReply __glXReply;
67
68static DevPrivateKeyRec glxClientPrivateKeyRec;
69#define glxClientPrivateKey (&glxClientPrivateKeyRec)
70
71/*
72** Client that called into GLX dispatch.
73*/
74ClientPtr __pGlxClient;
75
76/*
77** Forward declarations.
78*/
79static int __glXDispatch(ClientPtr);
80
81/*
82** Called when the extension is reset.
83*/
84static void ResetExtension(ExtensionEntry* extEntry)
85{
86    __glXFlushContextCache();
87}
88
89/*
90** Reset state used to keep track of large (multi-request) commands.
91*/
92void __glXResetLargeCommandStatus(__GLXclientState *cl)
93{
94    cl->largeCmdBytesSoFar = 0;
95    cl->largeCmdBytesTotal = 0;
96    cl->largeCmdRequestsSoFar = 0;
97    cl->largeCmdRequestsTotal = 0;
98}
99
100/*
101** This procedure is called when the client who created the context goes
102** away OR when glXDestroyContext is called.  In either case, all we do is
103** flag that the ID is no longer valid, and (maybe) free the context.
104** use.
105*/
106static int ContextGone(__GLXcontext* cx, XID id)
107{
108    cx->idExists = GL_FALSE;
109    if (!cx->isCurrent) {
110	__glXFreeContext(cx);
111    }
112
113    return True;
114}
115
116static __GLXcontext *glxPendingDestroyContexts;
117static __GLXcontext *glxAllContexts;
118static int glxServerLeaveCount;
119static int glxBlockClients;
120
121/*
122** Destroy routine that gets called when a drawable is freed.  A drawable
123** contains the ancillary buffers needed for rendering.
124*/
125static Bool DrawableGone(__GLXdrawable *glxPriv, XID xid)
126{
127    __GLXcontext *c, *next;
128
129    /* If this drawable was created using glx 1.3 drawable
130     * constructors, we added it as a glx drawable resource under both
131     * its glx drawable ID and it X drawable ID.  Remove the other
132     * resource now so we don't a callback for freed memory. */
133    if (glxPriv->otherId) {
134	    XID other = glxPriv->otherId;
135	    glxPriv->otherId = 0;
136	    if (xid == other)
137		    FreeResourceByType(glxPriv->drawId, __glXDrawableRes, TRUE);
138	    else
139		    FreeResourceByType(other, __glXDrawableRes, TRUE);
140    }
141
142    if (--glxPriv->refcnt)
143	    return True;
144
145    for (c = glxAllContexts; c; c = next) {
146	next = c->next;
147	if (c->isCurrent && (c->drawPriv == glxPriv || c->readPriv == glxPriv)) {
148	    int i;
149
150	    (*c->loseCurrent)(c);
151	    c->isCurrent = GL_FALSE;
152	    if (c == __glXLastContext)
153		__glXFlushContextCache();
154
155	    for (i = 1; i < currentMaxClients; i++) {
156		if (clients[i]) {
157		    __GLXclientState *cl = glxGetClient(clients[i]);
158
159		    if (cl->inUse) {
160			int j;
161
162			for (j = 0; j < cl->numCurrentContexts; j++) {
163			    if (cl->currentContexts[j] == c)
164				cl->currentContexts[j] = NULL;
165			}
166		    }
167		}
168	    }
169	}
170	if (c->drawPriv == glxPriv)
171	    c->drawPriv = NULL;
172	if (c->readPriv == glxPriv)
173	    c->readPriv = NULL;
174	if (!c->idExists && !c->isCurrent)
175	    __glXFreeContext(c);
176    }
177
178    glxPriv->destroy(glxPriv);
179
180    return True;
181}
182
183void __glXAddToContextList(__GLXcontext *cx)
184{
185    cx->next = glxAllContexts;
186    glxAllContexts = cx;
187}
188
189static void __glXRemoveFromContextList(__GLXcontext *cx)
190{
191    __GLXcontext *c, *prev;
192
193    if (cx == glxAllContexts)
194	glxAllContexts = cx->next;
195    else {
196	prev = glxAllContexts;
197	for (c = glxAllContexts; c; c = c->next) {
198	    if (c == cx)
199		prev->next = c->next;
200	    prev = c;
201	}
202    }
203}
204
205/*
206** Free a context.
207*/
208GLboolean __glXFreeContext(__GLXcontext *cx)
209{
210    if (cx->idExists || cx->isCurrent) return GL_FALSE;
211
212    free(cx->feedbackBuf);
213    free(cx->selectBuf);
214    if (cx == __glXLastContext) {
215	__glXFlushContextCache();
216    }
217
218    __glXRemoveFromContextList(cx);
219
220    /* We can get here through both regular dispatching from
221     * __glXDispatch() or as a callback from the resource manager.  In
222     * the latter case we need to lift the DRI lock manually. */
223
224    if (!glxBlockClients) {
225	__glXleaveServer(GL_FALSE);
226	cx->destroy(cx);
227	__glXenterServer(GL_FALSE);
228    } else {
229	cx->next = glxPendingDestroyContexts;
230	glxPendingDestroyContexts = cx;
231    }
232
233    return GL_TRUE;
234}
235
236extern RESTYPE __glXSwapBarrierRes;
237
238static int SwapBarrierGone(int screen, XID drawable)
239{
240    __GLXscreen *pGlxScreen = glxGetScreen(screenInfo.screens[screen]);
241
242    if (pGlxScreen->swapBarrierFuncs) {
243        pGlxScreen->swapBarrierFuncs->bindSwapBarrierFunc(screen, drawable, 0);
244    }
245    FreeResourceByType(drawable, __glXSwapBarrierRes, FALSE);
246    return True;
247}
248
249/************************************************************************/
250
251/*
252** These routines can be used to check whether a particular GL command
253** has caused an error.  Specifically, we use them to check whether a
254** given query has caused an error, in which case a zero-length data
255** reply is sent to the client.
256*/
257
258static GLboolean errorOccured = GL_FALSE;
259
260/*
261** The GL was will call this routine if an error occurs.
262*/
263void __glXErrorCallBack(GLenum code)
264{
265    errorOccured = GL_TRUE;
266}
267
268/*
269** Clear the error flag before calling the GL command.
270*/
271void __glXClearErrorOccured(void)
272{
273    errorOccured = GL_FALSE;
274}
275
276/*
277** Check if the GL command caused an error.
278*/
279GLboolean __glXErrorOccured(void)
280{
281    return errorOccured;
282}
283
284static int __glXErrorBase;
285int __glXEventBase;
286
287int __glXError(int error)
288{
289    return __glXErrorBase + error;
290}
291
292__GLXclientState *
293glxGetClient(ClientPtr pClient)
294{
295    return dixLookupPrivate(&pClient->devPrivates, glxClientPrivateKey);
296}
297
298static void
299glxClientCallback (CallbackListPtr	*list,
300		   pointer		closure,
301		   pointer		data)
302{
303    NewClientInfoRec	*clientinfo = (NewClientInfoRec *) data;
304    ClientPtr		pClient = clientinfo->client;
305    __GLXclientState	*cl = glxGetClient(pClient);
306    __GLXcontext	*cx;
307    int i;
308
309    switch (pClient->clientState) {
310    case ClientStateRunning:
311	/*
312	** By default, assume that the client supports
313	** GLX major version 1 minor version 0 protocol.
314	*/
315	cl->GLClientmajorVersion = 1;
316	cl->GLClientminorVersion = 0;
317	cl->client = pClient;
318	break;
319
320    case ClientStateGone:
321	for (i = 0; i < cl->numCurrentContexts; i++) {
322	    cx = cl->currentContexts[i];
323	    if (cx) {
324		cx->isCurrent = GL_FALSE;
325		if (!cx->idExists)
326		    __glXFreeContext(cx);
327	    }
328	}
329
330	free(cl->returnBuf);
331	free(cl->largeCmdBuf);
332	free(cl->currentContexts);
333	free(cl->GLClientextensions);
334	break;
335
336    default:
337	break;
338    }
339}
340
341/************************************************************************/
342
343static __GLXprovider *__glXProviderStack;
344
345void GlxPushProvider(__GLXprovider *provider)
346{
347    provider->next = __glXProviderStack;
348    __glXProviderStack = provider;
349}
350
351/*
352** Initialize the GLX extension.
353*/
354void GlxExtensionInit(void)
355{
356    ExtensionEntry *extEntry;
357    ScreenPtr pScreen;
358    int i;
359    __GLXprovider *p;
360    Bool glx_provided = False;
361
362    __glXContextRes = CreateNewResourceType((DeleteType)ContextGone,
363					    "GLXContext");
364    __glXDrawableRes = CreateNewResourceType((DeleteType)DrawableGone,
365					     "GLXDrawable");
366    __glXSwapBarrierRes = CreateNewResourceType((DeleteType)SwapBarrierGone,
367						"GLXSwapBarrier");
368    if (!__glXContextRes || !__glXDrawableRes || !__glXSwapBarrierRes)
369	return;
370
371    if (!dixRegisterPrivateKey(&glxClientPrivateKeyRec, PRIVATE_CLIENT, sizeof (__GLXclientState)))
372	return;
373    if (!AddCallback (&ClientStateCallback, glxClientCallback, 0))
374	return;
375
376    for (i = 0; i < screenInfo.numScreens; i++) {
377	pScreen = screenInfo.screens[i];
378
379	for (p = __glXProviderStack; p != NULL; p = p->next) {
380	    __GLXscreen *glxScreen;
381
382	    glxScreen = p->screenProbe(pScreen);
383	    if (glxScreen != NULL) {
384	        if (glxScreen->GLXminor < glxMinorVersion)
385		    glxMinorVersion = glxScreen->GLXminor;
386		LogMessage(X_INFO,
387			   "GLX: Initialized %s GL provider for screen %d\n",
388			   p->name, i);
389		break;
390	    }
391
392	}
393
394	if (!p)
395	    LogMessage(X_INFO,
396		       "GLX: no usable GL providers found for screen %d\n", i);
397	else
398	    glx_provided = True;
399    }
400
401    /* don't register extension if GL is not provided on any screen */
402    if (!glx_provided)
403	return;
404
405    /*
406    ** Add extension to server extensions.
407    */
408    extEntry = AddExtension(GLX_EXTENSION_NAME, __GLX_NUMBER_EVENTS,
409			    __GLX_NUMBER_ERRORS, __glXDispatch,
410			    __glXDispatch, ResetExtension,
411			    StandardMinorOpcode);
412    if (!extEntry) {
413	FatalError("__glXExtensionInit: AddExtensions failed\n");
414	return;
415    }
416    if (!AddExtensionAlias(GLX_EXTENSION_ALIAS, extEntry)) {
417	ErrorF("__glXExtensionInit: AddExtensionAlias failed\n");
418	return;
419    }
420
421    __glXErrorBase = extEntry->errorBase;
422    __glXEventBase = extEntry->eventBase;
423}
424
425/************************************************************************/
426
427void __glXFlushContextCache(void)
428{
429    __glXLastContext = 0;
430}
431
432/*
433** Make a context the current one for the GL (in this implementation, there
434** is only one instance of the GL, and we use it to serve all GL clients by
435** switching it between different contexts).  While we are at it, look up
436** a context by its tag and return its (__GLXcontext *).
437*/
438__GLXcontext *__glXForceCurrent(__GLXclientState *cl, GLXContextTag tag,
439				int *error)
440{
441    __GLXcontext *cx;
442
443    /*
444    ** See if the context tag is legal; it is managed by the extension,
445    ** so if it's invalid, we have an implementation error.
446    */
447    cx = (__GLXcontext *) __glXLookupContextByTag(cl, tag);
448    if (!cx) {
449	cl->client->errorValue = tag;
450	*error = __glXError(GLXBadContextTag);
451	return 0;
452    }
453
454    if (!cx->isDirect) {
455	if (cx->drawPriv == NULL) {
456	    /*
457	    ** The drawable has vanished.  It must be a window, because only
458	    ** windows can be destroyed from under us; GLX pixmaps are
459	    ** refcounted and don't go away until no one is using them.
460	    */
461	    *error = __glXError(GLXBadCurrentWindow);
462	    return 0;
463    	}
464    }
465
466    if (cx->wait && (*cx->wait)(cx, cl, error))
467	return NULL;
468
469    if (cx == __glXLastContext) {
470	/* No need to re-bind */
471	return cx;
472    }
473
474    /* Make this context the current one for the GL. */
475    if (!cx->isDirect) {
476	if (!(*cx->forceCurrent)(cx)) {
477	    /* Bind failed, and set the error code.  Bummer */
478	    cl->client->errorValue = cx->id;
479	    *error = __glXError(GLXBadContextState);
480	    return 0;
481    	}
482    }
483    __glXLastContext = cx;
484    return cx;
485}
486
487/************************************************************************/
488
489void glxSuspendClients(void)
490{
491    int i;
492
493    for (i = 1; i < currentMaxClients; i++) {
494	if (clients[i] && glxGetClient(clients[i])->inUse)
495	    IgnoreClient(clients[i]);
496    }
497
498    glxBlockClients = TRUE;
499}
500
501void glxResumeClients(void)
502{
503    __GLXcontext *cx, *next;
504    int i;
505
506    glxBlockClients = FALSE;
507
508    for (i = 1; i < currentMaxClients; i++) {
509	if (clients[i] && glxGetClient(clients[i])->inUse)
510	    AttendClient(clients[i]);
511    }
512
513    __glXleaveServer(GL_FALSE);
514    for (cx = glxPendingDestroyContexts; cx != NULL; cx = next) {
515	next = cx->next;
516
517	cx->destroy(cx);
518    }
519    glxPendingDestroyContexts = NULL;
520    __glXenterServer(GL_FALSE);
521}
522
523static void
524__glXnopEnterServer(GLboolean rendering)
525{
526}
527
528static void
529__glXnopLeaveServer(GLboolean rendering)
530{
531}
532
533static void (*__glXenterServerFunc)(GLboolean) = __glXnopEnterServer;
534static void (*__glXleaveServerFunc)(GLboolean)  = __glXnopLeaveServer;
535
536void __glXsetEnterLeaveServerFuncs(void (*enter)(GLboolean),
537				   void (*leave)(GLboolean))
538{
539  __glXenterServerFunc = enter;
540  __glXleaveServerFunc = leave;
541}
542
543
544void __glXenterServer(GLboolean rendering)
545{
546  glxServerLeaveCount--;
547
548  if (glxServerLeaveCount == 0)
549    (*__glXenterServerFunc)(rendering);
550}
551
552void __glXleaveServer(GLboolean rendering)
553{
554  if (glxServerLeaveCount == 0)
555    (*__glXleaveServerFunc)(rendering);
556
557  glxServerLeaveCount++;
558}
559
560/*
561** Top level dispatcher; all commands are executed from here down.
562*/
563static int __glXDispatch(ClientPtr client)
564{
565    REQUEST(xGLXSingleReq);
566    CARD8 opcode;
567    __GLXdispatchSingleProcPtr proc;
568    __GLXclientState *cl;
569    int retval;
570
571    opcode = stuff->glxCode;
572    cl = glxGetClient(client);
573    /* Mark it in use so we suspend it on VT switch. */
574    cl->inUse = TRUE;
575
576    /*
577    ** If we're expecting a glXRenderLarge request, this better be one.
578    */
579    if ((cl->largeCmdRequestsSoFar != 0) && (opcode != X_GLXRenderLarge)) {
580	client->errorValue = stuff->glxCode;
581	return __glXError(GLXBadLargeRequest);
582    }
583
584    /* If we're currently blocking GLX clients, just put this guy to
585     * sleep, reset the request and return. */
586    if (glxBlockClients) {
587	ResetCurrentRequest(client);
588	client->sequence--;
589	IgnoreClient(client);
590	return Success;
591    }
592
593    /*
594    ** Use the opcode to index into the procedure table.
595    */
596    proc = (__GLXdispatchSingleProcPtr) __glXGetProtocolDecodeFunction(& Single_dispatch_info,
597								       opcode,
598								       client->swapped);
599    if (proc != NULL) {
600	GLboolean rendering = opcode <= X_GLXRenderLarge;
601	__glXleaveServer(rendering);
602
603	__pGlxClient = client;
604
605	retval = (*proc)(cl, (GLbyte *) stuff);
606
607	__glXenterServer(rendering);
608    }
609    else {
610	retval = BadRequest;
611    }
612
613    return retval;
614}
615