/****************************** Module Header ******************************\ * Module Name: dc.c * * Copyright (c) 1985-1996, Microsoft Corporation * * This module contains User's DC APIs and related functions. * * History: * 23-Oct-1990 DarrinM Created. * 07-Feb-1991 MikeKe Added Revalidation code (None). * 17-Jul-1991 DarrinM Recreated from Win 3.1 source. * 21-Jan-1992 IanJa ANSI/Unicode neutral (null op). \***************************************************************************/ #include "precomp.h" #pragma hdrstop /* * Count of available cacheDC's. This is used in determining * a threshold count of DCX_CACHE types available. */ int gnDCECount = 0; /* * DEBUG Related Information. */ #ifdef DEBUG BOOL fDisableCache = FALSE; // TRUE to disable DC cache. #endif /***************************************************************************\ * ResetOrg * * Resets the origin of the DC associated with *pdce. * * History: * 17-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/ VOID ResetOrg( HRGN hrgn, PDCE pdce) { RECT rc; ULONG flags; if (pdce->flags & DCX_WINDOW) { rc = pdce->pwndOrg->rcWindow; } else { rc = pdce->pwndOrg->rcClient; } flags = (hrgn == NULL) ? SVR_ORIGIN : SVR_DELETEOLD; GreSelectVisRgn(pdce->hdc, hrgn, (PRECTL)&rc, flags); } /***************************************************************************\ * GetDC (API) * * Standard call to GetDC(). * * History: * 17-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/ HDC _GetDC( PWND pwnd) { /* * Special case for NULL: For backward compatibility we want to return * a window DC for the desktop that does not exclude its children. */ if (pwnd == NULL) { PDESKTOP pdesk = PtiCurrent()->rpdesk; if (pdesk) { return _GetDCEx(pdesk->pDeskInfo->spwnd, NULL, DCX_WINDOW | DCX_CACHE | DCX_NEEDFONT); } /* * The thread has no desktop. Fail the call. */ return NULL; } return _GetDCEx(pwnd, NULL, DCX_NEEDFONT | DCX_USESTYLE); } /***************************************************************************\ * _ReleaseDC (API) * * Release the DC retrieved from GetDC(). * * History: * 17-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/ BOOL _ReleaseDC( HDC hdc) { CheckCritIn(); return (ReleaseCacheDC(hdc, FALSE) == DCE_NORELEASE ? FALSE : TRUE); } /***************************************************************************\ * _GetScreenDC (API) * * Retrieve a cache-dc that encompasses the screen. This should only be * called from threads that contain a desktop. * * History: * 17-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/ HDC _GetScreenDC(VOID) { return _GetDCEx(PtiCurrent()->rpdesk->pDeskInfo->spwnd, NULL, DCX_WINDOW | DCX_CACHE); } /***************************************************************************\ * _GetWindowDC (API) * * Retrive a DC for the window. * * History: * 17-Jul-1991 DarrinM Ported from Win 3.1 sources. * 25-Jan-1996 ChrisWil Allow rgnClip so that WM_NCACTIVATE can clip. \***************************************************************************/ HDC _GetWindowDC( PWND pwnd) { #if 0 /* * For WIN31 and previous apps, we want to actually return back a * client DC. Before WIN40, the window rect and client rect were the * same, and there was this terrible hack to grab the window dc when * painting because window DCs never clip anything. Otherwise the * children of the minimized window would be clipped out of the fake * client area. So apps would call GetWindowDC() to redraw their icons, * since GetDC() would clip empty if the window had a class icon. */ if (TestWF(pwnd, WFMINIMIZED) && !TestWF(pwnd, WFWIN40COMPAT)) return(_GetDCEx(pwnd, hrgnClip, DCX_INTERNAL | DCX_CACHE | DCX_USESTYLE)); #endif return _GetDCEx(pwnd, NULL, DCX_WINDOW | DCX_USESTYLE); } /***************************************************************************\ * UserSetDCVisRgn * * Set the visrgn for the DCE. If the window has a (hrgnClipPublic), we use * that instead of the (hrgnClip) since it's a public-object. The other is * created and owned by the user-thread and can't be used if say we're in the * hung-app-drawing (different process). Both regions should be equalent in * data. * * History: * 10-Nov-1992 DavidPe Created. * 20-Dec-1995 ChrisWil Added (hrgnClipPublic) entry. \***************************************************************************/ VOID UserSetDCVisRgn( PDCE pdce) { HRGN hrgn = NULL; /* * If the visrgn calculated is empt, set the flag DCX_PWNDORGINVISIBLE, * otherwise clear it (it could've been set earlier on). */ if (!CalcVisRgn(&hrgn, pdce->pwndOrg, pdce->pwndClip, pdce->flags)) { pdce->flags |= DCX_PWNDORGINVISIBLE; } else { pdce->flags &= ~DCX_PWNDORGINVISIBLE; } /* * Deal with INTERSECTRGN and EXCLUDERGN. */ if (pdce->flags & DCX_INTERSECTRGN) { UserAssert(pdce->hrgnClipPublic != MAXREGION); if (pdce->hrgnClipPublic == NULL) { GreSetRectRgn(hrgn, 0, 0, 0, 0); } else { IntersectRgn(hrgn, hrgn, pdce->hrgnClipPublic); } } else if (pdce->flags & DCX_EXCLUDERGN) { UserAssert(pdce->hrgnClipPublic != NULL); if (pdce->hrgnClipPublic == MAXREGION) { GreSetRectRgn(hrgn, 0, 0, 0, 0); } else { SubtractRgn(hrgn, hrgn, pdce->hrgnClipPublic); } } ResetOrg(hrgn, pdce); } /***************************************************************************\ * UserGetClientRgn * * Return a copy of the client region and rectangle for the given hwnd. * * The caller must enter the user critical section before calling this function. * * History: * 27-Sep-1993 WendyWu Created. \***************************************************************************/ HRGN UserGetClientRgn( HWND hwnd, LPRECT lprc) { HRGN hrgnClient = (HRGN)NULL; PWND pwnd; /* * Must be in critical section. */ CheckCritIn(); if (pwnd = ValidateHwnd(hwnd)) { CalcVisRgn(&hrgnClient, pwnd, pwnd, DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN); *lprc = pwnd->rcClient; } return hrgnClient; } /***************************************************************************\ * UserGetHwnd * * Return a hwnd and the associated pwo for the given display hdc. * * It returns FALSE if no hwnd corresponds to the hdc is found or if the * hwnd has incorrect styles for a device format window. * * The caller must enter the user critical section before calling this function. * * History: * 27-Sep-1993 WendyWu Created. \***************************************************************************/ BOOL UserGetHwnd( HDC hdc, HWND *phwnd, PVOID *ppwo, BOOL bCheckStyle) { PWND pwnd; PDCE pdce; /* * Must be in critical section. */ CheckCritIn(); /* * Find pdce and pwnd for this DC. * * Note: the SAMEHANDLE macro strips out the user defined bits in the * handle before doing the comparison. This is important because when * GRE calls this function, it may have lost track of the OWNDC bit. */ for (pdce = gpDispInfo->pdceFirst; pdce != NULL; pdce = pdce->pdceNext) { if (pdce->hdc == hdc) // this should be undone once SAMEHANDLE is fixed for kmode break; } /* * Return FALSE If it is not in the pdce list. */ if ((pdce == NULL) || (pdce->pwndOrg == NULL)) return FALSE; pwnd = pdce->pwndOrg; /* * The window style must be clipchildren and clipsiblings. * the window's class must not be parentdc */ if (bCheckStyle) { if (!TestWF(pwnd, WFCLIPCHILDREN) || !TestWF(pwnd, WFCLIPSIBLINGS) || TestCF(pwnd, CFPARENTDC)) { #ifdef DEBUG RIPMSG0(RIP_WARNING, "UserGetHwnd: Bad OpenGL window style or class"); #endif return FALSE; } } /* * Return the hwnd with the correct styles for a device format window. */ *phwnd = HW(pwnd); *ppwo = pwnd->pwo; return TRUE; } /***************************************************************************\ * UserAssociateHwnd * * Associate a gdi WNDOBJ with hwnd. The caller must enter the user * critical section before calling this function. * * History: * 13-Jan-1994 HockL Created. \***************************************************************************/ VOID UserAssociateHwnd( HWND hwnd, PVOID pwo) { PWND pwnd; /* * Must be in critical section. */ CheckCritIn(); if (pwnd = ValidateHwnd(hwnd)) { pwnd->pwo = pwo; gcountPWO++; } } /***************************************************************************\ * UserReleaseDC * * * History: * 25-Jan-1996 ChrisWil Created comment block. \***************************************************************************/ BOOL UserReleaseDC( HDC hdc) { BOOL b; EnterCrit(); b = _ReleaseDC(hdc); LeaveCrit(); return b; } /***************************************************************************\ * InvalidateDce * * * History: * 17-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/ VOID InvalidateDce( PDCE pdce) { GreLockDisplay(gpDispInfo->pDevLock); if (!(pdce->flags & DCX_INUSE)) { /* * Accumulate any bounds for this CE * since we're about to mark it invalid. */ SpbCheckDce(pdce); /* * Mark this cache entry as invalid * (and clear all the other flags) */ pdce->flags &= DCX_CACHE; // Don't clear this bit! pdce->flags |= DCX_INVALID; pdce->pwndOrg = NULL; pdce->pwndClip = NULL; pdce->hrgnClip = NULL; pdce->hrgnClipPublic = NULL; /* * Remove the vis rgn since it is still owned - if we did not, * gdi would not be able to clean up properly if the app that * owns this vis rgn exist while the vis rgn is still selected. */ GreSelectVisRgn(pdce->hdc, NULL, NULL, SVR_DELETEOLD); } else { PWND pwndOrg = pdce->pwndOrg; PWND pwndClip = pdce->pwndClip; /* * In case the window's clipping style bits changed, * reset the DCE flags from the window style bits. * Note that minimized windows never exclude their children. */ pdce->flags &= ~(DCX_CLIPCHILDREN | DCX_CLIPSIBLINGS); #if 1 /* * Chicago stuff... */ if (TestCF(pwndOrg, CFPARENTDC) && (TestWF(pwndOrg, WFWIN31COMPAT) || !TestWF(pwndClip, WFCLIPCHILDREN)) && (TestWF(pwndOrg, WFVISIBLE) == TestWF(pwndClip, WFVISIBLE))) { if (TestWF(pwndClip, WFCLIPSIBLINGS)) pdce->flags |= DCX_CLIPSIBLINGS; } else { if (TestWF(pwndOrg, WFCLIPCHILDREN) && !TestWF(pwndOrg, WFMINIMIZED)) pdce->flags |= DCX_CLIPCHILDREN; if (TestWF(pwndOrg, WFCLIPSIBLINGS)) pdce->flags |= DCX_CLIPSIBLINGS; } #else /* * for parentclip windows we don't clipchildren. It's parentclip * iff pdce->pwndClip != pdce->pwndOrg */ if (TestWF(pdce->pwndClip, WFCLIPCHILDREN) && !TestWF(pdce->pwndClip, WFMINIMIZED) && //pdce->pwndClip != PWNDDESKTOP(pdce->pwndClip) && !(pdce->flags & DCX_WINDOW) && (pdce->pwndClip == pdce->pwndOrg)) { pdce->flags |= DCX_CLIPCHILDREN; } if (TestWF(pdce->pwndClip, WFCLIPSIBLINGS)) pdce->flags |= DCX_CLIPSIBLINGS; #endif /* * Mark that any saved visrgn needs to be recomputed. */ pdce->flags |= DCX_SAVEDRGNINVALID; UserSetDCVisRgn(pdce); } GreUnlockDisplay(gpDispInfo->pDevLock); } /***************************************************************************\ * DeleteHrgnClip * * * History: * 17-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/ VOID DeleteHrgnClip( PDCE pdce) { /* * Clear these flags first in case we get a DCHook() callback... */ pdce->flags &= ~(DCX_EXCLUDERGN | DCX_INTERSECTRGN); /* * Blow away pdce->hrgnClip and clear the associated flags. * Do not delete hrgnClip if DCX_NODELETERGN is set! */ if (!(pdce->flags & DCX_NODELETERGN)) { if (pdce->hrgnClip > MAXREGION) GreDeleteObject(pdce->hrgnClip); } else { pdce->flags &= ~DCX_NODELETERGN; } if (pdce->hrgnClipPublic > MAXREGION) GreDeleteObject(pdce->hrgnClipPublic); pdce->hrgnClip = NULL; pdce->hrgnClipPublic = NULL; /* * Restore the saved visrgn. */ if (pdce->hrgnSavedVis != NULL) { GreSelectVisRgn(pdce->hdc, pdce->hrgnSavedVis, NULL, SVR_DELETEOLD); pdce->hrgnSavedVis = NULL; } /* * If the saved visrgn was invalidated by an InvalidateDC() * while we had it checked out, then invalidate the entry now. * * NOTE: This sucks - cause we recalc the vis rgn if this is called * from ReleaseDC(), because we haven't cleared the DCX_INUSE bit * set. In the future, we want to recalc visrgns *LAZILY* not * automatically like here (waste of time because maybe this window * won't call GetDC() again for awhile). (ScottLu) */ if (pdce->flags & DCX_SAVEDRGNINVALID) InvalidateDce(pdce); } /***************************************************************************\ * SelectFixedFont * * * History: * 17-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/ VOID SelectFixedFont( PDCE pdce) { DWORD version = pdce->pwndOrg->dwExpWinVer; /* * HIWORD of version is a flag that indicates that * an old application is compatible with the proportional * system font. */ if ((LOWORD(version) < VER30) && !HIWORD(version)) GreSelectFont(pdce->hdc, ghFontSysFixed); } /***************************************************************************\ * GetDCEx (API) * * * History: * 17-Jul-1991 DarrinM Ported from Win 3.1 sources. * 20-Dec-1995 ChrisWil Added (hrgnClipPublic) entry. \***************************************************************************/ HDC _GetDCEx( PWND pwnd, HRGN hrgnClip, DWORD flags) { HRGN hrgn; HDC hdcMatch; PWND pwndClip; PWND pwndOrg; PDCE pdce; PDCE *ppdce; PDCE *ppdceNotInUse; DWORD flagsMatch; BOOL bpwndOrgVisible; /* * Lock the device while we're playing with visrgns. */ GreLockDisplay(gpDispInfo->pDevLock); if (pwnd == NULL) pwnd = PtiCurrent()->rpdesk->pDeskInfo->spwnd; hdcMatch = NULL; pwndOrg = pwndClip = pwnd; bpwndOrgVisible = IsVisible(pwndOrg); if (PpiCurrent()->W32PF_Flags & W32PF_OWNDCCLEANUP) { DelayedDestroyCacheDC(); } /* * If necessary, compute DCX flags from window style. */ if (flags & DCX_USESTYLE) { flags &= ~(DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN | DCX_PARENTCLIP); if (!(flags & DCX_WINDOW)) { if (TestCF(pwndOrg, CFPARENTDC)) flags |= DCX_PARENTCLIP; if (!(flags & DCX_CACHE)) { /* * If the DCX_CACHE flag is present, override OWNDC/CLASSDC. * Otherwise, calculate from appropriate style bits. */ flags |= DCX_CACHE; if (TestCF(pwndOrg, CFOWNDC)) { /* * Look for a non-cache entry that matches pwndOrg... */ flags &= ~DCX_CACHE; } else if (TestCF(pwndOrg, CFCLASSDC)) { UserAssert(pwndOrg->pcls->pdce); /* * Look for a non-cache entry that matches hdc... */ hdcMatch = pwndOrg->pcls->pdce->hdc; flags &= ~DCX_CACHE; } } if (TestWF(pwndOrg, WFCLIPCHILDREN)) flags |= DCX_CLIPCHILDREN; if (TestWF(pwndOrg, WFCLIPSIBLINGS)) flags |= DCX_CLIPSIBLINGS; /* * Minimized windows never exclude their children. */ if (TestWF(pwndOrg, WFMINIMIZED)) { flags &= ~DCX_CLIPCHILDREN; if (pwndOrg->pcls->spicn) flags |= DCX_CACHE; } } else { if (TestWF(pwndClip, WFCLIPSIBLINGS)) flags |= DCX_CLIPSIBLINGS; flags |= DCX_CACHE; /* * Window DCs never exclude children. */ } } /* * Deal with all the Win 3.0-compatible clipping rules: * * DCX_NOCHILDCLIP overrides: * DCX_PARENTCLIP/CS_OWNDC/CS_CLASSDC * DCX_PARENTCLIP overrides: * DCX_CLIPSIBLINGS/DCX_CLIPCHILDREN/CS_OWNDC/CS_CLASSDC */ if (flags & DCX_NOCLIPCHILDREN) { flags &= ~(DCX_PARENTCLIP | DCX_CLIPCHILDREN); flags |= DCX_CACHE; } if (flags & DCX_PARENTCLIP) { PWND pwndParent; /* * If this window has no parent. This can occur if the app is * calling GetDC in response to a CBT_CREATEWND callback. In this * case, the parent is not yet setup. */ if (pwndOrg->spwndParent == NULL) pwndParent = PtiCurrent()->rpdesk->pDeskInfo->spwnd; else pwndParent = pwndOrg->spwndParent; /* * Always get the DC from the cache. */ flags |= DCX_CACHE; /* * We can't use a shared DC if the visibility of the * child does not match the parent's, or if a * CLIPSIBLINGS or CLIPCHILDREN DC is requested. * * In 3.1, we pay attention to the CLIPSIBLINGS and CLIPCHILDREN * bits of CS_PARENTDC windows, by overriding CS_PARENTDC if * either of these flags are requested. * * BACKWARD COMPATIBILITY HACK * * If parent is CLIPCHILDREN, get a cache DC, but don't * use parent's DC. Windows PowerPoint depends on this * behavior in order to draw the little gray rect between * its scroll bars correctly. */ if ((TestWF(pwndOrg, WFWIN31COMPAT) || !TestWF(pwndParent, WFCLIPCHILDREN)) && !(flags & (DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN)) && TestWF(pwndParent, WFVISIBLE) == TestWF(pwndOrg, WFVISIBLE)) { pwndClip = pwndParent; #ifdef DEBUG if (flags & DCX_CLIPCHILDREN) RIPMSG0(RIP_WARNING, "WS_CLIPCHILDREN overridden by CS_PARENTDC"); if (flags & DCX_CLIPSIBLINGS) RIPMSG0(RIP_WARNING, "WS_CLIPSIBLINGS overridden by CS_PARENTDC"); #endif /* * Make sure flags reflect hwndClip rather than hwndOrg. * But, we must never clip the children (since that's who * wants to do the drawing!) */ flags &= ~(DCX_CLIPCHILDREN | DCX_CLIPSIBLINGS); if (TestWF(pwndClip, WFCLIPSIBLINGS)) flags |= DCX_CLIPSIBLINGS; } } /* * Make sure we don't return an OWNDC if the calling thread didn't * create this window - need to returned cached always in this case. * * Win95 does not contain this code. Why? */ if (!(flags & DCX_CACHE)) { if (pwndOrg == NULL || GETPTI(pwndOrg) != PtiCurrent()) flags |= DCX_CACHE; } flagsMatch = flags & DCX_MATCHMASK; if (!(flags & DCX_CACHE)) { /* * Handle CS_OWNDC and CS_CLASSDC cases specially. Based on the * supplied match information, we need to find the appropriate DCE. */ for (ppdce = &gpDispInfo->pdceFirst; (pdce = *ppdce); ppdce = &pdce->pdceNext) { if (pdce->flags & DCX_CACHE) continue; /* * Look for the entry that matches hdcMatch or pwndOrg... */ if (!(pdce->pwndOrg == pwndOrg || pdce->hdc == hdcMatch)) continue; /* * NOTE: The "Multiple-BeginPaint()-of-OWNDC-Window" Conundrum * * There is a situation having to do with OWNDC or CLASSDC window * DCs that can theoretically arise that is handled specially * here and in ReleaseCacheDC(). These DCs are identified with * the DCX_CACHE bit CLEAR. * * In the case where BeginPaint() (or a similar operation) is * called more than once without an intervening EndPaint(), the * DCX_INTERSECTRGN (or DCX_EXCLUDERGN) bit may already be set * when we get here. * * Theoretically, the correct thing to do is to save the current * hrgnClip, and set up the new one here. In ReleaseCacheDC, the * saved hrgnClip is restored and the visrgn recomputed. * * All of this is only necessary if BOTH calls involve an * hrgnClip that causes the visrgn to be changed (i.e., the * simple hrgnClip test clears the INTERSECTRGN or EXCLUDERGN bit * fails), which is not at all likely. * * When this code encounters this multiple-BeginPaint case it * punts by honoring the new EXCLUDE/INTERSECTRGN bits, but it * first restores the DC to a wide-open visrgn before doing so. * This means that the first EndPaint() will restore the visrgn * to a wide-open DC, rather than clipped to the first * BeginPaint()'s update rgn. This is a good punt, because worst * case an app does a bit more drawing than it should. */ if ((pdce->flags & (DCX_EXCLUDERGN | DCX_INTERSECTRGN)) && (flags & (DCX_EXCLUDERGN | DCX_INTERSECTRGN))) { #ifdef DEBUG RIPMSG0(RIP_WARNING, "\r\nNested BeginPaint() calls, please fix Your app!"); #endif DeleteHrgnClip(pdce); } /* * If we matched exactly, no recomputation necessary * (we found a CS_OWNDC or a CS_CLASSDC that is already set up) * Otherwise, we have a CS_CLASSDC that needs recomputation. */ //if (pdce->pwndOrg == pwndOrg) { if (pdce->pwndOrg == pwndOrg && (bpwndOrgVisible && !(pdce->flags & DCX_PWNDORGINVISIBLE))){ goto HaveComputedEntry; } goto RecomputeEntry; } /* * If we got this far, we couldn't find the DC. * This should never happen! */ RIPMSG0(RIP_WARNING, "couldn't find DC bad code path"); NullExit: GreUnlockDisplay(gpDispInfo->pDevLock); return NULL; } else { /* * Make a quick pass through the cache, looking for an * exact match. */ SearchAgain: #ifdef DEBUG if (fDisableCache) goto SearchFailed; #endif for (ppdce = &gpDispInfo->pdceFirst; (pdce = *ppdce); ppdce = &pdce->pdceNext) { /* * If we find an entry that is not in use and whose clip flags * and clip window match, we can use it. * * NOTE: DCX_INTERSECT/EXCLUDERGN cache entries always have * DCX_INUSE set, so we'll never erroneously match one here. */ UserAssert(!(pdce->flags & (DCX_INTERSECTRGN | DCX_EXCLUDERGN)) || (pdce->flags & DCX_INUSE)); if ((pdce->pwndClip == pwndClip) && (flagsMatch == (pdce->flags & (DCX_MATCHMASK | DCX_INUSE | DCX_INVALID)))) { /* * Special case for icon - bug 9103 (win31) */ if (TestWF(pwndClip, WFMINIMIZED) && (pdce->pwndOrg != pdce->pwndClip)) { continue; } /* * If the pwndOrg of the DC we found is not visible and * the pwndOrg we're looking for is visble, then * the visrgn is no good, we can't reuse it so keep * looking. */ if (bpwndOrgVisible && pdce->flags & DCX_PWNDORGINVISIBLE) { continue; } /* * Set INUSE before performing any GDI operations, just * in case DCHook() has a mind to recalculate the visrgn... */ pdce->flags |= DCX_INUSE; /* * We found an entry with the proper visrgn. * If the origin doesn't match, update the CE and reset it. */ if (pwndOrg != pdce->pwndOrg) { /* * Need to flush any dirty rectangle stuff now. */ SpbCheckDce(pdce); pdce->pwndOrg = pwndOrg; ResetOrg(NULL, pdce); } goto HaveComputedEntry; } } #ifdef DEBUG SearchFailed: #endif /* * Couldn't find an exact match. Find some invalid or non-inuse * entry we can reuse. */ ppdceNotInUse = NULL; for (ppdce = &gpDispInfo->pdceFirst; (pdce = *ppdce); ppdce = &pdce->pdceNext) { /* * Skip non-cache entries */ if (!(pdce->flags & DCX_CACHE)) continue; if (pdce->flags & DCX_INVALID) { break; } else if (!(pdce->flags & DCX_INUSE)) { /* * Remember the non-inuse one, but keep looking for an invalid. */ ppdceNotInUse = ppdce; } } /* * If we broke out of the loop, we found an invalid entry to reuse. * Otherwise see if we found a non-inuse entry to reuse. */ if (pdce == NULL && ((ppdce = ppdceNotInUse) == NULL)) { /* * Create another DCE if we need it. */ if (!CreateCacheDC(pwndOrg, (DCX_INVALID | DCX_CACHE | DCX_NEEDFONT))) { goto NullExit; } goto SearchAgain; } /* * We've chosen an entry to reuse: now fill it in and recompute it. */ pdce = *ppdce; RecomputeEntry: /* * Any non-invalid entries that we reuse might still have some bounds * that need to be used to invalidate SPBs. Apply them here. */ if (!(pdce->flags & DCX_INVALID)) SpbCheckDce(pdce); /* * We want to compute only the matchable visrgn at first, * so we don't set up hrgnClip, or set the EXCLUDERGN or INTERSECTRGN * bits yet -- we'll deal with those later. */ pdce->flags = flagsMatch | DCX_INUSE; /* * Now recompute the visrgn (minus any hrgnClip shenanigans) */ hrgn = NULL; if (CalcVisRgn(&hrgn, pwndOrg, pwndClip, flagsMatch) == FALSE) { pdce->flags |= DCX_PWNDORGINVISIBLE; } pdce->pwndOrg = pwndOrg; pdce->pwndClip = pwndClip; pdce->hrgnClip = NULL; // Just in case... pdce->hrgnClipPublic = NULL; ResetOrg(hrgn, pdce); /* * When we arrive here, pdce (and *ppdce) point to * a cache entry whose visrgn and origin are set up. * All that remains to be done is to deal with EXCLUDE/INTERSECTRGN * and DCX_NEEDSFONT. */ HaveComputedEntry: /* * If the window clipping flags have changed in the window * since the last time this dc was invalidated, then recompute * this dc entry. */ if ((pdce->flags & DCX_MATCHMASK) != (flags & DCX_MATCHMASK)) goto RecomputeEntry; /* * Let's check these assertions just in case... */ UserAssert(pdce); UserAssert(*ppdce == pdce); UserAssert(pdce->flags & DCX_INUSE); UserAssert(!(pdce->flags & DCX_INVALID)); UserAssert((pdce->flags & DCX_MATCHMASK) == (flags & DCX_MATCHMASK)); /* * Move the dce to the head of the list so it's easy to find later. */ if (pdce != gpDispInfo->pdceFirst) { *ppdce = pdce->pdceNext; pdce->pdceNext = gpDispInfo->pdceFirst; gpDispInfo->pdceFirst = pdce; } /* * Time to deal with DCX_INTERSECTRGN or DCX_EXCLUDERGN. * * We handle these two bits specially, because cache entries * with these bits set cannot be reused with the bits set. This * is because the area described in hrgnClip would have to be * compared along with the bit, which is a pain, especially since * they'd never match very often anyhow. * * What we do instead is to save the visrgn of the window before * applying either of these two flags, which is then restored * at ReleaseCacheDC() time, along with the clearing of these bits. * This effectively converts a cache entry with either of these * bits set into a "normal" cache entry that can be matched. */ if (flags & DCX_INTERSECTRGN) { if (hrgnClip != MAXREGION) { /* * Save the visrgn for reuse on ReleaseDC(). * (do this BEFORE we set hrgnClip & pdce->flag bit, * so that if a DCHook() callback occurs it recalculates * without hrgnClip) */ #ifdef DEBUG if (pdce->hrgnSavedVis != NULL) RIPMSG0(RIP_ERROR, "Nested SaveVisRgn attempt in _GetDCEx"); #endif /* * get the current vis region into hrgnSavedVis. Temporarily * store a dummy one in the DC. */ pdce->hrgnSavedVis = GreCreateRectRgn(0,0,0,0); GreSelectVisRgn(pdce->hdc,pdce->hrgnSavedVis, NULL, SVR_SWAP); pdce->hrgnClip = hrgnClip; if (flags & DCX_NODELETERGN) pdce->flags |= DCX_NODELETERGN; pdce->flags |= DCX_INTERSECTRGN; if (hrgnClip == NULL) { GreSetRectRgn(hrgnGDC, 0, 0, 0, 0); pdce->hrgnClipPublic = NULL; } else { IntersectRgn(hrgnGDC, pdce->hrgnSavedVis, hrgnClip); /* * Make a copy of the hrgnClip and make it public * so that we can use it in calculations in HungDraw. */ pdce->hrgnClipPublic = GreCreateRectRgn(0, 0, 0, 0); CopyRgn(pdce->hrgnClipPublic, hrgnClip); GreSetRegionOwner(pdce->hrgnClipPublic, OBJECT_OWNER_PUBLIC); } /* * Clear the SAVEDRGNINVALID bit, since we're just * about to set it properly now. If the dce later * gets invalidated, it'll set this bit so we know * to recompute it when we restore the visrgn. */ pdce->flags &= ~DCX_SAVEDRGNINVALID; /* * Select in the new region. we use the SWAP_REGION mode * so that hrgnGDC always has a valid rgn */ GreSelectVisRgn(pdce->hdc, hrgnGDC, NULL, SVR_SWAP); } } else if (flags & DCX_EXCLUDERGN) { if (hrgnClip != NULL) { /* * Save the visrgn for reuse on ReleaseDC(). * (do this BEFORE we set hrgnClip & pdce->flag bit, * so that if a DCHook() callback occurs it recalculates * without hrgnClip) */ #ifdef DEBUG if (pdce->hrgnSavedVis != NULL) RIPMSG0(RIP_ERROR, "Nested SaveVisRgn attempt in _GetDCEx"); #endif /* * get the current vis region into hrgnSavedVis. Temporarily * store a dummy one in the DC. */ pdce->hrgnSavedVis = GreCreateRectRgn(0,0,0,0); GreSelectVisRgn(pdce->hdc,pdce->hrgnSavedVis, NULL, SVR_SWAP); pdce->hrgnClip = hrgnClip; if (flags & DCX_NODELETERGN) pdce->flags |= DCX_NODELETERGN; pdce->flags |= DCX_EXCLUDERGN; if (hrgnClip == MAXREGION) { GreSetRectRgn(hrgnGDC, 0, 0, 0, 0); pdce->hrgnClipPublic = MAXREGION; } else { SubtractRgn(hrgnGDC, pdce->hrgnSavedVis, hrgnClip); /* * Make a copy of the hrgnClip and make it public * so that we can use it in calculations in HungDraw. */ pdce->hrgnClipPublic = GreCreateRectRgn(0, 0, 0, 0); CopyRgn(pdce->hrgnClipPublic, hrgnClip); GreSetRegionOwner(pdce->hrgnClipPublic, OBJECT_OWNER_PUBLIC); } /* * Clear the SAVEDRGNINVALID bit, since we're just * about to set it properly now. If the dce later * gets invalidated, it'll set this bit so we know * to recompute it when we restore the visrgn. */ pdce->flags &= ~DCX_SAVEDRGNINVALID; /* * Select in the new region. we use the SWAP_REGION mode * so that hrgnGDC always has a valid rgn */ GreSelectVisRgn(pdce->hdc, hrgnGDC, NULL, SVR_SWAP); } } } /* * Whew! Set ownership and return the bloody DC. * Only set ownership for cache dcs. Own dcs have already been owned. * The reason why we don't want to set the ownership over again is * because the console sets its owndcs to PUBLIC so gdisrv can use * them without asserting. We don't want to set the ownership back * again. */ if (pdce->flags & DCX_CACHE) { if (!GreSetDCOwner(pdce->hdc, OBJECT_OWNER_CURRENT)) { RIPMSG1(RIP_WARNING, "GetDCEx: SetDCOwner Failed %lX", pdce->hdc); } /* * Decrement the Free DCE Count. This should always be >= 0, * since we'll create a new dce if the cache is all in use. */ gnDCECount--; UserAssert(gnDCECount >= 0); pdce->ptiOwner = PtiCurrent(); } /* * Last but not least, check to see if we need to select in the * fixed-pitch system font for an old application. We don't * want to diddle the font of CS_OWNDC or CS_CLASSDC guys, though. */ if ((flags & DCX_NEEDFONT) && (flags & DCX_CACHE)) SelectFixedFont(pdce); GreUnlockDisplay(gpDispInfo->pDevLock); /* * Keep count of DCs assigned to the window, but be sure that no * rollover occurs. If the window is freed and this count is * non-zero, a callback to the client must be made to free * the client-side DC. * * If the DC is DCX_PARENTCLIP, pdce->pwndClip == pwnd->pwndParent. * The only advantage to incrementing the DC count of the parent * also would be handle the case where a DCX_PARENTCLIP DC is * retrieved, the parent of the window is changed and then the * original parent is deleted. This would only save one trip * (at most) down the DC list in xxxFreeWindow so it's not * worth the overhead. */ if (pwnd->cDC != DCE_SIZE_DCLIMIT) { ++pwnd->cDC; } return pdce->hdc; } /***************************************************************************\ * ReleaseCacheDC * * * History: * 17-Jul-1991 DarrinM Ported from Win 3.1 sources. * 20-Dec-1995 ChrisWil Added (hrgnClipPublic) entry. \***************************************************************************/ UINT ReleaseCacheDC( HDC hdc, BOOL fEndPaint) { PDCE pdce; PDCE *ppdce; for (ppdce = &gpDispInfo->pdceFirst; (pdce = *ppdce); ppdce = &pdce->pdceNext) { if (pdce->hdc == hdc) { /* * Check for redundant releases or release of an invalid entry */ if ((pdce->flags & (DCX_DESTROYTHIS | DCX_INVALID | DCX_INUSE)) != DCX_INUSE) return DCE_NORELEASE; /* * Lock the display since we may be playing with visrgns. */ GreLockDisplay(gpDispInfo->pDevLock); /* * If we have an EXCLUDERGN or INTERSECTRGN cache entry, * convert it back to a "normal" cache entry by restoring * the visrgn and blowing away hrgnClip. * * Note that for non-DCX_CACHE DCs, we only do this if * we're being called from EndPaint(). */ if ((pdce->flags & (DCX_EXCLUDERGN | DCX_INTERSECTRGN)) && ((pdce->flags & DCX_CACHE) || fEndPaint)) { DeleteHrgnClip(pdce); } /* * Decrement references to this DC. If we get to the * DCE_SIZE_DCLIMIT, keep the count so that xxxFreeWindow will * be sure to the callback to delete the client-side DC. */ if (pdce->pwndOrg->cDC != DCE_SIZE_DCLIMIT) { --pdce->pwndOrg->cDC; } /* * If this is a permanent DC, then don't reset its state. */ if (pdce->flags & DCX_CACHE) { /* * Restore the DC state and mark the entry as not in use. * Set owner back to server as well, since it's going back * into the cache. */ if (!(pdce->flags & DCX_NORESETATTRS)) { /* * If bSetupDC() failed, the DC is busy (ie. in-use * by another thread), so don't release it. */ if ( (!(GreCleanDC(hdc))) || (!(GreSetDCOwner(hdc, OBJECT_OWNER_NONE))) ) { GreUnlockDisplay(gpDispInfo->pDevLock); return DCE_NORELEASE; } } else if (!GreSetDCOwner(pdce->hdc, OBJECT_OWNER_NONE)) { GreUnlockDisplay(gpDispInfo->pDevLock); return DCE_NORELEASE; } pdce->ptiOwner = NULL; pdce->flags &= ~DCX_INUSE; /* * Increment the Free DCE count. This holds the count * of available DCEs. Check the threshold, and destroy * the dce if it's above the mark. */ if (++gnDCECount > DCE_SIZE_CACHETHRESHOLD) { if (DestroyCacheDC(ppdce, pdce->hdc)) { GreUnlockDisplay(gpDispInfo->pDevLock); return DCE_FREED; } } } GreUnlockDisplay(gpDispInfo->pDevLock); return DCE_RELEASED; } } /* * Yell if DC couldn't be found... */ RIPERR0(ERROR_DC_NOT_FOUND, RIP_VERBOSE, ""); return DCE_NORELEASE; } /***************************************************************************\ * CreateCacheDC * * * History: * 17-Jul-1991 DarrinM Ported from Win 3.1 sources. * 20-Dec-1995 ChrisWil Added (hrgnClipPublic) entry. \***************************************************************************/ HDC CreateCacheDC( PWND pwndOrg, DWORD flags) { PDCE pdce; HDC hdc; if ((pdce = (PDCE)UserAllocPool(sizeof(DCE), TAG_DCE)) == NULL) return NULL; if ((hdc = GreCreateDisplayDC(gpDispInfo->hDev, DCTYPE_DIRECT, FALSE)) == NULL) { UserFreePool(pdce); return NULL; } /* * Mark it as undeleteable so no application can delete it out of our * cache! */ GreMarkUndeletableDC(hdc); if (flags & DCX_OWNDC) { /* * Set the ownership of owndcs immediately: that way console can set * the owernship to PUBLIC when it calls GetDC so that both the input * thread and the service threads can use the same owndc. */ GreSetDCOwner(hdc, OBJECT_OWNER_CURRENT); pdce->ptiOwner = PtiCurrent(); } else { /* * Otherwise it is a cache dc... set its owner to none - nothing * is using it - equivalent of "being in the cache" but unaccessible * to other processes. */ GreSetDCOwner(hdc, OBJECT_OWNER_NONE); pdce->ptiOwner = NULL; /* * Increment the available-cacheDC count. Once this hits our * threshold, then we can free-up some of the entries. */ gnDCECount++; } /* * Link this entry into the cache entry list. */ pdce->pdceNext = gpDispInfo->pdceFirst; gpDispInfo->pdceFirst = pdce; pdce->hdc = hdc; pdce->flags = flags; pdce->pwndOrg = pwndOrg; pdce->pwndClip = pwndOrg; pdce->hrgnClip = NULL; pdce->hrgnClipPublic = NULL; pdce->hrgnSavedVis = NULL; /* * If we're creating a permanent DC, then compute it now. */ if (!(flags & DCX_CACHE)) { /* * Set up the class DC now... */ if (TestCF(pwndOrg, CFCLASSDC)) pwndOrg->pcls->pdce = pdce; /* * Finish setting up DCE and force eventual visrgn calculation. */ UserAssert(!(flags & DCX_WINDOW)); pdce->flags |= DCX_INUSE; InvalidateDce(pdce); } /* * If there are any spb's around then enable bounds accumulation. */ if (AnySpbs()) GreGetBounds(pdce->hdc, NULL, DCB_ENABLE | DCB_SET | DCB_WINDOWMGR); return pdce->hdc; } /***************************************************************************\ * WindowFromCacheDC * * * History: * 17-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/ PWND WindowFromCacheDC( HDC hdc) { PDCE pdce; for (pdce = gpDispInfo->pdceFirst; pdce; pdce = pdce->pdceNext) { if (pdce->hdc == hdc) return (pdce->flags & DCX_DESTROYTHIS) ? NULL : pdce->pwndOrg; } return NULL; } /***************************************************************************\ * DelayedDestroyCacheDC * * * History: * 16-Jun-1992 DavidPe Created. \***************************************************************************/ VOID DelayedDestroyCacheDC(VOID) { PDCE *ppdce; PDCE pdce; /* * Zip through the cache looking for a DCX_DESTROYTHIS hdc. */ for (ppdce = &gpDispInfo->pdceFirst; *ppdce != NULL; ) { /* * If we found a DCE on this thread that we tried to destroy * earlier, try and destroy it again. */ pdce = *ppdce; if (pdce->flags & DCX_DESTROYTHIS) DestroyCacheDC(ppdce, pdce->hdc); /* * Step to the next DC. If the DC was deleted, there * is no need to calculate address of the next entry. */ if (pdce == *ppdce) ppdce = &pdce->pdceNext; } PpiCurrent()->W32PF_Flags &= ~W32PF_OWNDCCLEANUP; } /***************************************************************************\ * DestroyCacheDC * * * History: * 17-Jul-1991 DarrinM Ported from Win 3.1 sources. * 20-Dec-1995 ChrisWil Added (hrgnClipPublic) entry. \***************************************************************************/ BOOL DestroyCacheDC( PDCE *ppdce, HDC hdc) { PDCE pdce; /* * Zip through the cache looking for hdc. */ if (ppdce == NULL) { for (ppdce = &gpDispInfo->pdceFirst; (pdce = *ppdce); ppdce = &pdce->pdceNext) { if (pdce->hdc == hdc) break; } } if (ppdce == NULL) return FALSE; /* * Set this here so we know this DCE is supposed to be deleted. */ pdce = *ppdce; pdce->flags |= DCX_DESTROYTHIS; /* * Free up the dce object and contents. */ if (pdce->hrgnClip != NULL) { GreDeleteObject(pdce->hrgnClip); pdce->hrgnClip = NULL; } if (pdce->hrgnClipPublic != NULL) { GreDeleteObject(pdce->hrgnClipPublic); pdce->hrgnClipPublic = NULL; } if (pdce->hrgnSavedVis != NULL) { GreDeleteObject(pdce->hrgnSavedVis); pdce->hrgnSavedVis = NULL; } /* * If GreSetDCOwner() or GreDeleteDC() fail, the * DC is in-use by another thread. Set * W32PF_OWNDCCLEANUP so we know to scan for and * delete this DCE later. */ if (!GreSetDCOwner(hdc, OBJECT_OWNER_PUBLIC)) { PpiCurrent()->W32PF_Flags |= W32PF_OWNDCCLEANUP; return FALSE; } /* * Set the don't rip flag so our routine RipIfCacheDC() doesn't * rip (called back from gdi). */ pdce->flags |= DCX_DONTRIPONDESTROY; #ifdef DEBUG GreMarkDeletableDC(hdc); // So GRE doesn't RIP. #endif if (!GreDeleteDC(hdc)) { #ifdef DEBUG GreMarkUndeletableDC(hdc); #endif pdce->flags &= ~DCX_DONTRIPONDESTROY; PpiCurrent()->W32PF_Flags |= W32PF_OWNDCCLEANUP; return FALSE; } /* * Decrement this dc-entry from the free-list count. */ if (pdce->flags & DCX_CACHE) { if (!(pdce->flags & DCX_INUSE)) gnDCECount--; #ifdef DEBUG if (gnDCECount < 0) RIPMSG1(RIP_ERROR, "DCE Over Decrement: count == %d\n", gnDCECount); #endif } #ifdef DEBUG pdce->pwndOrg = NULL; pdce->pwndClip = NULL; #endif /* * Unlink the DCE from the list. */ *ppdce = pdce->pdceNext; UserFreePool(pdce); return TRUE; } /***************************************************************************\ * InvalidateGDIWindows * * * History: \***************************************************************************/ VOID InvalidateGDIWindows( PWND pwnd) { if (pwnd != NULL) { if (pwnd->pwo != NULL) { HRGN hrgnClient = NULL; CalcVisRgn(&hrgnClient, pwnd, pwnd, DCX_CLIPCHILDREN | DCX_CLIPSIBLINGS); GreSetClientRgn(pwnd->pwo, hrgnClient, &(pwnd->rcClient)); } pwnd = pwnd->spwndChild; while (pwnd != NULL) { InvalidateGDIWindows(pwnd); pwnd = pwnd->spwndNext; } } } /***************************************************************************\ * InvalidateDCCache * * This function is called when the visrgn of a window is changing for * some reason. It is responsible for ensuring that all of the cached * visrgns in the DC cache that are affected by the visrgn change are * invalidated. * * Operations that affect the visrgn of a window (i.e., things that better * call this routine one way or another:) * * Hiding or showing self or parent * Moving, sizing, or Z-order change of self or parent * Minimizing or unminimizing self or parent * Screen or paint locking of self or parent * LockWindowUpdate of self or parent * * Invalidates any cache entries associated with pwnd and/or any children of * pwnd by either recalcing them on the fly if they're in use, or causing * them to be recalced later. * * History: * 17-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/ BOOL InvalidateDCCache( PWND pwndInvalid, DWORD flags) { PWND pwnd; PDCE pdce; PTHREADINFO ptiCurrent = PtiCurrent(); /* * Invalidation implies screen real estate is changing so we must * jiggle the mouse, because the a different window may be underneath * the mouse, which needs to get a mouse move in order to change the * mouse pointer. * * The check for the tracking is added for full-drag-windows. In doing * full-drag, BltValidBits() is called from setting the window-pos. * This resulted in an extra-mousemove being queued from this routine. * So, when we're tracking, don't queue a mousemove. This pointer is * null when tracking is off, so it won't effect the normal case. */ if (!(ptiCurrent->TIF_flags & TIF_MOVESIZETRACKING)) SetFMouseMoved(); /* * The visrgn of pwnd is changing. First see if a change to this * visrgn will also affect other window's visrgns: * * 1) if parent is clipchildren, we need to invalidate parent * 2) if clipsiblings, we need to invalidate our sibling's visrgns. * * We don't optimize the case where we're NOT clipsiblings, and our * parent is clipchildren: very rare case. * We also don't optimize the fact that a clipsiblings window visrgn * change only affects the visrgns of windows BELOW it. */ if (flags & IDC_DEFAULT) { flags = 0; if ((pwndInvalid->spwndParent != NULL) && (pwndInvalid != PWNDDESKTOP(pwndInvalid))) { /* * If the parent is a clip-children window, then * a change to our visrgn will affect his visrgn, and * possibly those of our siblings. So, invalidate starting * from our parent. Note that we don't need to invalidate * any window DCs associated with our parent. */ if (TestWF(pwndInvalid->spwndParent, WFCLIPCHILDREN)) { flags = IDC_CLIENTONLY; pwndInvalid = pwndInvalid->spwndParent; } else if (TestWF(pwndInvalid, WFCLIPSIBLINGS)) { /* * If we are clip-siblings, chances are that our siblings are * too. A change to our visrgn might affect our siblings, * so invalidate all of our siblings. * * NOTE! This code assumes that if pwndInvalid is NOT * CLIPSIBLINGs, that either it does not overlap other * CLIPSIBLINGs windows, or that none of the siblings are * CLIPSIBLINGs. This is a reasonable assumption, because * mixing CLIPSIBLINGs and non CLIPSIBLINGs windows that * overlap is generally unpredictable anyhow. */ flags = IDC_CHILDRENONLY; pwndInvalid = pwndInvalid->spwndParent; } } } /* * Go through the list of DCE's, looking for any that need to be * invalidated or recalculated. Basically, any DCE that contains * a window handle that is equal to pwndInvalid or a child of pwndInvalid * needs to be invalidated. */ for (pdce = gpDispInfo->pdceFirst; pdce; pdce = pdce->pdceNext) { if (pdce->flags & (DCX_INVALID | DCX_DESTROYTHIS)) continue; /* * HACK ALERT * * A minimized client DC must never exclude its children, even if * its WS_CLIPCHILDREN bit is set. For CS_OWNDC windows we must * update the flags of the DCE to reflect the change in window state * when the visrgn is eventually recomputed. */ if (!(pdce->flags & (DCX_CACHE | DCX_WINDOW))) { if (TestWF(pdce->pwndOrg, WFCLIPCHILDREN)) pdce->flags |= DCX_CLIPCHILDREN; if (TestWF(pdce->pwndOrg, WFMINIMIZED)) pdce->flags &= ~DCX_CLIPCHILDREN; } /* * This code assumes that if pdce->pwndClip != pdce->pwndOrg, * that pdce->pwndClip == pdce->pwndOrg->spwndParent. To ensure * that both windows are visited, we start the walk upwards from * the lower of the two, or pwndOrg. */ UserAssert((pdce->pwndClip == pdce->pwndOrg) || (pdce->pwndClip == pdce->pwndOrg->spwndParent)); /* * Walk upwards from pdce->pwndOrg, to see if we encounter * pwndInvalid. */ for (pwnd = pdce->pwndOrg; pwnd; pwnd = pwnd->spwndParent) { if (pwnd == pwndInvalid) { if (pwndInvalid == pdce->pwndOrg) { /* * Ignore DCEs for pwndInvalid if IDC_CHILDRENONLY. */ if (flags & IDC_CHILDRENONLY) continue; /* * Ignore window DCEs for pwndInvalid if IDC_CLIENTONLY */ if ((flags & IDC_CLIENTONLY) && (pdce->flags & DCX_WINDOW)) continue; } InvalidateDce(pdce); } } } /* * Update WNDOBJs in gdi if they exist. */ GreLockDisplay(gpDispInfo->pDevLock); if (gcountPWO != 0) InvalidateGDIWindows(pwndInvalid); GreClientRgnUpdated(gcountPWO != 0); GreUnlockDisplay(gpDispInfo->pDevLock); return TRUE; } /***************************************************************************\ * _WindowFromDC (API) * * Takes a dc, returns the window associated with it. * * History: * 23-Jun-1991 ScottLu Created. \***************************************************************************/ PWND _WindowFromDC( HDC hdc) { PDCE pdce; for (pdce = gpDispInfo->pdceFirst; pdce; pdce = pdce->pdceNext) { if (!(pdce->flags & DCX_INUSE) || (pdce->flags & DCX_CREATEDC)) continue; if (pdce->hdc == hdc) return pdce->pwndOrg; } return NULL; } /***************************************************************************\ * FastWindowFromDC * * * History: * 23-Jun-1991 ScottLu Created. \***************************************************************************/ PWND FastWindowFromDC( HDC hdc) { PDCE *ppdce; PDCE pdceT; if ((gpDispInfo->pdceFirst->hdc == hdc) && (gpDispInfo->pdceFirst->flags & DCX_INUSE)) { return gpDispInfo->pdceFirst->pwndOrg; } for (ppdce = &gpDispInfo->pdceFirst; *ppdce; ppdce = &(*ppdce)->pdceNext) { if (((*ppdce)->hdc == hdc) && ((*ppdce)->flags & DCX_INUSE)) { /* * Unlink/link to make it first. */ pdceT = *ppdce; *ppdce = pdceT->pdceNext; pdceT->pdceNext = gpDispInfo->pdceFirst; gpDispInfo->pdceFirst = pdceT; return pdceT->pwndOrg; } } return NULL; } /***************************************************************************\ * RipIfCacheDC * * * History: \***************************************************************************/ #ifdef DEBUG VOID RipIfCacheDC( HDC hdc) { PDCE pdce; /* * This is called on debug systems by gdi when it is destroying a dc * to make sure it isn't in the cache. */ EnterCrit(); for (pdce = gpDispInfo->pdceFirst; pdce; pdce = pdce->pdceNext) { if (pdce->hdc == hdc && !(pdce->flags & DCX_DONTRIPONDESTROY)) { RIPMSG1(RIP_ERROR, "Deleting DC in DC cache - contact JohnC. hdc == %08lx\n", pdce->hdc); } } LeaveCrit(); } #endif