/****************************** Module Header ******************************\ * Module Name: dc.c * * Copyright (c) 1985 - 1999, 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 /* * DBG Related Information. */ #if DBG BOOL fDisableCache; // TRUE to disable DC cache. #endif /***************************************************************************\ * DecrementFreeDCECount * \***************************************************************************/ __inline VOID DecrementFreeDCECount( VOID) { UserAssert(gnDCECount >= 0); gnDCECount--; } /***************************************************************************\ * IncrementFreeDCECount * \***************************************************************************/ __inline VOID IncrementFreeDCECount( VOID) { UserAssert(gnDCECount >= 0); gnDCECount++; } /***************************************************************************\ * SetMonitorRegion * * The region is in meta dc coordinates, so convert to monitor coords. \***************************************************************************/ VOID SetMonitorRegion( PMONITOR pMonitor, HRGN hrgnDst, HRGN hrgnSrc) { if (IntersectRgn(hrgnDst, hrgnSrc, pMonitor->hrgnMonitor) == ERROR) { GreSetRectRgn(hrgnDst, 0, 0, 0, 0); return; } GreOffsetRgn(hrgnDst, -pMonitor->rcMonitor.left, -pMonitor->rcMonitor.top); } /***************************************************************************\ * ResetOrg * * Resets the origin of the DC associated with *pdce, and selects * a new visrgn. * * History: * 17-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/ VOID ResetOrg( HRGN hrgn, PDCE pdce, BOOL fSetVisRgn) { RECT rc; PWND pwndLayer; /* * For compatibility purposes, make sure that the DC's for the * desktop windows originate at the primary monitor, i.e. (0,0). */ if (GETFNID(pdce->pwndOrg) == FNID_DESKTOP) { rc.left = rc.top = 0; rc.right = SYSMET(CXVIRTUALSCREEN); rc.bottom = SYSMET(CYVIRTUALSCREEN); } else if (pdce->DCX_flags & DCX_WINDOW) { rc = pdce->pwndOrg->rcWindow; } else { rc = pdce->pwndOrg->rcClient; } if (pdce->pMonitor != NULL) { OffsetRect(&rc, -pdce->pMonitor->rcMonitor.left, -pdce->pMonitor->rcMonitor.top); if (hrgn != NULL) { SetMonitorRegion(pdce->pMonitor, hrgn, hrgn); } } if (((pwndLayer = GetStyleWindow(pdce->pwndOrg, WEFPREDIRECTED)) != NULL) && (pdce->DCX_flags & DCX_REDIRECTED)) { int x = pwndLayer->rcWindow.left; int y = pwndLayer->rcWindow.top; /* * For layered redirection DCs, the surface origin is the * window origin, so offset both the rectangle and the * region appropriately. */ OffsetRect(&rc, -x, -y); if (hrgn != NULL) { GreOffsetRgn(hrgn, -x, -y); } } else if (GetStyleWindow(pdce->pwndOrg, WEFLAYERED) != NULL) { /* * Layered windows can only draw to the screen via the redirection * DCs or UpdateLayeredWindow, so select an empty visrgn into this * screen DC. */ if (hrgn != NULL) { GreSetRectRgn(hrgn, 0, 0, 0, 0); } } GreSetDCOrg(pdce->hdc, rc.left, rc.top, (PRECTL)&rc); if (fSetVisRgn) { GreSelectVisRgn(pdce->hdc, hrgn, SVR_DELETEOLD); } } /***************************************************************************\ * 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); } /* * The thread has no desktop. Fail the call. */ return NULL; } return _GetDCEx(pwnd, NULL, 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); } /***************************************************************************\ * _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) { 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; HRGN hrgnClipPublic; BOOL fTempPublic; PWND pwndLayer; /* * 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->DCX_flags)) { pdce->DCX_flags |= DCX_PWNDORGINVISIBLE; } else { pdce->DCX_flags &= ~DCX_PWNDORGINVISIBLE; } /* * For redirected windows, hrgnClipPublic was offset to 0,0 in _GetDCEx() * because all coordinates in the DC being used are relative to the * bitmap and not the screen. But the region we just got from CalcVisRgn() * is in screen coordinates. So we need to offset hrgnClipPublic back into * screen coordinates so that we can properly intersect it. */ if ((pdce->hrgnClipPublic > HRGN_SPECIAL_LAST) && ((pwndLayer = GetStyleWindow(pdce->pwndOrg, WEFPREDIRECTED)) != NULL)) { hrgnClipPublic = CreateEmptyRgnPublic(); CopyRgn(hrgnClipPublic, pdce->hrgnClipPublic); GreOffsetRgn(hrgnClipPublic, pwndLayer->rcWindow.left, pwndLayer->rcWindow.top); fTempPublic = TRUE; } else { hrgnClipPublic = pdce->hrgnClipPublic; fTempPublic = FALSE; } /* * Deal with INTERSECTRGN and EXCLUDERGN. */ if (pdce->DCX_flags & DCX_INTERSECTRGN) { UserAssert(hrgnClipPublic != HRGN_FULL); if (hrgnClipPublic == NULL) { SetEmptyRgn(hrgn); } else { IntersectRgn(hrgn, hrgn, hrgnClipPublic); } } else if (pdce->DCX_flags & DCX_EXCLUDERGN) { UserAssert(hrgnClipPublic != NULL); if (hrgnClipPublic == HRGN_FULL) { SetEmptyRgn(hrgn); } else { SubtractRgn(hrgn, hrgn, hrgnClipPublic); } } ResetOrg(hrgn, pdce, TRUE); if (fTempPublic) { GreDeleteObject(hrgnClipPublic); } } /***************************************************************************\ * 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, BOOL bWindowInsteadOfClient) { HRGN hrgnClient = (HRGN)NULL; PWND pwnd; /* * Must be in critical section. */ CheckCritIn(); if (pwnd = ValidateHwnd(hwnd)) { if (bWindowInsteadOfClient) { /* * Never clip children for WO_RGN_WINDOW so that NetMeeting * gets the unioned window area: */ CalcVisRgn(&hrgnClient, pwnd, pwnd, DCX_WINDOW | (TestWF(pwnd, WFCLIPSIBLINGS) ? DCX_CLIPSIBLINGS : 0)); } else { CalcVisRgn(&hrgnClient, pwnd, pwnd, DCX_CLIPCHILDREN | DCX_CLIPSIBLINGS); } *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)) { RIPMSG0(RIP_WARNING, "UserGetHwnd: Bad OpenGL window style or class"); return FALSE; } } /* * Return the hwnd with the correct styles for a device format window. */ *phwnd = HW(pwnd); *ppwo = _GetProp(pwnd, PROP_WNDOBJ, TRUE); return TRUE; } /***************************************************************************\ * UserAssociateHwnd * * Associate a gdi WNDOBJ with hwnd. The caller must enter the user * critical section before calling this function. * * If 'pwo' is NULL, the association is removed. * * History: * 13-Jan-1994 HockL Created. \***************************************************************************/ VOID UserAssociateHwnd( HWND hwnd, PVOID pwo) { PWND pwnd; /* * Must be in critical section. */ CheckCritIn(); if (pwnd = ValidateHwnd(hwnd)) { if (pwo != NULL) { if (InternalSetProp(pwnd, PROP_WNDOBJ, pwo, PROPF_INTERNAL | PROPF_NOPOOL)) gcountPWO++; } else { if (InternalRemoveProp(pwnd, PROP_WNDOBJ, TRUE)) gcountPWO--; } } } /***************************************************************************\ * UserReleaseDC * * Enter's the critical section and calls _ReleaseDC. * * History: * 25-Jan-1996 ChrisWil Created comment block. \***************************************************************************/ BOOL UserReleaseDC( HDC hdc) { BOOL b; EnterCrit(); b = _ReleaseDC(hdc); LeaveCrit(); return b; } /***************************************************************************\ * InvalidateDce * * If the DCE is not in use, removes all information and marks it invalid. * Otherwise, it resets the DCE flags based on the window styles and * recalculates the vis rgn. * * History: * 17-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/ VOID InvalidateDce( PDCE pdce) { GreLockDisplay(gpDispInfo->hDev); if (!(pdce->DCX_flags & DCX_INUSE)) { /* * Accumulate any bounds for this CE * since we're about to mark it invalid. */ SpbCheckDce(pdce); MarkDCEInvalid(pdce); 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, 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->DCX_flags &= ~(DCX_CLIPCHILDREN | DCX_CLIPSIBLINGS); /* * Chicago stuff... */ if (TestCF(pwndOrg, CFPARENTDC) && (TestWF(pwndOrg, WFWIN31COMPAT) || !TestWF(pwndClip, WFCLIPCHILDREN)) && (TestWF(pwndOrg, WFVISIBLE) == TestWF(pwndClip, WFVISIBLE))) { if (TestWF(pwndClip, WFCLIPSIBLINGS)) pdce->DCX_flags |= DCX_CLIPSIBLINGS; } else { if (TestWF(pwndOrg, WFCLIPCHILDREN) && !TestWF(pwndOrg, WFMINIMIZED)) pdce->DCX_flags |= DCX_CLIPCHILDREN; if (TestWF(pwndOrg, WFCLIPSIBLINGS)) pdce->DCX_flags |= DCX_CLIPSIBLINGS; } /* * Mark that any saved visrgn needs to be recomputed. */ pdce->DCX_flags |= DCX_SAVEDRGNINVALID; UserSetDCVisRgn(pdce); } GreUnlockDisplay(gpDispInfo->hDev); } /***************************************************************************\ * DeleteHrgnClip * * Deletes the clipping regions in the DCE, restores the saved visrgn, * and invalidates the DCE if saved visrgn is invalid. * * 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->DCX_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->DCX_flags & DCX_NODELETERGN)) { DeleteMaybeSpecialRgn(pdce->hrgnClip); } else { pdce->DCX_flags &= ~DCX_NODELETERGN; } DeleteMaybeSpecialRgn(pdce->hrgnClipPublic); pdce->hrgnClip = NULL; pdce->hrgnClipPublic = NULL; /* * If the saved visrgn was invalidated by an InvalidateDce() * while we had it checked out, then invalidate the entry now. */ if (pdce->DCX_flags & DCX_SAVEDRGNINVALID) { InvalidateDce(pdce); /* * We've just gone through InvalidateDce, so the visrgn in the * DC has been properly reset. Simply nuke the old saved visrgn. */ if (pdce->hrgnSavedVis != NULL) { GreDeleteObject(pdce->hrgnSavedVis); pdce->hrgnSavedVis = NULL; } } else { /* * The saved visrgn is still valid, select it back into the * DC so the entry may be re-used without recomputing. */ if (pdce->hrgnSavedVis != NULL) { GreSelectVisRgn(pdce->hdc, pdce->hrgnSavedVis, SVR_DELETEOLD); pdce->hrgnSavedVis = NULL; } } } /***************************************************************************\ * 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 DCX_flags) { HRGN hrgn; HDC hdcMatch; PWND pwndClip; PWND pwndOrg; PDCE pdce; PDCE *ppdce; PDCE *ppdceNotInUse; DWORD DCX_flagsMatch; BOOL bpwndOrgVisible; PWND pwndLayer; HBITMAP hbmLayer; BOOL fVisRgnError = FALSE; /* * Lock the device while we're playing with visrgns. */ GreLockDisplay(gpDispInfo->hDev); 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 (DCX_flags & DCX_USESTYLE) { DCX_flags &= ~(DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN | DCX_PARENTCLIP); if (!(DCX_flags & DCX_WINDOW)) { if (TestCF(pwndOrg, CFPARENTDC)) DCX_flags |= DCX_PARENTCLIP; /* * If the DCX_CACHE flag is present, override OWNDC/CLASSDC. * Otherwise, calculate from appropriate style bits. */ if (!(DCX_flags & DCX_CACHE) && !TestCF(pwndOrg, CFOWNDC)) { if (TestCF(pwndOrg, CFCLASSDC)) { /* * Look for a non-cache entry that matches hdc... */ if (pwndOrg->pcls->pdce != NULL) { hdcMatch = pwndOrg->pcls->pdce->hdc; } } else { DCX_flags |= DCX_CACHE; } } if (TestWF(pwndOrg, WFCLIPCHILDREN)) DCX_flags |= DCX_CLIPCHILDREN; if (TestWF(pwndOrg, WFCLIPSIBLINGS)) DCX_flags |= DCX_CLIPSIBLINGS; /* * Minimized windows never exclude their children. */ if (TestWF(pwndOrg, WFMINIMIZED)) { DCX_flags &= ~DCX_CLIPCHILDREN; if (pwndOrg->pcls->spicn) DCX_flags |= DCX_CACHE; } } else { if (TestWF(pwndClip, WFCLIPSIBLINGS)) DCX_flags |= DCX_CLIPSIBLINGS; DCX_flags |= DCX_CACHE; /* * Window DCs never exclude children. */ } } /* * Deal with all the Win 3.0-compatible clipping rules: * * DCX_NOCLIPCHILDREN overrides: * DCX_PARENTCLIP/CS_OWNDC/CS_CLASSDC * DCX_PARENTCLIP overrides: * DCX_CLIPSIBLINGS/DCX_CLIPCHILDREN/CS_OWNDC/CS_CLASSDC */ if (DCX_flags & DCX_NOCLIPCHILDREN) { DCX_flags &= ~(DCX_PARENTCLIP | DCX_CLIPCHILDREN); DCX_flags |= DCX_CACHE; } /* * Deal with layered windows. */ if ((pwndLayer = GetStyleWindow(pwndOrg, WEFPREDIRECTED)) != NULL && (hbmLayer = GetRedirectionBitmap(pwndLayer)) != NULL) { /* * Get a layered redirection DC. */ DCX_flags |= DCX_REDIRECTED; /* * When the window we're getting the DC for is the layered and * redirected window, don't allow to clip to its parent, since * clipping must not exceed the size of the backing bitmap. */ if (pwndOrg == pwndLayer) { DCX_flags &= ~DCX_PARENTCLIP; } /* * Convert hrgnClip from screen to the redirection DC coordinates. */ if (hrgnClip > HRGN_SPECIAL_LAST) { if (DCX_flags & DCX_NODELETERGN) { HRGN hrgnClipSave = hrgnClip; hrgnClip = CreateEmptyRgnPublic(); CopyRgn(hrgnClip, hrgnClipSave); DCX_flags &= ~DCX_NODELETERGN; } GreOffsetRgn(hrgnClip, -pwndLayer->rcWindow.left, -pwndLayer->rcWindow.top); } } else { pwndLayer = NULL; hbmLayer = NULL; } if (DCX_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. */ DCX_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 (!(DCX_flags & (DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN)) && (TestWF(pwndOrg, WFWIN31COMPAT) || !TestWF(pwndParent, WFCLIPCHILDREN)) && TestWF(pwndParent, WFVISIBLE) == TestWF(pwndOrg, WFVISIBLE)) { pwndClip = pwndParent; #if DBG if (DCX_flags & DCX_CLIPCHILDREN) RIPMSG0(RIP_WARNING, "WS_CLIPCHILDREN overridden by CS_PARENTDC"); if (DCX_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!) */ DCX_flags &= ~(DCX_CLIPCHILDREN | DCX_CLIPSIBLINGS); if (TestWF(pwndClip, WFCLIPSIBLINGS)) DCX_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 (!(DCX_flags & DCX_CACHE)) { if (pwndOrg == NULL || GETPTI(pwndOrg) != PtiCurrent()) DCX_flags |= DCX_CACHE; } DCX_flagsMatch = DCX_flags & DCX_MATCHMASK; if (!(DCX_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->DCX_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->DCX_flags & (DCX_EXCLUDERGN | DCX_INTERSECTRGN)) && (DCX_flags & (DCX_EXCLUDERGN | DCX_INTERSECTRGN))) { RIPMSG0(RIP_WARNING, "Nested BeginPaint() calls, please fix Your app!"); DeleteHrgnClip(pdce); } if (pdce->DCX_flags & DCX_REDIRECTED) { /* * We're giving out the same DC again. Since it may not have * been released, transfer any accumulated bits if needed. */ UpdateRedirectedDC(pdce); /* * As this point, the DC may get converted back to a screen * DC, so we must select the screen surface back into the DC. */ UserVerify(GreSelectRedirectionBitmap(pdce->hdc, NULL)); } /* * 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 && bpwndOrgVisible && (pdce->DCX_flags & DCX_REDIRECTED) == (DCX_flags & DCX_REDIRECTED) && !(pdce->DCX_flags & DCX_PWNDORGINVISIBLE)) { goto HaveComputedEntry; } goto RecomputeEntry; } RIPMSG1(RIP_WARNING, "Couldn't find DC for %p - bad code path", pwndOrg); NullExit: GreUnlockDisplay(gpDispInfo->hDev); return NULL; } else { /* * Make a quick pass through the cache, looking for an * exact match. */ SearchAgain: #if DBG if (fDisableCache) { goto SearchFailed; } #endif /* * CONSIDER (adams): Put this check into the loop above so we don't * touch all these pages twice? */ 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->DCX_flags & (DCX_INTERSECTRGN | DCX_EXCLUDERGN)) || (pdce->DCX_flags & DCX_INUSE)); if ((pdce->pwndClip == pwndClip) && pdce->pMonitor == NULL && (DCX_flagsMatch == (pdce->DCX_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->DCX_flags & DCX_PWNDORGINVISIBLE) { continue; } /* * Set INUSE before performing any GDI operations, just * in case DCHook() has a mind to recalculate the visrgn... */ pdce->DCX_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, FALSE); } goto HaveComputedEntry; } } #if DBG 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->DCX_flags & DCX_CACHE)) continue; /* * Skip monitor-specific entires */ if (pdce->pMonitor != NULL) continue; if (pdce->DCX_flags & DCX_INVALID) { break; } else if (!(pdce->DCX_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_flags & DCX_REDIRECTED), NULL)) { 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->DCX_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->DCX_flags = DCX_flagsMatch | DCX_INUSE; #if DBG || defined(PRERELEASE) /* * We're about to select the visrgn into the DC, even though it's * not yet completely setup. Turn off the visrgn validation for now. * It will be turned on before this function returns. */ GreValidateVisrgn(pdce->hdc, FALSE); #endif /* * Now recompute the visrgn (minus any hrgnClip shenanigans) */ if (TestWF(pwndOrg, WEFPREDIRECTED)) { DCX_flagsMatch |= DCX_REDIRECTEDBITMAP; } hrgn = NULL; if (CalcVisRgn(&hrgn, pwndOrg, pwndClip, DCX_flagsMatch) == FALSE) { pdce->DCX_flags |= DCX_PWNDORGINVISIBLE; } pdce->pwndOrg = pwndOrg; pdce->pwndClip = pwndClip; pdce->hrgnClip = NULL; // Just in case... pdce->hrgnClipPublic = NULL; ResetOrg(hrgn, pdce, TRUE); if (hrgn == NULL) { fVisRgnError = TRUE; } /* * 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 */ 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->DCX_flags & DCX_MATCHMASK) != (DCX_flags & DCX_MATCHMASK)) goto RecomputeEntry; /* * Let's check these assertions just in case... */ UserAssert(pdce); UserAssert(*ppdce == pdce); UserAssert(pdce->DCX_flags & DCX_INUSE); UserAssert(!(pdce->DCX_flags & DCX_INVALID)); UserAssert((pdce->DCX_flags & DCX_MATCHMASK) == (DCX_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; } #if DBG || defined(PRERELEASE) /* * We're about to mess with the visrgn in this DC, even though it's * not yet completely setup. Turn off the visrgn validation for now. * It will be turned on before this function returns. */ GreValidateVisrgn(pdce->hdc, FALSE); #endif /* * 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 (DCX_flags & DCX_INTERSECTRGN) { if (hrgnClip != HRGN_FULL) { SetEmptyRgn(ghrgnGDC); /* * 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) */ UserAssertMsg0(!pdce->hrgnSavedVis, "Nested SaveVisRgn attempt in _GetDCEx"); /* * get the current vis region into hrgnSavedVis. Temporarily * store a dummy one in the DC. */ pdce->hrgnSavedVis = CreateEmptyRgn(); GreSelectVisRgn(pdce->hdc,pdce->hrgnSavedVis, SVR_SWAP); pdce->hrgnClip = hrgnClip; if (DCX_flags & DCX_NODELETERGN) pdce->DCX_flags |= DCX_NODELETERGN; pdce->DCX_flags |= DCX_INTERSECTRGN; if (hrgnClip == NULL) { pdce->hrgnClipPublic = NULL; } else { IntersectRgn(ghrgnGDC, 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 = CreateEmptyRgnPublic(); CopyRgn(pdce->hrgnClipPublic, hrgnClip); } /* * 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->DCX_flags &= ~DCX_SAVEDRGNINVALID; /* * Select in the new region. we use the SWAP_REGION mode * so that ghrgnGDC always has a valid rgn */ GreSelectVisRgn(pdce->hdc, ghrgnGDC, SVR_SWAP); } } else if (DCX_flags & DCX_EXCLUDERGN) { if (hrgnClip != NULL) { SetEmptyRgn(ghrgnGDC); /* * 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) */ UserAssertMsg0(!pdce->hrgnSavedVis, "Nested SaveVisRgn attempt in _GetDCEx"); /* * get the current vis region into hrgnSavedVis. Temporarily * store a dummy one in the DC. */ pdce->hrgnSavedVis = CreateEmptyRgn(); GreSelectVisRgn(pdce->hdc,pdce->hrgnSavedVis, SVR_SWAP); pdce->hrgnClip = hrgnClip; if (DCX_flags & DCX_NODELETERGN) pdce->DCX_flags |= DCX_NODELETERGN; pdce->DCX_flags |= DCX_EXCLUDERGN; if (hrgnClip == HRGN_FULL) { pdce->hrgnClipPublic = HRGN_FULL; } else { SubtractRgn(ghrgnGDC, 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 = CreateEmptyRgnPublic(); CopyRgn(pdce->hrgnClipPublic, hrgnClip); } /* * 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->DCX_flags &= ~DCX_SAVEDRGNINVALID; /* * Select in the new region. we use the SWAP_REGION mode * so that ghrgnGDC always has a valid rgn */ GreSelectVisRgn(pdce->hdc, ghrgnGDC, SVR_SWAP); } } } if (pdce->DCX_flags & DCX_REDIRECTED) { UserAssert(pwndLayer != NULL); UserAssert(hbmLayer != NULL); UserVerify(GreSelectRedirectionBitmap(pdce->hdc, hbmLayer)); /* * Enable bounds accumulation, so we know if there was any drawing * done into that DC and the actual rect we need to update when * this DC is released. */ GreGetBounds(pdce->hdc, NULL, GGB_ENABLE_WINMGR); /* * In case the visrgn couldn't be allocated, clear it in the * dc again, since we just selected a new surface. */ if (fVisRgnError) { GreSelectVisRgn(pdce->hdc, NULL, SVR_DELETEOLD); } } /* * 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->DCX_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. */ DecrementFreeDCECount(); pdce->ptiOwner = PtiCurrent(); } if (TestWF(pwnd, WEFLAYOUTRTL) && !(DCX_flags & DCX_NOMIRROR)) { GreSetLayout(pdce->hdc, -1, LAYOUT_RTL); } #if DBG || defined(PRERELEASE) GreValidateVisrgn(pdce->hdc, TRUE); #endif GreUnlockDisplay(gpDispInfo->hDev); return pdce->hdc; } /***************************************************************************\ * ReleaseCacheDC * * Releases a DC from the cache. * * 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->DCX_flags & (DCX_DESTROYTHIS | DCX_INVALID | DCX_INUSE)) != DCX_INUSE) return DCE_NORELEASE; /* * Lock the display since we may be playing with visrgns. */ GreLockDisplay(gpDispInfo->hDev); if (pdce->DCX_flags & DCX_REDIRECTED) { UpdateRedirectedDC(pdce); } /* * If this is a permanent DC, then don't reset its state. */ if (pdce->DCX_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->DCX_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->hDev); return DCE_NORELEASE; } } else if (!GreSetDCOwner(pdce->hdc, OBJECT_OWNER_NONE)) { GreUnlockDisplay(gpDispInfo->hDev); return DCE_NORELEASE; } pdce->ptiOwner = NULL; pdce->DCX_flags &= ~DCX_INUSE; #if DBG || defined(PRERELEASE) /* * Turn off checked only surface validation for now, since * we may select a different surface (screen) in this DC that * may not correspond to the visrgn currently in the DC. When * the DC is given out again, it will be revalidated. */ GreValidateVisrgn(pdce->hdc, FALSE); #endif /* * The DC is no longer in use, so unselect the redirection * bitmap from it. */ if (pdce->DCX_flags & DCX_REDIRECTED) { UserVerify(GreSelectRedirectionBitmap(pdce->hdc, NULL)); } /* * 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. */ IncrementFreeDCECount(); if (gnDCECount > DCE_SIZE_CACHETHRESHOLD) { if (DestroyCacheDC(ppdce, pdce->hdc)) { GreUnlockDisplay(gpDispInfo->hDev); return DCE_FREED; } } } /* * 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->DCX_flags & (DCX_EXCLUDERGN | DCX_INTERSECTRGN)) && ((pdce->DCX_flags & DCX_CACHE) || fEndPaint)) { DeleteHrgnClip(pdce); } GreUnlockDisplay(gpDispInfo->hDev); return DCE_RELEASED; } } /* * Yell if DC couldn't be found... */ RIPERR1(ERROR_DC_NOT_FOUND, RIP_WARNING, "Invalid device context (DC) handle passed to ReleaseCacheDC (0x%08lx)", hdc); return DCE_NORELEASE; } /***************************************************************************\ * CreateCacheDC * * Creates a DCE and adds it to the cache. * * History: * 17-Jul-1991 DarrinM Ported from Win 3.1 sources. * 20-Dec-1995 ChrisWil Added (hrgnClipPublic) entry. \***************************************************************************/ HDC CreateCacheDC( PWND pwndOrg, DWORD DCX_flags, PMONITOR pMonitor ) { PDCE pdce; HDC hdc; HANDLE hDev; if ((pdce = (PDCE)UserAllocPool(sizeof(DCE), TAG_DCE)) == NULL) return NULL; if (pMonitor == NULL) { hDev = gpDispInfo->hDev; } else { hDev = pMonitor->hDev; } if ((hdc = GreCreateDisplayDC(hDev, DCTYPE_DIRECT, FALSE)) == NULL) { UserFreePool(pdce); return NULL; } /* * Link this entry into the cache entry list. */ pdce->pdceNext = gpDispInfo->pdceFirst; gpDispInfo->pdceFirst = pdce; pdce->hdc = hdc; pdce->DCX_flags = DCX_flags; pdce->pwndOrg = pwndOrg; pdce->pwndClip = pwndOrg; pdce->hrgnClip = NULL; pdce->hrgnClipPublic = NULL; pdce->hrgnSavedVis = NULL; pdce->pMonitor = pMonitor; /* * Mark it as undeleteable so no application can delete it out of our * cache! */ GreMarkUndeletableDC(hdc); if (DCX_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. */ IncrementFreeDCECount(); } /* * If we're creating a permanent DC, then compute it now. */ if (!(DCX_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(!(DCX_flags & DCX_WINDOW)); pdce->DCX_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 * * Returns the window associated with a DC. * * 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->DCX_flags & DCX_DESTROYTHIS) ? NULL : pdce->pwndOrg; } return NULL; } /***************************************************************************\ * DelayedDestroyCacheDC * * Destroys DCE's which have been partially destroyed. * * 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->DCX_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 * * Removes a DC from the cache, freeing all resources associated * with it. * * 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->DCX_flags |= DCX_DESTROYTHIS; /* * Free up the dce object and contents. */ if (!(pdce->DCX_flags & DCX_NODELETERGN)) { DeleteMaybeSpecialRgn(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; } #if DBG GreMarkDeletableDC(hdc); // So GRE doesn't RIP. #endif if (!GreDeleteDC(hdc)) { #if DBG GreMarkUndeletableDC(hdc); #endif PpiCurrent()->W32PF_Flags |= W32PF_OWNDCCLEANUP; return FALSE; } /* * Decrement this dc-entry from the free-list count. */ if (pdce->DCX_flags & DCX_CACHE) { if (!(pdce->DCX_flags & DCX_INUSE)) { DecrementFreeDCECount(); } } #if DBG pdce->pwndOrg = NULL; pdce->pwndClip = NULL; #endif /* * Unlink the DCE from the list. */ *ppdce = pdce->pdceNext; UserFreePool(pdce); return TRUE; } /***************************************************************************\ * InvalidateGDIWindows * * Recalculates the visrgn of all descendents of pwnd on behalf of GRE. * * History: \***************************************************************************/ VOID InvalidateGDIWindows( PWND pwnd) { PVOID pwo; if (pwnd != NULL) { if ((pwo = _GetProp(pwnd, PROP_WNDOBJ, TRUE)) != NULL) { HRGN hrgnClient = NULL; if (GreWindowInsteadOfClient(pwo)) { /* * Never clip children for WO_RGN_WINDOW so that NetMeeting * gets the unioned window area: */ CalcVisRgn(&hrgnClient, pwnd, pwnd, DCX_WINDOW | (TestWF(pwnd, WFCLIPSIBLINGS) ? DCX_CLIPSIBLINGS : 0)); } else { CalcVisRgn(&hrgnClient, pwnd, pwnd, DCX_CLIPCHILDREN | DCX_CLIPSIBLINGS); } GreSetClientRgn(pwo, hrgnClient, &(pwnd->rcClient)); } pwnd = pwnd->spwndChild; while (pwnd != NULL) { InvalidateGDIWindows(pwnd); pwnd = pwnd->spwndNext; } } } /***************************************************************************\ * zzzInvalidateDCCache * * 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 zzzInvalidateDCCache( PWND pwndInvalid, DWORD flags) { PWND pwnd; PDCE pdce; PTHREADINFO ptiCurrent = PtiCurrent(); TL tlpwndInvalid; FLONG fl; /* * Invalidation implies screen real estate is changing so we must * jiggle the mouse, because 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, zzzBltValidBits() 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. */ ThreadLockAlwaysWithPti(ptiCurrent, pwndInvalid, &tlpwndInvalid); if (!(ptiCurrent->TIF_flags & TIF_MOVESIZETRACKING) && !(flags & IDC_NOMOUSE)) { #ifdef REDIRECTION if (!IsGlobalHooked(ptiCurrent, WHF_FROM_WH(WH_HITTEST))) #endif // REDIRECTION zzzSetFMouseMoved(); } /* * 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->DCX_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->DCX_flags & (DCX_CACHE | DCX_WINDOW))) { if (TestWF(pdce->pwndOrg, WFCLIPCHILDREN)) pdce->DCX_flags |= DCX_CLIPCHILDREN; if (TestWF(pdce->pwndOrg, WFMINIMIZED)) pdce->DCX_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. * * This can happen if someone gets a DCX_PARENTCLIP dc and then * changes the parent. */ #if DBG if ((pdce->pwndClip != pdce->pwndOrg) && (pdce->pwndClip != pdce->pwndOrg->spwndParent)) { RIPMSG1(RIP_WARNING, "HDC %lX clipped to wrong parent", pdce->hdc); } #endif /* * 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) break; /* * Ignore window DCEs for pwndInvalid if IDC_CLIENTONLY */ if ((flags & IDC_CLIENTONLY) && (pdce->DCX_flags & DCX_WINDOW)) break; } InvalidateDce(pdce); break; } } } /* * Update WNDOBJs in gdi if they exist. */ GreLockDisplay(gpDispInfo->hDev); fl = (flags & IDC_MOVEBLT) ? GCR_DELAYFINALUPDATE : 0; if (gcountPWO != 0) { InvalidateGDIWindows(pwndInvalid); fl |= GCR_WNDOBJEXISTS; } GreClientRgnUpdated(fl); GreUpdateSpriteVisRgn(gpDispInfo->hDev); GreUnlockDisplay(gpDispInfo->hDev); ThreadUnlock(&tlpwndInvalid); 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->DCX_flags & DCX_INUSE) || (pdce->DCX_flags & DCX_CREATEDC)) continue; if (pdce->hdc == hdc) return pdce->pwndOrg; } return NULL; } /***************************************************************************\ * FastWindowFromDC * * Returns the window associated with a DC, and puts it at the * front of the list. * * History: * 23-Jun-1991 ScottLu Created. \***************************************************************************/ PWND FastWindowFromDC( HDC hdc) { PDCE *ppdce; PDCE pdceT; if ((gpDispInfo->pdceFirst->hdc == hdc) && (gpDispInfo->pdceFirst->DCX_flags & DCX_INUSE)) { return gpDispInfo->pdceFirst->pwndOrg; } for (ppdce = &gpDispInfo->pdceFirst; *ppdce; ppdce = &(*ppdce)->pdceNext) { if (((*ppdce)->hdc == hdc) && ((*ppdce)->DCX_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; } /***************************************************************************\ * GetDCOrgOnScreen * * This function gets the DC origin of a window in screen coordinates. The * DC origin is always in the surface coordinates. For screen DCs the * surface is the screen, so their origin is already in the screen * coordinates. For redirected DCs, GreGetDCOrg will return the origin * of the DC in the redirected surface coordinates to which we will add * the origin of the redirected window that the surface is backing. * * 11/25/1998 vadimg created \***************************************************************************/ BOOL GetDCOrgOnScreen(HDC hdc, LPPOINT ppt) { if (GreGetDCOrg(hdc, ppt)) { POINT ptScreen; /* * Get the origin of the redirected window in screen coordinates. */ if (UserGetRedirectedWindowOrigin(hdc, &ptScreen)) { ppt->x += ptScreen.x; ppt->y += ptScreen.y; return TRUE; } } return FALSE; } /***************************************************************************\ * UserGetRedirectedWindowOrigin * * The DC origin is in the surface coordinates. For screen DCs, the surface * is the screen and so their origin is in the screen coordinates. But for * redirected DCs, the backing surface origin is the same as the window * being redirected. This function retrieves the screen origin of a redirected * window corresponding to a redirection DC. It returns FALSE if this isn't * a valid DC or it's not a redirected DC. * * 11/18/1998 vadimg created \***************************************************************************/ BOOL UserGetRedirectedWindowOrigin(HDC hdc, LPPOINT ppt) { PWND pwnd; PDCE pdce; if ((pdce = LookupDC(hdc)) == NULL) return FALSE; if (!(pdce->DCX_flags & DCX_REDIRECTED)) return FALSE; pwnd = GetStyleWindow(pdce->pwndOrg, WEFPREDIRECTED); ppt->x = pwnd->rcWindow.left; ppt->y = pwnd->rcWindow.top; return TRUE; } /***************************************************************************\ * LookupDC * * Validate a DC by returning a correspnding pdce. * * 11/12/1997 vadimg created \***************************************************************************/ PDCE LookupDC(HDC hdc) { PDCE pdce; for (pdce = gpDispInfo->pdceFirst; pdce != NULL; pdce = pdce->pdceNext) { if (pdce->DCX_flags & (DCX_INVALID | DCX_DESTROYTHIS)) continue; if (pdce->hdc == hdc && pdce->pMonitor == NULL && (pdce->DCX_flags & DCX_INUSE)) { return pdce; } } return NULL; } /***************************************************************************\ * GetMonitorDC * * 11/06/97 vadimg ported from Memphis \***************************************************************************/ #define DCX_LEAVEBITS (DCX_WINDOW | DCX_CLIPCHILDREN | DCX_CLIPSIBLINGS | \ DCX_PARENTCLIP | DCX_LOCKWINDOWUPDATE | DCX_NOCLIPCHILDREN | \ DCX_USESTYLE | DCX_EXCLUDEUPDATE | DCX_INTERSECTUPDATE | \ DCX_EXCLUDERGN | DCX_INTERSECTRGN) HDC GetMonitorDC(PDCE pdceOrig, PMONITOR pMonitor) { PDCE pdce; POINT pt; RECT rc; TryAgain: for (pdce = gpDispInfo->pdceFirst; pdce != NULL; pdce = pdce->pdceNext) { /* * Find an available DC for this monitor. */ if (pdce->DCX_flags & (DCX_INUSE | DCX_DESTROYTHIS)) continue; if (pdce->pMonitor != pMonitor) continue; if (!(pdce->DCX_flags & DCX_INVALID)) SpbCheckDce(pdce); /* * Copy DC properties and style bits. */ GreSetDCOwner(pdce->hdc, OBJECT_OWNER_CURRENT); pdce->pwndOrg = pdceOrig->pwndOrg; pdce->pwndClip = pdceOrig->pwndClip; pdce->ptiOwner = pdceOrig->ptiOwner; pdce->DCX_flags = (DCX_INUSE | DCX_CACHE) | (pdceOrig->DCX_flags & DCX_LEAVEBITS); if (pdceOrig->hrgnClip > HRGN_FULL) { UserAssert(pdce->hrgnClip == NULL); UserAssert(pdceOrig->DCX_flags & (DCX_INTERSECTRGN | DCX_EXCLUDERGN)); pdce->hrgnClip = CreateEmptyRgn(); SetMonitorRegion(pMonitor, pdce->hrgnClip, pdceOrig->hrgnClip); } else { pdce->hrgnClip = pdceOrig->hrgnClip; } /* * Setup the visrgn clipped to this monitor. */ GreCopyVisRgn(pdceOrig->hdc, ghrgnGDC); SetMonitorRegion(pMonitor, ghrgnGDC, ghrgnGDC); GreSelectVisRgn(pdce->hdc, ghrgnGDC, SVR_COPYNEW); GreGetDCOrgEx(pdceOrig->hdc, &pt, &rc); OffsetRect(&rc, -pMonitor->rcMonitor.left, -pMonitor->rcMonitor.top); GreSetDCOrg(pdce->hdc, rc.left, rc.top, (PRECTL)&rc); /* * Decrement the Free DCE Count. This should always be >= 0, * since we'll create a new dce if the cache is all in use. */ DecrementFreeDCECount(); return pdce->hdc; } /* * If this call succeeds a new DC will be available in the cache, * so the loop will find it and properly set it up. */ if (CreateCacheDC(NULL, DCX_INVALID | DCX_CACHE, pMonitor) == NULL) return NULL; goto TryAgain; } /***************************************************************************\ * OrderRects * * Order the rectangles, so that they flow from left to right. This is needed * when combining a mirrored region (see MirrorRegion) * * * History: \***************************************************************************/ VOID OrderRects( LPRECT lpR, int nRects) { RECT R; int i, j; // // Sort Left to right // for (i = 0; i < nRects; i++) { for (j = i + 1; j < nRects && (lpR + j)->top == (lpR + i)->top; j++) { if ((lpR + j)->left < (lpR + i)->left) { R = *(lpR + i); *(lpR + i) = *(lpR + j); *(lpR + j) = R; } } } } /***************************************************************************\ * MirrorRegion * * Mirror a region in a window. This is done by mirroring the rects that * constitute the region. 'bUseClient' param controls whether the region is a * client one or not. * * History: \***************************************************************************/ BOOL MirrorRegion( PWND pwnd, HRGN hrgn, BOOL bUseClient) { int nRects, i, nDataSize, Saveleft, cx; HRGN hrgn2 = NULL; RECT *lpR; RGNDATA *lpRgnData; BOOL bRet = FALSE; if (TestWF(pwnd, WEFLAYOUTRTL) && hrgn > HRGN_SPECIAL_LAST) { nDataSize = GreGetRegionData(hrgn, 0, NULL); if (nDataSize && (lpRgnData = (RGNDATA *)UserAllocPool(nDataSize, TAG_MIRROR))) { if (GreGetRegionData(hrgn, nDataSize, lpRgnData)) { nRects = lpRgnData->rdh.nCount; lpR = (RECT *)lpRgnData->Buffer; if (bUseClient) { cx = pwnd->rcClient.right - pwnd->rcClient.left; } else { cx = pwnd->rcWindow.right - pwnd->rcWindow.left; } Saveleft = lpRgnData->rdh.rcBound.left; lpRgnData->rdh.rcBound.left = cx - lpRgnData->rdh.rcBound.right; lpRgnData->rdh.rcBound.right = cx - Saveleft; for (i = 0; ileft; lpR->left = cx - lpR->right; lpR->right = cx - Saveleft; lpR++; } OrderRects((RECT *)lpRgnData->Buffer, nRects); hrgn2 = GreExtCreateRegion(NULL, nDataSize, lpRgnData); if (hrgn2) { GreCombineRgn(hrgn, hrgn2, NULL, RGN_COPY); GreDeleteObject((HGDIOBJ)hrgn2); bRet = TRUE; } } UserFreePool(lpRgnData); } } return bRet; }