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