dixutils.c revision 1b5d61b8
105b261ecSmrg/*********************************************************** 205b261ecSmrg 305b261ecSmrgCopyright 1987, 1998 The Open Group 405b261ecSmrg 505b261ecSmrgPermission to use, copy, modify, distribute, and sell this software and its 605b261ecSmrgdocumentation for any purpose is hereby granted without fee, provided that 705b261ecSmrgthe above copyright notice appear in all copies and that both that 805b261ecSmrgcopyright notice and this permission notice appear in supporting 905b261ecSmrgdocumentation. 1005b261ecSmrg 1105b261ecSmrgThe above copyright notice and this permission notice shall be included in 1205b261ecSmrgall copies or substantial portions of the Software. 1305b261ecSmrg 1405b261ecSmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1505b261ecSmrgIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1605b261ecSmrgFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1705b261ecSmrgOPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 1805b261ecSmrgAN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 1905b261ecSmrgCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 2005b261ecSmrg 2105b261ecSmrgExcept as contained in this notice, the name of The Open Group shall not be 2205b261ecSmrgused in advertising or otherwise to promote the sale, use or other dealings 2305b261ecSmrgin this Software without prior written authorization from The Open Group. 2405b261ecSmrg 2505b261ecSmrgCopyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. 2605b261ecSmrg 2705b261ecSmrg All Rights Reserved 2805b261ecSmrg 2935c4bbdfSmrgPermission to use, copy, modify, and distribute this software and its 3035c4bbdfSmrgdocumentation for any purpose and without fee is hereby granted, 3105b261ecSmrgprovided that the above copyright notice appear in all copies and that 3235c4bbdfSmrgboth that copyright notice and this permission notice appear in 3305b261ecSmrgsupporting documentation, and that the name of Digital not be 3405b261ecSmrgused in advertising or publicity pertaining to distribution of the 3535c4bbdfSmrgsoftware without specific, written prior permission. 3605b261ecSmrg 3705b261ecSmrgDIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 3805b261ecSmrgALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 3905b261ecSmrgDIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 4005b261ecSmrgANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 4105b261ecSmrgWHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 4205b261ecSmrgARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 4305b261ecSmrgSOFTWARE. 4405b261ecSmrg 4505b261ecSmrg******************************************************************/ 4605b261ecSmrg 4705b261ecSmrg/* 4805b261ecSmrg 4905b261ecSmrg(c)Copyright 1988,1991 Adobe Systems Incorporated. All rights reserved. 5005b261ecSmrg 5105b261ecSmrgPermission to use, copy, modify, distribute, and sublicense this software and its 5205b261ecSmrgdocumentation for any purpose and without fee is hereby granted, provided that 5305b261ecSmrgthe above copyright notices appear in all copies and that both those copyright 5405b261ecSmrgnotices and this permission notice appear in supporting documentation and that 5505b261ecSmrgthe name of Adobe Systems Incorporated not be used in advertising or publicity 5605b261ecSmrgpertaining to distribution of the software without specific, written prior 5705b261ecSmrgpermission. No trademark license to use the Adobe trademarks is hereby 5805b261ecSmrggranted. If the Adobe trademark "Display PostScript"(tm) is used to describe 5905b261ecSmrgthis software, its functionality or for any other purpose, such use shall be 6005b261ecSmrglimited to a statement that this software works in conjunction with the Display 6105b261ecSmrgPostScript system. Proper trademark attribution to reflect Adobe's ownership 6205b261ecSmrgof the trademark shall be given whenever any such reference to the Display 6305b261ecSmrgPostScript system is made. 6405b261ecSmrg 6505b261ecSmrgADOBE MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THE SOFTWARE FOR ANY 6605b261ecSmrgPURPOSE. IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. ADOBE 6705b261ecSmrgDISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED 6805b261ecSmrgWARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON- 6905b261ecSmrgINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL ADOBE BE LIABLE TO YOU 7005b261ecSmrgOR ANY OTHER PARTY FOR ANY SPECIAL, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY 7105b261ecSmrgDAMAGES WHATSOEVER WHETHER IN AN ACTION OF CONTRACT,NEGLIGENCE, STRICT 7205b261ecSmrgLIABILITY OR ANY OTHER ACTION ARISING OUT OF OR IN CONNECTION WITH THE USE OR 7305b261ecSmrgPERFORMANCE OF THIS SOFTWARE. ADOBE WILL NOT PROVIDE ANY TRAINING OR OTHER 7405b261ecSmrgSUPPORT FOR THE SOFTWARE. 7505b261ecSmrg 7605b261ecSmrgAdobe, PostScript, and Display PostScript are trademarks of Adobe Systems 7705b261ecSmrgIncorporated which may be registered in certain jurisdictions. 7805b261ecSmrg 7905b261ecSmrgAuthor: Adobe Systems Incorporated 8005b261ecSmrg 8105b261ecSmrg*/ 8205b261ecSmrg 8305b261ecSmrg#ifdef HAVE_DIX_CONFIG_H 8405b261ecSmrg#include <dix-config.h> 8505b261ecSmrg#endif 8605b261ecSmrg 8705b261ecSmrg#include <X11/X.h> 8805b261ecSmrg#include <X11/Xmd.h> 8905b261ecSmrg#include "misc.h" 9005b261ecSmrg#include "windowstr.h" 9105b261ecSmrg#include "dixstruct.h" 9205b261ecSmrg#include "pixmapstr.h" 936747b715Smrg#include "gcstruct.h" 9405b261ecSmrg#include "scrnintstr.h" 9505b261ecSmrg#define XK_LATIN1 9605b261ecSmrg#include <X11/keysymdef.h> 9705b261ecSmrg#include "xace.h" 9805b261ecSmrg 9905b261ecSmrg/* 10005b261ecSmrg * CompareTimeStamps returns -1, 0, or +1 depending on if the first 10105b261ecSmrg * argument is less than, equal to or greater than the second argument. 10205b261ecSmrg */ 10305b261ecSmrg 1046747b715Smrgint 10505b261ecSmrgCompareTimeStamps(TimeStamp a, TimeStamp b) 10605b261ecSmrg{ 10705b261ecSmrg if (a.months < b.months) 10835c4bbdfSmrg return EARLIER; 10905b261ecSmrg if (a.months > b.months) 11035c4bbdfSmrg return LATER; 11105b261ecSmrg if (a.milliseconds < b.milliseconds) 11235c4bbdfSmrg return EARLIER; 11305b261ecSmrg if (a.milliseconds > b.milliseconds) 11435c4bbdfSmrg return LATER; 11505b261ecSmrg return SAMETIME; 11605b261ecSmrg} 11705b261ecSmrg 11805b261ecSmrg/* 11905b261ecSmrg * convert client times to server TimeStamps 12005b261ecSmrg */ 12105b261ecSmrg 12205b261ecSmrg#define HALFMONTH ((unsigned long) 1<<31) 1236747b715SmrgTimeStamp 12405b261ecSmrgClientTimeToServerTime(CARD32 c) 12505b261ecSmrg{ 12605b261ecSmrg TimeStamp ts; 12735c4bbdfSmrg 12805b261ecSmrg if (c == CurrentTime) 12935c4bbdfSmrg return currentTime; 13005b261ecSmrg ts.months = currentTime.months; 13105b261ecSmrg ts.milliseconds = c; 13235c4bbdfSmrg if (c > currentTime.milliseconds) { 13335c4bbdfSmrg if (((unsigned long) c - currentTime.milliseconds) > HALFMONTH) 13435c4bbdfSmrg ts.months -= 1; 13505b261ecSmrg } 13635c4bbdfSmrg else if (c < currentTime.milliseconds) { 13735c4bbdfSmrg if (((unsigned long) currentTime.milliseconds - c) > HALFMONTH) 13835c4bbdfSmrg ts.months += 1; 13905b261ecSmrg } 14005b261ecSmrg return ts; 14105b261ecSmrg} 14205b261ecSmrg 14305b261ecSmrg/* 14405b261ecSmrg * ISO Latin-1 case conversion routine 14505b261ecSmrg * 14605b261ecSmrg * this routine always null-terminates the result, so 14705b261ecSmrg * beware of too-small buffers 14805b261ecSmrg */ 14905b261ecSmrg 15005b261ecSmrgstatic unsigned char 15135c4bbdfSmrgISOLatin1ToLower(unsigned char source) 15205b261ecSmrg{ 15335c4bbdfSmrg unsigned char dest; 15435c4bbdfSmrg 15505b261ecSmrg if ((source >= XK_A) && (source <= XK_Z)) 15635c4bbdfSmrg dest = source + (XK_a - XK_A); 15705b261ecSmrg else if ((source >= XK_Agrave) && (source <= XK_Odiaeresis)) 15835c4bbdfSmrg dest = source + (XK_agrave - XK_Agrave); 15905b261ecSmrg else if ((source >= XK_Ooblique) && (source <= XK_Thorn)) 16035c4bbdfSmrg dest = source + (XK_oslash - XK_Ooblique); 16105b261ecSmrg else 16235c4bbdfSmrg dest = source; 16305b261ecSmrg return dest; 16405b261ecSmrg} 16505b261ecSmrg 16605b261ecSmrgint 16735c4bbdfSmrgCompareISOLatin1Lowered(const unsigned char *s1, int s1len, 16835c4bbdfSmrg const unsigned char *s2, int s2len) 16935c4bbdfSmrg{ 17035c4bbdfSmrg unsigned char c1, c2; 17135c4bbdfSmrg 17235c4bbdfSmrg for (;;) { 17335c4bbdfSmrg /* note -- compare against zero so that -1 ignores len */ 17435c4bbdfSmrg c1 = s1len-- ? *s1++ : '\0'; 17535c4bbdfSmrg c2 = s2len-- ? *s2++ : '\0'; 17635c4bbdfSmrg if (!c1 || 17735c4bbdfSmrg (c1 != c2 && 17835c4bbdfSmrg (c1 = ISOLatin1ToLower(c1)) != (c2 = ISOLatin1ToLower(c2)))) 17935c4bbdfSmrg break; 18005b261ecSmrg } 18105b261ecSmrg return (int) c1 - (int) c2; 18205b261ecSmrg} 18305b261ecSmrg 18405b261ecSmrg/* 18505b261ecSmrg * dixLookupWindow and dixLookupDrawable: 18605b261ecSmrg * Look up the window/drawable taking into account the client doing the 18705b261ecSmrg * lookup, the type of drawable desired, and the type of access desired. 18805b261ecSmrg * Return Success with *pDraw set if the window/drawable exists and the client 18905b261ecSmrg * is allowed access, else return an error code with *pDraw set to NULL. The 19005b261ecSmrg * access mask values are defined in resource.h. The type mask values are 19105b261ecSmrg * defined in pixmap.h, with zero equivalent to M_DRAWABLE. 19205b261ecSmrg */ 1936747b715Smrgint 19405b261ecSmrgdixLookupDrawable(DrawablePtr *pDraw, XID id, ClientPtr client, 19535c4bbdfSmrg Mask type, Mask access) 19605b261ecSmrg{ 19705b261ecSmrg DrawablePtr pTmp; 1984642e01fSmrg int rc; 1994642e01fSmrg 20005b261ecSmrg *pDraw = NULL; 20105b261ecSmrg 20235c4bbdfSmrg rc = dixLookupResourceByClass((void **) &pTmp, id, RC_DRAWABLE, client, 20335c4bbdfSmrg access); 2044642e01fSmrg 205475c125cSmrg if (rc != Success) 20635c4bbdfSmrg client->errorValue = id; 207475c125cSmrg 2084642e01fSmrg if (rc == BadValue) 20935c4bbdfSmrg return BadDrawable; 2104642e01fSmrg if (rc != Success) 21135c4bbdfSmrg return rc; 21205b261ecSmrg if (!((1 << pTmp->type) & (type ? type : M_DRAWABLE))) 21335c4bbdfSmrg return BadMatch; 21405b261ecSmrg 21505b261ecSmrg *pDraw = pTmp; 21605b261ecSmrg return Success; 21705b261ecSmrg} 21805b261ecSmrg 2196747b715Smrgint 22005b261ecSmrgdixLookupWindow(WindowPtr *pWin, XID id, ClientPtr client, Mask access) 22105b261ecSmrg{ 22205b261ecSmrg int rc; 22335c4bbdfSmrg 22435c4bbdfSmrg rc = dixLookupDrawable((DrawablePtr *) pWin, id, client, M_WINDOW, access); 22535c4bbdfSmrg /* dixLookupDrawable returns BadMatch iff id is a valid Drawable 22635c4bbdfSmrg but is not a Window. Users of dixLookupWindow expect a BadWindow 22735c4bbdfSmrg error in this case; they don't care that it's a valid non-Window XID */ 22835c4bbdfSmrg if (rc == BadMatch) 22935c4bbdfSmrg rc = BadWindow; 23035c4bbdfSmrg /* Similarly, users of dixLookupWindow don't want BadDrawable. */ 23135c4bbdfSmrg if (rc == BadDrawable) 23235c4bbdfSmrg rc = BadWindow; 23335c4bbdfSmrg return rc; 23405b261ecSmrg} 23505b261ecSmrg 2366747b715Smrgint 23705b261ecSmrgdixLookupGC(GCPtr *pGC, XID id, ClientPtr client, Mask access) 23805b261ecSmrg{ 23935c4bbdfSmrg return dixLookupResourceByType((void **) pGC, id, RT_GC, client, access); 24005b261ecSmrg} 24105b261ecSmrg 2426747b715Smrgint 2436747b715SmrgdixLookupFontable(FontPtr *pFont, XID id, ClientPtr client, Mask access) 2446747b715Smrg{ 2456747b715Smrg int rc; 2466747b715Smrg GC *pGC; 24735c4bbdfSmrg 24835c4bbdfSmrg client->errorValue = id; /* EITHER font or gc */ 24935c4bbdfSmrg rc = dixLookupResourceByType((void **) pFont, id, RT_FONT, client, 25035c4bbdfSmrg access); 2516747b715Smrg if (rc != BadFont) 25235c4bbdfSmrg return rc; 25335c4bbdfSmrg rc = dixLookupResourceByType((void **) &pGC, id, RT_GC, client, access); 2546747b715Smrg if (rc == BadGC) 25535c4bbdfSmrg return BadFont; 2566747b715Smrg if (rc == Success) 25735c4bbdfSmrg *pFont = pGC->font; 2586747b715Smrg return rc; 2596747b715Smrg} 2606747b715Smrg 2616747b715Smrgint 26205b261ecSmrgdixLookupClient(ClientPtr *pClient, XID rid, ClientPtr client, Mask access) 26305b261ecSmrg{ 26435c4bbdfSmrg void *pRes; 2654642e01fSmrg int rc = BadValue, clientIndex = CLIENT_ID(rid); 26605b261ecSmrg 2674642e01fSmrg if (!clientIndex || !clients[clientIndex] || (rid & SERVER_BIT)) 26835c4bbdfSmrg goto bad; 26905b261ecSmrg 270b86d567bSmrg rc = dixLookupResourceByClass(&pRes, rid, RC_ANY, client, DixGetAttrAccess); 2714642e01fSmrg if (rc != Success) 27235c4bbdfSmrg goto bad; 27305b261ecSmrg 2744642e01fSmrg rc = XaceHook(XACE_CLIENT_ACCESS, client, clients[clientIndex], access); 2754642e01fSmrg if (rc != Success) 27635c4bbdfSmrg goto bad; 27705b261ecSmrg 2784642e01fSmrg *pClient = clients[clientIndex]; 2794642e01fSmrg return Success; 28035c4bbdfSmrg bad: 28135c4bbdfSmrg if (client) 2824642e01fSmrg client->errorValue = rid; 2834642e01fSmrg *pClient = NULL; 2844642e01fSmrg return rc; 28505b261ecSmrg} 28605b261ecSmrg 28705b261ecSmrgint 28805b261ecSmrgAlterSaveSetForClient(ClientPtr client, WindowPtr pWin, unsigned mode, 2894642e01fSmrg Bool toRoot, Bool map) 29005b261ecSmrg{ 29105b261ecSmrg int numnow; 29205b261ecSmrg SaveSetElt *pTmp = NULL; 29305b261ecSmrg int j; 29405b261ecSmrg 29505b261ecSmrg numnow = client->numSaved; 29605b261ecSmrg j = 0; 29735c4bbdfSmrg if (numnow) { 29835c4bbdfSmrg pTmp = client->saveSet; 29935c4bbdfSmrg while ((j < numnow) && (SaveSetWindow(pTmp[j]) != (void *) pWin)) 30035c4bbdfSmrg j++; 30105b261ecSmrg } 30235c4bbdfSmrg if (mode == SetModeInsert) { 30335c4bbdfSmrg if (j < numnow) /* duplicate */ 30435c4bbdfSmrg return Success; 30535c4bbdfSmrg numnow++; 30635c4bbdfSmrg pTmp = (SaveSetElt *) realloc(client->saveSet, sizeof(*pTmp) * numnow); 30735c4bbdfSmrg if (!pTmp) 30835c4bbdfSmrg return BadAlloc; 30935c4bbdfSmrg client->saveSet = pTmp; 31035c4bbdfSmrg client->numSaved = numnow; 31135c4bbdfSmrg SaveSetAssignWindow(client->saveSet[numnow - 1], pWin); 31235c4bbdfSmrg SaveSetAssignToRoot(client->saveSet[numnow - 1], toRoot); 31335c4bbdfSmrg SaveSetAssignMap(client->saveSet[numnow - 1], map); 31435c4bbdfSmrg return Success; 31505b261ecSmrg } 31635c4bbdfSmrg else if ((mode == SetModeDelete) && (j < numnow)) { 31735c4bbdfSmrg while (j < numnow - 1) { 31835c4bbdfSmrg pTmp[j] = pTmp[j + 1]; 31935c4bbdfSmrg j++; 32035c4bbdfSmrg } 32135c4bbdfSmrg numnow--; 32235c4bbdfSmrg if (numnow) { 32335c4bbdfSmrg pTmp = 32435c4bbdfSmrg (SaveSetElt *) realloc(client->saveSet, sizeof(*pTmp) * numnow); 32535c4bbdfSmrg if (pTmp) 32635c4bbdfSmrg client->saveSet = pTmp; 32735c4bbdfSmrg } 32835c4bbdfSmrg else { 3296747b715Smrg free(client->saveSet); 33035c4bbdfSmrg client->saveSet = (SaveSetElt *) NULL; 33135c4bbdfSmrg } 33235c4bbdfSmrg client->numSaved = numnow; 33335c4bbdfSmrg return Success; 33405b261ecSmrg } 3356747b715Smrg return Success; 33605b261ecSmrg} 33705b261ecSmrg 33805b261ecSmrgvoid 33905b261ecSmrgDeleteWindowFromAnySaveSet(WindowPtr pWin) 34005b261ecSmrg{ 34105b261ecSmrg int i; 34205b261ecSmrg ClientPtr client; 34335c4bbdfSmrg 34435c4bbdfSmrg for (i = 0; i < currentMaxClients; i++) { 34535c4bbdfSmrg client = clients[i]; 34635c4bbdfSmrg if (client && client->numSaved) 34735c4bbdfSmrg (void) AlterSaveSetForClient(client, pWin, SetModeDelete, FALSE, 34835c4bbdfSmrg TRUE); 34905b261ecSmrg } 35005b261ecSmrg} 35105b261ecSmrg 35205b261ecSmrg/* No-op Don't Do Anything : sometimes we need to be able to call a procedure 35305b261ecSmrg * that doesn't do anything. For example, on screen with only static 35405b261ecSmrg * colormaps, if someone calls install colormap, it's easier to have a dummy 35535c4bbdfSmrg * procedure to call than to check if there's a procedure 35605b261ecSmrg */ 3576747b715Smrgvoid 35805b261ecSmrgNoopDDA(void) 35905b261ecSmrg{ 36005b261ecSmrg} 36105b261ecSmrg 36205b261ecSmrgtypedef struct _BlockHandler { 3631b5d61b8Smrg ServerBlockHandlerProcPtr BlockHandler; 3641b5d61b8Smrg ServerWakeupHandlerProcPtr WakeupHandler; 36535c4bbdfSmrg void *blockData; 36635c4bbdfSmrg Bool deleted; 36705b261ecSmrg} BlockHandlerRec, *BlockHandlerPtr; 36805b261ecSmrg 36935c4bbdfSmrgstatic BlockHandlerPtr handlers; 37035c4bbdfSmrgstatic int numHandlers; 37135c4bbdfSmrgstatic int sizeHandlers; 37235c4bbdfSmrgstatic Bool inHandler; 37335c4bbdfSmrgstatic Bool handlerDeleted; 37405b261ecSmrg 37505b261ecSmrg/** 37635c4bbdfSmrg * 37705b261ecSmrg * \param pTimeout DIX doesn't want to know how OS represents time 37805b261ecSmrg * \param pReadMask nor how it represents the det of descriptors 37905b261ecSmrg */ 38005b261ecSmrgvoid 3811b5d61b8SmrgBlockHandler(void *pTimeout) 38205b261ecSmrg{ 38305b261ecSmrg int i, j; 38435c4bbdfSmrg 38505b261ecSmrg ++inHandler; 38605b261ecSmrg for (i = 0; i < numHandlers; i++) 38735c4bbdfSmrg if (!handlers[i].deleted) 3881b5d61b8Smrg (*handlers[i].BlockHandler) (handlers[i].blockData, pTimeout); 3891b5d61b8Smrg 3901b5d61b8Smrg for (i = 0; i < screenInfo.numGPUScreens; i++) 3911b5d61b8Smrg (*screenInfo.gpuscreens[i]->BlockHandler) (screenInfo.gpuscreens[i], pTimeout); 3921b5d61b8Smrg 3931b5d61b8Smrg for (i = 0; i < screenInfo.numScreens; i++) 3941b5d61b8Smrg (*screenInfo.screens[i]->BlockHandler) (screenInfo.screens[i], pTimeout); 3951b5d61b8Smrg 39635c4bbdfSmrg if (handlerDeleted) { 39735c4bbdfSmrg for (i = 0; i < numHandlers;) 39835c4bbdfSmrg if (handlers[i].deleted) { 39935c4bbdfSmrg for (j = i; j < numHandlers - 1; j++) 40035c4bbdfSmrg handlers[j] = handlers[j + 1]; 40135c4bbdfSmrg numHandlers--; 40235c4bbdfSmrg } 40335c4bbdfSmrg else 40435c4bbdfSmrg i++; 40535c4bbdfSmrg handlerDeleted = FALSE; 40605b261ecSmrg } 40705b261ecSmrg --inHandler; 40805b261ecSmrg} 40905b261ecSmrg 41005b261ecSmrg/** 41105b261ecSmrg * 41205b261ecSmrg * \param result 32 bits of undefined result from the wait 41305b261ecSmrg * \param pReadmask the resulting descriptor mask 41405b261ecSmrg */ 41505b261ecSmrgvoid 4161b5d61b8SmrgWakeupHandler(int result) 41705b261ecSmrg{ 41805b261ecSmrg int i, j; 41905b261ecSmrg 42005b261ecSmrg ++inHandler; 42105b261ecSmrg for (i = 0; i < screenInfo.numScreens; i++) 4221b5d61b8Smrg (*screenInfo.screens[i]->WakeupHandler) (screenInfo.screens[i], result); 42335c4bbdfSmrg for (i = 0; i < screenInfo.numGPUScreens; i++) 4241b5d61b8Smrg (*screenInfo.gpuscreens[i]->WakeupHandler) (screenInfo.gpuscreens[i], result); 4251b5d61b8Smrg for (i = numHandlers - 1; i >= 0; i--) 4261b5d61b8Smrg if (!handlers[i].deleted) 4271b5d61b8Smrg (*handlers[i].WakeupHandler) (handlers[i].blockData, result); 42835c4bbdfSmrg if (handlerDeleted) { 42935c4bbdfSmrg for (i = 0; i < numHandlers;) 43035c4bbdfSmrg if (handlers[i].deleted) { 43135c4bbdfSmrg for (j = i; j < numHandlers - 1; j++) 43235c4bbdfSmrg handlers[j] = handlers[j + 1]; 43335c4bbdfSmrg numHandlers--; 43435c4bbdfSmrg } 43535c4bbdfSmrg else 43635c4bbdfSmrg i++; 43735c4bbdfSmrg handlerDeleted = FALSE; 43805b261ecSmrg } 43905b261ecSmrg --inHandler; 44005b261ecSmrg} 44105b261ecSmrg 44205b261ecSmrg/** 44305b261ecSmrg * Reentrant with BlockHandler and WakeupHandler, except wakeup won't 44405b261ecSmrg * get called until next time 44505b261ecSmrg */ 4466747b715SmrgBool 4471b5d61b8SmrgRegisterBlockAndWakeupHandlers(ServerBlockHandlerProcPtr blockHandler, 4481b5d61b8Smrg ServerWakeupHandlerProcPtr wakeupHandler, 44935c4bbdfSmrg void *blockData) 45005b261ecSmrg{ 45105b261ecSmrg BlockHandlerPtr new; 45205b261ecSmrg 45335c4bbdfSmrg if (numHandlers >= sizeHandlers) { 4546747b715Smrg new = (BlockHandlerPtr) realloc(handlers, (numHandlers + 1) * 45535c4bbdfSmrg sizeof(BlockHandlerRec)); 45635c4bbdfSmrg if (!new) 45735c4bbdfSmrg return FALSE; 45835c4bbdfSmrg handlers = new; 45935c4bbdfSmrg sizeHandlers = numHandlers + 1; 46005b261ecSmrg } 46105b261ecSmrg handlers[numHandlers].BlockHandler = blockHandler; 46205b261ecSmrg handlers[numHandlers].WakeupHandler = wakeupHandler; 46305b261ecSmrg handlers[numHandlers].blockData = blockData; 46405b261ecSmrg handlers[numHandlers].deleted = FALSE; 46505b261ecSmrg numHandlers = numHandlers + 1; 46605b261ecSmrg return TRUE; 46705b261ecSmrg} 46805b261ecSmrg 4696747b715Smrgvoid 4701b5d61b8SmrgRemoveBlockAndWakeupHandlers(ServerBlockHandlerProcPtr blockHandler, 4711b5d61b8Smrg ServerWakeupHandlerProcPtr wakeupHandler, 47235c4bbdfSmrg void *blockData) 47305b261ecSmrg{ 47435c4bbdfSmrg int i; 47505b261ecSmrg 47605b261ecSmrg for (i = 0; i < numHandlers; i++) 47735c4bbdfSmrg if (handlers[i].BlockHandler == blockHandler && 47835c4bbdfSmrg handlers[i].WakeupHandler == wakeupHandler && 47935c4bbdfSmrg handlers[i].blockData == blockData) { 48035c4bbdfSmrg if (inHandler) { 48135c4bbdfSmrg handlerDeleted = TRUE; 48235c4bbdfSmrg handlers[i].deleted = TRUE; 48335c4bbdfSmrg } 48435c4bbdfSmrg else { 48535c4bbdfSmrg for (; i < numHandlers - 1; i++) 48635c4bbdfSmrg handlers[i] = handlers[i + 1]; 48735c4bbdfSmrg numHandlers--; 48835c4bbdfSmrg } 48935c4bbdfSmrg break; 49035c4bbdfSmrg } 49105b261ecSmrg} 49205b261ecSmrg 49305b261ecSmrgvoid 49435c4bbdfSmrgInitBlockAndWakeupHandlers(void) 49505b261ecSmrg{ 4966747b715Smrg free(handlers); 49705b261ecSmrg handlers = (BlockHandlerPtr) 0; 49805b261ecSmrg numHandlers = 0; 49905b261ecSmrg sizeHandlers = 0; 50005b261ecSmrg} 50105b261ecSmrg 50205b261ecSmrg/* 50305b261ecSmrg * A general work queue. Perform some task before the server 50405b261ecSmrg * sleeps for input. 50505b261ecSmrg */ 50605b261ecSmrg 50735c4bbdfSmrgWorkQueuePtr workQueue; 50835c4bbdfSmrgstatic WorkQueuePtr *workQueueLast = &workQueue; 50905b261ecSmrg 51005b261ecSmrgvoid 51105b261ecSmrgProcessWorkQueue(void) 51205b261ecSmrg{ 51335c4bbdfSmrg WorkQueuePtr q, *p; 51405b261ecSmrg 51505b261ecSmrg p = &workQueue; 51605b261ecSmrg /* 51705b261ecSmrg * Scan the work queue once, calling each function. Those 51805b261ecSmrg * which return TRUE are removed from the queue, otherwise 51905b261ecSmrg * they will be called again. This must be reentrant with 52005b261ecSmrg * QueueWorkProc. 52105b261ecSmrg */ 52235c4bbdfSmrg while ((q = *p)) { 52335c4bbdfSmrg if ((*q->function) (q->client, q->closure)) { 52435c4bbdfSmrg /* remove q from the list */ 52535c4bbdfSmrg *p = q->next; /* don't fetch until after func called */ 52635c4bbdfSmrg free(q); 52735c4bbdfSmrg } 52835c4bbdfSmrg else { 52935c4bbdfSmrg p = &q->next; /* don't fetch until after func called */ 53035c4bbdfSmrg } 53105b261ecSmrg } 53205b261ecSmrg workQueueLast = p; 53305b261ecSmrg} 53405b261ecSmrg 53505b261ecSmrgvoid 53605b261ecSmrgProcessWorkQueueZombies(void) 53705b261ecSmrg{ 53835c4bbdfSmrg WorkQueuePtr q, *p; 53905b261ecSmrg 54005b261ecSmrg p = &workQueue; 54135c4bbdfSmrg while ((q = *p)) { 54235c4bbdfSmrg if (q->client && q->client->clientGone) { 54335c4bbdfSmrg (void) (*q->function) (q->client, q->closure); 54435c4bbdfSmrg /* remove q from the list */ 54535c4bbdfSmrg *p = q->next; /* don't fetch until after func called */ 54635c4bbdfSmrg free(q); 54735c4bbdfSmrg } 54835c4bbdfSmrg else { 54935c4bbdfSmrg p = &q->next; /* don't fetch until after func called */ 55035c4bbdfSmrg } 55105b261ecSmrg } 55205b261ecSmrg workQueueLast = p; 55305b261ecSmrg} 55405b261ecSmrg 5556747b715SmrgBool 55635c4bbdfSmrgQueueWorkProc(Bool (*function) (ClientPtr pClient, void *closure), 55735c4bbdfSmrg ClientPtr client, void *closure) 55805b261ecSmrg{ 55935c4bbdfSmrg WorkQueuePtr q; 56005b261ecSmrg 5616747b715Smrg q = malloc(sizeof *q); 56205b261ecSmrg if (!q) 56335c4bbdfSmrg return FALSE; 56405b261ecSmrg q->function = function; 56505b261ecSmrg q->client = client; 56605b261ecSmrg q->closure = closure; 56705b261ecSmrg q->next = NULL; 56805b261ecSmrg *workQueueLast = q; 56905b261ecSmrg workQueueLast = &q->next; 57005b261ecSmrg return TRUE; 57105b261ecSmrg} 57205b261ecSmrg 57305b261ecSmrg/* 57405b261ecSmrg * Manage a queue of sleeping clients, awakening them 57505b261ecSmrg * when requested, by using the OS functions IgnoreClient 57605b261ecSmrg * and AttendClient. Note that this *ignores* the troubles 57705b261ecSmrg * with request data interleaving itself with events, but 57805b261ecSmrg * we'll leave that until a later time. 57905b261ecSmrg */ 58005b261ecSmrg 58105b261ecSmrgtypedef struct _SleepQueue { 58235c4bbdfSmrg struct _SleepQueue *next; 58335c4bbdfSmrg ClientPtr client; 58435c4bbdfSmrg ClientSleepProcPtr function; 58535c4bbdfSmrg void *closure; 58605b261ecSmrg} SleepQueueRec, *SleepQueuePtr; 58705b261ecSmrg 58835c4bbdfSmrgstatic SleepQueuePtr sleepQueue = NULL; 58905b261ecSmrg 5906747b715SmrgBool 59135c4bbdfSmrgClientSleep(ClientPtr client, ClientSleepProcPtr function, void *closure) 59205b261ecSmrg{ 59335c4bbdfSmrg SleepQueuePtr q; 59405b261ecSmrg 5956747b715Smrg q = malloc(sizeof *q); 59605b261ecSmrg if (!q) 59735c4bbdfSmrg return FALSE; 59805b261ecSmrg 59935c4bbdfSmrg IgnoreClient(client); 60005b261ecSmrg q->next = sleepQueue; 60105b261ecSmrg q->client = client; 60205b261ecSmrg q->function = function; 60305b261ecSmrg q->closure = closure; 60405b261ecSmrg sleepQueue = q; 60505b261ecSmrg return TRUE; 60605b261ecSmrg} 60705b261ecSmrg 60805b261ecSmrgBool 60935c4bbdfSmrgClientSignal(ClientPtr client) 61005b261ecSmrg{ 61135c4bbdfSmrg SleepQueuePtr q; 61205b261ecSmrg 61305b261ecSmrg for (q = sleepQueue; q; q = q->next) 61435c4bbdfSmrg if (q->client == client) { 61535c4bbdfSmrg return QueueWorkProc(q->function, q->client, q->closure); 61635c4bbdfSmrg } 61705b261ecSmrg return FALSE; 61805b261ecSmrg} 61905b261ecSmrg 62035c4bbdfSmrgint 62135c4bbdfSmrgClientSignalAll(ClientPtr client, ClientSleepProcPtr function, void *closure) 62235c4bbdfSmrg{ 62335c4bbdfSmrg SleepQueuePtr q; 62435c4bbdfSmrg int count = 0; 62535c4bbdfSmrg 62635c4bbdfSmrg for (q = sleepQueue; q; q = q->next) { 62735c4bbdfSmrg if (!(client == CLIENT_SIGNAL_ANY || q->client == client)) 62835c4bbdfSmrg continue; 62935c4bbdfSmrg 63035c4bbdfSmrg if (!(function == CLIENT_SIGNAL_ANY || q->function == function)) 63135c4bbdfSmrg continue; 63235c4bbdfSmrg 63335c4bbdfSmrg if (!(closure == CLIENT_SIGNAL_ANY || q->closure == closure)) 63435c4bbdfSmrg continue; 63535c4bbdfSmrg 63635c4bbdfSmrg count += QueueWorkProc(q->function, q->client, q->closure); 63735c4bbdfSmrg } 63835c4bbdfSmrg 63935c4bbdfSmrg return count; 64035c4bbdfSmrg} 64135c4bbdfSmrg 6426747b715Smrgvoid 64335c4bbdfSmrgClientWakeup(ClientPtr client) 64405b261ecSmrg{ 64535c4bbdfSmrg SleepQueuePtr q, *prev; 64605b261ecSmrg 64705b261ecSmrg prev = &sleepQueue; 64835c4bbdfSmrg while ((q = *prev)) { 64935c4bbdfSmrg if (q->client == client) { 65035c4bbdfSmrg *prev = q->next; 65135c4bbdfSmrg free(q); 65235c4bbdfSmrg if (client->clientGone) 65335c4bbdfSmrg /* Oops -- new zombie cleanup code ensures this only 65435c4bbdfSmrg * happens from inside CloseDownClient; don't want to 65535c4bbdfSmrg * recurse here... 65635c4bbdfSmrg */ 65735c4bbdfSmrg /* CloseDownClient(client) */ ; 65835c4bbdfSmrg else 65935c4bbdfSmrg AttendClient(client); 66035c4bbdfSmrg break; 66135c4bbdfSmrg } 66235c4bbdfSmrg prev = &q->next; 66305b261ecSmrg } 66405b261ecSmrg} 66505b261ecSmrg 66605b261ecSmrgBool 66735c4bbdfSmrgClientIsAsleep(ClientPtr client) 66805b261ecSmrg{ 66935c4bbdfSmrg SleepQueuePtr q; 67005b261ecSmrg 67105b261ecSmrg for (q = sleepQueue; q; q = q->next) 67235c4bbdfSmrg if (q->client == client) 67335c4bbdfSmrg return TRUE; 67405b261ecSmrg return FALSE; 67505b261ecSmrg} 67605b261ecSmrg 67705b261ecSmrg/* 67805b261ecSmrg * Generic Callback Manager 67905b261ecSmrg */ 68005b261ecSmrg 68105b261ecSmrg/* ===== Private Procedures ===== */ 68205b261ecSmrg 68305b261ecSmrgstatic int numCallbackListsToCleanup = 0; 68405b261ecSmrgstatic CallbackListPtr **listsToCleanup = NULL; 68505b261ecSmrg 6866747b715Smrgstatic Bool 68735c4bbdfSmrg_AddCallback(CallbackListPtr *pcbl, CallbackProcPtr callback, void *data) 68805b261ecSmrg{ 68935c4bbdfSmrg CallbackPtr cbr; 69005b261ecSmrg 6916747b715Smrg cbr = malloc(sizeof(CallbackRec)); 69205b261ecSmrg if (!cbr) 69335c4bbdfSmrg return FALSE; 69405b261ecSmrg cbr->proc = callback; 69505b261ecSmrg cbr->data = data; 69605b261ecSmrg cbr->next = (*pcbl)->list; 69705b261ecSmrg cbr->deleted = FALSE; 69805b261ecSmrg (*pcbl)->list = cbr; 69905b261ecSmrg return TRUE; 70005b261ecSmrg} 70105b261ecSmrg 70235c4bbdfSmrgstatic Bool 70335c4bbdfSmrg_DeleteCallback(CallbackListPtr *pcbl, CallbackProcPtr callback, void *data) 70405b261ecSmrg{ 70505b261ecSmrg CallbackListPtr cbl = *pcbl; 70635c4bbdfSmrg CallbackPtr cbr, pcbr; 70735c4bbdfSmrg 70835c4bbdfSmrg for (pcbr = NULL, cbr = cbl->list; cbr != NULL; pcbr = cbr, cbr = cbr->next) { 70935c4bbdfSmrg if ((cbr->proc == callback) && (cbr->data == data)) 71035c4bbdfSmrg break; 71105b261ecSmrg } 71235c4bbdfSmrg if (cbr != NULL) { 71335c4bbdfSmrg if (cbl->inCallback) { 71435c4bbdfSmrg ++(cbl->numDeleted); 71535c4bbdfSmrg cbr->deleted = TRUE; 71635c4bbdfSmrg } 71735c4bbdfSmrg else { 71835c4bbdfSmrg if (pcbr == NULL) 71935c4bbdfSmrg cbl->list = cbr->next; 72035c4bbdfSmrg else 72135c4bbdfSmrg pcbr->next = cbr->next; 72235c4bbdfSmrg free(cbr); 72335c4bbdfSmrg } 72435c4bbdfSmrg return TRUE; 72505b261ecSmrg } 72605b261ecSmrg return FALSE; 72705b261ecSmrg} 72805b261ecSmrg 72935c4bbdfSmrgvoid 73035c4bbdfSmrg_CallCallbacks(CallbackListPtr *pcbl, void *call_data) 73105b261ecSmrg{ 73205b261ecSmrg CallbackListPtr cbl = *pcbl; 73335c4bbdfSmrg CallbackPtr cbr, pcbr; 73405b261ecSmrg 73505b261ecSmrg ++(cbl->inCallback); 73635c4bbdfSmrg for (cbr = cbl->list; cbr != NULL; cbr = cbr->next) { 73735c4bbdfSmrg (*(cbr->proc)) (pcbl, cbr->data, call_data); 73805b261ecSmrg } 73905b261ecSmrg --(cbl->inCallback); 74005b261ecSmrg 74135c4bbdfSmrg if (cbl->inCallback) 74235c4bbdfSmrg return; 74305b261ecSmrg 74405b261ecSmrg /* Was the entire list marked for deletion? */ 74505b261ecSmrg 74635c4bbdfSmrg if (cbl->deleted) { 74735c4bbdfSmrg DeleteCallbackList(pcbl); 74835c4bbdfSmrg return; 74905b261ecSmrg } 75005b261ecSmrg 75105b261ecSmrg /* Were some individual callbacks on the list marked for deletion? 75205b261ecSmrg * If so, do the deletions. 75305b261ecSmrg */ 75405b261ecSmrg 75535c4bbdfSmrg if (cbl->numDeleted) { 75635c4bbdfSmrg for (pcbr = NULL, cbr = cbl->list; (cbr != NULL) && cbl->numDeleted;) { 75735c4bbdfSmrg if (cbr->deleted) { 75835c4bbdfSmrg if (pcbr) { 75935c4bbdfSmrg cbr = cbr->next; 76035c4bbdfSmrg free(pcbr->next); 76135c4bbdfSmrg pcbr->next = cbr; 76235c4bbdfSmrg } 76335c4bbdfSmrg else { 76435c4bbdfSmrg cbr = cbr->next; 76535c4bbdfSmrg free(cbl->list); 76635c4bbdfSmrg cbl->list = cbr; 76735c4bbdfSmrg } 76835c4bbdfSmrg cbl->numDeleted--; 76935c4bbdfSmrg } 77035c4bbdfSmrg else { /* this one wasn't deleted */ 77135c4bbdfSmrg 77235c4bbdfSmrg pcbr = cbr; 77335c4bbdfSmrg cbr = cbr->next; 77435c4bbdfSmrg } 77535c4bbdfSmrg } 77605b261ecSmrg } 77705b261ecSmrg} 77805b261ecSmrg 77905b261ecSmrgstatic void 78035c4bbdfSmrg_DeleteCallbackList(CallbackListPtr *pcbl) 78105b261ecSmrg{ 78205b261ecSmrg CallbackListPtr cbl = *pcbl; 78335c4bbdfSmrg CallbackPtr cbr, nextcbr; 78405b261ecSmrg int i; 78505b261ecSmrg 78635c4bbdfSmrg if (cbl->inCallback) { 78735c4bbdfSmrg cbl->deleted = TRUE; 78835c4bbdfSmrg return; 78905b261ecSmrg } 79005b261ecSmrg 79135c4bbdfSmrg for (i = 0; i < numCallbackListsToCleanup; i++) { 79235c4bbdfSmrg if (listsToCleanup[i] == pcbl) { 79335c4bbdfSmrg listsToCleanup[i] = NULL; 79435c4bbdfSmrg break; 79535c4bbdfSmrg } 79605b261ecSmrg } 79705b261ecSmrg 79835c4bbdfSmrg for (cbr = cbl->list; cbr != NULL; cbr = nextcbr) { 79935c4bbdfSmrg nextcbr = cbr->next; 80035c4bbdfSmrg free(cbr); 80105b261ecSmrg } 8026747b715Smrg free(cbl); 80305b261ecSmrg *pcbl = NULL; 80405b261ecSmrg} 80505b261ecSmrg 80605b261ecSmrgstatic Bool 8074642e01fSmrgCreateCallbackList(CallbackListPtr *pcbl) 80805b261ecSmrg{ 80935c4bbdfSmrg CallbackListPtr cbl; 81005b261ecSmrg int i; 81105b261ecSmrg 81235c4bbdfSmrg if (!pcbl) 81335c4bbdfSmrg return FALSE; 8146747b715Smrg cbl = malloc(sizeof(CallbackListRec)); 81535c4bbdfSmrg if (!cbl) 81635c4bbdfSmrg return FALSE; 81705b261ecSmrg cbl->inCallback = 0; 81805b261ecSmrg cbl->deleted = FALSE; 81905b261ecSmrg cbl->numDeleted = 0; 82005b261ecSmrg cbl->list = NULL; 82105b261ecSmrg *pcbl = cbl; 82205b261ecSmrg 82335c4bbdfSmrg for (i = 0; i < numCallbackListsToCleanup; i++) { 82435c4bbdfSmrg if (!listsToCleanup[i]) { 82535c4bbdfSmrg listsToCleanup[i] = pcbl; 82635c4bbdfSmrg return TRUE; 82735c4bbdfSmrg } 82805b261ecSmrg } 82905b261ecSmrg 83035c4bbdfSmrg listsToCleanup = (CallbackListPtr **) xnfrealloc(listsToCleanup, 83135c4bbdfSmrg sizeof(CallbackListPtr *) * 83235c4bbdfSmrg (numCallbackListsToCleanup 83335c4bbdfSmrg + 1)); 83405b261ecSmrg listsToCleanup[numCallbackListsToCleanup] = pcbl; 83505b261ecSmrg numCallbackListsToCleanup++; 83605b261ecSmrg return TRUE; 83705b261ecSmrg} 83805b261ecSmrg 83905b261ecSmrg/* ===== Public Procedures ===== */ 84005b261ecSmrg 8416747b715SmrgBool 84235c4bbdfSmrgAddCallback(CallbackListPtr *pcbl, CallbackProcPtr callback, void *data) 84305b261ecSmrg{ 84435c4bbdfSmrg if (!pcbl) 84535c4bbdfSmrg return FALSE; 84635c4bbdfSmrg if (!*pcbl) { /* list hasn't been created yet; go create it */ 84735c4bbdfSmrg if (!CreateCallbackList(pcbl)) 84835c4bbdfSmrg return FALSE; 84905b261ecSmrg } 8504642e01fSmrg return _AddCallback(pcbl, callback, data); 85105b261ecSmrg} 85205b261ecSmrg 8536747b715SmrgBool 85435c4bbdfSmrgDeleteCallback(CallbackListPtr *pcbl, CallbackProcPtr callback, void *data) 85505b261ecSmrg{ 85635c4bbdfSmrg if (!pcbl || !*pcbl) 85735c4bbdfSmrg return FALSE; 8584642e01fSmrg return _DeleteCallback(pcbl, callback, data); 85905b261ecSmrg} 86005b261ecSmrg 86105b261ecSmrgvoid 86205b261ecSmrgDeleteCallbackList(CallbackListPtr *pcbl) 86305b261ecSmrg{ 86435c4bbdfSmrg if (!pcbl || !*pcbl) 86535c4bbdfSmrg return; 8664642e01fSmrg _DeleteCallbackList(pcbl); 86705b261ecSmrg} 86805b261ecSmrg 8696747b715Smrgvoid 87035c4bbdfSmrgDeleteCallbackManager(void) 87105b261ecSmrg{ 87205b261ecSmrg int i; 87305b261ecSmrg 87435c4bbdfSmrg for (i = 0; i < numCallbackListsToCleanup; i++) { 87535c4bbdfSmrg DeleteCallbackList(listsToCleanup[i]); 87605b261ecSmrg } 8776747b715Smrg free(listsToCleanup); 87805b261ecSmrg 87905b261ecSmrg numCallbackListsToCleanup = 0; 88005b261ecSmrg listsToCleanup = NULL; 88105b261ecSmrg} 88235c4bbdfSmrg 88335c4bbdfSmrgvoid 88435c4bbdfSmrgInitCallbackManager(void) 88535c4bbdfSmrg{ 88635c4bbdfSmrg DeleteCallbackManager(); 88735c4bbdfSmrg} 88835c4bbdfSmrg 88935c4bbdfSmrg/** 89035c4bbdfSmrg * Coordinates the global GL context used by modules in the X Server 89135c4bbdfSmrg * doing rendering with OpenGL. 89235c4bbdfSmrg * 89335c4bbdfSmrg * When setting a GL context (glXMakeCurrent() or eglMakeCurrent()), 89435c4bbdfSmrg * there is an expensive implied glFlush() required by the GLX and EGL 89535c4bbdfSmrg * APIs, so modules don't want to have to do it on every request. But 89635c4bbdfSmrg * the individual modules using GL also don't know about each other, 89735c4bbdfSmrg * so they have to coordinate who owns the current context. 89835c4bbdfSmrg * 89935c4bbdfSmrg * When you're about to do a MakeCurrent, you should set this variable 90035c4bbdfSmrg * to your context's address, and you can skip MakeCurrent if it's 90135c4bbdfSmrg * already set to yours. 90235c4bbdfSmrg * 90335c4bbdfSmrg * When you're about to do a DestroyContext, you should set this to 90435c4bbdfSmrg * NULL if it's set to your context. 90535c4bbdfSmrg * 90635c4bbdfSmrg * When you're about to do an unbindContext on a DRI driver, you 90735c4bbdfSmrg * should set this to NULL. Despite the unbindContext interface 90835c4bbdfSmrg * sounding like it only unbinds the passed in context, it actually 90935c4bbdfSmrg * unconditionally clears the dispatch table even if the given 91035c4bbdfSmrg * context wasn't current. 91135c4bbdfSmrg */ 91235c4bbdfSmrgvoid *lastGLContext = NULL; 913