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