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.
498 lines
15 KiB
498 lines
15 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: hungapp.c
|
|
*
|
|
* Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
*
|
|
*
|
|
* History:
|
|
* 03-10-92 DavidPe Created.
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
/***************************************************************************\
|
|
* SetHungFlag
|
|
*
|
|
* Sets the specified redraw-if-hung flag in the window and adds the
|
|
* window to the list of windows to redraw if hung.
|
|
* Windows that are not top-level get the bit set, but aren't added to the list
|
|
*
|
|
* 08-23-93 JimA Created.
|
|
\***************************************************************************/
|
|
#define CHRLINCR 10
|
|
|
|
VOID SetHungFlag(
|
|
PWND pwnd,
|
|
WORD wFlag)
|
|
{
|
|
/*
|
|
* If the window has no hung redraw bits set and it's a top-level
|
|
* window, add it to the redraw list.
|
|
*/
|
|
if (!TestWF(pwnd, WFANYHUNGREDRAW) && pwnd->spwndParent == PWNDDESKTOP(pwnd)) {
|
|
/*
|
|
* Add pwnd to the Hung Redraw Volatile Window Pointer List.
|
|
*/
|
|
VWPLAdd(&gpvwplHungRedraw, pwnd, CHRLINCR);
|
|
}
|
|
|
|
SetWF(pwnd, wFlag);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* ClearHungFlag
|
|
*
|
|
* Clears the specified redraw-if-hung flag in the window and if no other
|
|
* redraw-if-hung flags remain, remove the window from list of windows
|
|
* to be redrawn if hung.
|
|
* Many windows have WFREDRAW* bits set, but aren't in the list (only those
|
|
* that were top-level were added).
|
|
*
|
|
* 08-23-93 JimA Created.
|
|
\***************************************************************************/
|
|
|
|
VOID ClearHungFlag(
|
|
PWND pwnd,
|
|
WORD wFlag)
|
|
{
|
|
BOOL fInRedrawList = TestWF(pwnd, WFANYHUNGREDRAW);
|
|
|
|
ClrWF(pwnd, wFlag);
|
|
if (!TestWF(pwnd, WFANYHUNGREDRAW) && fInRedrawList) {
|
|
/*
|
|
* Remove the window from the redraw list and possibly compact it.
|
|
*/
|
|
VWPLRemove(&gpvwplHungRedraw, pwnd);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* FHungApp
|
|
*
|
|
*
|
|
* 02-28-92 DavidPe Created.
|
|
\***************************************************************************/
|
|
|
|
BOOL FHungApp(
|
|
PTHREADINFO pti,
|
|
DWORD dwTimeFromLastRead)
|
|
{
|
|
|
|
/*
|
|
* An app is considered hung if it isn't waiting for input, isn't in
|
|
* startup processing, and hasn't called PeekMessage() within the
|
|
* specified timeout.
|
|
*/
|
|
if (((NtGetTickCount() - GET_TIME_LAST_READ(pti)) > dwTimeFromLastRead) &&
|
|
!((pti->pcti->fsWakeMask & QS_INPUT) && (PsGetThreadFreezeCount(pti->pEThread) == 0)) &&
|
|
!(pti->ppi->W32PF_Flags & W32PF_APPSTARTING)) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxRedrawHungWindowFrame
|
|
*
|
|
*
|
|
* 02-28-92 DavidPe Created.
|
|
\***************************************************************************/
|
|
VOID xxxRedrawHungWindowFrame(
|
|
PWND pwnd,
|
|
BOOL fActive)
|
|
{
|
|
HDC hdc;
|
|
UINT wFlags = DC_NC | DC_NOSENDMSG;
|
|
|
|
CheckLock(pwnd);
|
|
|
|
#ifdef HUNGAPP_GHOSTING
|
|
ClearHungFlag(pwnd, WFREDRAWFRAMEIFHUNG);
|
|
SignalGhost(pwnd);
|
|
return;
|
|
#endif // HUNGAPP_GHOSTING
|
|
|
|
if (IsInsideUserApiHook()) {
|
|
return;
|
|
}
|
|
|
|
if (fActive) {
|
|
wFlags |= DC_ACTIVE;
|
|
}
|
|
|
|
hdc = _GetDCEx(pwnd, NULL, DCX_USESTYLE | DCX_WINDOW);
|
|
xxxDrawCaptionBar(pwnd, hdc, wFlags);
|
|
_ReleaseDC(hdc);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxRedrawHungWindow
|
|
*
|
|
* If the hrgnFullDrag is NULL, redraw the hung window's entire update
|
|
* region, otherwise, only redraw the intersection of the window's update
|
|
* region with the FullDrag region.
|
|
*
|
|
* 02-28-92 DavidPe Created.
|
|
\***************************************************************************/
|
|
VOID xxxRedrawHungWindow(
|
|
PWND pwnd,
|
|
HRGN hrgnFullDrag)
|
|
{
|
|
HDC hdc;
|
|
HBRUSH hbr;
|
|
HRGN hrgnUpdate;
|
|
RECT rc;
|
|
TL tlpwnd;
|
|
UINT flags;
|
|
W32PID sid;
|
|
DWORD dwColor;
|
|
PWND pwndDesk;
|
|
TL tlpwndDesk;
|
|
|
|
CheckCritIn();
|
|
CheckLock(pwnd);
|
|
|
|
if (pwnd->hrgnUpdate == NULL) {
|
|
return;
|
|
}
|
|
|
|
#ifdef HUNGAPP_GHOSTING
|
|
|
|
/*
|
|
* Don't bother doing anything here when the window isn't even visible.
|
|
*/
|
|
if (!TestWF(pwnd, WFVISIBLE)) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* This function can be called from the full-drag code to quick redraw
|
|
* windows that aren't hung. In that case check if that thread is hung.
|
|
*/
|
|
if ((hrgnFullDrag == NULL) || (hrgnFullDrag != NULL &&
|
|
FHungApp(GETPTI(pwnd), CMSHUNGAPPTIMEOUT))) {
|
|
SignalGhost(pwnd);
|
|
return;
|
|
}
|
|
UserAssert(gptiCurrent != gptiRit);
|
|
#endif
|
|
|
|
/*
|
|
* First calculate hrgnUpdate.
|
|
*/
|
|
if (pwnd->hrgnUpdate > HRGN_FULL) {
|
|
hrgnUpdate = CreateEmptyRgn();
|
|
if (hrgnUpdate == NULL) {
|
|
hrgnUpdate = HRGN_FULL;
|
|
|
|
} else if (CopyRgn(hrgnUpdate, pwnd->hrgnUpdate) == ERROR) {
|
|
GreDeleteObject(hrgnUpdate);
|
|
hrgnUpdate = HRGN_FULL;
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
* For our purposes, we need a real hrgnUpdate, so try and
|
|
* create one if even if the entire window needs updating.
|
|
*/
|
|
CopyRect(&rc, &pwnd->rcWindow);
|
|
hrgnUpdate = GreCreateRectRgnIndirect(&rc);
|
|
if (hrgnUpdate == NULL) {
|
|
hrgnUpdate = HRGN_FULL;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* If we're redrawing because we're full dragging and if the window's
|
|
* update region does not intersect with the Full drag
|
|
* update region, don't erase the hung window again. This is to prevent
|
|
* flickering when a window has been invalidated by another window doing
|
|
* full drag and hasn't received the paint message yet.
|
|
* This way, only if there is a new region that has been invalidated will
|
|
* we redraw the hung window.
|
|
*/
|
|
if (hrgnFullDrag && hrgnUpdate != HRGN_FULL &&
|
|
IntersectRgn(hrgnUpdate, hrgnUpdate, hrgnFullDrag) == NULLREGION) {
|
|
GreDeleteObject(hrgnUpdate);
|
|
return;
|
|
}
|
|
|
|
ThreadLock(pwnd, &tlpwnd);
|
|
|
|
if (IsInsideUserApiHook()) {
|
|
xxxInternalInvalidate(pwnd, hrgnUpdate, RDW_INVALIDATE |
|
|
RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN);
|
|
} else {
|
|
hdc = _GetDCEx(pwnd, hrgnUpdate, DCX_USESTYLE | DCX_WINDOW |
|
|
DCX_INTERSECTRGN | DCX_NODELETERGN | DCX_LOCKWINDOWUPDATE);
|
|
xxxDrawWindowFrame(pwnd, hdc, DF_HUNGREDRAW | (TestwndFrameOn(pwnd) ? DF_ACTIVE : 0L));
|
|
_ReleaseDC(hdc);
|
|
}
|
|
|
|
CopyRect(&rc, &pwnd->rcWindow);
|
|
xxxCalcClientRect(pwnd, &rc, TRUE);
|
|
SetRectRgnIndirect(ghrgnInv2, &rc);
|
|
|
|
if (hrgnUpdate > HRGN_FULL) {
|
|
switch (IntersectRgn(hrgnUpdate, hrgnUpdate, ghrgnInv2)) {
|
|
|
|
case ERROR:
|
|
GreDeleteObject(hrgnUpdate);
|
|
hrgnUpdate = HRGN_FULL;
|
|
break;
|
|
|
|
case NULLREGION:
|
|
/*
|
|
* There is nothing in the client area to repaint.
|
|
* Blow the region away, and decrement the paint count
|
|
* if possible.
|
|
*/
|
|
GreDeleteObject(hrgnUpdate);
|
|
hrgnUpdate = NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Erase the rest of the window.
|
|
* When pwnd isn't WFCLIPCHILDREN, make sure valid children bits
|
|
* don't get overwritten if the child is in the middle of BeginPaint
|
|
* or just completed it's painting and it's hrgnUpdate is NULL.
|
|
*/
|
|
if (hrgnUpdate != NULL && !TestWF(pwnd, WFCLIPCHILDREN)) {
|
|
RECT rcT;
|
|
PWND pwndT;
|
|
|
|
if (hrgnUpdate == HRGN_FULL) {
|
|
rc = pwnd->rcWindow;
|
|
} else {
|
|
GreGetRgnBox(hrgnUpdate, &rc);
|
|
}
|
|
|
|
for (pwndT = pwnd->spwndChild; pwndT != NULL;
|
|
pwndT = pwndT->spwndNext) {
|
|
|
|
if (TestWF(pwndT, WFVISIBLE) &&
|
|
(TestWF(pwndT, WFSTARTPAINT) || pwndT->hrgnUpdate == NULL) &&
|
|
IntersectRect(&rcT, &rc, &pwndT->rcWindow)) {
|
|
|
|
/*
|
|
* This invalidate call won't leave the critial section. In
|
|
* reality the entire xxxRedrawHungWindow must not leave
|
|
* the critical section.
|
|
*/
|
|
BEGINATOMICCHECK();
|
|
xxxInternalInvalidate(pwndT, hrgnUpdate, RDW_INVALIDATE |
|
|
RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN);
|
|
ENDATOMICCHECK();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Get a window dc so that the menu and scroll bar areas are erased
|
|
* appropriately. But make sure it is clipped so that the children
|
|
* get clipped out correctly! If we don't do this, this we could erase
|
|
* children that aren't invalid.
|
|
*
|
|
* Note: DCX_WINDOW and DCX_USESTYLE will never clip out children.
|
|
* Need to pass the clipping styles in directly, instead of passing
|
|
* DCX_USESTYLE.
|
|
*/
|
|
flags = DCX_INTERSECTRGN | DCX_WINDOW | DCX_CACHE;
|
|
if (TestWF(pwnd, WFCLIPSIBLINGS))
|
|
flags |= DCX_CLIPSIBLINGS;
|
|
if (TestWF(pwnd, WFCLIPCHILDREN))
|
|
flags |= DCX_CLIPCHILDREN;
|
|
|
|
hdc = _GetDCEx(pwnd, hrgnUpdate, flags);
|
|
|
|
if (pwnd == pwnd->head.rpdesk->pDeskInfo->spwndBkGnd) {
|
|
pwndDesk = PWNDDESKTOP(pwnd);
|
|
ThreadLock(pwndDesk, &tlpwndDesk);
|
|
xxxInternalPaintDesktop(PWNDDESKTOP(pwnd), hdc, TRUE);
|
|
ThreadUnlock(&tlpwndDesk);
|
|
} else {
|
|
rc = pwnd->rcWindow;
|
|
|
|
OffsetRect(&rc, -pwnd->rcWindow.left, -pwnd->rcWindow.top);
|
|
|
|
/*
|
|
* Erase the rest of the window using the window's class background
|
|
* brush.
|
|
*/
|
|
if ((hbr = pwnd->pcls->hbrBackground) != NULL) {
|
|
if (hbr <= (HBRUSH)COLOR_ENDCOLORS + 1) {
|
|
hbr = SYSHBRUSH((ULONG_PTR)hbr - 1);
|
|
}
|
|
} else {
|
|
/*
|
|
* Use the window brush for windows and 3.x dialogs, and use
|
|
* the COLOR3D brush for 4.x dialogs.
|
|
*/
|
|
if (TestWF(pwnd, WFDIALOGWINDOW) && TestWF(pwnd, WFWIN40COMPAT)) {
|
|
hbr = SYSHBR(3DFACE);
|
|
} else {
|
|
hbr = SYSHBR(WINDOW);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If the window's class background brush is public, use it.
|
|
*/
|
|
sid = (W32PID)GreGetObjectOwner((HOBJ)hbr, BRUSH_TYPE);
|
|
if (sid == OBJECT_OWNER_PUBLIC ||
|
|
sid == (W32PID)(ULONG_PTR)PsGetCurrentProcessId()) {
|
|
|
|
FillRect(hdc, &rc, hbr);
|
|
} else {
|
|
/*
|
|
* The window's class background brush is not public.
|
|
*
|
|
* We get its color and set the color of our own public brush
|
|
* and use that for the background brush.
|
|
*/
|
|
|
|
/*
|
|
* If the window is a console window, get the console background brush.
|
|
* This brush will be different than the console class brush if the user
|
|
* changed the console background color.
|
|
*/
|
|
if (gatomConsoleClass == pwnd->pcls->atomClassName) {
|
|
dwColor = _GetWindowLong(pwnd, GWL_CONSOLE_BKCOLOR);
|
|
} else {
|
|
if ((dwColor = GreGetBrushColor(hbr)) == -1) {
|
|
dwColor = GreGetBrushColor(SYSHBR(WINDOW));
|
|
}
|
|
}
|
|
|
|
GreSetSolidBrush(ghbrHungApp, dwColor);
|
|
|
|
FillRect(hdc, &rc, ghbrHungApp);
|
|
}
|
|
}
|
|
_ReleaseDC(hdc);
|
|
|
|
/*
|
|
* The window has been erased and framed. It only did this because the
|
|
* app hasn't done it yet:
|
|
*
|
|
* - the app hasn't erased and frame yet.
|
|
* - the app is in the middle of erasing and framing.
|
|
*
|
|
* The app could not of completed erasing and framing, because the
|
|
* WFREDRAWIFHUNG bit is cleared when this successfully completes.
|
|
*
|
|
* Given that the app may be in the middle of erasing and framing, we
|
|
* need to set both the erase and frame bits *again* so it erasing and
|
|
* frames over again (if we don't, it never will). If the app hasn't
|
|
* done any erasing/framing yet, this is a nop.
|
|
*/
|
|
SetWF(pwnd, WFSENDNCPAINT);
|
|
SetWF(pwnd, WFSENDERASEBKGND);
|
|
|
|
/*
|
|
* Always set WFUPDATEDIRTY: we don't want the app to draw, then stop
|
|
* and have the hung app thread draw, and then allow the app to validate
|
|
* itself: Mark the update region dirty - cannot be validated until the
|
|
* app calls a painting function and acknowledges the update region.
|
|
*/
|
|
SetWF(pwnd, WFUPDATEDIRTY);
|
|
|
|
ThreadUnlock(&tlpwnd);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxHungAppDemon
|
|
*
|
|
* NOTE: RIT timers (like this one) get called while inside the critical
|
|
* section.
|
|
*
|
|
* We keep a list of redraw-if-hung windows in a list that remains in a
|
|
* single page to avoid touching the windows themselves each time through
|
|
* this routine. Touching the windows causes a bunch of unnecessary paging
|
|
* and in effect keeps all of the pages that contain top-level windows
|
|
* resident at all times; this is very wasteful.
|
|
*
|
|
* 02-28-92 DavidPe Created.
|
|
\***************************************************************************/
|
|
VOID xxxHungAppDemon(
|
|
PWND pwnd,
|
|
UINT message,
|
|
UINT_PTR nID,
|
|
LPARAM lParam)
|
|
{
|
|
TL tlpwnd;
|
|
DWORD nPwndHungRedraw;
|
|
PWND pwndHungRedraw;
|
|
|
|
UNREFERENCED_PARAMETER(message);
|
|
UNREFERENCED_PARAMETER(nID);
|
|
UNREFERENCED_PARAMETER(lParam);
|
|
UNREFERENCED_PARAMETER(pwnd);
|
|
|
|
CheckLock(pwnd);
|
|
|
|
/*
|
|
* See if we should start the screen saver.
|
|
*/
|
|
IdleTimerProc();
|
|
|
|
/*
|
|
* If it is time to hide the app starting cursor, do it.
|
|
*/
|
|
if (NtGetTickCount() >= gtimeStartCursorHide) {
|
|
/*
|
|
* No need to DeferWinEventNotify()
|
|
*/
|
|
zzzCalcStartCursorHide(NULL, 0);
|
|
}
|
|
|
|
/*
|
|
* Now check to see if there are any top-level windows that need
|
|
* redrawing.
|
|
*/
|
|
if (grpdeskRitInput == NULL || grpdeskRitInput->pDeskInfo->spwnd == NULL) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Walk down the list of redraw-if-hung windows. Loop until we hit the
|
|
* end of the array or find a NULL.
|
|
*/
|
|
nPwndHungRedraw = 0;
|
|
pwndHungRedraw = NULL;
|
|
while (pwndHungRedraw = VWPLNext(gpvwplHungRedraw, pwndHungRedraw, &nPwndHungRedraw)) {
|
|
/*
|
|
* See if the app is hung. If so, do the appropriate redrawing.
|
|
*/
|
|
if (FHungApp(GETPTI(pwndHungRedraw), CMSHUNGAPPTIMEOUT)) {
|
|
ThreadLock(pwndHungRedraw, &tlpwnd);
|
|
if (TestWF(pwndHungRedraw, WFREDRAWFRAMEIFHUNG)) {
|
|
/*
|
|
* WFREDRAWFRAMEIFHUNG will be cleared in the process of
|
|
* drawing the frame, so no need to clear it here.
|
|
*/
|
|
xxxRedrawHungWindowFrame(pwndHungRedraw,
|
|
TestwndFrameOn(pwndHungRedraw));
|
|
}
|
|
|
|
if (TestWF(pwndHungRedraw, WFREDRAWIFHUNG)) {
|
|
ClearHungFlag(pwndHungRedraw, WFREDRAWIFHUNG);
|
|
xxxRedrawHungWindow(pwndHungRedraw, NULL);
|
|
}
|
|
|
|
ThreadUnlock(&tlpwnd);
|
|
}
|
|
}
|
|
}
|