📄 painting.c
字号:
/*
* ReactOS W32 Subsystem
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: painting.c 28033 2007-07-30 03:30:55Z tkreuzer $
*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* PURPOSE: Window painting function
* FILE: subsys/win32k/ntuser/painting.c
* PROGRAMER: Filip Navara (xnavara@volny.cz)
* REVISION HISTORY:
* 06/06/2001 Created (?)
* 18/11/2003 Complete rewrite
*/
/* INCLUDES ******************************************************************/
#include <w32k.h>
#define NDEBUG
#include <debug.h>
/* PRIVATE FUNCTIONS **********************************************************/
/**
* @name IntIntersectWithParents
*
* Intersect window rectangle with all parent client rectangles.
*
* @param Child
* Pointer to child window to start intersecting from.
* @param WindowRect
* Pointer to rectangle that we want to intersect in screen
* coordinates on input and intersected rectangle on output (if TRUE
* is returned).
*
* @return
* If any parent is minimized or invisible or the resulting rectangle
* is empty then FALSE is returned. Otherwise TRUE is returned.
*/
BOOL FASTCALL
IntIntersectWithParents(PWINDOW_OBJECT Child, PRECT WindowRect)
{
PWINDOW_OBJECT ParentWindow;
ParentWindow = Child->Parent;
while (ParentWindow != NULL)
{
if (!(ParentWindow->Style & WS_VISIBLE) ||
(ParentWindow->Style & WS_MINIMIZE))
{
return FALSE;
}
if (!IntGdiIntersectRect(WindowRect, WindowRect, &ParentWindow->ClientRect))
{
return FALSE;
}
/* FIXME: Layered windows. */
ParentWindow = ParentWindow->Parent;
}
return TRUE;
}
BOOL FASTCALL
IntValidateParent(PWINDOW_OBJECT Child, HRGN hValidateRgn, BOOL Recurse)
{
PWINDOW_OBJECT ParentWindow = Child->Parent;
while (ParentWindow)
{
if (ParentWindow->Style & WS_CLIPCHILDREN)
break;
if (ParentWindow->UpdateRegion != 0)
{
if (Recurse)
return FALSE;
IntInvalidateWindows(ParentWindow, hValidateRgn,
RDW_VALIDATE | RDW_NOCHILDREN);
}
ParentWindow = ParentWindow->Parent;
}
return TRUE;
}
/**
* @name IntCalcWindowRgn
*
* Get a window or client region.
*/
HRGN FASTCALL
IntCalcWindowRgn(PWINDOW_OBJECT Window, BOOL Client)
{
HRGN hRgnWindow;
UINT RgnType;
if (Client)
hRgnWindow = UnsafeIntCreateRectRgnIndirect(&Window->ClientRect);
else
hRgnWindow = UnsafeIntCreateRectRgnIndirect(&Window->WindowRect);
if (Window->WindowRegion != NULL && !(Window->Style & WS_MINIMIZE))
{
NtGdiOffsetRgn(hRgnWindow,
-Window->WindowRect.left,
-Window->WindowRect.top);
RgnType = NtGdiCombineRgn(hRgnWindow, hRgnWindow, Window->WindowRegion, RGN_AND);
NtGdiOffsetRgn(hRgnWindow,
Window->WindowRect.left,
Window->WindowRect.top);
}
return hRgnWindow;
}
/**
* @name IntGetNCUpdateRgn
*
* Get non-client update region of a window and optionally validate it.
*
* @param Window
* Pointer to window to get the NC update region from.
* @param Validate
* Set to TRUE to force validating the NC update region.
*
* @return
* Handle to NC update region. The caller is responsible for deleting
* it.
*/
HRGN FASTCALL
IntGetNCUpdateRgn(PWINDOW_OBJECT Window, BOOL Validate)
{
HRGN hRgnNonClient;
HRGN hRgnWindow;
UINT RgnType;
if (Window->UpdateRegion != NULL &&
Window->UpdateRegion != (HRGN)1)
{
hRgnNonClient = NtGdiCreateRectRgn(0, 0, 0, 0);
/*
* If region creation fails it's safe to fallback to whole
* window region.
*/
if (hRgnNonClient == NULL)
{
return (HRGN)1;
}
hRgnWindow = IntCalcWindowRgn(Window, TRUE);
if (hRgnWindow == NULL)
{
NtGdiDeleteObject(hRgnNonClient);
return (HRGN)1;
}
RgnType = NtGdiCombineRgn(hRgnNonClient, Window->UpdateRegion,
hRgnWindow, RGN_DIFF);
if (RgnType == ERROR)
{
NtGdiDeleteObject(hRgnWindow);
NtGdiDeleteObject(hRgnNonClient);
return (HRGN)1;
}
else if (RgnType == NULLREGION)
{
NtGdiDeleteObject(hRgnWindow);
NtGdiDeleteObject(hRgnNonClient);
return NULL;
}
/*
* Remove the nonclient region from the standard update region if
* we were asked for it.
*/
if (Validate)
{
if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
hRgnWindow, RGN_AND) == NULLREGION)
{
GDIOBJ_SetOwnership(GdiHandleTable, Window->UpdateRegion, PsGetCurrentProcess());
NtGdiDeleteObject(Window->UpdateRegion);
Window->UpdateRegion = NULL;
if (!(Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT))
MsqDecPaintCountQueue(Window->MessageQueue);
}
}
NtGdiDeleteObject(hRgnWindow);
return hRgnNonClient;
}
else
{
return Window->UpdateRegion;
}
}
/*
* IntPaintWindows
*
* Internal function used by IntRedrawWindow.
*/
VOID FASTCALL
co_IntPaintWindows(PWINDOW_OBJECT Window, ULONG Flags, BOOL Recurse)
{
HDC hDC;
HWND hWnd = Window->hSelf;
HRGN TempRegion;
if (Flags & (RDW_ERASENOW | RDW_UPDATENOW))
{
if (Window->UpdateRegion)
{
if (!IntValidateParent(Window, Window->UpdateRegion, Recurse))
return;
}
if (Flags & RDW_UPDATENOW)
{
if (Window->UpdateRegion != NULL ||
Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT)
{
co_IntSendMessage(hWnd, WM_PAINT, 0, 0);
}
}
else
{
if (Window->Flags & WINDOWOBJECT_NEED_NCPAINT)
{
TempRegion = IntGetNCUpdateRgn(Window, TRUE);
Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
MsqDecPaintCountQueue(Window->MessageQueue);
co_IntSendMessage(hWnd, WM_NCPAINT, (WPARAM)TempRegion, 0);
if ((HANDLE) 1 != TempRegion && NULL != TempRegion)
{
/* NOTE: The region can already be deleted! */
GDIOBJ_FreeObj(GdiHandleTable, TempRegion, GDI_OBJECT_TYPE_REGION | GDI_OBJECT_TYPE_SILENT);
}
}
if (Window->Flags & WINDOWOBJECT_NEED_ERASEBKGND)
{
if (Window->UpdateRegion)
{
hDC = UserGetDCEx(Window, Window->UpdateRegion,
DCX_CACHE | DCX_USESTYLE |
DCX_INTERSECTRGN | DCX_KEEPCLIPRGN);
if (co_IntSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)hDC, 0))
{
Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
}
UserReleaseDC(Window, hDC, FALSE);
}
}
}
}
/*
* Check that the window is still valid at this point
*/
if (!IntIsWindow(hWnd))
{
return;
}
/*
* Paint child windows.
*/
if (!(Flags & RDW_NOCHILDREN) && !(Window->Style & WS_MINIMIZE) &&
((Flags & RDW_ALLCHILDREN) || !(Window->Style & WS_CLIPCHILDREN)))
{
HWND *List, *phWnd;
if ((List = IntWinListChildren(Window)))
{
/* FIXME: Handle WS_EX_TRANSPARENT */
for (phWnd = List; *phWnd; ++phWnd)
{
Window = UserGetWindowObject(*phWnd);
if (Window && (Window->Style & WS_VISIBLE))
{
USER_REFERENCE_ENTRY Ref;
UserRefObjectCo(Window, &Ref);
co_IntPaintWindows(Window, Flags, TRUE);
UserDerefObjectCo(Window);
}
}
ExFreePool(List);
}
}
}
/*
* IntInvalidateWindows
*
* Internal function used by IntRedrawWindow.
*/
VOID FASTCALL
IntInvalidateWindows(PWINDOW_OBJECT Window, HRGN hRgn, ULONG Flags)
{
INT RgnType;
BOOL HadPaintMessage, HadNCPaintMessage;
BOOL HasPaintMessage, HasNCPaintMessage;
/*
* If the nonclient is not to be redrawn, clip the region to the client
* rect
*/
if (0 != (Flags & RDW_INVALIDATE) && 0 == (Flags & RDW_FRAME))
{
HRGN hRgnClient;
hRgnClient = UnsafeIntCreateRectRgnIndirect(&Window->ClientRect);
RgnType = NtGdiCombineRgn(hRgn, hRgn, hRgnClient, RGN_AND);
NtGdiDeleteObject(hRgnClient);
}
/*
* Clip the given region with window rectangle (or region)
*/
if (!Window->WindowRegion || (Window->Style & WS_MINIMIZE))
{
HRGN hRgnWindow;
hRgnWindow = UnsafeIntCreateRectRgnIndirect(&Window->WindowRect);
RgnType = NtGdiCombineRgn(hRgn, hRgn, hRgnWindow, RGN_AND);
NtGdiDeleteObject(hRgnWindow);
}
else
{
NtGdiOffsetRgn(hRgn,
-Window->WindowRect.left,
-Window->WindowRect.top);
RgnType = NtGdiCombineRgn(hRgn, hRgn, Window->WindowRegion, RGN_AND);
NtGdiOffsetRgn(hRgn,
Window->WindowRect.left,
Window->WindowRect.top);
}
/*
* Save current state of pending updates
*/
HadPaintMessage = Window->UpdateRegion != NULL ||
Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT;
HadNCPaintMessage = Window->Flags & WINDOWOBJECT_NEED_NCPAINT;
/*
* Update the region and flags
*/
if (Flags & RDW_INVALIDATE && RgnType != NULLREGION)
{
if (Window->UpdateRegion == NULL)
{
Window->UpdateRegion = NtGdiCreateRectRgn(0, 0, 0, 0);
GDIOBJ_SetOwnership(GdiHandleTable, Window->UpdateRegion, NULL);
}
if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
hRgn, RGN_OR) == NULLREGION)
{
GDIOBJ_SetOwnership(GdiHandleTable, Window->UpdateRegion, PsGetCurrentProcess());
NtGdiDeleteObject(Window->UpdateRegion);
Window->UpdateRegion = NULL;
}
if (Flags & RDW_FRAME)
Window->Flags |= WINDOWOBJECT_NEED_NCPAINT;
if (Flags & RDW_ERASE)
Window->Flags |= WINDOWOBJECT_NEED_ERASEBKGND;
Flags |= RDW_FRAME;
}
if (Flags & RDW_VALIDATE && RgnType != NULLREGION)
{
if (Window->UpdateRegion != NULL)
{
if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
hRgn, RGN_DIFF) == NULLREGION)
{
GDIOBJ_SetOwnership(GdiHandleTable, Window->UpdateRegion, PsGetCurrentProcess());
NtGdiDeleteObject(Window->UpdateRegion);
Window->UpdateRegion = NULL;
}
}
if (Window->UpdateRegion == NULL)
Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
if (Flags & RDW_NOFRAME)
Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
if (Flags & RDW_NOERASE)
Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
}
if (Flags & RDW_INTERNALPAINT)
{
Window->Flags |= WINDOWOBJECT_NEED_INTERNALPAINT;
}
if (Flags & RDW_NOINTERNALPAINT)
{
Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
}
/*
* Process children if needed
*/
if (!(Flags & RDW_NOCHILDREN) && !(Window->Style & WS_MINIMIZE) &&
((Flags & RDW_ALLCHILDREN) || !(Window->Style & WS_CLIPCHILDREN)))
{
PWINDOW_OBJECT Child;
for (Child = Window->FirstChild; Child; Child = Child->NextSibling)
{
if (Child->Style & WS_VISIBLE)
{
/*
* Recursive call to update children UpdateRegion
*/
HRGN hRgnTemp = NtGdiCreateRectRgn(0, 0, 0, 0);
NtGdiCombineRgn(hRgnTemp, hRgn, 0, RGN_COPY);
IntInvalidateWindows(Child, hRgnTemp, Flags);
NtGdiDeleteObject(hRgnTemp);
}
}
}
/*
* Fake post paint messages to window message queue if needed
*/
HasPaintMessage = Window->UpdateRegion != NULL ||
Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT;
HasNCPaintMessage = Window->Flags & WINDOWOBJECT_NEED_NCPAINT;
if (HasPaintMessage != HadPaintMessage)
{
if (HadPaintMessage)
MsqDecPaintCountQueue(Window->MessageQueue);
else
MsqIncPaintCountQueue(Window->MessageQueue);
}
if (HasNCPaintMessage != HadNCPaintMessage)
{
if (HadNCPaintMessage)
MsqDecPaintCountQueue(Window->MessageQueue);
else
MsqIncPaintCountQueue(Window->MessageQueue);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -