mirror of
https://github.com/reactos/reactos.git
synced 2024-08-24 20:28:57 +00:00
![Hervé Poussineau](/assets/img/avatar_default.png)
Initialization of device mode list will be done later, just before switching to graphics mode. If no graphic display is available, we will now fail when starting first GUI application in co_IntGraphicsCheck(). Add a bugcheck here to prevent frozen screen.
1047 lines
26 KiB
C
1047 lines
26 KiB
C
/*
|
|
* PROJECT: ReactOS Win32k subsystem
|
|
* LICENSE: GPL - See COPYING in the top level directory
|
|
* FILE: win32ss/user/ntuser/windc.c
|
|
* PURPOSE: Window DC management
|
|
* COPYRIGHT: Copyright 2007 ReactOS Team
|
|
*/
|
|
|
|
#include <win32k.h>
|
|
DBG_DEFAULT_CHANNEL(UserDce);
|
|
|
|
/* GLOBALS *******************************************************************/
|
|
|
|
/* NOTE: I think we should store this per window station (including GDI objects) */
|
|
/* Answer: No, use the DCE pMonitor to compare with! */
|
|
|
|
static LIST_ENTRY LEDce;
|
|
static INT DCECount = 0; // Count of DCE in system.
|
|
|
|
#define DCX_CACHECOMPAREMASK (DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN | \
|
|
DCX_NORESETATTRS | DCX_LOCKWINDOWUPDATE | \
|
|
DCX_LAYEREDWIN | DCX_CACHE | DCX_WINDOW | \
|
|
DCX_PARENTCLIP)
|
|
|
|
/* FUNCTIONS *****************************************************************/
|
|
|
|
CODE_SEG("INIT")
|
|
NTSTATUS
|
|
NTAPI
|
|
InitDCEImpl(VOID)
|
|
{
|
|
InitializeListHead(&LEDce);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// This should be moved to dc.c or dcutil.c.
|
|
//
|
|
HDC FASTCALL
|
|
DceCreateDisplayDC(VOID)
|
|
{
|
|
UNICODE_STRING DriverName = RTL_CONSTANT_STRING(L"DISPLAY");
|
|
|
|
if (!co_IntGraphicsCheck(TRUE))
|
|
KeBugCheckEx(VIDEO_DRIVER_INIT_FAILURE, 0, 0, 0, USER_VERSION);
|
|
|
|
return IntGdiCreateDC(&DriverName, NULL, NULL, NULL, FALSE);
|
|
}
|
|
|
|
/* Returns the DCE pointer from the HDC handle */
|
|
DCE*
|
|
FASTCALL
|
|
DceGetDceFromDC(HDC hdc)
|
|
{
|
|
PLIST_ENTRY ListEntry;
|
|
DCE* dce;
|
|
|
|
ListEntry = LEDce.Flink;
|
|
while (ListEntry != &LEDce)
|
|
{
|
|
dce = CONTAINING_RECORD(ListEntry, DCE, List);
|
|
ListEntry = ListEntry->Flink;
|
|
if (dce->hDC == hdc)
|
|
return dce;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static
|
|
PREGION FASTCALL
|
|
DceGetVisRgn(PWND Window, ULONG Flags, HWND hWndChild, ULONG CFlags)
|
|
{
|
|
PREGION Rgn;
|
|
Rgn = VIS_ComputeVisibleRegion( Window,
|
|
0 == (Flags & DCX_WINDOW),
|
|
0 != (Flags & DCX_CLIPCHILDREN),
|
|
0 != (Flags & DCX_CLIPSIBLINGS));
|
|
/* Caller expects a non-null region */
|
|
if (!Rgn)
|
|
Rgn = IntSysCreateRectpRgn(0, 0, 0, 0);
|
|
return Rgn;
|
|
}
|
|
|
|
PDCE FASTCALL
|
|
DceAllocDCE(PWND Window OPTIONAL, DCE_TYPE Type)
|
|
{
|
|
PDCE pDce;
|
|
|
|
pDce = ExAllocatePoolWithTag(PagedPool, sizeof(DCE), USERTAG_DCE);
|
|
if(!pDce)
|
|
return NULL;
|
|
|
|
pDce->hDC = DceCreateDisplayDC();
|
|
if (!pDce->hDC)
|
|
{
|
|
ExFreePoolWithTag(pDce, USERTAG_DCE);
|
|
return NULL;
|
|
}
|
|
DCECount++;
|
|
TRACE("Alloc DCE's! %d\n",DCECount);
|
|
pDce->hwndCurrent = (Window ? Window->head.h : NULL);
|
|
pDce->pwndOrg = Window;
|
|
pDce->pwndClip = Window;
|
|
pDce->hrgnClip = NULL;
|
|
pDce->hrgnClipPublic = NULL;
|
|
pDce->hrgnSavedVis = NULL;
|
|
pDce->ppiOwner = NULL;
|
|
|
|
InsertTailList(&LEDce, &pDce->List);
|
|
|
|
DCU_SetDcUndeletable(pDce->hDC);
|
|
|
|
if (Type == DCE_WINDOW_DC || Type == DCE_CLASS_DC) // Window DCE have ownership.
|
|
{
|
|
pDce->ptiOwner = GetW32ThreadInfo();
|
|
}
|
|
else
|
|
{
|
|
TRACE("FREE DCATTR!!!! NOT DCE_WINDOW_DC!!!!! hDC-> %p\n", pDce->hDC);
|
|
GreSetDCOwner(pDce->hDC, GDI_OBJ_HMGR_NONE);
|
|
pDce->ptiOwner = NULL;
|
|
}
|
|
|
|
if (Type == DCE_CACHE_DC)
|
|
{
|
|
pDce->DCXFlags = DCX_CACHE | DCX_DCEEMPTY;
|
|
}
|
|
else
|
|
{
|
|
pDce->DCXFlags = DCX_DCEBUSY;
|
|
if (Window)
|
|
{
|
|
if (Type == DCE_WINDOW_DC)
|
|
{
|
|
if (Window->style & WS_CLIPCHILDREN) pDce->DCXFlags |= DCX_CLIPCHILDREN;
|
|
if (Window->style & WS_CLIPSIBLINGS) pDce->DCXFlags |= DCX_CLIPSIBLINGS;
|
|
}
|
|
}
|
|
}
|
|
return(pDce);
|
|
}
|
|
|
|
static VOID APIENTRY
|
|
DceSetDrawable( PWND Window OPTIONAL,
|
|
HDC hDC,
|
|
ULONG Flags,
|
|
BOOL SetClipOrigin)
|
|
{
|
|
RECTL rect = {0,0,0,0};
|
|
|
|
if (Window)
|
|
{
|
|
if (Flags & DCX_WINDOW)
|
|
{
|
|
rect = Window->rcWindow;
|
|
}
|
|
else
|
|
{
|
|
rect = Window->rcClient;
|
|
}
|
|
}
|
|
|
|
/* Set DC Origin and Window Rectangle */
|
|
GreSetDCOrg( hDC, rect.left, rect.top, &rect);
|
|
}
|
|
|
|
|
|
static VOID FASTCALL
|
|
DceDeleteClipRgn(DCE* Dce)
|
|
{
|
|
Dce->DCXFlags &= ~(DCX_EXCLUDERGN | DCX_INTERSECTRGN);
|
|
|
|
if (Dce->DCXFlags & DCX_KEEPCLIPRGN )
|
|
{
|
|
Dce->DCXFlags &= ~DCX_KEEPCLIPRGN;
|
|
}
|
|
else if (Dce->hrgnClip != NULL)
|
|
{
|
|
GreDeleteObject(Dce->hrgnClip);
|
|
}
|
|
|
|
Dce->hrgnClip = NULL;
|
|
|
|
/* Make it dirty so that the vis rgn gets recomputed next time */
|
|
Dce->DCXFlags |= DCX_DCEDIRTY;
|
|
IntGdiSetHookFlags(Dce->hDC, DCHF_INVALIDATEVISRGN);
|
|
}
|
|
|
|
VOID
|
|
FASTCALL
|
|
DceUpdateVisRgn(DCE *Dce, PWND Window, ULONG Flags)
|
|
{
|
|
PREGION RgnVisible = NULL;
|
|
ULONG DcxFlags;
|
|
PWND DesktopWindow;
|
|
|
|
if (Flags & DCX_PARENTCLIP)
|
|
{
|
|
PWND Parent;
|
|
|
|
Parent = Window->spwndParent;
|
|
if (!Parent)
|
|
{
|
|
RgnVisible = NULL;
|
|
goto noparent;
|
|
}
|
|
|
|
if (Parent->style & WS_CLIPSIBLINGS)
|
|
{
|
|
DcxFlags = DCX_CLIPSIBLINGS |
|
|
(Flags & ~(DCX_CLIPCHILDREN | DCX_WINDOW));
|
|
}
|
|
else
|
|
{
|
|
DcxFlags = Flags & ~(DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN | DCX_WINDOW);
|
|
}
|
|
RgnVisible = DceGetVisRgn(Parent, DcxFlags, Window->head.h, Flags);
|
|
}
|
|
else if (Window == NULL)
|
|
{
|
|
DesktopWindow = UserGetWindowObject(IntGetDesktopWindow());
|
|
if (NULL != DesktopWindow)
|
|
{
|
|
RgnVisible = IntSysCreateRectpRgnIndirect(&DesktopWindow->rcWindow);
|
|
}
|
|
else
|
|
{
|
|
RgnVisible = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RgnVisible = DceGetVisRgn(Window, Flags, 0, 0);
|
|
}
|
|
|
|
noparent:
|
|
if (Flags & DCX_INTERSECTRGN)
|
|
{
|
|
PREGION RgnClip = NULL;
|
|
|
|
if (Dce->hrgnClip != NULL)
|
|
RgnClip = REGION_LockRgn(Dce->hrgnClip);
|
|
|
|
if (RgnClip)
|
|
{
|
|
IntGdiCombineRgn(RgnVisible, RgnVisible, RgnClip, RGN_AND);
|
|
REGION_UnlockRgn(RgnClip);
|
|
}
|
|
else
|
|
{
|
|
if (RgnVisible != NULL)
|
|
{
|
|
REGION_Delete(RgnVisible);
|
|
}
|
|
RgnVisible = IntSysCreateRectpRgn(0, 0, 0, 0);
|
|
}
|
|
}
|
|
else if ((Flags & DCX_EXCLUDERGN) && Dce->hrgnClip != NULL)
|
|
{
|
|
PREGION RgnClip = REGION_LockRgn(Dce->hrgnClip);
|
|
IntGdiCombineRgn(RgnVisible, RgnVisible, RgnClip, RGN_DIFF);
|
|
REGION_UnlockRgn(RgnClip);
|
|
}
|
|
|
|
Dce->DCXFlags &= ~DCX_DCEDIRTY;
|
|
GdiSelectVisRgn(Dce->hDC, RgnVisible);
|
|
/* Tell GDI driver */
|
|
if (Window)
|
|
IntEngWindowChanged(Window, WOC_RGN_CLIENT);
|
|
|
|
if (RgnVisible != NULL)
|
|
{
|
|
REGION_Delete(RgnVisible);
|
|
}
|
|
}
|
|
|
|
static INT FASTCALL
|
|
DceReleaseDC(DCE* dce, BOOL EndPaint)
|
|
{
|
|
if (DCX_DCEBUSY != (dce->DCXFlags & (DCX_INDESTROY | DCX_DCEEMPTY | DCX_DCEBUSY)))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/* Restore previous visible region */
|
|
if (EndPaint)
|
|
{
|
|
DceUpdateVisRgn(dce, dce->pwndOrg, dce->DCXFlags);
|
|
}
|
|
|
|
if ((dce->DCXFlags & (DCX_INTERSECTRGN | DCX_EXCLUDERGN)) &&
|
|
((dce->DCXFlags & DCX_CACHE) || EndPaint))
|
|
{
|
|
DceDeleteClipRgn(dce);
|
|
}
|
|
|
|
if (dce->DCXFlags & DCX_CACHE)
|
|
{
|
|
if (!(dce->DCXFlags & DCX_NORESETATTRS))
|
|
{
|
|
// Clean the DC
|
|
if (!IntGdiCleanDC(dce->hDC)) return 0;
|
|
|
|
if (dce->DCXFlags & DCX_DCEDIRTY)
|
|
{
|
|
/* Don't keep around invalidated entries
|
|
* because SetDCState() disables hVisRgn updates
|
|
* by removing dirty bit. */
|
|
dce->hwndCurrent = 0;
|
|
dce->pwndOrg = NULL;
|
|
dce->pwndClip = NULL;
|
|
dce->DCXFlags &= DCX_CACHE;
|
|
dce->DCXFlags |= DCX_DCEEMPTY;
|
|
}
|
|
}
|
|
dce->DCXFlags &= ~DCX_DCEBUSY;
|
|
TRACE("Exit!!!!! DCX_CACHE!!!!!! hDC-> %p \n", dce->hDC);
|
|
if (!GreSetDCOwner(dce->hDC, GDI_OBJ_HMGR_NONE))
|
|
return 0;
|
|
dce->ptiOwner = NULL; // Reset ownership.
|
|
dce->ppiOwner = NULL;
|
|
|
|
#if 0 // Need to research and fix before this is a "growing" issue.
|
|
if (++DCECache > 32)
|
|
{
|
|
ListEntry = LEDce.Flink;
|
|
while (ListEntry != &LEDce)
|
|
{
|
|
pDCE = CONTAINING_RECORD(ListEntry, DCE, List);
|
|
ListEntry = ListEntry->Flink;
|
|
if (!(pDCE->DCXFlags & DCX_DCEBUSY))
|
|
{ /* Free the unused cache DCEs. */
|
|
DceFreeDCE(pDCE, TRUE);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
return 1; // Released!
|
|
}
|
|
|
|
|
|
HDC FASTCALL
|
|
UserGetDCEx(PWND Wnd OPTIONAL, HANDLE ClipRegion, ULONG Flags)
|
|
{
|
|
PWND Parent;
|
|
ULONG DcxFlags;
|
|
DCE* Dce = NULL;
|
|
BOOL UpdateClipOrigin = FALSE;
|
|
BOOL bUpdateVisRgn = TRUE;
|
|
HDC hDC = NULL;
|
|
PPROCESSINFO ppi;
|
|
PLIST_ENTRY ListEntry;
|
|
|
|
if (NULL == Wnd)
|
|
{
|
|
Flags &= ~DCX_USESTYLE;
|
|
Flags |= DCX_CACHE;
|
|
}
|
|
|
|
if (Flags & DCX_PARENTCLIP) Flags |= DCX_CACHE;
|
|
|
|
// When GetDC is called with hWnd nz, DCX_CACHE & _WINDOW are clear w _USESTYLE set.
|
|
if (Flags & DCX_USESTYLE)
|
|
{
|
|
Flags &= ~(DCX_CLIPCHILDREN | DCX_CLIPSIBLINGS | DCX_PARENTCLIP);
|
|
if (!(Flags & DCX_WINDOW)) // Not window rectangle
|
|
{
|
|
if (Wnd->pcls->style & CS_PARENTDC)
|
|
{
|
|
Flags |= DCX_PARENTCLIP;
|
|
}
|
|
|
|
if (!(Flags & DCX_CACHE) && // Not on the cheap wine list.
|
|
!(Wnd->pcls->style & CS_OWNDC) )
|
|
{
|
|
if (!(Wnd->pcls->style & CS_CLASSDC))
|
|
// The window is not POWNED or has any CLASS, so we are looking for cheap wine.
|
|
Flags |= DCX_CACHE;
|
|
else
|
|
{
|
|
if (Wnd->pcls->pdce) hDC = ((PDCE)Wnd->pcls->pdce)->hDC;
|
|
TRACE("We have CLASS!!\n");
|
|
}
|
|
}
|
|
|
|
if (Wnd->style & WS_CLIPSIBLINGS)
|
|
{
|
|
Flags |= DCX_CLIPSIBLINGS;
|
|
}
|
|
|
|
if (Wnd->style & WS_CLIPCHILDREN &&
|
|
!(Wnd->style & WS_MINIMIZE))
|
|
{
|
|
Flags |= DCX_CLIPCHILDREN;
|
|
}
|
|
/* If minized with icon in the set, we are forced to be cheap! */
|
|
if (Wnd->style & WS_MINIMIZE && Wnd->pcls->spicn)
|
|
{
|
|
Flags |= DCX_CACHE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Wnd->style & WS_CLIPSIBLINGS) Flags |= DCX_CLIPSIBLINGS;
|
|
Flags |= DCX_CACHE;
|
|
}
|
|
}
|
|
|
|
if (Flags & DCX_WINDOW) Flags &= ~DCX_CLIPCHILDREN;
|
|
|
|
if (Flags & DCX_NOCLIPCHILDREN)
|
|
{
|
|
Flags |= DCX_CACHE;
|
|
Flags &= ~(DCX_PARENTCLIP | DCX_CLIPCHILDREN);
|
|
}
|
|
|
|
Parent = (Wnd ? Wnd->spwndParent : NULL);
|
|
|
|
if (NULL == Wnd || !(Wnd->style & WS_CHILD) || NULL == Parent)
|
|
{
|
|
Flags &= ~DCX_PARENTCLIP;
|
|
Flags |= DCX_CLIPSIBLINGS;
|
|
}
|
|
|
|
/* It seems parent clip is ignored when clipping siblings or children */
|
|
if (Flags & (DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN)) Flags &= ~DCX_PARENTCLIP;
|
|
|
|
if (Flags & DCX_PARENTCLIP)
|
|
{
|
|
if ((Wnd->style & WS_VISIBLE) &&
|
|
(Parent->style & WS_VISIBLE))
|
|
{
|
|
Flags &= ~DCX_CLIPCHILDREN;
|
|
if (Parent->style & WS_CLIPSIBLINGS)
|
|
{
|
|
Flags |= DCX_CLIPSIBLINGS;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Window nz, check to see if we still own this or it is just cheap wine tonight.
|
|
if (!(Flags & DCX_CACHE))
|
|
{
|
|
if ( Wnd->head.pti != GetW32ThreadInfo())
|
|
Flags |= DCX_CACHE; // Ah~ Not Powned! Forced to be cheap~
|
|
}
|
|
|
|
DcxFlags = Flags & DCX_CACHECOMPAREMASK;
|
|
|
|
if (Flags & DCX_CACHE)
|
|
{ // Scan the cheap wine list for our match.
|
|
DCE* DceEmpty = NULL;
|
|
DCE* DceUnused = NULL;
|
|
KeEnterCriticalRegion();
|
|
ListEntry = LEDce.Flink;
|
|
while (ListEntry != &LEDce)
|
|
{
|
|
Dce = CONTAINING_RECORD(ListEntry, DCE, List);
|
|
ListEntry = ListEntry->Flink;
|
|
//
|
|
// The way I understand this, you can have more than one DC per window.
|
|
// Only one Owned if one was requested and saved and one Cached.
|
|
//
|
|
if ((Dce->DCXFlags & (DCX_CACHE | DCX_DCEBUSY)) == DCX_CACHE)
|
|
{
|
|
DceUnused = Dce;
|
|
if (Dce->DCXFlags & DCX_DCEEMPTY)
|
|
{
|
|
DceEmpty = Dce;
|
|
}
|
|
else if (Dce->hwndCurrent == (Wnd ? Wnd->head.h : NULL) &&
|
|
((Dce->DCXFlags & DCX_CACHECOMPAREMASK) == DcxFlags))
|
|
{
|
|
UpdateClipOrigin = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
Dce = NULL; // Loop issue?
|
|
}
|
|
KeLeaveCriticalRegion();
|
|
|
|
Dce = (DceEmpty == NULL) ? DceUnused : DceEmpty;
|
|
|
|
if (Dce == NULL)
|
|
{
|
|
Dce = DceAllocDCE(NULL, DCE_CACHE_DC);
|
|
}
|
|
if (Dce == NULL) return NULL;
|
|
|
|
Dce->hwndCurrent = (Wnd ? Wnd->head.h : NULL);
|
|
Dce->pwndOrg = Dce->pwndClip = Wnd;
|
|
}
|
|
else // If we are here, we are POWNED or having CLASS.
|
|
{
|
|
KeEnterCriticalRegion();
|
|
ListEntry = LEDce.Flink;
|
|
while (ListEntry != &LEDce)
|
|
{
|
|
Dce = CONTAINING_RECORD(ListEntry, DCE, List);
|
|
ListEntry = ListEntry->Flink;
|
|
|
|
// Skip Cache DCE entries.
|
|
if (!(Dce->DCXFlags & DCX_CACHE))
|
|
{
|
|
// Check for Window handle than HDC match for CLASS.
|
|
if (Dce->hwndCurrent == Wnd->head.h)
|
|
{
|
|
bUpdateVisRgn = FALSE;
|
|
break;
|
|
}
|
|
else if (Dce->hDC == hDC) break;
|
|
}
|
|
Dce = NULL; // Loop issue?
|
|
}
|
|
KeLeaveCriticalRegion();
|
|
|
|
if (Dce == NULL)
|
|
{
|
|
return(NULL);
|
|
}
|
|
|
|
if ( (Flags & (DCX_INTERSECTRGN|DCX_EXCLUDERGN)) &&
|
|
(Dce->DCXFlags & (DCX_INTERSECTRGN|DCX_EXCLUDERGN)) )
|
|
{
|
|
DceDeleteClipRgn(Dce);
|
|
}
|
|
}
|
|
// First time use hax, need to use DceAllocDCE during window display init.
|
|
if (NULL == Dce)
|
|
{
|
|
return(NULL);
|
|
}
|
|
|
|
if (!GreIsHandleValid(Dce->hDC))
|
|
{
|
|
ERR("FIXME: Got DCE with invalid hDC! %p\n", Dce->hDC);
|
|
Dce->hDC = DceCreateDisplayDC();
|
|
/* FIXME: Handle error */
|
|
}
|
|
|
|
Dce->DCXFlags = Flags | DCX_DCEBUSY;
|
|
|
|
/*
|
|
* Bump it up! This prevents the random errors in wine dce tests and with
|
|
* proper bits set in DCX_CACHECOMPAREMASK.
|
|
* Reference:
|
|
* http://www.reactos.org/archives/public/ros-dev/2008-July/010498.html
|
|
* http://www.reactos.org/archives/public/ros-dev/2008-July/010499.html
|
|
*/
|
|
RemoveEntryList(&Dce->List);
|
|
InsertHeadList(&LEDce, &Dce->List);
|
|
|
|
/* Introduced in rev 6691 and modified later. */
|
|
if ( (Flags & DCX_INTERSECTUPDATE) && !ClipRegion )
|
|
{
|
|
Flags |= DCX_INTERSECTRGN | DCX_KEEPCLIPRGN;
|
|
Dce->DCXFlags |= DCX_INTERSECTRGN | DCX_KEEPCLIPRGN;
|
|
ClipRegion = Wnd->hrgnUpdate;
|
|
bUpdateVisRgn = TRUE;
|
|
}
|
|
|
|
if (ClipRegion == HRGN_WINDOW)
|
|
{
|
|
if (!(Flags & DCX_WINDOW))
|
|
{
|
|
Dce->hrgnClip = NtGdiCreateRectRgn(
|
|
Wnd->rcClient.left,
|
|
Wnd->rcClient.top,
|
|
Wnd->rcClient.right,
|
|
Wnd->rcClient.bottom);
|
|
}
|
|
else
|
|
{
|
|
Dce->hrgnClip = NtGdiCreateRectRgn(
|
|
Wnd->rcWindow.left,
|
|
Wnd->rcWindow.top,
|
|
Wnd->rcWindow.right,
|
|
Wnd->rcWindow.bottom);
|
|
}
|
|
Dce->DCXFlags &= ~DCX_KEEPCLIPRGN;
|
|
bUpdateVisRgn = TRUE;
|
|
}
|
|
else if (ClipRegion != NULL)
|
|
{
|
|
if (Dce->hrgnClip != NULL)
|
|
{
|
|
ERR("Should not be called!!\n");
|
|
GreDeleteObject(Dce->hrgnClip);
|
|
Dce->hrgnClip = NULL;
|
|
}
|
|
Dce->hrgnClip = ClipRegion;
|
|
bUpdateVisRgn = TRUE;
|
|
}
|
|
|
|
if (IntGdiSetHookFlags(Dce->hDC, DCHF_VALIDATEVISRGN)) bUpdateVisRgn = TRUE;
|
|
|
|
DceSetDrawable(Wnd, Dce->hDC, Flags, UpdateClipOrigin);
|
|
|
|
if (bUpdateVisRgn) DceUpdateVisRgn(Dce, Wnd, Flags);
|
|
|
|
if (Dce->DCXFlags & DCX_CACHE)
|
|
{
|
|
TRACE("ENTER!!!!!! DCX_CACHE!!!!!! hDC-> %p\n", Dce->hDC);
|
|
// Need to set ownership so Sync dcattr will work.
|
|
GreSetDCOwner(Dce->hDC, GDI_OBJ_HMGR_POWNED);
|
|
Dce->ptiOwner = GetW32ThreadInfo(); // Set the temp owning
|
|
}
|
|
|
|
if ( Wnd &&
|
|
Wnd->ExStyle & WS_EX_LAYOUTRTL &&
|
|
!(Flags & DCX_KEEPLAYOUT) )
|
|
{
|
|
NtGdiSetLayout(Dce->hDC, -1, LAYOUT_RTL);
|
|
}
|
|
|
|
if (Dce->DCXFlags & DCX_PROCESSOWNED)
|
|
{
|
|
ppi = PsGetCurrentProcessWin32Process();
|
|
ppi->W32PF_flags |= W32PF_OWNDCCLEANUP;
|
|
Dce->ptiOwner = NULL;
|
|
Dce->ppiOwner = ppi;
|
|
}
|
|
|
|
return(Dce->hDC);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* DceFreeDCE
|
|
*/
|
|
void FASTCALL
|
|
DceFreeDCE(PDCE pdce, BOOLEAN Force)
|
|
{
|
|
BOOL Hit = FALSE;
|
|
|
|
ASSERT(pdce != NULL);
|
|
if (NULL == pdce) return;
|
|
|
|
pdce->DCXFlags |= DCX_INDESTROY;
|
|
|
|
if (Force &&
|
|
GreGetObjectOwner(pdce->hDC) != GDI_OBJ_HMGR_POWNED)
|
|
{
|
|
TRACE("Change ownership for DCE! -> %p\n" , pdce);
|
|
// NOTE: Windows sets W32PF_OWNDCCLEANUP and moves on.
|
|
if (GreIsHandleValid(pdce->hDC))
|
|
{
|
|
GreSetDCOwner(pdce->hDC, GDI_OBJ_HMGR_POWNED);
|
|
}
|
|
else
|
|
{
|
|
ERR("Attempted to change ownership of an DCEhDC %p currently being destroyed!!!\n",
|
|
pdce->hDC);
|
|
Hit = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (GreGetObjectOwner(pdce->hDC) == GDI_OBJ_HMGR_PUBLIC)
|
|
GreSetDCOwner(pdce->hDC, GDI_OBJ_HMGR_POWNED);
|
|
}
|
|
|
|
if (!Hit) IntGdiDeleteDC(pdce->hDC, TRUE);
|
|
|
|
if (pdce->hrgnClip && !(pdce->DCXFlags & DCX_KEEPCLIPRGN))
|
|
{
|
|
GreDeleteObject(pdce->hrgnClip);
|
|
pdce->hrgnClip = NULL;
|
|
}
|
|
|
|
RemoveEntryList(&pdce->List);
|
|
|
|
ExFreePoolWithTag(pdce, USERTAG_DCE);
|
|
|
|
DCECount--;
|
|
TRACE("Freed DCE's! %d \n", DCECount);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* DceFreeWindowDCE
|
|
*
|
|
* Remove owned DCE and reset unreleased cache DCEs.
|
|
*/
|
|
void FASTCALL
|
|
DceFreeWindowDCE(PWND Window)
|
|
{
|
|
PDCE pDCE;
|
|
PLIST_ENTRY ListEntry;
|
|
|
|
if (DCECount <= 0)
|
|
{
|
|
ERR("FreeWindowDCE No Entry! %d\n",DCECount);
|
|
return;
|
|
}
|
|
|
|
ListEntry = LEDce.Flink;
|
|
while (ListEntry != &LEDce)
|
|
{
|
|
pDCE = CONTAINING_RECORD(ListEntry, DCE, List);
|
|
ListEntry = ListEntry->Flink;
|
|
if ( pDCE->hwndCurrent == Window->head.h &&
|
|
!(pDCE->DCXFlags & DCX_DCEEMPTY) )
|
|
{
|
|
if (!(pDCE->DCXFlags & DCX_CACHE)) /* Owned or Class DCE */
|
|
{
|
|
if (Window->pcls->style & CS_CLASSDC) /* Test Class first */
|
|
{
|
|
if (pDCE->DCXFlags & (DCX_INTERSECTRGN | DCX_EXCLUDERGN)) /* Class DCE */
|
|
DceDeleteClipRgn(pDCE);
|
|
// Update and reset Vis Rgn and clear the dirty bit.
|
|
// Should release VisRgn than reset it to default.
|
|
DceUpdateVisRgn(pDCE, Window, pDCE->DCXFlags);
|
|
pDCE->DCXFlags = DCX_DCEEMPTY|DCX_CACHE;
|
|
pDCE->hwndCurrent = 0;
|
|
pDCE->pwndOrg = pDCE->pwndClip = NULL;
|
|
|
|
TRACE("POWNED DCE going Cheap!! DCX_CACHE!! hDC-> %p \n",
|
|
pDCE->hDC);
|
|
if (!GreSetDCOwner( pDCE->hDC, GDI_OBJ_HMGR_NONE))
|
|
{
|
|
ERR("Fail Owner Switch hDC-> %p \n", pDCE->hDC);
|
|
break;
|
|
}
|
|
/* Do not change owner so thread can clean up! */
|
|
}
|
|
else if (Window->pcls->style & CS_OWNDC) /* Owned DCE */
|
|
{
|
|
DceFreeDCE(pDCE, FALSE);
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
ERR("Not POWNED or CLASSDC hwndCurrent -> %p \n",
|
|
pDCE->hwndCurrent);
|
|
// ASSERT(FALSE); /* bug 5320 */
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pDCE->DCXFlags & DCX_DCEBUSY) /* Shared cache DCE */
|
|
{
|
|
/* FIXME: AFAICS we are doing the right thing here so
|
|
* this should be a TRACE. But this is best left as an ERR
|
|
* because the 'application error' is likely to come from
|
|
* another part of Wine (i.e. it's our fault after all).
|
|
* We should change this to TRACE when ReactOS is more stable
|
|
* (for 1.0?).
|
|
*/
|
|
ERR("[%p] GetDC() without ReleaseDC()!\n", Window->head.h);
|
|
DceReleaseDC(pDCE, FALSE);
|
|
}
|
|
pDCE->DCXFlags |= DCX_DCEEMPTY;
|
|
pDCE->hwndCurrent = 0;
|
|
pDCE->pwndOrg = pDCE->pwndClip = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FASTCALL
|
|
DceFreeClassDCE(PDCE pdceClass)
|
|
{
|
|
PDCE pDCE;
|
|
PLIST_ENTRY ListEntry;
|
|
|
|
ListEntry = LEDce.Flink;
|
|
while (ListEntry != &LEDce)
|
|
{
|
|
pDCE = CONTAINING_RECORD(ListEntry, DCE, List);
|
|
ListEntry = ListEntry->Flink;
|
|
if (pDCE == pdceClass)
|
|
{
|
|
DceFreeDCE(pDCE, TRUE); // Might have gone cheap!
|
|
}
|
|
}
|
|
}
|
|
|
|
void FASTCALL
|
|
DceFreeThreadDCE(PTHREADINFO pti)
|
|
{
|
|
PDCE pDCE;
|
|
PLIST_ENTRY ListEntry;
|
|
|
|
ListEntry = LEDce.Flink;
|
|
while (ListEntry != &LEDce)
|
|
{
|
|
pDCE = CONTAINING_RECORD(ListEntry, DCE, List);
|
|
ListEntry = ListEntry->Flink;
|
|
if (pDCE->ptiOwner == pti)
|
|
{
|
|
if (pDCE->DCXFlags & DCX_CACHE)
|
|
{
|
|
DceFreeDCE(pDCE, TRUE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID FASTCALL
|
|
DceEmptyCache(VOID)
|
|
{
|
|
PDCE pDCE;
|
|
PLIST_ENTRY ListEntry;
|
|
|
|
ListEntry = LEDce.Flink;
|
|
while (ListEntry != &LEDce)
|
|
{
|
|
pDCE = CONTAINING_RECORD(ListEntry, DCE, List);
|
|
ListEntry = ListEntry->Flink;
|
|
DceFreeDCE(pDCE, TRUE);
|
|
}
|
|
}
|
|
|
|
VOID FASTCALL
|
|
DceResetActiveDCEs(PWND Window)
|
|
{
|
|
DCE *pDCE;
|
|
PDC dc;
|
|
PWND CurrentWindow;
|
|
INT DeltaX;
|
|
INT DeltaY;
|
|
PLIST_ENTRY ListEntry;
|
|
|
|
if (NULL == Window)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ListEntry = LEDce.Flink;
|
|
while (ListEntry != &LEDce)
|
|
{
|
|
pDCE = CONTAINING_RECORD(ListEntry, DCE, List);
|
|
ListEntry = ListEntry->Flink;
|
|
if (0 == (pDCE->DCXFlags & (DCX_DCEEMPTY|DCX_INDESTROY)))
|
|
{
|
|
if (Window->head.h == pDCE->hwndCurrent)
|
|
{
|
|
CurrentWindow = Window;
|
|
}
|
|
else
|
|
{
|
|
if (!pDCE->hwndCurrent)
|
|
CurrentWindow = NULL;
|
|
else
|
|
CurrentWindow = UserGetWindowObject(pDCE->hwndCurrent);
|
|
if (NULL == CurrentWindow)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (!GreIsHandleValid(pDCE->hDC) ||
|
|
(dc = DC_LockDc(pDCE->hDC)) == NULL)
|
|
{
|
|
continue;
|
|
}
|
|
if (Window == CurrentWindow || IntIsChildWindow(Window, CurrentWindow))
|
|
{
|
|
if (pDCE->DCXFlags & DCX_WINDOW)
|
|
{
|
|
DeltaX = CurrentWindow->rcWindow.left - dc->ptlDCOrig.x;
|
|
DeltaY = CurrentWindow->rcWindow.top - dc->ptlDCOrig.y;
|
|
dc->ptlDCOrig.x = CurrentWindow->rcWindow.left;
|
|
dc->ptlDCOrig.y = CurrentWindow->rcWindow.top;
|
|
}
|
|
else
|
|
{
|
|
DeltaX = CurrentWindow->rcClient.left - dc->ptlDCOrig.x;
|
|
DeltaY = CurrentWindow->rcClient.top - dc->ptlDCOrig.y;
|
|
dc->ptlDCOrig.x = CurrentWindow->rcClient.left;
|
|
dc->ptlDCOrig.y = CurrentWindow->rcClient.top;
|
|
}
|
|
|
|
if (NULL != dc->dclevel.prgnClip)
|
|
{
|
|
REGION_bOffsetRgn(dc->dclevel.prgnClip, DeltaX, DeltaY);
|
|
dc->fs |= DC_DIRTY_RAO;
|
|
}
|
|
if (NULL != pDCE->hrgnClip)
|
|
{
|
|
NtGdiOffsetRgn(pDCE->hrgnClip, DeltaX, DeltaY);
|
|
}
|
|
}
|
|
DC_UnlockDc(dc);
|
|
|
|
DceUpdateVisRgn(pDCE, CurrentWindow, pDCE->DCXFlags);
|
|
IntGdiSetHookFlags(pDCE->hDC, DCHF_VALIDATEVISRGN);
|
|
}
|
|
}
|
|
}
|
|
|
|
HWND FASTCALL
|
|
IntWindowFromDC(HDC hDc)
|
|
{
|
|
DCE *Dce;
|
|
PLIST_ENTRY ListEntry;
|
|
HWND Ret = NULL;
|
|
|
|
ListEntry = LEDce.Flink;
|
|
while (ListEntry != &LEDce)
|
|
{
|
|
Dce = CONTAINING_RECORD(ListEntry, DCE, List);
|
|
ListEntry = ListEntry->Flink;
|
|
if (Dce->hDC == hDc)
|
|
{
|
|
if (Dce->DCXFlags & DCX_INDESTROY)
|
|
Ret = NULL;
|
|
else
|
|
Ret = Dce->hwndCurrent;
|
|
break;
|
|
}
|
|
}
|
|
return Ret;
|
|
}
|
|
|
|
INT FASTCALL
|
|
UserReleaseDC(PWND Window, HDC hDc, BOOL EndPaint)
|
|
{
|
|
PDCE dce;
|
|
PLIST_ENTRY ListEntry;
|
|
INT nRet = 0;
|
|
BOOL Hit = FALSE;
|
|
|
|
TRACE("%p %p\n", Window, hDc);
|
|
ListEntry = LEDce.Flink;
|
|
while (ListEntry != &LEDce)
|
|
{
|
|
dce = CONTAINING_RECORD(ListEntry, DCE, List);
|
|
ListEntry = ListEntry->Flink;
|
|
if (dce->hDC == hDc)
|
|
{
|
|
Hit = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( Hit && (dce->DCXFlags & DCX_DCEBUSY))
|
|
{
|
|
nRet = DceReleaseDC(dce, EndPaint);
|
|
}
|
|
|
|
return nRet;
|
|
}
|
|
|
|
HDC FASTCALL
|
|
UserGetWindowDC(PWND Wnd)
|
|
{
|
|
return UserGetDCEx(Wnd, 0, DCX_USESTYLE | DCX_WINDOW);
|
|
}
|
|
|
|
HWND FASTCALL
|
|
UserGethWnd( HDC hdc, PWNDOBJ *pwndo)
|
|
{
|
|
EWNDOBJ* Clip;
|
|
PWND Wnd;
|
|
HWND hWnd;
|
|
|
|
hWnd = IntWindowFromDC(hdc);
|
|
|
|
if (hWnd && (Wnd = UserGetWindowObject(hWnd)))
|
|
{
|
|
Clip = (EWNDOBJ*)UserGetProp(Wnd, AtomWndObj, TRUE);
|
|
|
|
if ( Clip && Clip->Hwnd == hWnd )
|
|
{
|
|
if (pwndo) *pwndo = (PWNDOBJ)Clip;
|
|
}
|
|
}
|
|
return hWnd;
|
|
}
|
|
|
|
HDC APIENTRY
|
|
NtUserGetDCEx(HWND hWnd OPTIONAL, HANDLE ClipRegion, ULONG Flags)
|
|
{
|
|
PWND Wnd=NULL;
|
|
DECLARE_RETURN(HDC);
|
|
|
|
TRACE("Enter NtUserGetDCEx: hWnd %p, ClipRegion %p, Flags %x.\n",
|
|
hWnd, ClipRegion, Flags);
|
|
UserEnterExclusive();
|
|
|
|
if (hWnd && !(Wnd = UserGetWindowObject(hWnd)))
|
|
{
|
|
RETURN(NULL);
|
|
}
|
|
RETURN( UserGetDCEx(Wnd, ClipRegion, Flags));
|
|
|
|
CLEANUP:
|
|
TRACE("Leave NtUserGetDCEx, ret=%p\n", _ret_);
|
|
UserLeave();
|
|
END_CLEANUP;
|
|
}
|
|
|
|
/*
|
|
* NtUserGetWindowDC
|
|
*
|
|
* The NtUserGetWindowDC function retrieves the device context (DC) for the
|
|
* entire window, including title bar, menus, and scroll bars. A window device
|
|
* context permits painting anywhere in a window, because the origin of the
|
|
* device context is the upper-left corner of the window instead of the client
|
|
* area.
|
|
*
|
|
* Status
|
|
* @implemented
|
|
*/
|
|
HDC APIENTRY
|
|
NtUserGetWindowDC(HWND hWnd)
|
|
{
|
|
return NtUserGetDCEx(hWnd, 0, DCX_USESTYLE | DCX_WINDOW);
|
|
}
|
|
|
|
HDC APIENTRY
|
|
NtUserGetDC(HWND hWnd)
|
|
{
|
|
TRACE("NtUGetDC -> %p:%x\n", hWnd, !hWnd ? DCX_CACHE | DCX_WINDOW : DCX_USESTYLE);
|
|
|
|
return NtUserGetDCEx(hWnd, NULL, NULL == hWnd ? DCX_CACHE | DCX_WINDOW : DCX_USESTYLE);
|
|
}
|
|
|
|
/*!
|
|
* Select logical palette into device context.
|
|
* \param hDC handle to the device context
|
|
* \param hpal handle to the palette
|
|
* \param ForceBackground If this value is FALSE the logical palette will be copied to the device palette only when the applicatioon
|
|
* is in the foreground. If this value is TRUE then map the colors in the logical palette to the device
|
|
* palette colors in the best way.
|
|
* \return old palette
|
|
*
|
|
* \todo implement ForceBackground == TRUE
|
|
*/
|
|
HPALETTE
|
|
APIENTRY
|
|
NtUserSelectPalette(HDC hDC,
|
|
HPALETTE hpal,
|
|
BOOL ForceBackground)
|
|
{
|
|
HPALETTE oldPal;
|
|
UserEnterExclusive();
|
|
// Implement window checks
|
|
oldPal = GdiSelectPalette( hDC, hpal, ForceBackground);
|
|
UserLeave();
|
|
return oldPal;
|
|
}
|
|
|
|
/* EOF */
|