/* * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice including the dates of first publication and * either this permission notice or a reference to * http://oss.sgi.com/projects/FreeB/ * shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of Silicon Graphics, Inc. * shall not be used in advertising or otherwise to promote the sale, use or * other dealings in this Software without prior written authorization from * Silicon Graphics, Inc. */ #ifdef HAVE_DIX_CONFIG_H #include #endif #include #include "glxserver.h" #include #include #include #include "privates.h" #include #include "extinit.h" #include "glx_extinit.h" #include "unpack.h" #include "glxutil.h" #include "glxext.h" #include "indirect_table.h" #include "indirect_util.h" #include "glxvndabi.h" /* ** X resources. */ static int glxGeneration; RESTYPE __glXContextRes; RESTYPE __glXDrawableRes; static DevPrivateKeyRec glxClientPrivateKeyRec; static GlxServerVendor *glvnd_vendor = NULL; #define glxClientPrivateKey (&glxClientPrivateKeyRec) /* ** Forward declarations. */ static int __glXDispatch(ClientPtr); static GLboolean __glXFreeContext(__GLXcontext * cx); /* * This procedure is called when the client who created the context goes away * OR when glXDestroyContext is called. If the context is current for a client * the dispatch layer will have moved the context struct to a fake resource ID * and cx here will be NULL. Otherwise we really free the context. */ static int ContextGone(__GLXcontext * cx, XID id) { if (!cx) return TRUE; if (!cx->currentClient) __glXFreeContext(cx); return TRUE; } static __GLXcontext *glxPendingDestroyContexts; static __GLXcontext *glxAllContexts; static int glxBlockClients; /* ** Destroy routine that gets called when a drawable is freed. A drawable ** contains the ancillary buffers needed for rendering. */ static Bool DrawableGone(__GLXdrawable * glxPriv, XID xid) { __GLXcontext *c, *next; if (glxPriv->type == GLX_DRAWABLE_WINDOW) { /* If this was created by glXCreateWindow, free the matching resource */ if (glxPriv->drawId != glxPriv->pDraw->id) { if (xid == glxPriv->drawId) FreeResourceByType(glxPriv->pDraw->id, __glXDrawableRes, TRUE); else FreeResourceByType(glxPriv->drawId, __glXDrawableRes, TRUE); } /* otherwise this window was implicitly created by MakeCurrent */ } for (c = glxAllContexts; c; c = next) { next = c->next; if (c->currentClient && (c->drawPriv == glxPriv || c->readPriv == glxPriv)) { /* flush the context */ glFlush(); /* just force a re-bind the next time through */ (*c->loseCurrent) (c); lastGLContext = NULL; } if (c->drawPriv == glxPriv) c->drawPriv = NULL; if (c->readPriv == glxPriv) c->readPriv = NULL; } /* drop our reference to any backing pixmap */ if (glxPriv->type == GLX_DRAWABLE_PIXMAP) glxPriv->pDraw->pScreen->DestroyPixmap((PixmapPtr) glxPriv->pDraw); glxPriv->destroy(glxPriv); return TRUE; } Bool __glXAddContext(__GLXcontext * cx) { /* Register this context as a resource. */ if (!AddResource(cx->id, __glXContextRes, (void *)cx)) { return FALSE; } cx->next = glxAllContexts; glxAllContexts = cx; return TRUE; } static void __glXRemoveFromContextList(__GLXcontext * cx) { __GLXcontext *c, *prev; if (cx == glxAllContexts) glxAllContexts = cx->next; else { prev = glxAllContexts; for (c = glxAllContexts; c; c = c->next) { if (c == cx) prev->next = c->next; prev = c; } } } /* ** Free a context. */ static GLboolean __glXFreeContext(__GLXcontext * cx) { if (cx->idExists || cx->currentClient) return GL_FALSE; __glXRemoveFromContextList(cx); free(cx->feedbackBuf); free(cx->selectBuf); free(cx->largeCmdBuf); if (cx == lastGLContext) { lastGLContext = NULL; } /* We can get here through both regular dispatching from * __glXDispatch() or as a callback from the resource manager. In * the latter case we need to lift the DRI lock manually. */ if (!glxBlockClients) { cx->destroy(cx); } else { cx->next = glxPendingDestroyContexts; glxPendingDestroyContexts = cx; } return GL_TRUE; } /************************************************************************/ /* ** These routines can be used to check whether a particular GL command ** has caused an error. Specifically, we use them to check whether a ** given query has caused an error, in which case a zero-length data ** reply is sent to the client. */ static GLboolean errorOccured = GL_FALSE; /* ** The GL was will call this routine if an error occurs. */ void __glXErrorCallBack(GLenum code) { errorOccured = GL_TRUE; } /* ** Clear the error flag before calling the GL command. */ void __glXClearErrorOccured(void) { errorOccured = GL_FALSE; } /* ** Check if the GL command caused an error. */ GLboolean __glXErrorOccured(void) { return errorOccured; } static int __glXErrorBase; int __glXEventBase; int __glXError(int error) { return __glXErrorBase + error; } __GLXclientState * glxGetClient(ClientPtr pClient) { return dixLookupPrivate(&pClient->devPrivates, glxClientPrivateKey); } static void glxClientCallback(CallbackListPtr *list, void *closure, void *data) { NewClientInfoRec *clientinfo = (NewClientInfoRec *) data; ClientPtr pClient = clientinfo->client; __GLXclientState *cl = glxGetClient(pClient); switch (pClient->clientState) { case ClientStateGone: free(cl->returnBuf); free(cl->GLClientextensions); cl->returnBuf = NULL; cl->GLClientextensions = NULL; break; default: break; } } /************************************************************************/ static __GLXprovider *__glXProviderStack = &__glXDRISWRastProvider; void GlxPushProvider(__GLXprovider * provider) { provider->next = __glXProviderStack; __glXProviderStack = provider; } static Bool checkScreenVisuals(void) { int i, j; for (i = 0; i < screenInfo.numScreens; i++) { ScreenPtr screen = screenInfo.screens[i]; for (j = 0; j < screen->numVisuals; j++) { if ((screen->visuals[j].class == TrueColor || screen->visuals[j].class == DirectColor) && screen->visuals[j].nplanes > 12) return TRUE; } } return FALSE; } static void GetGLXDrawableBytes(void *value, XID id, ResourceSizePtr size) { __GLXdrawable *draw = value; size->resourceSize = 0; size->pixmapRefSize = 0; size->refCnt = 1; if (draw->type == GLX_DRAWABLE_PIXMAP) { SizeType pixmapSizeFunc = GetResourceTypeSizeFunc(RT_PIXMAP); ResourceSizeRec pixmapSize = { 0, }; pixmapSizeFunc((PixmapPtr)draw->pDraw, draw->pDraw->id, &pixmapSize); size->pixmapRefSize += pixmapSize.pixmapRefSize; } } static void xorgGlxCloseExtension(const ExtensionEntry *extEntry) { if (glvnd_vendor != NULL) { glxServer.destroyVendor(glvnd_vendor); glvnd_vendor = NULL; } lastGLContext = NULL; } static int xorgGlxHandleRequest(ClientPtr client) { return __glXDispatch(client); } static ScreenPtr screenNumToScreen(int screen) { if (screen < 0 || screen >= screenInfo.numScreens) return NULL; return screenInfo.screens[screen]; } static int maybe_swap32(ClientPtr client, int x) { return client->swapped ? bswap_32(x) : x; } static GlxServerVendor * vendorForScreen(ClientPtr client, int screen) { screen = maybe_swap32(client, screen); return glxServer.getVendorForScreen(client, screenNumToScreen(screen)); } /* this ought to be generated */ static int xorgGlxThunkRequest(ClientPtr client) { REQUEST(xGLXVendorPrivateReq); CARD32 vendorCode = maybe_swap32(client, stuff->vendorCode); GlxServerVendor *vendor = NULL; XID resource = 0; int ret; switch (vendorCode) { case X_GLXvop_QueryContextInfoEXT: { xGLXQueryContextInfoEXTReq *req = (void *)stuff; REQUEST_AT_LEAST_SIZE(*req); if (!(vendor = glxServer.getXIDMap(maybe_swap32(client, req->context)))) return __glXError(GLXBadContext); break; } case X_GLXvop_GetFBConfigsSGIX: { xGLXGetFBConfigsSGIXReq *req = (void *)stuff; REQUEST_AT_LEAST_SIZE(*req); if (!(vendor = vendorForScreen(client, req->screen))) return BadValue; break; } case X_GLXvop_CreateContextWithConfigSGIX: { xGLXCreateContextWithConfigSGIXReq *req = (void *)stuff; REQUEST_AT_LEAST_SIZE(*req); resource = maybe_swap32(client, req->context); if (!(vendor = vendorForScreen(client, req->screen))) return BadValue; break; } case X_GLXvop_CreateGLXPixmapWithConfigSGIX: { xGLXCreateGLXPixmapWithConfigSGIXReq *req = (void *)stuff; REQUEST_AT_LEAST_SIZE(*req); resource = maybe_swap32(client, req->glxpixmap); if (!(vendor = vendorForScreen(client, req->screen))) return BadValue; break; } case X_GLXvop_CreateGLXPbufferSGIX: { xGLXCreateGLXPbufferSGIXReq *req = (void *)stuff; REQUEST_AT_LEAST_SIZE(*req); resource = maybe_swap32(client, req->pbuffer); if (!(vendor = vendorForScreen(client, req->screen))) return BadValue; break; } /* same offset for the drawable for these three */ case X_GLXvop_DestroyGLXPbufferSGIX: case X_GLXvop_ChangeDrawableAttributesSGIX: case X_GLXvop_GetDrawableAttributesSGIX: { xGLXGetDrawableAttributesSGIXReq *req = (void *)stuff; REQUEST_AT_LEAST_SIZE(*req); if (!(vendor = glxServer.getXIDMap(maybe_swap32(client, req->drawable)))) return __glXError(GLXBadDrawable); break; } /* most things just use the standard context tag */ default: { /* size checked by vnd layer already */ GLXContextTag tag = maybe_swap32(client, stuff->contextTag); vendor = glxServer.getContextTag(client, tag); if (!vendor) return __glXError(GLXBadContextTag); break; } } /* If we're creating a resource, add the map now */ if (resource) { LEGAL_NEW_RESOURCE(resource, client); if (!glxServer.addXIDMap(resource, vendor)) return BadAlloc; } ret = glxServer.forwardRequest(vendor, client); if (ret == Success && vendorCode == X_GLXvop_DestroyGLXPbufferSGIX) { xGLXDestroyGLXPbufferSGIXReq *req = (void *)stuff; glxServer.removeXIDMap(maybe_swap32(client, req->pbuffer)); } if (ret != Success) glxServer.removeXIDMap(resource); return ret; } static GlxServerDispatchProc xorgGlxGetDispatchAddress(CARD8 minorOpcode, CARD32 vendorCode) { /* we don't support any other GLX opcodes */ if (minorOpcode != X_GLXVendorPrivate && minorOpcode != X_GLXVendorPrivateWithReply) return NULL; /* we only support some vendor private requests */ if (!__glXGetProtocolDecodeFunction(&VendorPriv_dispatch_info, vendorCode, FALSE)) return NULL; return xorgGlxThunkRequest; } static Bool xorgGlxServerPreInit(const ExtensionEntry *extEntry) { if (glxGeneration != serverGeneration) { /* Mesa requires at least one True/DirectColor visual */ if (!checkScreenVisuals()) return FALSE; __glXContextRes = CreateNewResourceType((DeleteType) ContextGone, "GLXContext"); __glXDrawableRes = CreateNewResourceType((DeleteType) DrawableGone, "GLXDrawable"); if (!__glXContextRes || !__glXDrawableRes) return FALSE; if (!dixRegisterPrivateKey (&glxClientPrivateKeyRec, PRIVATE_CLIENT, sizeof(__GLXclientState))) return FALSE; if (!AddCallback(&ClientStateCallback, glxClientCallback, 0)) return FALSE; __glXErrorBase = extEntry->errorBase; __glXEventBase = extEntry->eventBase; SetResourceTypeSizeFunc(__glXDrawableRes, GetGLXDrawableBytes); #if PRESENT __glXregisterPresentCompleteNotify(); #endif glxGeneration = serverGeneration; } return glxGeneration == serverGeneration; } static void xorgGlxInitGLVNDVendor(void) { if (glvnd_vendor == NULL) { GlxServerImports *imports = NULL; imports = glxServer.allocateServerImports(); if (imports != NULL) { imports->extensionCloseDown = xorgGlxCloseExtension; imports->handleRequest = xorgGlxHandleRequest; imports->getDispatchAddress = xorgGlxGetDispatchAddress; imports->makeCurrent = xorgGlxMakeCurrent; glvnd_vendor = glxServer.createVendor(imports); glxServer.freeServerImports(imports); } } } static void xorgGlxServerInit(CallbackListPtr *pcbl, void *param, void *ext) { const ExtensionEntry *extEntry = ext; int i; if (!xorgGlxServerPreInit(extEntry)) { return; } xorgGlxInitGLVNDVendor(); if (!glvnd_vendor) { return; } for (i = 0; i < screenInfo.numScreens; i++) { ScreenPtr pScreen = screenInfo.screens[i]; __GLXprovider *p; if (glxServer.getVendorForScreen(NULL, pScreen) != NULL) { // There's already a vendor registered. LogMessage(X_INFO, "GLX: Another vendor is already registered for screen %d\n", i); continue; } for (p = __glXProviderStack; p != NULL; p = p->next) { __GLXscreen *glxScreen = p->screenProbe(pScreen); if (glxScreen != NULL) { LogMessage(X_INFO, "GLX: Initialized %s GL provider for screen %d\n", p->name, i); break; } } if (p) { glxServer.setScreenVendor(pScreen, glvnd_vendor); } else { LogMessage(X_INFO, "GLX: no usable GL providers found for screen %d\n", i); } } } Bool xorgGlxCreateVendor(void) { return AddCallback(glxServer.extensionInitCallback, xorgGlxServerInit, NULL); } /************************************************************************/ /* ** Make a context the current one for the GL (in this implementation, there ** is only one instance of the GL, and we use it to serve all GL clients by ** switching it between different contexts). While we are at it, look up ** a context by its tag and return its (__GLXcontext *). */ __GLXcontext * __glXForceCurrent(__GLXclientState * cl, GLXContextTag tag, int *error) { ClientPtr client = cl->client; REQUEST(xGLXSingleReq); __GLXcontext *cx; /* ** See if the context tag is legal; it is managed by the extension, ** so if it's invalid, we have an implementation error. */ cx = __glXLookupContextByTag(cl, tag); if (!cx) { cl->client->errorValue = tag; *error = __glXError(GLXBadContextTag); return 0; } /* If we're expecting a glXRenderLarge request, this better be one. */ if (cx->largeCmdRequestsSoFar != 0 && stuff->glxCode != X_GLXRenderLarge) { client->errorValue = stuff->glxCode; *error = __glXError(GLXBadLargeRequest); return 0; } if (!cx->isDirect) { if (cx->drawPriv == NULL) { /* ** The drawable has vanished. It must be a window, because only ** windows can be destroyed from under us; GLX pixmaps are ** refcounted and don't go away until no one is using them. */ *error = __glXError(GLXBadCurrentWindow); return 0; } } if (cx->wait && (*cx->wait) (cx, cl, error)) return NULL; if (cx == lastGLContext) { /* No need to re-bind */ return cx; } /* Make this context the current one for the GL. */ if (!cx->isDirect) { /* * If it is being forced, it means that this context was already made * current. So it cannot just be made current again without decrementing * refcount's */ (*cx->loseCurrent) (cx); lastGLContext = cx; if (!(*cx->makeCurrent) (cx)) { /* Bind failed, and set the error code. Bummer */ lastGLContext = NULL; cl->client->errorValue = cx->id; *error = __glXError(GLXBadContextState); return 0; } } return cx; } /************************************************************************/ void glxSuspendClients(void) { int i; for (i = 1; i < currentMaxClients; i++) { if (clients[i] && glxGetClient(clients[i])->client) IgnoreClient(clients[i]); } glxBlockClients = TRUE; } void glxResumeClients(void) { __GLXcontext *cx, *next; int i; glxBlockClients = FALSE; for (i = 1; i < currentMaxClients; i++) { if (clients[i] && glxGetClient(clients[i])->client) AttendClient(clients[i]); } for (cx = glxPendingDestroyContexts; cx != NULL; cx = next) { next = cx->next; cx->destroy(cx); } glxPendingDestroyContexts = NULL; } static glx_gpa_proc _get_proc_address; void __glXsetGetProcAddress(glx_gpa_proc get_proc_address) { _get_proc_address = get_proc_address; } void *__glGetProcAddress(const char *proc) { void *ret = (void *) _get_proc_address(proc); return ret ? ret : (void *) NoopDDA; } /* ** Top level dispatcher; all commands are executed from here down. */ static int __glXDispatch(ClientPtr client) { REQUEST(xGLXSingleReq); CARD8 opcode; __GLXdispatchSingleProcPtr proc; __GLXclientState *cl; int retval = BadRequest; opcode = stuff->glxCode; cl = glxGetClient(client); if (!cl->client) cl->client = client; /* If we're currently blocking GLX clients, just put this guy to * sleep, reset the request and return. */ if (glxBlockClients) { ResetCurrentRequest(client); client->sequence--; IgnoreClient(client); return Success; } /* ** Use the opcode to index into the procedure table. */ proc = __glXGetProtocolDecodeFunction(&Single_dispatch_info, opcode, client->swapped); if (proc != NULL) retval = (*proc) (cl, (GLbyte *) stuff); return retval; }