PWND _GetNextDlgGroupItem( PWND pwndDlg, PWND pwnd, BOOL fPrev) { PWND pwndCurrent; BOOL fOnceAround = FALSE; pwnd = pwndCurrent = _GetChildControl(pwndDlg, pwnd); do { pwnd = (fPrev ? UT_PrevGroupItem(pwndDlg, pwnd) : UT_NextGroupItem(pwndDlg, pwnd)); if (pwnd == pwndCurrent) fOnceAround = TRUE; if (!pwndCurrent) pwndCurrent = pwnd; } while (!fOnceAround && ((TestWF(pwnd, WFDISABLED) || !TestWF(pwnd, WFVISIBLE)))); return pwnd; }
BOOL xxxEndPaint( PWND pwnd, LPPAINTSTRUCT lpps) { CheckLock(pwnd); ReleaseCacheDC(lpps->hdc, TRUE); if (TestWF(pwnd, WFDONTVALIDATE)) { if (ghrgnUpdateSave != NULL) { InternalInvalidate3(pwnd, ghrgnUpdateSave, RDW_INVALIDATE | RDW_ERASE); if (--gnUpdateSave == 0) { GreDeleteObject(ghrgnUpdateSave); ghrgnUpdateSave = NULL; } } ClrWF(pwnd, WFDONTVALIDATE); } ClrWF(pwnd, WFWMPAINTSENT); /* * This used to check that the update-region was empty before * doing the clear. However, this caused a problem with WOW * amipro/approach hanging. They were invalidating rects in * their WM_PAINT handler, and allowing the defwindowproc to * perform the validation for them. Since we were blocking * the BeginPaint in this case, it sent them into a infinite * loop (see bug 19036). */ ClrWF(pwnd, WFSTARTPAINT); /* * Reshow the caret if needed, but AFTER we've released the DC. * This way ShowCaret() can reuse the DC we just released. */ if (pwnd == PtiCurrent()->pq->caret.spwnd) zzzInternalShowCaret(); return TRUE; }
/***************************************************************************\ * * _NextSibblingOrAncestor * * Called by _NextControl. It returns the next control to pwndStart. If there * is a next window (pwndStart->spwndNext), then that is it. * Otherwise, the next control is up the parent chain. However, if it's already * at the top of the chain (pwndRoot == pwndStart->spwndParent), then the next * control is the first child of pwndRoot. But if it's not at the top of the chain, * then the next control is pwndStart->spwndParent or an ancestor. * \***************************************************************************/ PWND _NextSibblingOrAncestor (PWND pwndRoot, PWND pwndStart) { PWND pwndParent; #ifdef DEBUG PWND pwndNext; #endif // If there is a sibbling, go for it if (pwndStart->spwndNext != NULL) { return (REBASEALWAYS(pwndStart, spwndNext)); } // If it cannot go up the parent chain, then return the first sibbling. pwndParent = REBASEALWAYS(pwndStart, spwndParent); if (pwndParent == pwndRoot) { // Note that if pwndStart doesn't have any sibblings, // this will return pwndStart again return (REBASEALWAYS(pwndParent, spwndChild)); } // Otherwise walk up the parent chain looking for the first window with // a WS_EX_CONTROLPARENT parent. #ifdef DEBUG pwndNext = #else return #endif _GetChildControl(pwndRoot, pwndParent); #ifdef DEBUG if ((pwndNext != pwndParent) || !TestWF(pwndParent, WEFCONTROLPARENT)) { // Code looping through the controls in a dialog might go into an infinite // loop because of this (i.e., xxxRemoveDefaultButton, _GetNextDlgTabItem,..) // We've walked up the parent chain but will never walk down the child chain again // because there is a NON WS_EX_CONTROLPARENT parent window somewhere in the chain. RIPMSG0 (RIP_ERROR, "_NextSibblingOrAncestor: Non WS_EX_CONTROLPARENT window in parent chain"); } return pwndNext; #endif }
BOOL xxxSetSystemMenu( PWND pwnd, PMENU pMenu) { CheckLock(pwnd); CheckLock(pMenu); if (TestWF(pwnd, WFSYSMENU)) { PMENU pmenuT = pwnd->spmenuSys; if (LockWndMenu(pwnd, &pwnd->spmenuSys, pMenu)) _DestroyMenu(pmenuT); MNPositionSysMenu(pwnd, pMenu); return TRUE; } RIPERR0(ERROR_NO_SYSTEM_MENU, RIP_VERBOSE, ""); return FALSE; }
VOID xxxSendChildNCPaint( PWND pwnd) { TL tlpwnd; CheckLock(pwnd); ThreadLockNever(&tlpwnd); pwnd = pwnd->spwndChild; while (pwnd != NULL) { if ((pwnd->hrgnUpdate == NULL) && TestWF(pwnd, WFSENDNCPAINT)) { ThreadLockExchangeAlways(pwnd, &tlpwnd); xxxSendNCPaint(pwnd, HRGN_FULL); } pwnd = pwnd->spwndNext; } ThreadUnlock(&tlpwnd); }
VOID _ScreenToClient( PWND pwnd, PPOINT ppt) { /* * Client and screen coordinates are the same for the * desktop window. */ if (GETFNID(pwnd) != FNID_DESKTOP) { #ifdef USE_MIRRORING if (TestWF(pwnd, WEFLAYOUTRTL)) { ppt->x = pwnd->rcClient.right - ppt->x; } else #endif { ppt->x -= pwnd->rcClient.left; } ppt->y -= pwnd->rcClient.top; } }
void xxxCreateTooltip(PTOOLTIPWND pttwnd, LPWSTR pstr) { CheckLock(pttwnd); /* * Store new text */ pttwnd->pstr = pstr; /* * If already visible, hide it and show it in new place. * Otherwise, set timer to show. */ if (TestWF(pttwnd, WFVISIBLE)) { xxxSetWindowPos((PWND)pttwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSENDCHANGING); xxxShowTooltip(pttwnd); } else { SetTooltipTimer(pttwnd, TTT_SHOW, pttwnd->dwShowDelay); } }
BOOL xxxRemoveFullScreen(PWND pwnd) { PDESKTOP pdesk = pwnd->head.rpdesk; BOOL fYielded; fYielded = FALSE; if (pdesk == NULL) return fYielded; if (TestWF(pwnd, WFFULLSCREEN) && FCallTray(pdesk)) { ClrWF(pwnd, WFFULLSCREEN); if (!(--pdesk->cFullScreen)) { xxxSetTrayWindow(pdesk, STW_SAME); fYielded = TRUE; } UserAssert(pdesk->cFullScreen >= 0); } return(fYielded); }
PWND HotKeyToWindow( DWORD key) { PHOTKEYSTRUCT phk; int ckeys; ckeys = gcHotKey; if (ckeys == 0) return 0; phk = gpHotKeyList; while (ckeys) { if (phk->key == key) return TestWF(phk->spwnd, WFVISIBLE) ? phk->spwnd : NULL; phk++; ckeys--; } return 0; }
void xxxResetTooltip(PTOOLTIPWND pttwnd) { KillTooltipTimer(pttwnd); CheckLock(pttwnd); if (TestWF(pttwnd, WFVISIBLE)) { PWND spwndMessage; TL tlpwnd; xxxSetWindowPos((PWND)pttwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOZORDER); spwndMessage = PWNDMESSAGE(pttwnd); ThreadLockAlways(spwndMessage, &tlpwnd); xxxSetParent((PWND)pttwnd, spwndMessage); ThreadUnlock(&tlpwnd); } ZeroTooltip(pttwnd); pttwnd->head.rpdesk->dwDTFlags &= ~DF_TOOLTIP; }
BOOL _IsWindowVisible( PWND pwnd) { /* * Check if this is the iconic window being moved around with a mouse * If so, return a TRUE, though, strictly speaking, it is hidden. * This helps the Tracer guys from going crazy! * Fix for Bug #57 -- SANKAR -- 08-08-89 -- */ if (pwnd == NULL) return TRUE; for (;;) { if (!TestWF(pwnd, WFVISIBLE)) return FALSE; if (GETFNID(pwnd) == FNID_DESKTOP) break; pwnd = REBASEPWND(pwnd, spwndParent); } return TRUE; }
/***************************************************************************\ * _PrevControl * * History: \***************************************************************************/ PWND _PrevControl( PWND pwndRoot, PWND pwndStart, UINT uFlags) { BOOL fFirstFound; PWND pwndNext; PWND pwnd, pwndFirst; if (!pwndStart) return(NULL); UserAssert(pwndRoot != pwndStart); UserAssert(!TestWF(pwndStart, WEFCONTROLPARENT)); pwnd = _NextControl(pwndRoot, NULL, uFlags); pwndFirst = pwnd; fFirstFound = FALSE; while (pwndNext = _NextControl(pwndRoot, pwnd, uFlags)) { if (pwndNext == pwndStart) break; if (pwndNext == pwndFirst) { if (fFirstFound) { RIPMSG0(RIP_WARNING, "_PrevControl: Loop Detected"); break; } else { fFirstFound = TRUE; } } pwnd = pwndNext; } return pwnd; }
VOID SetDialogPointer(PWND pwnd, LONG_PTR lPtr) { if ((pwnd->cbwndExtra < DLGWINDOWEXTRA) || TestWF(pwnd, WFSERVERSIDEPROC) || (PpiCurrent() != GETPTI(pwnd)->ppi)) { RIPMSG1(RIP_WARNING, "SetDialogPointer: Unexpected pwnd:%#p", pwnd); return; } ((PDIALOG)pwnd)->pdlg = (PDLG)lPtr; if (lPtr == 0) { pwnd->fnid |= FNID_CLEANEDUP_BIT; ClrWF(pwnd, WFDIALOGWINDOW); } else { if (pwnd->fnid == 0) { pwnd->fnid = FNID_DIALOG; } SetWF(pwnd, WFDIALOGWINDOW); } }
// -------------------------------------------------------------------------- // // GetInitMenuParam() // // Gets the HMENU sent as the wParam of WM_INITMENU, and for menu bars, is // the actual menu to be interacted with. // // -------------------------------------------------------------------------- PMENU GetInitMenuParam(PWND pwndMenu, BOOL * lpfSystem) { // // Find out what menu we should be sending in WM_INITMENU: // If minimized/child/empty menubar, use system menu // if (TestWF(pwndMenu, WFMINIMIZED) || TestwndChild(pwndMenu) || (pwndMenu->spmenu == NULL) || !pwndMenu->spmenu->cItems) { if (lpfSystem != NULL) *lpfSystem = TRUE; return(GetSysMenu(pwndMenu, FALSE)); } else { if (lpfSystem != NULL) *lpfSystem = FALSE; return(pwndMenu->spmenu); } }
HDC xxxBeginPaint( PWND pwnd, LPPAINTSTRUCT lpps) { HRGN hrgnUpdate; HDC hdc; BOOL fSendEraseBkgnd; CheckLock(pwnd); UserAssert(IsWinEventNotifyDeferredOK()); if (TEST_PUDF(PUDF_DRAGGINGFULLWINDOW)) SetWF(pwnd, WFSTARTPAINT); /* * We're processing a WM_PAINT message: clear this flag. */ ClrWF(pwnd, WFPAINTNOTPROCESSED); /* * If this bit gets set while we are drawing the frame we will need * to redraw it. * * If necessary, send our WM_NCPAINT message now. * * please heed these notes * * We have to send this message BEFORE we diddle hwnd->hrgnUpdate, * because an app may call ValidateRect or InvalidateRect in its * handler, and it expects what it does to affect what gets drawn * in the later WM_PAINT. * * It is possible to get an invalidate when we leave the critical * section below, therefore we loop until UPDATEDIRTY is clear * meaning there were no additional invalidates. */ if (TestWF(pwnd, WFSENDNCPAINT)) { do { ClrWF(pwnd, WFUPDATEDIRTY); hrgnUpdate = GetNCUpdateRgn(pwnd, FALSE); xxxSendNCPaint(pwnd, hrgnUpdate); DeleteMaybeSpecialRgn(hrgnUpdate); } while (TestWF(pwnd, WFUPDATEDIRTY)); } else { ClrWF(pwnd, WFUPDATEDIRTY); } /* * Hide the caret if needed. Do this before we get the DC so * that if HideCaret() gets and releases a DC we will be able * to reuse it later here. * No need to DeferWinEventNotify() since pwnd is locked. */ if (pwnd == PtiCurrent()->pq->caret.spwnd) zzzInternalHideCaret(); /* * Send the check for sending an WM_ERASEBKGND to the * window. */ if (fSendEraseBkgnd = TestWF(pwnd, WFSENDERASEBKGND)) { ClrWF(pwnd, WFERASEBKGND); ClrWF(pwnd, WFSENDERASEBKGND); } /* * Validate the entire window. */ if (NEEDSPAINT(pwnd)) DecPaintCount(pwnd); ClrWF(pwnd, WFINTERNALPAINT); hrgnUpdate = pwnd->hrgnUpdate; pwnd->hrgnUpdate = NULL; if (TestWF(pwnd, WFDONTVALIDATE)) { if (ghrgnUpdateSave == NULL) { ghrgnUpdateSave = CreateEmptyRgn(); } if (ghrgnUpdateSave != NULL) { UnionRgn(ghrgnUpdateSave, ghrgnUpdateSave, hrgnUpdate); gnUpdateSave++; } } /* * Clear these flags for backward compatibility */ lpps->fIncUpdate = lpps->fRestore = FALSE; lpps->hdc = hdc = _GetDCEx(pwnd, hrgnUpdate, DCX_USESTYLE | DCX_INTERSECTRGN); if (UT_GetParentDCClipBox(pwnd, hdc, &lpps->rcPaint)) { /* * If necessary, erase our background, and possibly deal with * our children's frames and backgrounds. */ if (fSendEraseBkgnd) xxxSendEraseBkgnd(pwnd, hdc, hrgnUpdate); } /* * Now that we're completely erased, see if there are any children * that couldn't draw their own frames because their update regions * got deleted. */ xxxSendChildNCPaint(pwnd); /* * The erase and frame operation has occured. Clear the WFREDRAWIFHUNG * bit here. We don't want to clear it until we know the erase and * frame has occured, so we know we always have a consistent looking * window. */ ClearHungFlag(pwnd, WFREDRAWIFHUNG); lpps->fErase = (TestWF(pwnd, WFERASEBKGND) != 0); return hdc; }
HRGN GetNCUpdateRgn( PWND pwnd, BOOL fValidateFrame) { HRGN hrgnUpdate; if (pwnd->hrgnUpdate > HRGN_FULL) { /* * We must make a copy of our update region, because * it could change if we send a message, and we want to * make sure the whole thing is used for drawing our * frame and background. We can't use a global * temporary region, because more than one app may * be calling this routine. */ hrgnUpdate = CreateEmptyRgnPublic(); if (hrgnUpdate == NULL) { hrgnUpdate = HRGN_FULL; } else if (CopyRgn(hrgnUpdate, pwnd->hrgnUpdate) == ERROR) { GreDeleteObject(hrgnUpdate); hrgnUpdate = HRGN_FULL; } if (fValidateFrame) { /* * Now that we've taken care of any frame drawing, * intersect the update region with the window's * client area. Otherwise, apps that do ValidateRects() * to draw themselves (e.g., WinWord) won't ever * subtract off the part of the update region that * overlaps the frame but not the client. */ CalcWindowRgn(pwnd, ghrgnInv2, TRUE); switch (IntersectRgn(pwnd->hrgnUpdate, pwnd->hrgnUpdate, ghrgnInv2)) { case ERROR: /* * If an error occured, we can't leave things as * they are: invalidate the whole window and let * BeginPaint() take care of it. */ GreDeleteObject(pwnd->hrgnUpdate); pwnd->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(pwnd->hrgnUpdate); pwnd->hrgnUpdate = NULL; ClrWF(pwnd, WFUPDATEDIRTY); if (!TestWF(pwnd, WFINTERNALPAINT)) DecPaintCount(pwnd); break; } } } else { hrgnUpdate = pwnd->hrgnUpdate; } return hrgnUpdate; }
BOOL xxxSendEraseBkgnd( PWND pwnd, HDC hdcBeginPaint, HRGN hrgnUpdate) { PTHREADINFO ptiCurrent; BOOL fErased; HDC hdc; CheckLock(pwnd); /* * For minimized dudes in win3.1, we would've sent an * WM_ICONERASEBKGND and cleared the erase bit. Now that min * windows in 4.0 are all nonclient, don't bother erasing at * all. Pretend like we did. * * NOTE: * For < 4.0 windows, we may have to send a fake WM_ICONERASEKBGND * to keep 'em happy. Saves time not to though. Getting a DC and * sending the message ain't speedy. */ if ((hrgnUpdate == NULL) || TestWF(pwnd, WFMINIMIZED)) return FALSE; /* * If a DC to use was not passed in, get one. * We want one clipped to this window's update region. */ if (hdcBeginPaint == NULL) { hdc = _GetDCEx(pwnd, hrgnUpdate, DCX_USESTYLE | DCX_INTERSECTRGN | DCX_NODELETERGN); } else { hdc = hdcBeginPaint; } /* * If we're send the WM_ERASEBKGND to another process * we need to change the DC owner. * * We'd like to change the owner to pwnd->pti->idProcess, but * GDI won't let us assign ownership back to ourselves later. */ ptiCurrent = PtiCurrent(); if (GETPTI(pwnd)->ppi != ptiCurrent->ppi) GreSetDCOwner(hdc, OBJECT_OWNER_PUBLIC); /* * Send the event to the window. This contains the DC clipped to * the update-region. */ fErased = (BOOL)xxxSendMessage(pwnd, WM_ERASEBKGND, (WPARAM)hdc, 0L); /* * If we've changed the DC owner, change it back to * the current process. */ if (GETPTI(pwnd)->ppi != ptiCurrent->ppi) GreSetDCOwner(hdc, OBJECT_OWNER_CURRENT); /* * If the WM_ERASEBKGND message did not erase the * background, then set this flag to let BeginPaint() * know to ask the caller to do it via the fErase * flag in the PAINTSTRUCT. */ if (!fErased) { SetWF(pwnd, WFERASEBKGND); if (!TestWF(pwnd, WFWIN31COMPAT)) SetWF(pwnd, WFSENDERASEBKGND); } /* * If we got a cache DC in this routine, release it. */ if (hdcBeginPaint == NULL) { ReleaseCacheDC(hdc, TRUE); } return fErased; }
/* * Modal loop for when the user has selected the help icon from the titlebar * */ VOID xxxHelpLoop(PWND pwnd) { HWND hwndChild; PWND pwndChild; PWND pwndControl; MSG msg; RECT rc; int cBorders; PTHREADINFO ptiCurrent = PtiCurrent(); DLGENUMDATA DlgEnumData; TL tlpwndChild; CheckLock(pwnd); UserAssert(IsWinEventNotifyDeferredOK()); if (FWINABLE()) { xxxWindowEvent(EVENT_SYSTEM_CONTEXTHELPSTART, pwnd, OBJID_WINDOW, INDEXID_CONTAINER, 0); } zzzSetCursor(SYSCUR(HELP)); xxxCapture(ptiCurrent, pwnd, SCREEN_CAPTURE); cBorders = GetWindowBorders(pwnd->style, pwnd->ExStyle, TRUE, FALSE); CopyInflateRect(&rc, &pwnd->rcWindow, -cBorders * SYSMET(CXBORDER), -cBorders * SYSMET(CYBORDER)); while (ptiCurrent->pq->spwndCapture == pwnd) { if (!xxxPeekMessage(&msg, NULL, 0, 0, PM_NOYIELD | PM_NOREMOVE)) { xxxWaitMessage(); continue; } if (msg.message == WM_NCLBUTTONDOWN) { break; } else if (msg.message == WM_LBUTTONDOWN) { /* * If user clicked outside of window client, bail out now. */ if (!PtInRect(&rc, msg.pt)) break; /* * WindowHitTest() won't return a static control's handle */ hwndChild = xxxWindowHitTest(pwnd, msg.pt, NULL, 0); pwndChild = ValidateHwnd( hwndChild ); ThreadLock(pwndChild, &tlpwndChild); if (pwndChild && FIsParentDude(pwndChild)) { /* * If this is a dialog class, then one of three things has * happened: * * o This is a static text control * o This is the background of the dialog box. * * What we do is enumerate the child windows and see if * any of them contain the current cursor point. If they do, * change our window handle and continue on. Otherwise, * return doing nothing -- we don't want context-sensitive * help for a dialog background. * * If this is a group box, then we might have clicked on a * disabled control, so we enumerate child windows to see * if we get another control. */ /* * We're enumerating a dialog's children. So, if we don't * find any matches, hwndChild will be NULL and the check * below will drop out. */ DlgEnumData.pwndDialog = pwndChild; DlgEnumData.pwndControl = NULL; DlgEnumData.ptCurHelp = msg.pt; xxxInternalEnumWindow(pwndChild, EnumPwndDlgChildProc, (LPARAM)&DlgEnumData, BWL_ENUMCHILDREN); pwndControl = DlgEnumData.pwndControl; } else { pwndControl = pwndChild; } /* * If we click on nothing, just exit. */ if (pwndControl == pwnd) { pwndControl = NULL; } /* * HACK ALERT (Visual Basic 4.0) - they have their own non-window * based controls that draw directly on the main dialog. In order * to provide help for these controls, we pass along the WM_HELP * message iff the main dialog has a context id assigned. * * If the top level window has its own context help ID, * then pass it in the context help message. */ if (!pwndControl) { if (_GetProp(pwnd, MAKEINTATOM(gpsi->atomContextHelpIdProp), TRUE)) pwndControl = pwnd; } if (pwndControl) { PWND pwndSend; int id; TL tlpwndSend; TL tlpwndControl; ThreadLockAlways(pwndControl, &tlpwndControl); zzzSetCursor(SYSCUR(ARROW)); xxxReleaseCapture(); xxxRedrawTitle(pwnd, DC_BUTTONS); ClrWF(pwnd, WFHELPBUTTONDOWN); xxxGetMessage(&msg, NULL, 0, 0); if (FWINABLE()) { xxxWindowEvent(EVENT_OBJECT_STATECHANGE, pwnd, OBJID_TITLEBAR, INDEX_TITLEBAR_HELPBUTTON, FALSE); xxxWindowEvent(EVENT_SYSTEM_CONTEXTHELPEND, pwnd, OBJID_WINDOW, INDEXID_CONTAINER, FALSE); } /* * Determine the ID of the control * We used to always sign extend, but Win98 doesn't do that * so we only sign extend 0xffff. MCostea #218711 */ if (TestwndChild(pwndControl)) { id = PTR_TO_ID(pwndControl->spmenu); if (id == 0xffff) { id = -1; } } else { id = -1; } /* * Disabled controls and static controls won't pass this * on to their parent, so instead, we send the message to * their parent. */ if (TestWF(pwndControl, WFDISABLED)) { PWND pwndParent = _GetParent(pwndControl); if (!pwndParent) { ThreadUnlock( &tlpwndControl ); ThreadUnlock( &tlpwndChild ); return; } pwndSend = pwndParent; } else { pwndSend = pwndControl; } ThreadLockAlways(pwndSend, &tlpwndSend); xxxSendHelpMessage( pwndSend, HELPINFO_WINDOW, id, (HANDLE)HWq(pwndControl), GetContextHelpId(pwndControl)); ThreadUnlock(&tlpwndSend); ThreadUnlock(&tlpwndControl); ThreadUnlock(&tlpwndChild); return; } ThreadUnlock(&tlpwndChild); break; } else if ((msg.message == WM_RBUTTONDOWN) || (msg.message == WM_MBUTTONDOWN) || (msg.message == WM_XBUTTONDOWN)) { /* * fix bug 29852; break the loop for right and middle buttons * and pass along the messages to the control */ break; } else if (msg.message == WM_MOUSEMOVE) { if (PtInRect(&rc, msg.pt)) zzzSetCursor(SYSCUR(HELP)); else zzzSetCursor(SYSCUR(ARROW)); } else if (msg.message == WM_KEYDOWN && msg.wParam == VK_ESCAPE) { xxxGetMessage( &msg, NULL, 0, 0 ); break; } xxxGetMessage(&msg, NULL, 0, 0); xxxTranslateMessage(&msg, 0); xxxDispatchMessage(&msg); } xxxReleaseCapture(); zzzSetCursor(SYSCUR(ARROW)); xxxRedrawTitle(pwnd, DC_BUTTONS); ClrWF(pwnd, WFHELPBUTTONDOWN); if (FWINABLE()) { xxxWindowEvent(EVENT_OBJECT_STATECHANGE, pwnd, OBJID_TITLEBAR, INDEX_TITLEBAR_HELPBUTTON, 0); xxxWindowEvent(EVENT_SYSTEM_CONTEXTHELPEND, pwnd, OBJID_WINDOW, INDEXID_CONTAINER, 0); } }
VOID xxxInternalDoSyncPaint( PWND pwnd, DWORD flags) { CheckLock(pwnd); /* * Do the paint for this window. */ xxxSimpleDoSyncPaint(pwnd); /* * Normally we like to enumerate all of this window's children and have * them erase their backgrounds synchronously. However, this is a bad * thing to do if the window is NOT CLIPCHLIDREN. Here's the scenario * we want to to avoid: * * 1) Window 'A' is invalidated * 2) 'A' erases itself (or not, doesn't matter) * 3) 'A's children are enumerated and they erase themselves. * 4) 'A' paints over its children (remember, 'A' isn't CLIPCHILDREN) * 5) 'A's children paint but their backgrounds aren't their ERASEBKND * color (because 'A' painted over them) and everything looks like * dirt. */ if ((flags & DSP_ALLCHILDREN) || ((flags & DSP_ENUMCLIPPEDCHILDREN) && TestWF(pwnd, WFCLIPCHILDREN))) { TL tlpwnd; PBWL pbwl; HWND *phwnd; if (pbwl = BuildHwndList(pwnd->spwndChild, BWL_ENUMLIST, NULL)) { PTHREADINFO ptiCurrent = PtiCurrent(); HWND hwnd; /* * If the client dies during a callback, the hwnd list * will be freed in xxxDestroyThreadInfo. */ for (phwnd = pbwl->rghwnd; (hwnd = *phwnd) != (HWND)1; phwnd++) { if (hwnd == NULL) continue; if ((pwnd = (PWND)RevalidateHwnd(hwnd)) == NULL) continue; /* * Note: testing if a window is a child automatically * excludes the desktop window. */ if (TestWF(pwnd, WFCHILD) && (ptiCurrent != GETPTI(pwnd))) { /* * Don't cause any more intertask sendmessages cause it * does bad things to cbt's windowproc hooks. (Due to * SetParent allowing child windows in the topwindow * hierarchy. */ continue; } /* * Note that we pass only certain bits down as we recurse: * the other bits pertain to the current window only. */ ThreadLockAlwaysWithPti(ptiCurrent, pwnd, &tlpwnd); xxxInternalDoSyncPaint(pwnd, flags); ThreadUnlock(&tlpwnd); } FreeHwndList(pbwl); } } }
BOOL TWIN_DriverMessage(LPMSG lpMsg, HWND hWnd, UINT uMin, UINT uMax,UINT uFlg,BOOL bNoWait) { DWORD dwerv; HANDLE hTask; LPQUEUE lpQueue; if (hWnd && !IsWindow(hWnd)) hWnd = 0; if (hWnd) hTask = GetWindowTask(hWnd); else hTask = GetCurrentTask(); lpQueue = QueueGetPtr(hTask); /******************************************/ /* what is this? it is not called... */ /*hFocusTask = GetWindowTask(GetFocus()); */ /******************************************/ lpMsg->hwnd = 0; labLoop: while(1) { while (lpSendMessageStack && lpSendMessageStack->hReceivingTask == GetCurrentTask() && !lpSendMessageStack->bSendReceived) { TWIN_ReceiveMessage(FALSE); } /* try for a message from application queue */ if (QueueGetMsg(lpQueue, lpMsg, hWnd, uMin, uMax, uFlg)) { break; } /* try for a message from system queue */ if (QueueGetMsg(0,lpMsg,hWnd,uMin,uMax,uFlg)) { break; } if (uMin <= WM_PAINT && (!uMax || uMax >= WM_PAINT)) { /* finally, check if the window needs a paint message */ if(lpQueue->wQueueFlags & QFPAINT) { labTryNext: if((lpMsg->hwnd = InternalUpdateWindows())) { if (TestWF(lpMsg->hwnd, WFNCDIRTY)) { if (NonEmptyNCRect(lpMsg->hwnd)) { lpMsg->message = WM_NCPAINT; lpMsg->wParam = 0; lpMsg->lParam = 0L; break; } else { ClearWF(lpMsg->hwnd, WFNCDIRTY); } } if (TestWF(lpMsg->hwnd, WFDIRTY)) { if (IsIconic(lpMsg->hwnd) && GetClassIcon(lpMsg->hwnd)) { lpMsg->message = WM_PAINTICON; lpMsg->wParam = 1; } else { lpMsg->message = WM_PAINT; lpMsg->wParam = 0; } lpMsg->lParam = 0L; break; } else goto labTryNext; } lpQueue->wQueueFlags &= ~QFPAINT; } } if ((uMin <= WM_TIMER && (!uMax || uMax >= WM_TIMER)) || (uMin <= WM_SYSTIMER && (!uMax || uMax >= WM_SYSTIMER))) { if(lpQueue->wQueueFlags & QFTIMER) { if (TWIN_GetTimerMsg(hWnd,hTask,lpMsg,uFlg)) { break; } } } /* none of the above, so see if system is ready. */ if (!TWIN_InDriverWait) { TWIN_InDriverWait = TRUE; dwerv = DriverWaitEvent(TRUE); TWIN_InDriverWait = FALSE; if (!dwerv && !bNoWait) { /* * The code here used to call ReadyTask(GetCurrentTask()) * before calling InternalYield(), but that results in * a solid-run condition if more than one task is * defined, because it results in the other task looking * like it's ready to run, when all tasks should be waiting * on a driver event. So we yield if necessary, but do * not set ourselves as ready-to-run if we do. We will * be run again when we first get a message in our queue. */ #if defined(MAC_TASKING_PATCH) ReadyTask(GetCurrentTask()); #endif if (!InternalYield()) { TWIN_InDriverWait = TRUE; (void)DriverWaitEvent(FALSE); TWIN_InDriverWait = FALSE; } } else if (!(uFlg & PM_NOYIELD)) Yield(); } else if (!(uFlg & PM_NOYIELD)) InternalYield(); if(bNoWait) return FALSE; } if (CallGetMessageHooks(lpMsg,uFlg & PM_REMOVE)) goto labLoop; return TRUE; }
void xxxShowOwnedWindows( PWND pwndOwner, UINT cmdShow) { BOOL fShow; int cmdZoom; HWND *phwnd; PBWL pbwl; PWND pwnd, pwndTopOwner; TL tlpwnd; CheckLock(pwndOwner); /* * Not interested in child windows */ if (TestwndChild(pwndOwner)) return; if ((pbwl = BuildHwndList(PWNDDESKTOP(pwndOwner)->spwndChild, BWL_ENUMLIST, NULL)) == NULL) return; /* * NOTE: The following code assumes the values of SW_* are 1, 2, 3, and 4 */ fShow = (cmdShow >= SW_PARENTOPENING); cmdZoom = 0; if (cmdShow == SW_OTHERZOOM) cmdZoom = SIZEZOOMHIDE; if (cmdShow == SW_OTHERUNZOOM) cmdZoom = SIZEZOOMSHOW; /* * If zoom/unzoom, then open/close all popups owned by all other * windows. Otherwise, open/close popups owned by pwndOwner. */ for (phwnd = pbwl->rghwnd; *phwnd != (HWND)1; phwnd++) { /* * Lock the window before we play with it. * If the window handle is invalid, skip it */ if ((pwnd = RevalidateHwnd(*phwnd)) == NULL) continue; /* * Kanji windows can't be owned, so skip it. */ if (TestCF(pwnd, CFKANJIWINDOW)) continue; /* * If same as window passed in, skip it. */ if (pwnd == pwndOwner) continue; /* * Find ultimate owner of popup, but only go up as far as pwndOwner. */ if ((pwndTopOwner = pwnd->spwndOwner) != NULL) { /* * The TestwndHI is needed since if it has an icon, pwndOwner * is invalid. */ while (!TestwndHI(pwndTopOwner) && pwndTopOwner != pwndOwner && pwndTopOwner->spwndOwner != NULL) pwndTopOwner = pwndTopOwner->spwndOwner; } /* * Zoom/Unzoom case. */ if (cmdZoom != 0) { /* * If no parent, or parents are the same, skip. */ if (pwndTopOwner == NULL || pwndTopOwner == pwndOwner) continue; /* * If owner is iconic, then this window should stay hidden, * UNLESS the minimized window is disabled, in which case we'd * better show the window. */ if ( cmdShow == SW_OTHERUNZOOM && pwndTopOwner != NULL && TestWF(pwndTopOwner, WFMINIMIZED) && !TestWF(pwndTopOwner, WFDISABLED) ) continue; } else { /* * Hide/Iconize/Show/Open case. */ /* * If parents aren't the same, skip. */ if (pwndTopOwner != pwndOwner) continue; } /* * Hide or show if: * Showing & this is a hidden popup * OR * Hiding & this is a visible window */ if ((fShow && TestWF(pwnd, WFHIDDENPOPUP)) || (!fShow && TestWF(pwnd, WFVISIBLE))) { ThreadLockAlways(pwnd, &tlpwnd); xxxSendMessage(pwnd, WM_SHOWWINDOW, fShow, (LONG)cmdShow); ThreadUnlock(&tlpwnd); } } /* * Free the window list. */ FreeHwndList(pbwl); }
PWND _GetWindow( PWND pwnd, UINT cmd) { PWND pwndT; BOOL fRebase = FALSE; /* * If this is a desktop window, return NULL for sibling or * parent information. */ if (GETFNID(pwnd) == FNID_DESKTOP) { switch (cmd) { case GW_CHILD: break; default: return NULL; break; } } /* * Rebase the returned window at the end of the routine * to avoid multiple test for pwndT == NULL. */ pwndT = NULL; switch (cmd) { case GW_HWNDNEXT: pwndT = pwnd->spwndNext; fRebase = TRUE; break; case GW_HWNDFIRST: if (pwnd->spwndParent) { pwndT = REBASEPWND(pwnd, spwndParent); pwndT = REBASEPWND(pwndT, spwndChild); if (GetAppCompatFlags(NULL) & GACF_IGNORETOPMOST) { while (pwndT != NULL) { if (!TestWF(pwndT, WEFTOPMOST)) break; pwndT = REBASEPWND(pwndT, spwndNext); } } } break; case GW_HWNDLAST: pwndT = GetPrevPwnd(pwnd, NULL); break; case GW_HWNDPREV: pwndT = GetPrevPwnd(pwnd, pwnd); break; case GW_OWNER: pwndT = pwnd->spwndOwner; fRebase = TRUE; break; case GW_CHILD: pwndT = pwnd->spwndChild; fRebase = TRUE; break; #if !defined(_USERK_) case GW_ENABLEDPOPUP: pwndT = (PWND)NtUserCallHwnd(PtoHq(pwnd), SFI_DWP_GETENABLEDPOPUP); fRebase = TRUE; break; #endif default: RIPERR0(ERROR_INVALID_GW_COMMAND, RIP_VERBOSE, ""); return NULL; } if (pwndT != NULL && fRebase) pwndT = REBASEPTR(pwnd, pwndT); return pwndT; }
BOOL DoPaint( PWND pwndFilter, LPMSG lpMsg) { PWND pwnd; PWND pwndT; PTHREADINFO ptiCurrent = PtiCurrent(); #if 0 // CHRISWIL: WIN95 SPECIFIC /* * If there is a system modal up and it is attached to another task, * DON'T do paints. We don't want to return a message for a window in * another task! */ if (hwndSysModal && (hwndSysModal->hq != hqCurrent)) { /* * Poke this guy so he wakes up at some point in the future, * otherwise he may never wake up to realize he should paint. * Causes hangs - e.g. Photoshop installation program * PostThreadMessage32(Lpq(hqCurrent)->idThread, WM_NULL, 0, 0, 0); */ return FALSE; } #endif /* * If this is a system thread, then walk the windowstation desktop-list * to find the window which needs painting. For other threads, we * reference off the thread-desktop. */ if (ptiCurrent->TIF_flags & TIF_SYSTEMTHREAD) { PWINDOWSTATION pwinsta; PDESKTOP pdesk; if ((pwinsta = ptiCurrent->pwinsta) == NULL) { RIPMSG0(RIP_ERROR, "DoPaint: SYSTEMTHREAD does not have (pwinsta)"); return FALSE; } pwnd = NULL; for(pdesk = pwinsta->rpdeskList; pdesk; pdesk = pdesk->rpdeskNext) { if (pwnd = InternalDoPaint(pdesk->pDeskInfo->spwnd, ptiCurrent)) break; } } else { pwnd = InternalDoPaint(ptiCurrent->rpdesk->pDeskInfo->spwnd, ptiCurrent); } if (pwnd != NULL) { if (!CheckPwndFilter(pwnd, pwndFilter)) return FALSE; /* * We're returning a WM_PAINT message, so clear WFINTERNALPAINT so * it won't get sent again later. */ if (TestWF(pwnd, WFINTERNALPAINT)) { ClrWF(pwnd, WFINTERNALPAINT); /* * If there is no update region, then no more paint for this * window. */ if (pwnd->hrgnUpdate == NULL) DecPaintCount(pwnd); } /* * Set the STARTPAINT so that any other calls to BeginPaint while * painting is begin performed, will prevent painting on those * windows. * * Clear the UPDATEDIRTY since some apps (DBFast) don't call * GetUpdateRect, BeginPaint/EndPaint. */ ClrWF(pwnd, WFSTARTPAINT); ClrWF(pwnd, WFUPDATEDIRTY); /* * If we get an invalidate between now and the time the app calls * BeginPaint() and the windows parent is not CLIPCHILDREN, then * the parent will paint in the wrong order. So we are going to * cause the child to paint again. Look in beginpaint and internal * invalidate for other parts of this fix. * * Set a flag to signify that we are in the bad zone. * * Must go up the parent links to make sure all parents have * WFCLIPCHILDREN set otherwise set the WFWMPAINTSENT flag. * This is to fix Excel spreadsheet and fulldrag. The speadsheet * parent window (class XLDESK) has WFCLIPCHILDREN set but it's * parent (class XLMAIN) doesn't. So the main window erases the * background after the child window paints. * * JOHANNEC : 27-Jul-1994 */ /* * NT Bug 400167: As we walk up the tree, we need to stop short of * desktop windows and mother desktop windows. We can't do a test * for WFCLIPCHILDREN on the mother desktop window's parent because * it doesn't exist. This means that no desktop window will get * WFWMPAINTSENT set, but the message window will be able to get * WFWMPAINTSENT set. */ pwndT = pwnd; while (pwndT && (GETFNID(pwndT) != FNID_DESKTOP)) { if (!TestWF(pwndT->spwndParent, WFCLIPCHILDREN)) { SetWF(pwnd, WFWMPAINTSENT); break; } pwndT = pwndT->spwndParent; } /* * If the top level "tiled" owner/parent of this window is iconed, * send a WM_PAINTICON rather than a WM_PAINT. The wParam * is TRUE if this is the tiled window and FALSE if it is a * child/owned popup of the minimized window. * * BACKWARD COMPATIBILITY HACK * * 3.0 sent WM_PAINTICON with wParam == TRUE for no apparent * reason. Lotus Notes 2.1 depends on this for some reason * to properly change its icon when new mail arrives. */ if (!TestWF(pwnd, WFWIN40COMPAT) && TestWF(pwnd, WFMINIMIZED) && (pwnd->pcls->spicn != NULL)) { StoreMessage(lpMsg, pwnd, WM_PAINTICON, (DWORD)TRUE, 0L, 0L); } else { StoreMessage(lpMsg, pwnd, WM_PAINT, 0, 0L, 0L); } return TRUE; } return FALSE; }
int FindNCHit( PWND pwnd, LONG lPt) { POINT pt; RECT rcWindow; RECT rcClient; RECT rcClientAdj; int cBorders; int dxButton; pt.x = LOWORD(lPt); pt.y = HIWORD(lPt); if (!PtInRect(&pwnd->rcWindow, pt)) return HTNOWHERE; if (TestWF(pwnd, WFMINIMIZED)) { CopyInflateRect(&rcWindow, &pwnd->rcWindow, -(SYSMET(CXFIXEDFRAME) + SYSMET(CXBORDER)), -(SYSMET(CYFIXEDFRAME) + SYSMET(CYBORDER))); if (!PtInRect(&rcWindow, pt)) return HTCAPTION; goto CaptionHit; } // Get client rectangle rcClient = pwnd->rcClient; if (PtInRect(&rcClient, pt)) return HTCLIENT; // Are we in "pseudo" client, i.e. the client & scrollbars & border if (TestWF(pwnd, WEFCLIENTEDGE)) CopyInflateRect(&rcClientAdj, &rcClient, SYSMET(CXEDGE), SYSMET(CYEDGE)); else rcClientAdj = rcClient; if (TestWF(pwnd, WFVPRESENT)) rcClientAdj.right += SYSMET(CXVSCROLL); if (TestWF(pwnd, WFHPRESENT)) rcClientAdj.bottom += SYSMET(CYHSCROLL); if (!PtInRect(&rcClientAdj, pt)) { // Subtract out window borders cBorders = GetWindowBorders(pwnd->style, pwnd->ExStyle, TRUE, FALSE); CopyInflateRect(&rcWindow, &pwnd->rcWindow, -cBorders*SYSMET(CXBORDER), -cBorders*SYSMET(CYBORDER)); // Are we on the border? if (!PtInRect(&rcWindow, pt)) { // On a sizing border? if (!TestWF(pwnd, WFSIZEBOX)) { // // Old compatibility thing: For 3.x windows that just had // a border, we returned HTNOWHERE, believe it or not, // because our hit-testing code was so brain dead. // if (!TestWF(pwnd, WFWIN40COMPAT) && !TestWF(pwnd, WFDLGFRAME) && !TestWF(pwnd, WEFDLGMODALFRAME)) { return(HTNOWHERE); } else { return(HTBORDER); // We are on a dlg frame. } } else { int ht; // // Note this improvement. The HT codes are numbered so that // if you subtract HTSIZEFIRST-1 from them all, they sum up. I.E., // (HTLEFT - HTSIZEFIRST + 1) + (HTTOP - HTSIZEFIRST + 1) == // (HTTOPLEFT - HTSIZEFIRST + 1). // if (TestWF(pwnd, WEFTOOLWINDOW)) InflateRect(&rcWindow, -SYSMET(CXSMSIZE), -SYSMET(CYSMSIZE)); else InflateRect(&rcWindow, -SYSMET(CXSIZE), -SYSMET(CYSIZE)); if (pt.y < rcWindow.top) ht = (HTTOP - HTSIZEFIRST + 1); else if (pt.y >= rcWindow.bottom) ht = (HTBOTTOM - HTSIZEFIRST + 1); else ht = 0; if (pt.x < rcWindow.left) ht += (HTLEFT - HTSIZEFIRST + 1); else if (pt.x >= rcWindow.right) ht += (HTRIGHT - HTSIZEFIRST + 1); return (ht + HTSIZEFIRST - 1); } } // Are we above the client area? if (pt.y < rcClientAdj.top) { // Are we in the caption? if (TestWF(pwnd, WFBORDERMASK) == LOBYTE(WFCAPTION)) { CaptionHit: if (pt.y >= rcWindow.top) { if (TestWF(pwnd, WEFTOOLWINDOW)) { rcWindow.top += SYSMET(CYSMCAPTION); dxButton = SYSMET(CXSMSIZE); } else { rcWindow.top += SYSMET(CYCAPTION); dxButton = SYSMET(CXSIZE); } if ((pt.y >= rcWindow.top) && TestWF(pwnd, WFMPRESENT)) return(HTMENU); if ((pt.x >= rcWindow.left) && (pt.x < rcWindow.right) && (pt.y < rcWindow.top)) { // Are we in the window menu? if (TestWF(pwnd, WFSYSMENU)) { rcWindow.left += dxButton; if (pt.x < rcWindow.left) { if (!_HasCaptionIcon(pwnd)) // iconless windows have no sysmenu hit rect return(HTCAPTION); return(HTSYSMENU); } } else if (TestWF(pwnd, WFWIN40COMPAT)) return(HTCAPTION); // only a close button if window has a system menu // Are we in the close button? rcWindow.right -= dxButton; if (pt.x >= rcWindow.right) return HTCLOSE; if ((pt.x < rcWindow.right) && !TestWF(pwnd, WEFTOOLWINDOW)) { // Are we in the maximize/restore button? if (TestWF(pwnd, (WFMAXBOX | WFMINBOX))) { // Note that sizing buttons are same width for both // big captions and small captions. rcWindow.right -= dxButton; if (pt.x >= rcWindow.right) return HTZOOM; // Are we in the minimize button? rcWindow.right -= dxButton; if (pt.x >= rcWindow.right) return HTREDUCE; } else if (TestWF(pwnd, WEFCONTEXTHELP)) { rcWindow.right -= dxButton; if (pt.x >= rcWindow.right) return HTHELP; } } } } // We're in the caption proper return HTCAPTION; } // // Are we in the menu? // if (TestWF(pwnd, WFMPRESENT)) return HTMENU; } } else { // // NOTE: // We can only be here if we are on the client edge, horz scroll, // sizebox, or vert scroll. Hence, if we are not on the first 3, // we must be on the last one. // // // Are we on the client edge? // if (TestWF(pwnd, WEFCLIENTEDGE)) { InflateRect(&rcClientAdj, -SYSMET(CXEDGE), -SYSMET(CYEDGE)); if (!PtInRect(&rcClientAdj, pt)) return(HTBORDER); } // // Are we on the scrollbars? // if (TestWF(pwnd, WFHPRESENT) && (pt.y >= rcClient.bottom)) { UserAssert(pt.y < rcClientAdj.bottom); if (TestWF(pwnd, WFVPRESENT) && (pt.x >= rcClient.right)) return(SizeBoxHwnd(pwnd) ? HTBOTTOMRIGHT : HTGROWBOX); else return(HTHSCROLL); } else { UserAssert(TestWF(pwnd, WFVPRESENT)); UserAssert(pt.x >= rcClient.right); UserAssert(pt.x < rcClientAdj.right); return(HTVSCROLL); } } // // We give up. // // Win31 returned HTNOWHERE in this case; For compatibility, we will // keep it that way. // return(HTNOWHERE); }
VOID xxxSimpleDoSyncPaint( PWND pwnd) { HRGN hrgnUpdate; DWORD flags = 0; CheckLock(pwnd); /* * Since we're taking care of the frame drawing, we can consider * this WM_PAINT message processed. */ ClrWF(pwnd, WFPAINTNOTPROCESSED); /* * Make copies of these flags, because their state might * change after we send a message, and we don't want * to "lose" them. */ if (TestWF(pwnd, WFSENDNCPAINT)) flags |= DSP_FRAME; if (TestWF(pwnd, WFSENDERASEBKGND)) flags |= DSP_ERASE; if (flags & (DSP_ERASE | DSP_FRAME)) { if (!TestWF(pwnd, WFVISIBLE)) { /* * If there is no update region, just clear the bits. */ ClrWF(pwnd, WFSENDNCPAINT); ClrWF(pwnd, WFSENDERASEBKGND); ClrWF(pwnd, WFPIXIEHACK); ClrWF(pwnd, WFERASEBKGND); ClearHungFlag(pwnd, WFREDRAWIFHUNG); } else { PTHREADINFO ptiCurrent = PtiCurrent(); /* * If there is no update region, we don't have to * do any erasing, but we may need to send an NCPAINT. */ if (pwnd->hrgnUpdate == NULL) { ClrWF(pwnd, WFSENDERASEBKGND); ClrWF(pwnd, WFERASEBKGND); flags &= ~DSP_ERASE; } /* * Only mess with windows owned by the current thread. * NOTE: This means that WM_NCPAINT and WM_ERASEBKGND are * only sent intra-thread. */ if (GETPTI(pwnd) == ptiCurrent) { hrgnUpdate = GetNCUpdateRgn(pwnd, TRUE); if (flags & DSP_FRAME) { /* * If the message got sent before we got here then do * nothing. */ if (TestWF(pwnd, WFSENDNCPAINT)) xxxSendNCPaint(pwnd, hrgnUpdate); } if (flags & DSP_ERASE) { if (TestWF(pwnd, WFSENDNCPAINT)) { /* * If we got another invalidate during the NCPAINT * callback get the new update region */ DeleteMaybeSpecialRgn(hrgnUpdate); hrgnUpdate = GetNCUpdateRgn(pwnd, FALSE); } /* * If the message got sent before we got here * (e.g.: an UpdateWindow() inside WM_NCPAINT handler, * for example), don't do anything. * * WINPROJ.EXE (version 1.0) calls UpdateWindow() in * the WM_NCPAINT handlers for its subclassed listboxes * in the open dialog. */ if (TestWF(pwnd, WFSENDERASEBKGND)) { ClrWF(pwnd, WFSENDERASEBKGND); ClrWF(pwnd, WFERASEBKGND); xxxSendEraseBkgnd(pwnd, NULL, hrgnUpdate); } /* * The erase and frame operation has occured. Clear the * WFREDRAWIFHUNG bit here. We don't want to clear it until we * know the erase and frame has occured, so we know we always * have a consistent looking window. */ ClearHungFlag(pwnd, WFREDRAWIFHUNG); } DeleteMaybeSpecialRgn(hrgnUpdate); } else if (!TestwndChild(pwnd) && (pwnd != grpdeskRitInput->pDeskInfo->spwnd) && FHungApp(GETPTI(pwnd), CMSHUNGAPPTIMEOUT) && TestWF(pwnd, WFREDRAWIFHUNG)) { ClearHungFlag(pwnd, WFREDRAWIFHUNG); xxxRedrawHungWindow(pwnd, NULL); } } } }
/*****************************************************************************\ * xxxGetMenuBarInfo() * * This succeeds if the menu/menu item exists. * * Parameters: * pwnd window * idObject this can be OBJID_MENU, OBJID_SYSMENU, or OBJID_CLIENT * idItem which thing do we need info on? 0..cItems. 0 indicates * the menu itself, 1 is the first item on the menu... * pmbi Pointer to a MENUBARINFO structure that gets filled in * \*****************************************************************************/ BOOL WINAPI xxxGetMenuBarInfo(PWND pwnd, long idObject, long idItem, PMENUBARINFO pmbi) { PMENU pMenu; int cBorders; PITEM pItem; PPOPUPMENU ppopup; CheckLock(pwnd); /* * Validate menubarinfo structure */ if (pmbi->cbSize != sizeof(MENUBARINFO)) { RIPERR1(ERROR_INVALID_PARAMETER, RIP_WARNING, "MENUBARINFO.cbSize %d is wrong", pmbi->cbSize); return FALSE; } /* * Initialize the fields */ SetRectEmpty(&pmbi->rcBar); pmbi->hMenu = NULL; pmbi->hwndMenu = NULL; pmbi->fBarFocused = FALSE; pmbi->fFocused = FALSE; /* * Get the menu handle we will deal with. */ if (idObject == OBJID_MENU) { int cBorders; if (TestWF(pwnd, WFCHILD) || !pwnd->spmenu) return FALSE; pMenu = pwnd->spmenu; if (!pMenu) return FALSE; // If we have an item, is it in the valid range? if ((idItem < 0) || ((DWORD)idItem > pMenu->cItems)) return FALSE; /* * Menu handle */ pmbi->hMenu = PtoHq(pMenu); /* * Menu rect */ if (pMenu->cxMenu && pMenu->cyMenu) { if (!idItem) { cBorders = GetWindowBorders(pwnd->style, pwnd->ExStyle, TRUE, FALSE); pmbi->rcBar.left = pwnd->rcWindow.left + cBorders * SYSMET(CXBORDER); pmbi->rcBar.top = pwnd->rcWindow.top + cBorders * SYSMET(CYBORDER); if (TestWF(pwnd, WFCPRESENT)) { pmbi->rcBar.top += (TestWF(pwnd, WEFTOOLWINDOW) ? SYSMET(CYSMCAPTION) : SYSMET(CYCAPTION)); } pmbi->rcBar.right = pmbi->rcBar.left + pMenu->cxMenu; pmbi->rcBar.bottom = pmbi->rcBar.top + pMenu->cyMenu; } else { pItem = pMenu->rgItems + idItem - 1; pmbi->rcBar.left = pwnd->rcWindow.left + pItem->xItem; pmbi->rcBar.top = pwnd->rcWindow.top + pItem->yItem; pmbi->rcBar.right = pmbi->rcBar.left + pItem->cxItem; pmbi->rcBar.bottom = pmbi->rcBar.top + pItem->cyItem; } } /* * Are we currently in app menu bar mode? */ ppopup = GetpGlobalPopupMenu(pwnd); if (ppopup && ppopup->fHasMenuBar && !ppopup->fIsSysMenu && (ppopup->spwndNotify == pwnd)) { // Yes, we are. pmbi->fBarFocused = TRUE; if (!idItem) { pmbi->fFocused = TRUE; } else if (ppopup->ppopupmenuRoot->posSelectedItem == (UINT)idItem-1) { pmbi->fFocused = TRUE; UserAssert(ppopup->ppopupmenuRoot); pmbi->hwndMenu = HW(ppopup->ppopupmenuRoot->spwndNextPopup); } } } else if (idObject == OBJID_SYSMENU) { if (!TestWF(pwnd, WFSYSMENU)) return FALSE; pMenu = xxxGetSysMenu(pwnd, FALSE); if (!pMenu) return FALSE; // If we have an item, is it in the valid range? if ((idItem < 0) || ((DWORD)idItem > pMenu->cItems)) return FALSE; pmbi->hMenu = PtoHq(pMenu); /* * Menu rect */ if (_HasCaptionIcon(pwnd)) { // The menu and single item take up the same space cBorders = GetWindowBorders(pwnd->style, pwnd->ExStyle, TRUE, FALSE); pmbi->rcBar.left = pwnd->rcWindow.left + cBorders * SYSMET(CXBORDER); pmbi->rcBar.top = pwnd->rcWindow.top + cBorders * SYSMET(CYBORDER); pmbi->rcBar.right = pmbi->rcBar.left + (TestWF(pwnd, WEFTOOLWINDOW) ? SYSMET(CXSMSIZE) : SYSMET(CXSIZE)); pmbi->rcBar.bottom = pmbi->rcBar.top + (TestWF(pwnd, WEFTOOLWINDOW) ? SYSMET(CYSMSIZE) : SYSMET(CYSIZE)); } /* * Are we currently in system menu bar mode? */ ppopup = GetpGlobalPopupMenu(pwnd); if (ppopup && ppopup->fHasMenuBar && ppopup->fIsSysMenu && (ppopup->spwndNotify == pwnd)) { // Yes, we are. pmbi->fBarFocused = TRUE; if (!idItem) { pmbi->fFocused = TRUE; } else if (ppopup->ppopupmenuRoot->posSelectedItem == (UINT)idItem - 1) { pmbi->fFocused = TRUE; UserAssert(ppopup->ppopupmenuRoot); pmbi->hwndMenu = HW(ppopup->ppopupmenuRoot->spwndNextPopup); } } } else if (idObject == OBJID_CLIENT) { HMENU hMenu; hMenu = (HMENU)xxxSendMessage(pwnd, MN_GETHMENU, 0, 0); pMenu = HtoP(hMenu); if (!pMenu) return FALSE; // If we have an item, is it in the valid range? if ((idItem < 0) || ((DWORD)idItem > pMenu->cItems)) return FALSE; pmbi->hMenu = hMenu; if (!idItem) { pmbi->rcBar = pwnd->rcClient; } else { pItem = pMenu->rgItems + idItem - 1; pmbi->rcBar.left = pwnd->rcClient.left + pItem->xItem; pmbi->rcBar.top = pwnd->rcClient.top + pItem->yItem; pmbi->rcBar.right = pmbi->rcBar.left + pItem->cxItem; pmbi->rcBar.bottom = pmbi->rcBar.top + pItem->cyItem; } /* * Are we currently in popup mode with us as one of the popups * showing? */ ppopup = ((PMENUWND)pwnd)->ppopupmenu; if (ppopup && (ppopup->ppopupmenuRoot == GetpGlobalPopupMenu(pwnd))) { pmbi->fBarFocused = TRUE; if (!idItem) { pmbi->fFocused = TRUE; } else if ((UINT)idItem == ppopup->posSelectedItem + 1) { pmbi->fFocused = TRUE; pmbi->hwndMenu = HW(ppopup->spwndNextPopup); } } } else { return FALSE; } return TRUE; }
VOID DoQueuedSyncPaint( PWND pwnd, DWORD flags, PTHREADINFO pti) { PTHREADINFO ptiPwnd = GETPTI(pwnd); if ((ptiPwnd != pti) && TestWF(pwnd, WFSENDNCPAINT) && TestWF(pwnd, WFSENDERASEBKGND) && TestWF(pwnd, WFVISIBLE)) { PSMS psms = ptiPwnd->psmsReceiveList; /* * If this window already has a WM_SYNCPAINT queue'd up, then there's * no need to send another one. Also protects our heap from getting * chewed up. */ while (psms != NULL) { if ((psms->message == WM_SYNCPAINT) && (psms->spwnd == pwnd)) { break; } psms = psms->psmsReceiveNext; } if (psms == NULL) { /* * This will give this message the semantics of a notify * message (sendmessage no wait), without calling back * the WH_CALLWNDPROC hook. We don't want to do that * because that'll let all these processes with invalid * windows to process paint messages before they process * "synchronous" erasing or framing needs. * * Hi word of wParam must be zero or wow will drop it * * LATER mikeke * Do we need to send down the flags with DWP_ERASE and DSP_FRAME * in it? */ UserAssert(HIWORD(flags) == 0); QueueNotifyMessage(pwnd, WM_SYNCPAINT, flags, 0); /* * Set our syncpaint-pending flag, since we queued one up. This * will be used to check when we validate-parents for windows * without clipchildren. */ SetWF(pwnd, WFSYNCPAINTPENDING); } /* * If we posted a WM_SYNCPAINT for a top-level window that is not * of the current thread we're done; we'll pick up the children * when we process the message for real. If we're the desktop * however make sure we get all it children. */ if (pwnd != PWNDDESKTOP(pwnd)) return; } /* * Normally we like to enumerate all of this window's children and have * them erase their backgrounds synchronously. However, this is a bad * thing to do if the window is NOT CLIPCHLIDREN. Here's the scenario * we want to to avoid: * * 1. Window 'A' is invalidated * 2. 'A' erases itself (or not, doesn't matter) * 3. 'A's children are enumerated and they erase themselves. * 4. 'A' paints over its children (remember, 'A' isn't CLIPCHILDREN) * 5. 'A's children paint but their backgrounds aren't their ERASEBKND * color (because 'A' painted over them) and everything looks like * dirt. */ if ((flags & DSP_ALLCHILDREN) || ((flags & DSP_ENUMCLIPPEDCHILDREN) && TestWF(pwnd, WFCLIPCHILDREN))) { PWND pwndT; for (pwndT = pwnd->spwndChild; pwndT; pwndT = pwndT->spwndNext) { /* * Don't cause any more intertask sendmessages cause it does * bad things to cbt's windowproc hooks. (Due to SetParent * allowing child windows in the topwindow hierarchy. * The child bit also catches the desktop window; we want to */ if (TestWF(pwndT, WFCHILD) && (pti != GETPTI(pwndT))) continue; /* * Note that we pass only certain bits down as we recurse: * the other bits pertain to the current window only. */ DoQueuedSyncPaint(pwndT, flags, pti); } } }
/***************************************************************************\ * * SetShellWindow() * * Returns true if shell window is successfully set. Note that we return * FALSE if a shell window already exists. I.E., this works on a first * come, first serve basis. * * We also do NOT allow child windows to be shell windows. Other than that, * it's up to the caller to size her window appropriately. * * The pwndBkGnd is provided for the explorer shell. Since the shellwnd * and the window which does the drawing of background wallpapers are * different, we need to provide means by which we can draw directly on * the background window during hung-app drawing. The pwnd and pwndBkGnd * will be identical if called through the SetShellWindow() api. * * \***************************************************************************/ BOOL xxxSetShellWindow(PWND pwnd, PWND pwndBkGnd) { PTHREADINFO ptiCurrent = PtiCurrent(); PDESKTOPINFO pdeskinfo = GETDESKINFO(ptiCurrent); PPROCESSINFO ppiShellProcess; UserAssert(pwnd); /* * Fail the call if another shell window exists */ if (pdeskinfo->spwndShell != NULL) return(FALSE); /* * The shell window must be * (1) Top-level * (2) Unowned * (3) Not topmost */ if (TestwndChild(pwnd) || (pwnd->spwndOwner != NULL) || TestWF(pwnd, WEFTOPMOST)) { RIPMSG0(RIP_WARNING, "xxxSetShellWindow: Invalid type of window"); return(FALSE); } /* * Chicago has a totally different input model which has special code * that checks for Ctrl-Esc and sends it to the shell. We can get * the same functionality, without totally re-writing our input model * by just automatically installing the Ctrl-Esc as a hotkey for the * shell window. The hotkey delivery code has a special case which * turns this into a WM_SYSCOMMAND message instead of a WM_HOTKEY * message. * * We don't both checking for failure. Somebody could already have * a Ctrl-Esc handler installed. */ _RegisterHotKey(pwnd,SC_TASKLIST,MOD_CONTROL,VK_ESCAPE); /* * This is the shell window wright. * So get the process id for the shell. */ ppiShellProcess = GETPTI(pwnd)->ppi; /* * Set the shell process id to the desktop only if it's the first instance */ if ((ppiShellProcess != NULL) && (pdeskinfo->ppiShellProcess == NULL)) { pdeskinfo->ppiShellProcess = ppiShellProcess; } Lock(&pdeskinfo->spwndShell, pwnd); Lock(&pdeskinfo->spwndBkGnd, pwndBkGnd); /* * Push window to bottom of stack. */ SetWF(pdeskinfo->spwndShell, WFBOTTOMMOST); xxxSetWindowPos(pdeskinfo->spwndShell, PWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); return(TRUE); }
BOOL xxxShowWindow( PWND pwnd, DWORD cmdShowAnimate) { BOOL fVisOld, fVisNew; UINT swpFlags = SWP_NOMOVE | SWP_NOSIZE; PTHREADINFO pti; BOOL bFirstMain = FALSE; int cmdShow = LOWORD(cmdShowAnimate); CheckLock(pwnd); fVisOld = TestWF(pwnd, WFVISIBLE); pti = PtiCurrent(); /* * See if this is the first "main" top level * window being created by this application - if show, assume it * is showing with the SW_SHOWDEFAULT command. * * Checks for: * - cmdShow is a "default" show command * - we haven't done startupinfo yet (we only use it once) * - this is not a child (it is a top level window) * - this has a titlebar (indicator of the main window) * - it isn't owned (indicator of the main window) */ if ((pti->ppi->usi.dwFlags & STARTF_USESHOWWINDOW) && !TestwndChild(pwnd) && (TestWF(pwnd, WFBORDERMASK) == (BYTE)LOBYTE(WFCAPTION)) && (pwnd->spwndOwner == NULL)) { bFirstMain = TRUE; switch (cmdShow) { case SW_SHOWNORMAL: case SW_SHOW: /* * Then assume default! */ cmdShow = SW_SHOWDEFAULT; break; } } /* * If this application specified SW_SHOWDEFAULT, then we get the * real SW_* command from the application's STARTUPINFO structure * (STARTUPINFO is passed to CreateProcess() when this application * was launched). */ if (cmdShow == SW_SHOWDEFAULT) { /* * Call the client to get the SW_* command from the STARTUPINFO * for this process. */ if (pti->ppi->usi.dwFlags & STARTF_USESHOWWINDOW) { bFirstMain = TRUE; cmdShow = pti->ppi->usi.wShowWindow; #if 0 switch (cmdShow) { case SW_SHOWMINIMIZED: case SW_MINIMIZE: /* * If the default show was "minimized", then make sure it doesn't * become active. Minimized is effectively "background". */ cmdShow = SW_SHOWMINNOACTIVE; break; } #endif } } // // This is in case someone said SW_SHOWDEFAULT but has no startupinfo. // Or in case cmdShow inside of STARTUPINFO is SW_SHOWDEFAULT. // if (cmdShow == SW_SHOWDEFAULT) cmdShow = SW_SHOWNORMAL; /* * Turn off startup info. We turn this off after the first call to * ShowWindow. If we don't apps can be started by progman with * the start info being minimized and then be restored and then * call ShowWindow(SW_SHOW) and the app would minimize again. * Notepad had that problem 2985. */ if (bFirstMain) { pti->ppi->usi.dwFlags &= ~(STARTF_USESHOWWINDOW|STARTF_USESIZE \ | STARTF_USEPOSITION); } /* * Take care of all the OLD show commands with columns & iconslot. */ if (cmdShow & 0xFF00) { if ((cmdShow & 0xFF80) == (int)0xFF80) cmdShow = SW_SHOWMINNOACTIVE; else cmdShow = SW_SHOW; } /* * Change to new fullscreen if needed and in same desktop */ if ((pwnd->bFullScreen != WINDOWED) && (pwnd->head.rpdesk == grpdeskRitInput)) { if ((cmdShow == SW_SHOWNORMAL) || (cmdShow == SW_RESTORE) || (cmdShow == SW_MAXIMIZE) || (cmdShow == SW_SHOWMAXIMIZED)) { cmdShow = SW_SHOWMINIMIZED; if (pwnd->bFullScreen == FULLSCREENMIN) { pwnd->bFullScreen = FULLSCREEN; } if (gpqForeground != NULL && gpqForeground->spwndActive == pwnd) { xxxMakeWindowForegroundWithState(NULL, 0); } } } switch (cmdShow) { case SW_SHOWNOACTIVATE: case SW_SHOWNORMAL: case SW_RESTORE: /* * If min/max, let xxxMinMaximize() do all the work. */ if (TestWF(pwnd, WFMINIMIZED) || TestWF(pwnd, WFMAXIMIZED)) { xxxMinMaximize(pwnd, (UINT)cmdShow, cmdShowAnimate & MINMAX_ANIMATE); return fVisOld; } else { /* * Ignore if the window is already visible. */ if (fVisOld) { return fVisOld; } swpFlags |= SWP_SHOWWINDOW; if ( cmdShow == SW_SHOWNOACTIVATE) { swpFlags |= SWP_NOZORDER; #ifdef NEVER /* * This is what win3.1 does. On NT, since each "queue" has * its own active window, there is often no active window. * In this case, win3.1 turns a SHOWNOACTIVATE into a "SHOW * with activate". Since win3.1 almost always has an active * window, this almost never happens. So on NT, we're not * going to do this check - that way we'll be more compatible * with win3.1 because we'll usally not activate (like win3.1). * With this check, this causes FoxPro 2.5 for Windows to not * properly activate its command window when first coming up. */ if (pti->pq->spwndActive != NULL) swpFlags |= SWP_NOACTIVATE; #else swpFlags |= SWP_NOACTIVATE; #endif } } break; case SW_SHOWMINNOACTIVE: case SW_SHOWMINIMIZED: case SW_SHOWMAXIMIZED: case SW_MINIMIZE: xxxMinMaximize(pwnd, (UINT)cmdShow, cmdShowAnimate & MINMAX_ANIMATE); return fVisOld; case SW_SHOWNA: swpFlags |= SWP_SHOWWINDOW | SWP_NOACTIVATE; // LATER removed this to be compatible with SHOWNOACTIVATE //if (pti->pq->spwndActive != NULL) // swpFlags |= SWP_NOACTIVATE; break; case SW_SHOW: /* * Don't bother if it is already visible. */ if (fVisOld) return fVisOld; swpFlags |= SWP_SHOWWINDOW; if (cmdShow == SW_SHOWNOACTIVATE) { swpFlags |= SWP_NOZORDER | SWP_NOACTIVATE; } break; case SW_HIDE: /* * Don't bother if it is already hidden. */ if (!fVisOld) return fVisOld; swpFlags |= SWP_HIDEWINDOW; if (pwnd != pti->pq->spwndActive) swpFlags |= (SWP_NOACTIVATE | SWP_NOZORDER); break; default: RIPERR0(ERROR_INVALID_SHOWWIN_COMMAND, RIP_VERBOSE, ""); return fVisOld; } /* * If we're changing from visible to hidden or vise-versa, send * WM_SHOWWINDOW. */ fVisNew = !(cmdShow == SW_HIDE); if (fVisNew != fVisOld) { xxxSendMessage(pwnd, WM_SHOWWINDOW, fVisNew, 0L); if (!TestWF(pwnd, WFWIN31COMPAT)) { xxxSendMessage(pwnd, WM_SETVISIBLE, fVisNew, 0L); } } if (!TestwndChild(pwnd)) { if (TestCF(pwnd, CFSAVEBITS)) { /* * Activate to prevent discarding saved bits??? */ if (cmdShow == SW_SHOW || cmdShow == SW_SHOWNORMAL) { xxxActivateWindow(pwnd, AW_USE); swpFlags |= SWP_NOZORDER | SWP_NOACTIVATE; } } } else { /* * Children can't get activation... */ swpFlags |= (SWP_NOACTIVATE | SWP_NOZORDER); } /* * If our parent is hidden, don't bother to call xxxSetWindowPos. */ if (_FChildVisible(pwnd)) { xxxSetWindowPos(pwnd, (PWND)NULL, 0, 0, 0, 0, swpFlags); } else { if (cmdShow == SW_HIDE) ClrWF(pwnd, WFVISIBLE); else SetWF(pwnd, WFVISIBLE); } /* * Send size and move messages AFTER repainting */ if (TestWF(pwnd, WFSENDSIZEMOVE)) { ClrWF(pwnd, WFSENDSIZEMOVE); if (TestWF(pwnd, WFMINIMIZED)) { xxxSendSizeMessage(pwnd, SIZEICONIC); } else if (TestWF(pwnd, WFMAXIMIZED)) { xxxSendSizeMessage(pwnd, SIZEFULLSCREEN); } else { xxxSendSizeMessage(pwnd, SIZENORMAL); } if (pwnd->spwndParent) { xxxSendMessage(pwnd, WM_MOVE, 0, MAKELONG( pwnd->rcClient.left - pwnd->spwndParent->rcClient.left, pwnd->rcClient.top - pwnd->spwndParent->rcClient. top)); } else { /* * NULL parent implies this window is a zombie */ xxxSendMessage(pwnd, WM_MOVE, 0, MAKELONG( pwnd->rcClient.left, pwnd->rcClient.top)); } } /* * If hiding and is active-foreground window, activate someone else. * If hiding a active window make someone active. */ if (cmdShow == SW_HIDE) { if ((pwnd == pti->pq->spwndActive) && (pti->pq == gpqForeground)) { xxxActivateWindow(pwnd, AW_SKIP); } else { xxxCheckFocus(pwnd); } } return fVisOld; }
VOID xxxCapture( PTHREADINFO pti, PWND pwnd, UINT code) { if ((gspwndScreenCapture == NULL) || (code == FULLSCREEN_CAPTURE) || ((pwnd == NULL) && (code == NO_CAP_CLIENT) && (pti->pq != GETPTI(gspwndScreenCapture)->pq))) { PQ pq; PWND pwndCaptureOld = NULL; if (code == FULLSCREEN_CAPTURE) { if (pwnd) { Lock(&gspwndScreenCapture, pwnd); /* * We're going full screen so clear the mouse owner */ Unlock(&gspwndMouseOwner); } else { Unlock(&gspwndScreenCapture); } } /* * Internal capture works like Win 3.1 capture unlike the NT capture * which can be lost if the user clicks down on another application */ if (code == CLIENT_CAPTURE_INTERNAL) { Lock(&gspwndInternalCapture, pwnd); code = CLIENT_CAPTURE; } /* * Free the internal capture if the app (thread) that did the internal * capture is freeing the capture. */ if ((code == NO_CAP_CLIENT) && gspwndInternalCapture && (pti == GETPTI(gspwndInternalCapture))) { Unlock(&gspwndInternalCapture); } if ((pq = pti->pq) != NULL) { /* * Make consistent picture of mouse tracking stuff for apps */ if ((pq->spwndLastMouseMessage != NULL) && ((pwnd == pq->spwndLastMouseMessage) && (code != CLIENT_CAPTURE)) || ((pq->spwndCapture == pq->spwndLastMouseMessage) && (pq->codeCapture != CLIENT_CAPTURE))) { ResetMouseTracking(pq, NULL); } pwndCaptureOld = pq->spwndCapture; Lock(&pq->spwndCapture, pwnd); pq->codeCapture = code; } /* * If there was a capture window and we're releasing it, post * a WM_MOUSEMOVE to the window we're over so they can know about * the current mouse position. */ if ((pwnd == NULL) && (pwndCaptureOld != NULL)) SetFMouseMoved(); /* * New for win95 - send WM_CAPTURECHANGED. * * The FNID_DELETED_BIT is set in xxxFreeWindow which means we * DON'T want to send the message. */ if (pwndCaptureOld && TestWF(pwndCaptureOld, WFWIN40COMPAT) && !(pwndCaptureOld->fnid & FNID_DELETED_BIT)) { TL tlpwnd; ThreadLock(pwndCaptureOld, &tlpwnd); xxxSendMessageCallback(pwndCaptureOld, WM_CAPTURECHANGED, FALSE, (LPARAM)HW(pwnd), NULL, 0, FALSE); ThreadUnlock(&tlpwnd); } } }