dixutils.c revision 35c4bbdf
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 { 36305b261ecSmrg BlockHandlerProcPtr BlockHandler; 36405b261ecSmrg WakeupHandlerProcPtr 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 38135c4bbdfSmrgBlockHandler(void *pTimeout, void *pReadmask) 38205b261ecSmrg{ 38305b261ecSmrg int i, j; 38435c4bbdfSmrg 38505b261ecSmrg ++inHandler; 38605b261ecSmrg for (i = 0; i < screenInfo.numScreens; i++) 38735c4bbdfSmrg (*screenInfo.screens[i]->BlockHandler) (screenInfo.screens[i], 38835c4bbdfSmrg pTimeout, pReadmask); 38935c4bbdfSmrg for (i = 0; i < screenInfo.numGPUScreens; i++) 39035c4bbdfSmrg (*screenInfo.gpuscreens[i]->BlockHandler) (screenInfo.gpuscreens[i], 39135c4bbdfSmrg pTimeout, pReadmask); 39205b261ecSmrg for (i = 0; i < numHandlers; i++) 39335c4bbdfSmrg if (!handlers[i].deleted) 39435c4bbdfSmrg (*handlers[i].BlockHandler) (handlers[i].blockData, 39535c4bbdfSmrg pTimeout, pReadmask); 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 41635c4bbdfSmrgWakeupHandler(int result, void *pReadmask) 41705b261ecSmrg{ 41805b261ecSmrg int i, j; 41905b261ecSmrg 42005b261ecSmrg ++inHandler; 42105b261ecSmrg for (i = numHandlers - 1; i >= 0; i--) 42235c4bbdfSmrg if (!handlers[i].deleted) 42335c4bbdfSmrg (*handlers[i].WakeupHandler) (handlers[i].blockData, 42435c4bbdfSmrg result, pReadmask); 42505b261ecSmrg for (i = 0; i < screenInfo.numScreens; i++) 42635c4bbdfSmrg (*screenInfo.screens[i]->WakeupHandler) (screenInfo.screens[i], 42735c4bbdfSmrg result, pReadmask); 42835c4bbdfSmrg for (i = 0; i < screenInfo.numGPUScreens; i++) 42935c4bbdfSmrg (*screenInfo.gpuscreens[i]->WakeupHandler) (screenInfo.gpuscreens[i], 43035c4bbdfSmrg result, pReadmask); 43135c4bbdfSmrg if (handlerDeleted) { 43235c4bbdfSmrg for (i = 0; i < numHandlers;) 43335c4bbdfSmrg if (handlers[i].deleted) { 43435c4bbdfSmrg for (j = i; j < numHandlers - 1; j++) 43535c4bbdfSmrg handlers[j] = handlers[j + 1]; 43635c4bbdfSmrg numHandlers--; 43735c4bbdfSmrg } 43835c4bbdfSmrg else 43935c4bbdfSmrg i++; 44035c4bbdfSmrg handlerDeleted = FALSE; 44105b261ecSmrg } 44205b261ecSmrg --inHandler; 44305b261ecSmrg} 44405b261ecSmrg 44505b261ecSmrg/** 44605b261ecSmrg * Reentrant with BlockHandler and WakeupHandler, except wakeup won't 44705b261ecSmrg * get called until next time 44805b261ecSmrg */ 4496747b715SmrgBool 45035c4bbdfSmrgRegisterBlockAndWakeupHandlers(BlockHandlerProcPtr blockHandler, 45135c4bbdfSmrg WakeupHandlerProcPtr wakeupHandler, 45235c4bbdfSmrg void *blockData) 45305b261ecSmrg{ 45405b261ecSmrg BlockHandlerPtr new; 45505b261ecSmrg 45635c4bbdfSmrg if (numHandlers >= sizeHandlers) { 4576747b715Smrg new = (BlockHandlerPtr) realloc(handlers, (numHandlers + 1) * 45835c4bbdfSmrg sizeof(BlockHandlerRec)); 45935c4bbdfSmrg if (!new) 46035c4bbdfSmrg return FALSE; 46135c4bbdfSmrg handlers = new; 46235c4bbdfSmrg sizeHandlers = numHandlers + 1; 46305b261ecSmrg } 46405b261ecSmrg handlers[numHandlers].BlockHandler = blockHandler; 46505b261ecSmrg handlers[numHandlers].WakeupHandler = wakeupHandler; 46605b261ecSmrg handlers[numHandlers].blockData = blockData; 46705b261ecSmrg handlers[numHandlers].deleted = FALSE; 46805b261ecSmrg numHandlers = numHandlers + 1; 46905b261ecSmrg return TRUE; 47005b261ecSmrg} 47105b261ecSmrg 4726747b715Smrgvoid 47335c4bbdfSmrgRemoveBlockAndWakeupHandlers(BlockHandlerProcPtr blockHandler, 47435c4bbdfSmrg WakeupHandlerProcPtr wakeupHandler, 47535c4bbdfSmrg void *blockData) 47605b261ecSmrg{ 47735c4bbdfSmrg int i; 47805b261ecSmrg 47905b261ecSmrg for (i = 0; i < numHandlers; i++) 48035c4bbdfSmrg if (handlers[i].BlockHandler == blockHandler && 48135c4bbdfSmrg handlers[i].WakeupHandler == wakeupHandler && 48235c4bbdfSmrg handlers[i].blockData == blockData) { 48335c4bbdfSmrg if (inHandler) { 48435c4bbdfSmrg handlerDeleted = TRUE; 48535c4bbdfSmrg handlers[i].deleted = TRUE; 48635c4bbdfSmrg } 48735c4bbdfSmrg else { 48835c4bbdfSmrg for (; i < numHandlers - 1; i++) 48935c4bbdfSmrg handlers[i] = handlers[i + 1]; 49035c4bbdfSmrg numHandlers--; 49135c4bbdfSmrg } 49235c4bbdfSmrg break; 49335c4bbdfSmrg } 49405b261ecSmrg} 49505b261ecSmrg 49605b261ecSmrgvoid 49735c4bbdfSmrgInitBlockAndWakeupHandlers(void) 49805b261ecSmrg{ 4996747b715Smrg free(handlers); 50005b261ecSmrg handlers = (BlockHandlerPtr) 0; 50105b261ecSmrg numHandlers = 0; 50205b261ecSmrg sizeHandlers = 0; 50305b261ecSmrg} 50405b261ecSmrg 50505b261ecSmrg/* 50605b261ecSmrg * A general work queue. Perform some task before the server 50705b261ecSmrg * sleeps for input. 50805b261ecSmrg */ 50905b261ecSmrg 51035c4bbdfSmrgWorkQueuePtr workQueue; 51135c4bbdfSmrgstatic WorkQueuePtr *workQueueLast = &workQueue; 51205b261ecSmrg 51305b261ecSmrgvoid 51405b261ecSmrgProcessWorkQueue(void) 51505b261ecSmrg{ 51635c4bbdfSmrg WorkQueuePtr q, *p; 51705b261ecSmrg 51805b261ecSmrg p = &workQueue; 51905b261ecSmrg /* 52005b261ecSmrg * Scan the work queue once, calling each function. Those 52105b261ecSmrg * which return TRUE are removed from the queue, otherwise 52205b261ecSmrg * they will be called again. This must be reentrant with 52305b261ecSmrg * QueueWorkProc. 52405b261ecSmrg */ 52535c4bbdfSmrg while ((q = *p)) { 52635c4bbdfSmrg if ((*q->function) (q->client, q->closure)) { 52735c4bbdfSmrg /* remove q from the list */ 52835c4bbdfSmrg *p = q->next; /* don't fetch until after func called */ 52935c4bbdfSmrg free(q); 53035c4bbdfSmrg } 53135c4bbdfSmrg else { 53235c4bbdfSmrg p = &q->next; /* don't fetch until after func called */ 53335c4bbdfSmrg } 53405b261ecSmrg } 53505b261ecSmrg workQueueLast = p; 53605b261ecSmrg} 53705b261ecSmrg 53805b261ecSmrgvoid 53905b261ecSmrgProcessWorkQueueZombies(void) 54005b261ecSmrg{ 54135c4bbdfSmrg WorkQueuePtr q, *p; 54205b261ecSmrg 54305b261ecSmrg p = &workQueue; 54435c4bbdfSmrg while ((q = *p)) { 54535c4bbdfSmrg if (q->client && q->client->clientGone) { 54635c4bbdfSmrg (void) (*q->function) (q->client, q->closure); 54735c4bbdfSmrg /* remove q from the list */ 54835c4bbdfSmrg *p = q->next; /* don't fetch until after func called */ 54935c4bbdfSmrg free(q); 55035c4bbdfSmrg } 55135c4bbdfSmrg else { 55235c4bbdfSmrg p = &q->next; /* don't fetch until after func called */ 55335c4bbdfSmrg } 55405b261ecSmrg } 55505b261ecSmrg workQueueLast = p; 55605b261ecSmrg} 55705b261ecSmrg 5586747b715SmrgBool 55935c4bbdfSmrgQueueWorkProc(Bool (*function) (ClientPtr pClient, void *closure), 56035c4bbdfSmrg ClientPtr client, void *closure) 56105b261ecSmrg{ 56235c4bbdfSmrg WorkQueuePtr q; 56305b261ecSmrg 5646747b715Smrg q = malloc(sizeof *q); 56505b261ecSmrg if (!q) 56635c4bbdfSmrg return FALSE; 56705b261ecSmrg q->function = function; 56805b261ecSmrg q->client = client; 56905b261ecSmrg q->closure = closure; 57005b261ecSmrg q->next = NULL; 57105b261ecSmrg *workQueueLast = q; 57205b261ecSmrg workQueueLast = &q->next; 57305b261ecSmrg return TRUE; 57405b261ecSmrg} 57505b261ecSmrg 57605b261ecSmrg/* 57705b261ecSmrg * Manage a queue of sleeping clients, awakening them 57805b261ecSmrg * when requested, by using the OS functions IgnoreClient 57905b261ecSmrg * and AttendClient. Note that this *ignores* the troubles 58005b261ecSmrg * with request data interleaving itself with events, but 58105b261ecSmrg * we'll leave that until a later time. 58205b261ecSmrg */ 58305b261ecSmrg 58405b261ecSmrgtypedef struct _SleepQueue { 58535c4bbdfSmrg struct _SleepQueue *next; 58635c4bbdfSmrg ClientPtr client; 58735c4bbdfSmrg ClientSleepProcPtr function; 58835c4bbdfSmrg void *closure; 58905b261ecSmrg} SleepQueueRec, *SleepQueuePtr; 59005b261ecSmrg 59135c4bbdfSmrgstatic SleepQueuePtr sleepQueue = NULL; 59205b261ecSmrg 5936747b715SmrgBool 59435c4bbdfSmrgClientSleep(ClientPtr client, ClientSleepProcPtr function, void *closure) 59505b261ecSmrg{ 59635c4bbdfSmrg SleepQueuePtr q; 59705b261ecSmrg 5986747b715Smrg q = malloc(sizeof *q); 59905b261ecSmrg if (!q) 60035c4bbdfSmrg return FALSE; 60105b261ecSmrg 60235c4bbdfSmrg IgnoreClient(client); 60305b261ecSmrg q->next = sleepQueue; 60405b261ecSmrg q->client = client; 60505b261ecSmrg q->function = function; 60605b261ecSmrg q->closure = closure; 60705b261ecSmrg sleepQueue = q; 60805b261ecSmrg return TRUE; 60905b261ecSmrg} 61005b261ecSmrg 61105b261ecSmrgBool 61235c4bbdfSmrgClientSignal(ClientPtr client) 61305b261ecSmrg{ 61435c4bbdfSmrg SleepQueuePtr q; 61505b261ecSmrg 61605b261ecSmrg for (q = sleepQueue; q; q = q->next) 61735c4bbdfSmrg if (q->client == client) { 61835c4bbdfSmrg return QueueWorkProc(q->function, q->client, q->closure); 61935c4bbdfSmrg } 62005b261ecSmrg return FALSE; 62105b261ecSmrg} 62205b261ecSmrg 62335c4bbdfSmrgint 62435c4bbdfSmrgClientSignalAll(ClientPtr client, ClientSleepProcPtr function, void *closure) 62535c4bbdfSmrg{ 62635c4bbdfSmrg SleepQueuePtr q; 62735c4bbdfSmrg int count = 0; 62835c4bbdfSmrg 62935c4bbdfSmrg for (q = sleepQueue; q; q = q->next) { 63035c4bbdfSmrg if (!(client == CLIENT_SIGNAL_ANY || q->client == client)) 63135c4bbdfSmrg continue; 63235c4bbdfSmrg 63335c4bbdfSmrg if (!(function == CLIENT_SIGNAL_ANY || q->function == function)) 63435c4bbdfSmrg continue; 63535c4bbdfSmrg 63635c4bbdfSmrg if (!(closure == CLIENT_SIGNAL_ANY || q->closure == closure)) 63735c4bbdfSmrg continue; 63835c4bbdfSmrg 63935c4bbdfSmrg count += QueueWorkProc(q->function, q->client, q->closure); 64035c4bbdfSmrg } 64135c4bbdfSmrg 64235c4bbdfSmrg return count; 64335c4bbdfSmrg} 64435c4bbdfSmrg 6456747b715Smrgvoid 64635c4bbdfSmrgClientWakeup(ClientPtr client) 64705b261ecSmrg{ 64835c4bbdfSmrg SleepQueuePtr q, *prev; 64905b261ecSmrg 65005b261ecSmrg prev = &sleepQueue; 65135c4bbdfSmrg while ((q = *prev)) { 65235c4bbdfSmrg if (q->client == client) { 65335c4bbdfSmrg *prev = q->next; 65435c4bbdfSmrg free(q); 65535c4bbdfSmrg if (client->clientGone) 65635c4bbdfSmrg /* Oops -- new zombie cleanup code ensures this only 65735c4bbdfSmrg * happens from inside CloseDownClient; don't want to 65835c4bbdfSmrg * recurse here... 65935c4bbdfSmrg */ 66035c4bbdfSmrg /* CloseDownClient(client) */ ; 66135c4bbdfSmrg else 66235c4bbdfSmrg AttendClient(client); 66335c4bbdfSmrg break; 66435c4bbdfSmrg } 66535c4bbdfSmrg prev = &q->next; 66605b261ecSmrg } 66705b261ecSmrg} 66805b261ecSmrg 66905b261ecSmrgBool 67035c4bbdfSmrgClientIsAsleep(ClientPtr client) 67105b261ecSmrg{ 67235c4bbdfSmrg SleepQueuePtr q; 67305b261ecSmrg 67405b261ecSmrg for (q = sleepQueue; q; q = q->next) 67535c4bbdfSmrg if (q->client == client) 67635c4bbdfSmrg return TRUE; 67705b261ecSmrg return FALSE; 67805b261ecSmrg} 67905b261ecSmrg 68005b261ecSmrg/* 68105b261ecSmrg * Generic Callback Manager 68205b261ecSmrg */ 68305b261ecSmrg 68405b261ecSmrg/* ===== Private Procedures ===== */ 68505b261ecSmrg 68605b261ecSmrgstatic int numCallbackListsToCleanup = 0; 68705b261ecSmrgstatic CallbackListPtr **listsToCleanup = NULL; 68805b261ecSmrg 6896747b715Smrgstatic Bool 69035c4bbdfSmrg_AddCallback(CallbackListPtr *pcbl, CallbackProcPtr callback, void *data) 69105b261ecSmrg{ 69235c4bbdfSmrg CallbackPtr cbr; 69305b261ecSmrg 6946747b715Smrg cbr = malloc(sizeof(CallbackRec)); 69505b261ecSmrg if (!cbr) 69635c4bbdfSmrg return FALSE; 69705b261ecSmrg cbr->proc = callback; 69805b261ecSmrg cbr->data = data; 69905b261ecSmrg cbr->next = (*pcbl)->list; 70005b261ecSmrg cbr->deleted = FALSE; 70105b261ecSmrg (*pcbl)->list = cbr; 70205b261ecSmrg return TRUE; 70305b261ecSmrg} 70405b261ecSmrg 70535c4bbdfSmrgstatic Bool 70635c4bbdfSmrg_DeleteCallback(CallbackListPtr *pcbl, CallbackProcPtr callback, void *data) 70705b261ecSmrg{ 70805b261ecSmrg CallbackListPtr cbl = *pcbl; 70935c4bbdfSmrg CallbackPtr cbr, pcbr; 71035c4bbdfSmrg 71135c4bbdfSmrg for (pcbr = NULL, cbr = cbl->list; cbr != NULL; pcbr = cbr, cbr = cbr->next) { 71235c4bbdfSmrg if ((cbr->proc == callback) && (cbr->data == data)) 71335c4bbdfSmrg break; 71405b261ecSmrg } 71535c4bbdfSmrg if (cbr != NULL) { 71635c4bbdfSmrg if (cbl->inCallback) { 71735c4bbdfSmrg ++(cbl->numDeleted); 71835c4bbdfSmrg cbr->deleted = TRUE; 71935c4bbdfSmrg } 72035c4bbdfSmrg else { 72135c4bbdfSmrg if (pcbr == NULL) 72235c4bbdfSmrg cbl->list = cbr->next; 72335c4bbdfSmrg else 72435c4bbdfSmrg pcbr->next = cbr->next; 72535c4bbdfSmrg free(cbr); 72635c4bbdfSmrg } 72735c4bbdfSmrg return TRUE; 72805b261ecSmrg } 72905b261ecSmrg return FALSE; 73005b261ecSmrg} 73105b261ecSmrg 73235c4bbdfSmrgvoid 73335c4bbdfSmrg_CallCallbacks(CallbackListPtr *pcbl, void *call_data) 73405b261ecSmrg{ 73505b261ecSmrg CallbackListPtr cbl = *pcbl; 73635c4bbdfSmrg CallbackPtr cbr, pcbr; 73705b261ecSmrg 73805b261ecSmrg ++(cbl->inCallback); 73935c4bbdfSmrg for (cbr = cbl->list; cbr != NULL; cbr = cbr->next) { 74035c4bbdfSmrg (*(cbr->proc)) (pcbl, cbr->data, call_data); 74105b261ecSmrg } 74205b261ecSmrg --(cbl->inCallback); 74305b261ecSmrg 74435c4bbdfSmrg if (cbl->inCallback) 74535c4bbdfSmrg return; 74605b261ecSmrg 74705b261ecSmrg /* Was the entire list marked for deletion? */ 74805b261ecSmrg 74935c4bbdfSmrg if (cbl->deleted) { 75035c4bbdfSmrg DeleteCallbackList(pcbl); 75135c4bbdfSmrg return; 75205b261ecSmrg } 75305b261ecSmrg 75405b261ecSmrg /* Were some individual callbacks on the list marked for deletion? 75505b261ecSmrg * If so, do the deletions. 75605b261ecSmrg */ 75705b261ecSmrg 75835c4bbdfSmrg if (cbl->numDeleted) { 75935c4bbdfSmrg for (pcbr = NULL, cbr = cbl->list; (cbr != NULL) && cbl->numDeleted;) { 76035c4bbdfSmrg if (cbr->deleted) { 76135c4bbdfSmrg if (pcbr) { 76235c4bbdfSmrg cbr = cbr->next; 76335c4bbdfSmrg free(pcbr->next); 76435c4bbdfSmrg pcbr->next = cbr; 76535c4bbdfSmrg } 76635c4bbdfSmrg else { 76735c4bbdfSmrg cbr = cbr->next; 76835c4bbdfSmrg free(cbl->list); 76935c4bbdfSmrg cbl->list = cbr; 77035c4bbdfSmrg } 77135c4bbdfSmrg cbl->numDeleted--; 77235c4bbdfSmrg } 77335c4bbdfSmrg else { /* this one wasn't deleted */ 77435c4bbdfSmrg 77535c4bbdfSmrg pcbr = cbr; 77635c4bbdfSmrg cbr = cbr->next; 77735c4bbdfSmrg } 77835c4bbdfSmrg } 77905b261ecSmrg } 78005b261ecSmrg} 78105b261ecSmrg 78205b261ecSmrgstatic void 78335c4bbdfSmrg_DeleteCallbackList(CallbackListPtr *pcbl) 78405b261ecSmrg{ 78505b261ecSmrg CallbackListPtr cbl = *pcbl; 78635c4bbdfSmrg CallbackPtr cbr, nextcbr; 78705b261ecSmrg int i; 78805b261ecSmrg 78935c4bbdfSmrg if (cbl->inCallback) { 79035c4bbdfSmrg cbl->deleted = TRUE; 79135c4bbdfSmrg return; 79205b261ecSmrg } 79305b261ecSmrg 79435c4bbdfSmrg for (i = 0; i < numCallbackListsToCleanup; i++) { 79535c4bbdfSmrg if (listsToCleanup[i] == pcbl) { 79635c4bbdfSmrg listsToCleanup[i] = NULL; 79735c4bbdfSmrg break; 79835c4bbdfSmrg } 79905b261ecSmrg } 80005b261ecSmrg 80135c4bbdfSmrg for (cbr = cbl->list; cbr != NULL; cbr = nextcbr) { 80235c4bbdfSmrg nextcbr = cbr->next; 80335c4bbdfSmrg free(cbr); 80405b261ecSmrg } 8056747b715Smrg free(cbl); 80605b261ecSmrg *pcbl = NULL; 80705b261ecSmrg} 80805b261ecSmrg 80905b261ecSmrgstatic Bool 8104642e01fSmrgCreateCallbackList(CallbackListPtr *pcbl) 81105b261ecSmrg{ 81235c4bbdfSmrg CallbackListPtr cbl; 81305b261ecSmrg int i; 81405b261ecSmrg 81535c4bbdfSmrg if (!pcbl) 81635c4bbdfSmrg return FALSE; 8176747b715Smrg cbl = malloc(sizeof(CallbackListRec)); 81835c4bbdfSmrg if (!cbl) 81935c4bbdfSmrg return FALSE; 82005b261ecSmrg cbl->inCallback = 0; 82105b261ecSmrg cbl->deleted = FALSE; 82205b261ecSmrg cbl->numDeleted = 0; 82305b261ecSmrg cbl->list = NULL; 82405b261ecSmrg *pcbl = cbl; 82505b261ecSmrg 82635c4bbdfSmrg for (i = 0; i < numCallbackListsToCleanup; i++) { 82735c4bbdfSmrg if (!listsToCleanup[i]) { 82835c4bbdfSmrg listsToCleanup[i] = pcbl; 82935c4bbdfSmrg return TRUE; 83035c4bbdfSmrg } 83105b261ecSmrg } 83205b261ecSmrg 83335c4bbdfSmrg listsToCleanup = (CallbackListPtr **) xnfrealloc(listsToCleanup, 83435c4bbdfSmrg sizeof(CallbackListPtr *) * 83535c4bbdfSmrg (numCallbackListsToCleanup 83635c4bbdfSmrg + 1)); 83705b261ecSmrg listsToCleanup[numCallbackListsToCleanup] = pcbl; 83805b261ecSmrg numCallbackListsToCleanup++; 83905b261ecSmrg return TRUE; 84005b261ecSmrg} 84105b261ecSmrg 84205b261ecSmrg/* ===== Public Procedures ===== */ 84305b261ecSmrg 8446747b715SmrgBool 84535c4bbdfSmrgAddCallback(CallbackListPtr *pcbl, CallbackProcPtr callback, void *data) 84605b261ecSmrg{ 84735c4bbdfSmrg if (!pcbl) 84835c4bbdfSmrg return FALSE; 84935c4bbdfSmrg if (!*pcbl) { /* list hasn't been created yet; go create it */ 85035c4bbdfSmrg if (!CreateCallbackList(pcbl)) 85135c4bbdfSmrg return FALSE; 85205b261ecSmrg } 8534642e01fSmrg return _AddCallback(pcbl, callback, data); 85405b261ecSmrg} 85505b261ecSmrg 8566747b715SmrgBool 85735c4bbdfSmrgDeleteCallback(CallbackListPtr *pcbl, CallbackProcPtr callback, void *data) 85805b261ecSmrg{ 85935c4bbdfSmrg if (!pcbl || !*pcbl) 86035c4bbdfSmrg return FALSE; 8614642e01fSmrg return _DeleteCallback(pcbl, callback, data); 86205b261ecSmrg} 86305b261ecSmrg 86405b261ecSmrgvoid 86505b261ecSmrgDeleteCallbackList(CallbackListPtr *pcbl) 86605b261ecSmrg{ 86735c4bbdfSmrg if (!pcbl || !*pcbl) 86835c4bbdfSmrg return; 8694642e01fSmrg _DeleteCallbackList(pcbl); 87005b261ecSmrg} 87105b261ecSmrg 8726747b715Smrgvoid 87335c4bbdfSmrgDeleteCallbackManager(void) 87405b261ecSmrg{ 87505b261ecSmrg int i; 87605b261ecSmrg 87735c4bbdfSmrg for (i = 0; i < numCallbackListsToCleanup; i++) { 87835c4bbdfSmrg DeleteCallbackList(listsToCleanup[i]); 87905b261ecSmrg } 8806747b715Smrg free(listsToCleanup); 88105b261ecSmrg 88205b261ecSmrg numCallbackListsToCleanup = 0; 88305b261ecSmrg listsToCleanup = NULL; 88405b261ecSmrg} 88535c4bbdfSmrg 88635c4bbdfSmrgvoid 88735c4bbdfSmrgInitCallbackManager(void) 88835c4bbdfSmrg{ 88935c4bbdfSmrg DeleteCallbackManager(); 89035c4bbdfSmrg} 89135c4bbdfSmrg 89235c4bbdfSmrg/** 89335c4bbdfSmrg * Coordinates the global GL context used by modules in the X Server 89435c4bbdfSmrg * doing rendering with OpenGL. 89535c4bbdfSmrg * 89635c4bbdfSmrg * When setting a GL context (glXMakeCurrent() or eglMakeCurrent()), 89735c4bbdfSmrg * there is an expensive implied glFlush() required by the GLX and EGL 89835c4bbdfSmrg * APIs, so modules don't want to have to do it on every request. But 89935c4bbdfSmrg * the individual modules using GL also don't know about each other, 90035c4bbdfSmrg * so they have to coordinate who owns the current context. 90135c4bbdfSmrg * 90235c4bbdfSmrg * When you're about to do a MakeCurrent, you should set this variable 90335c4bbdfSmrg * to your context's address, and you can skip MakeCurrent if it's 90435c4bbdfSmrg * already set to yours. 90535c4bbdfSmrg * 90635c4bbdfSmrg * When you're about to do a DestroyContext, you should set this to 90735c4bbdfSmrg * NULL if it's set to your context. 90835c4bbdfSmrg * 90935c4bbdfSmrg * When you're about to do an unbindContext on a DRI driver, you 91035c4bbdfSmrg * should set this to NULL. Despite the unbindContext interface 91135c4bbdfSmrg * sounding like it only unbinds the passed in context, it actually 91235c4bbdfSmrg * unconditionally clears the dispatch table even if the given 91335c4bbdfSmrg * context wasn't current. 91435c4bbdfSmrg */ 91535c4bbdfSmrgvoid *lastGLContext = NULL; 916