|
|
/****************************** 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; i<nRects; i++){ Saveleft = lpR->left; 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; }
|