glxext.c revision b1d344b3
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#define NEED_REPLIES
32#ifdef HAVE_DIX_CONFIG_H
33#include <dix-config.h>
34#endif
35
36#include <string.h>
37#include "glxserver.h"
38#include <windowstr.h>
39#include <propertyst.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 int glxClientPrivateKeyIndex;
69static DevPrivateKey glxClientPrivateKey = &glxClientPrivateKeyIndex;
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;
128
129    for (c = glxAllContexts; c; c = c->next) {
130	if (c->isCurrent && (c->drawPriv == glxPriv || c->readPriv == glxPriv)) {
131	    int i;
132
133	    (*c->loseCurrent)(c);
134	    c->isCurrent = GL_FALSE;
135	    if (c == __glXLastContext)
136		__glXFlushContextCache();
137
138	    for (i = 1; i < currentMaxClients; i++) {
139		if (clients[i]) {
140		    __GLXclientState *cl = glxGetClient(clients[i]);
141
142		    if (cl->inUse) {
143			int j;
144
145			for (j = 0; j < cl->numCurrentContexts; j++) {
146			    if (cl->currentContexts[j] == c)
147				cl->currentContexts[j] = NULL;
148			}
149		    }
150		}
151	    }
152
153	    if (!c->idExists) {
154		__glXFreeContext(c);
155	    }
156	}
157	if (c->drawPriv == glxPriv)
158	    c->drawPriv = NULL;
159	if (c->readPriv == glxPriv)
160	    c->readPriv = NULL;
161    }
162
163    glxPriv->destroy(glxPriv);
164
165    return True;
166}
167
168void __glXAddToContextList(__GLXcontext *cx)
169{
170    cx->next = glxAllContexts;
171    glxAllContexts = cx;
172}
173
174void __glXRemoveFromContextList(__GLXcontext *cx)
175{
176    __GLXcontext *c, **prev;
177
178    prev = &glxAllContexts;
179    for (c = glxAllContexts; c; c = c->next)
180	if (c == cx)
181	    *prev = c->next;
182}
183
184/*
185** Free a context.
186*/
187GLboolean __glXFreeContext(__GLXcontext *cx)
188{
189    if (cx->idExists || cx->isCurrent) return GL_FALSE;
190
191    if (cx->feedbackBuf) xfree(cx->feedbackBuf);
192    if (cx->selectBuf) xfree(cx->selectBuf);
193    if (cx == __glXLastContext) {
194	__glXFlushContextCache();
195    }
196
197    __glXRemoveFromContextList(cx);
198
199    /* We can get here through both regular dispatching from
200     * __glXDispatch() or as a callback from the resource manager.  In
201     * the latter case we need to lift the DRI lock manually. */
202
203    if (!glxBlockClients) {
204	__glXleaveServer(GL_FALSE);
205	cx->destroy(cx);
206	__glXenterServer(GL_FALSE);
207    } else {
208	cx->next = glxPendingDestroyContexts;
209	glxPendingDestroyContexts = cx;
210    }
211
212    return GL_TRUE;
213}
214
215extern RESTYPE __glXSwapBarrierRes;
216
217static int SwapBarrierGone(int screen, XID drawable)
218{
219    __GLXscreen *pGlxScreen = glxGetScreen(screenInfo.screens[screen]);
220
221    if (pGlxScreen->swapBarrierFuncs) {
222        pGlxScreen->swapBarrierFuncs->bindSwapBarrierFunc(screen, drawable, 0);
223    }
224    FreeResourceByType(drawable, __glXSwapBarrierRes, FALSE);
225    return True;
226}
227
228/************************************************************************/
229
230/*
231** These routines can be used to check whether a particular GL command
232** has caused an error.  Specifically, we use them to check whether a
233** given query has caused an error, in which case a zero-length data
234** reply is sent to the client.
235*/
236
237static GLboolean errorOccured = GL_FALSE;
238
239/*
240** The GL was will call this routine if an error occurs.
241*/
242void __glXErrorCallBack(GLenum code)
243{
244    errorOccured = GL_TRUE;
245}
246
247/*
248** Clear the error flag before calling the GL command.
249*/
250void __glXClearErrorOccured(void)
251{
252    errorOccured = GL_FALSE;
253}
254
255/*
256** Check if the GL command caused an error.
257*/
258GLboolean __glXErrorOccured(void)
259{
260    return errorOccured;
261}
262
263static int __glXErrorBase;
264
265int __glXError(int error)
266{
267    return __glXErrorBase + error;
268}
269
270__GLXclientState *
271glxGetClient(ClientPtr pClient)
272{
273    return dixLookupPrivate(&pClient->devPrivates, glxClientPrivateKey);
274}
275
276static void
277glxClientCallback (CallbackListPtr	*list,
278		   pointer		closure,
279		   pointer		data)
280{
281    NewClientInfoRec	*clientinfo = (NewClientInfoRec *) data;
282    ClientPtr		pClient = clientinfo->client;
283    __GLXclientState	*cl = glxGetClient(pClient);
284    __GLXcontext	*cx;
285    int i;
286
287    switch (pClient->clientState) {
288    case ClientStateRunning:
289	/*
290	** By default, assume that the client supports
291	** GLX major version 1 minor version 0 protocol.
292	*/
293	cl->GLClientmajorVersion = 1;
294	cl->GLClientminorVersion = 0;
295	cl->client = pClient;
296	break;
297
298    case ClientStateGone:
299	for (i = 0; i < cl->numCurrentContexts; i++) {
300	    cx = cl->currentContexts[i];
301	    if (cx) {
302		cx->isCurrent = GL_FALSE;
303		if (!cx->idExists)
304		    __glXFreeContext(cx);
305	    }
306	}
307
308	if (cl->returnBuf) xfree(cl->returnBuf);
309	if (cl->largeCmdBuf) xfree(cl->largeCmdBuf);
310	if (cl->currentContexts) xfree(cl->currentContexts);
311	if (cl->GLClientextensions) xfree(cl->GLClientextensions);
312	break;
313
314    default:
315	break;
316    }
317}
318
319/************************************************************************/
320
321static __GLXprovider *__glXProviderStack;
322
323void GlxPushProvider(__GLXprovider *provider)
324{
325    provider->next = __glXProviderStack;
326    __glXProviderStack = provider;
327}
328
329/*
330** Initialize the GLX extension.
331*/
332void GlxExtensionInit(void)
333{
334    ExtensionEntry *extEntry;
335    ScreenPtr pScreen;
336    int i;
337    __GLXprovider *p;
338    Bool glx_provided = False;
339
340    __glXContextRes = CreateNewResourceType((DeleteType)ContextGone);
341    __glXDrawableRes = CreateNewResourceType((DeleteType)DrawableGone);
342    __glXSwapBarrierRes = CreateNewResourceType((DeleteType)SwapBarrierGone);
343
344    if (!dixRequestPrivate(glxClientPrivateKey, sizeof (__GLXclientState)))
345	return;
346    if (!AddCallback (&ClientStateCallback, glxClientCallback, 0))
347	return;
348
349    for (i = 0; i < screenInfo.numScreens; i++) {
350	pScreen = screenInfo.screens[i];
351
352	for (p = __glXProviderStack; p != NULL; p = p->next) {
353	    if (p->screenProbe(pScreen) != NULL) {
354		LogMessage(X_INFO,
355			   "GLX: Initialized %s GL provider for screen %d\n",
356			   p->name, i);
357		break;
358	    }
359	}
360
361	if (!p)
362	    LogMessage(X_INFO,
363		       "GLX: no usable GL providers found for screen %d\n", i);
364	else
365	    glx_provided = True;
366    }
367
368    /* don't register extension if GL is not provided on any screen */
369    if (!glx_provided)
370	return;
371
372    /*
373    ** Add extension to server extensions.
374    */
375    extEntry = AddExtension(GLX_EXTENSION_NAME, __GLX_NUMBER_EVENTS,
376			    __GLX_NUMBER_ERRORS, __glXDispatch,
377			    __glXDispatch, ResetExtension,
378			    StandardMinorOpcode);
379    if (!extEntry) {
380	FatalError("__glXExtensionInit: AddExtensions failed\n");
381	return;
382    }
383    if (!AddExtensionAlias(GLX_EXTENSION_ALIAS, extEntry)) {
384	ErrorF("__glXExtensionInit: AddExtensionAlias failed\n");
385	return;
386    }
387
388    __glXErrorBase = extEntry->errorBase;
389}
390
391/************************************************************************/
392
393void __glXFlushContextCache(void)
394{
395    __glXLastContext = 0;
396}
397
398/*
399** Make a context the current one for the GL (in this implementation, there
400** is only one instance of the GL, and we use it to serve all GL clients by
401** switching it between different contexts).  While we are at it, look up
402** a context by its tag and return its (__GLXcontext *).
403*/
404__GLXcontext *__glXForceCurrent(__GLXclientState *cl, GLXContextTag tag,
405				int *error)
406{
407    __GLXcontext *cx;
408
409    /*
410    ** See if the context tag is legal; it is managed by the extension,
411    ** so if it's invalid, we have an implementation error.
412    */
413    cx = (__GLXcontext *) __glXLookupContextByTag(cl, tag);
414    if (!cx) {
415	cl->client->errorValue = tag;
416	*error = __glXError(GLXBadContextTag);
417	return 0;
418    }
419
420    if (!cx->isDirect) {
421	if (cx->drawPriv == NULL) {
422	    /*
423	    ** The drawable has vanished.  It must be a window, because only
424	    ** windows can be destroyed from under us; GLX pixmaps are
425	    ** refcounted and don't go away until no one is using them.
426	    */
427	    *error = __glXError(GLXBadCurrentWindow);
428	    return 0;
429    	}
430    }
431
432    if (cx == __glXLastContext) {
433	/* No need to re-bind */
434	return cx;
435    }
436
437    /* Make this context the current one for the GL. */
438    if (!cx->isDirect) {
439	if (!(*cx->forceCurrent)(cx)) {
440	    /* Bind failed, and set the error code.  Bummer */
441	    cl->client->errorValue = cx->id;
442	    *error = __glXError(GLXBadContextState);
443	    return 0;
444    	}
445    }
446    __glXLastContext = cx;
447    return cx;
448}
449
450/************************************************************************/
451
452void glxSuspendClients(void)
453{
454    int i;
455
456    for (i = 1; i < currentMaxClients; i++) {
457	if (clients[i] && glxGetClient(clients[i])->inUse)
458	    IgnoreClient(clients[i]);
459    }
460
461    glxBlockClients = TRUE;
462}
463
464void glxResumeClients(void)
465{
466    __GLXcontext *cx, *next;
467    int i;
468
469    glxBlockClients = FALSE;
470
471    for (i = 1; i < currentMaxClients; i++) {
472	if (clients[i] && glxGetClient(clients[i])->inUse)
473	    AttendClient(clients[i]);
474    }
475
476    __glXleaveServer(GL_FALSE);
477    for (cx = glxPendingDestroyContexts; cx != NULL; cx = next) {
478	next = cx->next;
479
480	cx->destroy(cx);
481    }
482    glxPendingDestroyContexts = NULL;
483    __glXenterServer(GL_FALSE);
484}
485
486static void
487__glXnopEnterServer(GLboolean rendering)
488{
489}
490
491static void
492__glXnopLeaveServer(GLboolean rendering)
493{
494}
495
496static void (*__glXenterServerFunc)(GLboolean) = __glXnopEnterServer;
497static void (*__glXleaveServerFunc)(GLboolean)  = __glXnopLeaveServer;
498
499void __glXsetEnterLeaveServerFuncs(void (*enter)(GLboolean),
500				   void (*leave)(GLboolean))
501{
502  __glXenterServerFunc = enter;
503  __glXleaveServerFunc = leave;
504}
505
506
507void __glXenterServer(GLboolean rendering)
508{
509  glxServerLeaveCount--;
510
511  if (glxServerLeaveCount == 0)
512    (*__glXenterServerFunc)(rendering);
513}
514
515void __glXleaveServer(GLboolean rendering)
516{
517  if (glxServerLeaveCount == 0)
518    (*__glXleaveServerFunc)(rendering);
519
520  glxServerLeaveCount++;
521}
522
523/*
524** Top level dispatcher; all commands are executed from here down.
525*/
526static int __glXDispatch(ClientPtr client)
527{
528    REQUEST(xGLXSingleReq);
529    CARD8 opcode;
530    __GLXdispatchSingleProcPtr proc;
531    __GLXclientState *cl;
532    int retval;
533
534    opcode = stuff->glxCode;
535    cl = glxGetClient(client);
536    /* Mark it in use so we suspend it on VT switch. */
537    cl->inUse = TRUE;
538
539    /*
540    ** If we're expecting a glXRenderLarge request, this better be one.
541    */
542    if ((cl->largeCmdRequestsSoFar != 0) && (opcode != X_GLXRenderLarge)) {
543	client->errorValue = stuff->glxCode;
544	return __glXError(GLXBadLargeRequest);
545    }
546
547    /* If we're currently blocking GLX clients, just put this guy to
548     * sleep, reset the request and return. */
549    if (glxBlockClients) {
550	ResetCurrentRequest(client);
551	client->sequence--;
552	IgnoreClient(client);
553	return(client->noClientException);
554    }
555
556    /*
557    ** Use the opcode to index into the procedure table.
558    */
559    proc = (__GLXdispatchSingleProcPtr) __glXGetProtocolDecodeFunction(& Single_dispatch_info,
560								       opcode,
561								       client->swapped);
562    if (proc != NULL) {
563	GLboolean rendering = opcode <= X_GLXRenderLarge;
564	__glXleaveServer(rendering);
565
566	__pGlxClient = client;
567
568	retval = (*proc)(cl, (GLbyte *) stuff);
569
570	__glXenterServer(rendering);
571    }
572    else {
573	retval = BadRequest;
574    }
575
576    return retval;
577}
578