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