Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2335 lines
72 KiB

/****************************** 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;
}