BOOL _ShowWindowAsync( PWND pwnd, int cmdShow) { return PostEventMessage(GETPTI(pwnd), GETPTI(pwnd)->pq, QEVENT_SHOWWINDOW, NULL, 0,(DWORD)HWq(pwnd), MAKELONG(cmdShow, gfAnimate)); }
PWND InternalDoPaint( PWND pwnd, PTHREADINFO ptiCurrent) { PWND pwndT; /* * Enumerate all windows, top-down, looking for one that * needs repainting. Skip windows of other tasks. */ for ( ; pwnd != NULL; pwnd = pwnd->spwndNext) { if (GETPTI(pwnd) == ptiCurrent) { if (NEEDSPAINT(pwnd)) { /* * If this window is transparent, we don't want to * send it a WM_PAINT until all its siblings below it * have been repainted. If we find an unpainted sibling * below, return it instead. */ if (TestWF(pwnd, WEFTRANSPARENT)) { pwndT = pwnd; while ((pwndT = pwndT->spwndNext) != NULL) { /* * Make sure sibling window belongs to same app */ if ((GETPTI(pwndT) == ptiCurrent) && NEEDSPAINT(pwndT)) { if (TestWF(pwndT, WEFTRANSPARENT)) continue; return pwndT; } } } return pwnd; } } if (pwnd->spwndChild && (pwndT = InternalDoPaint(pwnd->spwndChild, ptiCurrent))) { return pwndT; } } return pwnd; }
/***************************************************************************\ * * _GetListBoxInfo() * * Currently returns back the # of items per column. There is no way to get * or calculate this info any other way in a multicolumn list. * * For now, no structure is returned. If we ever need one more thing, make one. * * Since I have to run on multiple platforms, I can't define a message. * To do so would require that * * I make changes to the thunk table * * I make sure the 32-bit define doesn't collide with some NT new msg * * I use a different value on Win '95 vs Memphis due to additions * * I test apps extensively since many of them pass on bogus valued * messages to the listbox handler which checks to see if they * are in range. In other words, any value I pick is probably * going to flake out MSVC++ 4.0. * * Ergo an API instead. * \***************************************************************************/ DWORD WINAPI _GetListBoxInfo(PWND pwnd) { PCLS pcls; DWORD dwRet = 0; BOOL fOtherProcess; /* * Make sure it is a combobox or a dropdown */ pcls = pwnd->pcls; if ((pcls->atomClassName != gpsi->atomSysClass[ICLS_LISTBOX]) && (GETFNID(pwnd) != FNID_LISTBOX)) { RIPERR1(ERROR_INVALID_PARAMETER, RIP_WARNING, "pwnd %#p is not a listbox", pwnd); return 0; } if (fOtherProcess = (GETPTI(pwnd)->ppi != PpiCurrent())) { KeAttachProcess(&GETPTI(pwnd)->ppi->Process->Pcb); } try { PLBIV ccxPlbSnap; /* * Snap and probe the pointer to the LBIV, since it is client-side. */ ccxPlbSnap = ((PLBWND)pwnd)->pLBIV; if (!ccxPlbSnap) { goto errorexit; } ProbeForRead(ccxPlbSnap, sizeof(LBIV), DATAALIGN); if (ccxPlbSnap->fMultiColumn) { dwRet = ccxPlbSnap->itemsPerColumn; } else { dwRet = ccxPlbSnap->cMac; } } except (W32ExceptionHandler(FALSE, RIP_WARNING)) { dwRet = 0; } errorexit: if (fOtherProcess) { KeDetachProcess(); } return dwRet; }
PWND xxxSetCapture( PWND pwnd) { PQ pq; PWND pwndCaptureOld; HWND hwndCaptureOld; pq = (PQ)PtiCurrent()->pq; /* * Don't allow the app to set capture to a window * from another queue. */ if ((pwnd != NULL) && GETPTI(pwnd)->pq != pq) return NULL; /* * If full screen capture don't allow any other capture */ if (gspwndScreenCapture) return NULL; pwndCaptureOld = pq->spwndCapture; hwndCaptureOld = HW(pwndCaptureOld); xxxCapture(PtiCurrent(), pwnd, CLIENT_CAPTURE); if (hwndCaptureOld != NULL) { if (RevalidateHwnd(hwndCaptureOld)) return pwndCaptureOld; } return NULL; }
BOOL FCallerOk( PWND pwnd) { PTHREADINFO pti = PtiCurrent(); if ((GETPTI(pwnd)->TIF_flags & (TIF_SYSTEMTHREAD | TIF_CSRSSTHREAD)) && !(pti->TIF_flags & (TIF_SYSTEMTHREAD | TIF_CSRSSTHREAD))) { return FALSE; } if (GETPTI(pwnd)->pEThread->Cid.UniqueProcess == gpidLogon && pti->pEThread->Cid.UniqueProcess != gpidLogon) { return FALSE; } return TRUE; }
VOID IncPaintCount( PWND pwnd) { PTHREADINFO pti = GETPTI(pwnd); if (pti->cPaintsReady++ == 0) SetWakeBit(pti, QS_PAINT); }
VOID DecPaintCount( PWND pwnd) { PTHREADINFO pti = GETPTI(pwnd); if (--pti->cPaintsReady == 0) { pti->pcti->fsWakeBits &= ~QS_PAINT; pti->pcti->fsChangeBits &= ~QS_PAINT; } }
/***************************************************************************\ * MNEndMenuStateNotify * * spwndNotify might have been created by a thread other than the one * the menu mode is running on. If this is the case, this function * NULLs out pMenuState for the thread that owns spwndNotify. * * 05-21-96 GerardoB Created \***************************************************************************/ void MNEndMenuStateNotify (PMENUSTATE pMenuState) { PTHREADINFO ptiNotify; if (pMenuState->pGlobalPopupMenu->spwndNotify != NULL) { ptiNotify = GETPTI(pMenuState->pGlobalPopupMenu->spwndNotify); if (ptiNotify != pMenuState->ptiMenuStateOwner) { UserAssert(ptiNotify->pMenuState == pMenuState); ptiNotify->pMenuState = NULL; } } }
/***************************************************************************\ * xxxMessageEvent * * Description: Called when a hooked DDE message is sent or posted. flags * specifies the applicable MF_ flag. This is called in the server side * context of the sender or poster which may or may not be a DDEML process. * pdmhd contains DDE data extracted and copied from the client side. * * History: * 12-1-91 sanfords Created. \***************************************************************************/ VOID xxxMessageEvent( PWND pwndTo, UINT message, WPARAM wParam, LPARAM lParam, DWORD flag, PDDEML_MSG_HOOK_DATA pdmhd) { PEVENT_PACKET pep; PWND pwndFrom; TL tlpep; PTHREADINFO pti; CheckCritIn(); pep = (PEVENT_PACKET)UserAllocPoolWithQuota(sizeof(EVENT_PACKET) - sizeof(DWORD) + sizeof(MONMSGSTRUCT), TAG_DDE8); if (pep == NULL) { return; } pep->EventType = flag; pep->fSense = TRUE; pep->cbEventData = sizeof(MONMSGSTRUCT); #define pmsgs ((MONMSGSTRUCT *)&pep->Data) pmsgs->cb = sizeof(MONMSGSTRUCT); pmsgs->hwndTo = PtoH(pwndTo); pmsgs->dwTime = NtGetTickCount(); pwndFrom = RevalidateHwnd((HWND)wParam); if (pwndFrom != NULL) { pmsgs->hTask = GETPTI(pwndFrom)->pEThread->Cid.UniqueThread; } else { pmsgs->hTask = 0; } pmsgs->wMsg = message; pmsgs->wParam = wParam; pmsgs->lParam = lParam; if (pdmhd != NULL) { pmsgs->dmhd = *pdmhd; } #undef pmsgs pti = PtiCurrent(); ThreadLockPool(pti, pep, &tlpep); xxxCsEvent(pep, sizeof(MONMSGSTRUCT)); ThreadUnlockAndFreePool(pti, &tlpep); }
/***************************************************************************\ * xxxCancelMouseMoveTracking * * History * 12/07/96 GerardoB Created \***************************************************************************/ void xxxCancelMouseMoveTracking (DWORD dwDTFlags, PWND pwndTrack, int htEx, DWORD dwDTCancel) { CheckLock(pwndTrack); /* * Hottracking */ if ((dwDTFlags & DF_HOTTRACKING) && (dwDTCancel & DF_HOTTRACKING)) { /* * The current state must be owned by the current queue. * Otherwise, we're about to do an inter-queue cancelation. */ UserAssert(PtiCurrent()->pq == GETPTI(pwndTrack)->pq); xxxHotTrack(pwndTrack, htEx, FALSE); } /* * Tooltips */ if ((dwDTFlags & DF_TOOLTIPSHOWING) && (dwDTCancel & DF_TOOLTIP)) { PTOOLTIPWND pttwnd = (PTOOLTIPWND)PWNDTOOLTIP(pwndTrack); TL tlpwnd; ThreadLockAlways(pttwnd, &tlpwnd); xxxResetTooltip(pttwnd); ThreadUnlock(&tlpwnd); } /* * Mouse Leave */ if ((dwDTFlags & DF_TRACKMOUSELEAVE) && (dwDTCancel & DF_TRACKMOUSELEAVE)) { _PostMessage(pwndTrack, ((htEx == HTCLIENT) ? WM_MOUSELEAVE : WM_NCMOUSELEAVE), 0, 0); } /* * Mouse Hover */ if ((dwDTFlags & DF_TRACKMOUSEHOVER) && (dwDTCancel & DF_TRACKMOUSEHOVER)) { _KillSystemTimer(pwndTrack, IDSYS_MOUSEHOVER); } }
void xxxClientShutdown( PWND pwnd, DWORD wParam, DWORD lParam) { PBWL pbwl; PTHREADINFO ptiT; BOOL fExit; /* * Build a list of windows first. */ fExit = TRUE; ptiT = GETPTI(pwnd); /* * If the request was cancelled, then do nothing. */ if (ptiT->TIF_flags & TIF_SHUTDOWNCOMPLETE) { return; } if ((pbwl = BuildHwndList(ptiT->rpdesk->pDeskInfo->spwnd->spwndChild, BWL_ENUMLIST, ptiT)) == NULL) { /* * Can't allocate memory to notify this thread's windows of shutdown. */ goto SafeExit; } if (wParam & WMCS_QUERYEND) { fExit = xxxClientShutdown2(pbwl, WM_QUERYENDSESSION, wParam); } else { xxxClientShutdown2(pbwl, WM_ENDSESSION, wParam); fExit = TRUE; } FreeHwndList(pbwl); SafeExit: ptiT->TIF_flags |= (TIF_SHUTDOWNCOMPLETE | (fExit ? TIF_ALLOWSHUTDOWN : 0)); }
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); } }
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); } } }
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); } } } }
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; }
/***************************************************************************\ * * _GetComboBoxInfo() * * This returns combobox information for either a combo or its dropdown * list. * \***************************************************************************/ BOOL WINAPI _GetComboBoxInfo(PWND pwnd, PCOMBOBOXINFO pcbi) { PCLS pcls; COMBOBOXINFO cbi = { sizeof cbi, }; BOOL fOtherProcess; BOOL bRetval = FALSE; WORD wWindowType = 0; /* * Make sure it is a combobox or a dropdown */ pcls = pwnd->pcls; if ((GETFNID(pwnd) == FNID_COMBOBOX) || (pcls->atomClassName == gpsi->atomSysClass[ICLS_COMBOBOX])) { wWindowType = FNID_COMBOBOX; } else if ((GETFNID(pwnd) == FNID_COMBOLISTBOX) || (pcls->atomClassName == gpsi->atomSysClass[ICLS_COMBOLISTBOX])) { wWindowType = FNID_COMBOLISTBOX; } else { RIPERR1(ERROR_WINDOW_NOT_COMBOBOX, RIP_WARNING, "pwnd %#p not a combobox or dropdown", pwnd); return FALSE; } /* * Validate combo structure */ if (pcbi->cbSize != sizeof(COMBOBOXINFO)) { RIPERR1(ERROR_INVALID_PARAMETER, RIP_WARNING, "COMBOBOXINFO.cbSize %d is wrong", pcbi->cbSize); return FALSE; } if (fOtherProcess = (GETPTI(pwnd)->ppi != PpiCurrent())) { KeAttachProcess(&GETPTI(pwnd)->ppi->Process->Pcb); } try { PCBOX ccxPcboxSnap; PWND ccxPwndSnap; HWND ccxHwndSnap; /* * Snap and probe the CBOX structure, since it is client side. */ if (wWindowType == FNID_COMBOBOX) { ccxPcboxSnap = ((PCOMBOWND)pwnd)->pcbox; } else { PLBIV ccxPlbSnap; /* * If this is a listbox, we must snap and probe the LBIV structure * in order to get to the CBOX structure. */ ccxPlbSnap = ((PLBWND)pwnd)->pLBIV; if (!ccxPlbSnap) { goto errorexit; } ProbeForRead(ccxPlbSnap, sizeof(LBIV), DATAALIGN); ccxPcboxSnap = ccxPlbSnap->pcbox; } if (!ccxPcboxSnap) { goto errorexit; } ProbeForRead(ccxPcboxSnap, sizeof(CBOX), DATAALIGN); /* * Get the combo information now */ /* * Snap and probe the client side pointer to the Combo window */ ccxPwndSnap = ccxPcboxSnap->spwnd; ProbeForRead(ccxPwndSnap, sizeof(HEAD), DATAALIGN); cbi.hwndCombo = HWCCX(ccxPwndSnap); /* * Snap & probe the client side pointer to the Edit window. * To compare spwndEdit and pwnd, we should compare handles * since spwndEdit is a client-side address and pwnd is a * kernel-mode address, */ ccxPwndSnap = ccxPcboxSnap->spwndEdit; /* * If combobox is not fully initialized and spwndEdit is NULL, * we should fail. */ ProbeForRead(ccxPwndSnap, sizeof(HEAD), DATAALIGN); ccxHwndSnap = HWCCX(ccxPwndSnap); if (ccxHwndSnap == HW(pwnd)) { /* * ComboBox doesn't have Edit control. */ cbi.hwndItem = NULL; } else { cbi.hwndItem = HWCCX(ccxPwndSnap); } /* * Snap and probe the client side pointer to the List window */ ccxPwndSnap = ccxPcboxSnap->spwndList; /* * If combobox is not fully initialized and spwndList is NULL, * we should fail. */ ProbeForRead(ccxPwndSnap, sizeof(HEAD), DATAALIGN); cbi.hwndList = HWCCX(ccxPwndSnap); /* * Snap the rest of the combo information. * We don't need to probe any of these, since there are no more indirections. */ cbi.rcItem = ccxPcboxSnap->editrc; cbi.rcButton = ccxPcboxSnap->buttonrc; /* * Button state */ cbi.stateButton = 0; if (ccxPcboxSnap->CBoxStyle == CBS_SIMPLE) { cbi.stateButton |= STATE_SYSTEM_INVISIBLE; } if (ccxPcboxSnap->fButtonPressed) { cbi.stateButton |= STATE_SYSTEM_PRESSED; } } except (W32ExceptionHandler(FALSE, RIP_WARNING)) { goto errorexit; } *pcbi = cbi; bRetval = TRUE; errorexit: if (fOtherProcess) { KeDetachProcess(); } return bRetval; }
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); } } }
void xxxTrackMouseMove(PWND pwnd, int htEx, UINT message) { BOOL fNewpwndTrack; DWORD dwDTCancel = 0; TL tlpwnd; LPWSTR pstr; PDESKTOP pdesk = PtiCurrent()->rpdesk; PTHREADINFO ptiTrack; #if DBG /* * Let's warn if this function gets reenterd so we can make sure * nothing bad will follow. This should be a rare situation. * Look in gptiReEntered to find out who is already here. */ static UINT gcReEntered = 0; static PTHREADINFO gptiReEntered; if(gcReEntered++ != 0){ RIPMSG2(RIP_WARNING, "Reentered xxxTrackMouseMove; previous thread was %#p, current thread is %#p", gptiReEntered, PtiCurrent()); } gptiReEntered = PtiCurrent(); CheckLock(pwnd); /* * We must be on an interactive window station. */ if (pdesk->rpwinstaParent != NULL && pdesk->rpwinstaParent->dwWSF_Flags & WSF_NOIO) { RIPMSG0(RIP_ERROR, "Can't use tooltips on non-interactive winsta"); } { static POINT pt = {0, 0}; #ifdef UNDONE /* * We might have taken a guess on the hit test (see FindNCHitEx) * so if we're at the same point and same window, something * might be fishy */ if ((pt.x == gpsi->ptCursor.x) && (pt.y == gpsi->ptCursor.y) && (pdesk->spwndTrack == pwnd)) { RIPMSG1(RIP_WARNING, "xxxTrackMouseMove: Same point & window. %#p", pwnd); } #endif /* * Something is supposed to have changed or we're wasting time */ UserAssert((pt.x != gpsi->ptCursor.x) || (pt.y != gpsi->ptCursor.y) || (pdesk->spwndTrack != pwnd) || (pdesk->htEx != htEx) || (message != WM_MOUSEMOVE)); /* * Remember last tracked point */ pt = gpsi->ptCursor; } /* * pwnd is supposed to be on the current thread and queue */ UserAssert(PtiCurrent() == GETPTI(pwnd)); UserAssert(PtiCurrent()->pq == GETPTI(pwnd)->pq); #endif /* * Have we switched windows? */ fNewpwndTrack = (pdesk->spwndTrack != pwnd); /* * If no tracking is taking place, just go set the new * tracking state */ if (!(pdesk->dwDTFlags & DF_MOUSEMOVETRK)) { goto SetNewState; } /* * Potentially while we leave the critical section below in * xxxCancelMouseMoveTracking, spwndTrack could be destroyed and unlocked * and then we go and create the tooltip. This would mean that * DF_TOOLTIPACTIVE (part of DF_MOUSEMOVETRK test above) would be set, * but pdesk->spwndTrack would be NULL and we can AV dereferencing * pdesk->spwndTrack below. Prevent this by making the check here. */ if (pdesk->spwndTrack == NULL) { goto SetNewState; } /* * Nuke hottracking and deactivate tooltip state, if any. * Do it sychronously if we're tracking on the current queue; * Otherwise, post an event and let it happen later. */ ptiTrack = GETPTI(pdesk->spwndTrack); if (PtiCurrent()->pq == ptiTrack->pq) { dwDTCancel |= DF_HOTTRACKING; } else if (pdesk->dwDTFlags & (DF_HOTTRACKING | DF_TOOLTIPACTIVE)) { PostEventMessage(ptiTrack, ptiTrack->pq, QEVENT_CANCELMOUSEMOVETRK, pdesk->spwndTrack, pdesk->dwDTFlags, pdesk->htEx, DF_HOTTRACKING); /* * Paranoid assertion. If we're switching queues, we must * be switching windows. Did we just go through * ReattachThreads? */ UserAssert(pwnd != pdesk->spwndTrack); pdesk->dwDTFlags &= ~(DF_HOTTRACKING | DF_TOOLTIPACTIVE); } /* * If we're on the client area or the user clicked, * nuke the tooltip (if any). * Since we might want to re-show the tooltip, we don't nuke it * now if we swichted windows (we'll nuke it later if needed) */ if ((htEx == HTCLIENT) || (message != WM_MOUSEMOVE)) { dwDTCancel |= DF_TOOLTIPACTIVE; } /* * If we switched windows or crossed client/nonclinet boundaries, * end track mouse leave/hover. */ if (fNewpwndTrack || ((pdesk->htEx == HTCLIENT) ^ (htEx == HTCLIENT))) { dwDTCancel |= DF_TRACKMOUSEEVENT; } /* * Cancel whatever is active and needs to go away */ ThreadLockAlways(pdesk->spwndTrack, &tlpwnd); xxxCancelMouseMoveTracking(pdesk->dwDTFlags, pdesk->spwndTrack, pdesk->htEx, dwDTCancel); ThreadUnlock(&tlpwnd); pdesk->dwDTFlags &= ~dwDTCancel; SetNewState: /* * Hottracking/tooltip on mouse move if on NC hitest and enabled */ if ((htEx != HTCLIENT) && (message == WM_MOUSEMOVE) && TestEffectUP(HOTTRACKING)) { /* * Hottrack the new hit test area */ if (xxxHotTrack(pwnd, htEx, TRUE)) { pdesk->dwDTFlags |= DF_HOTTRACKING; } /* * Remove/set the tool tip. * We always do this synchronously because it doesn't mess * with pwnd's or spwnTrack's queue */ if ((pstr = IsTooltipHittest(pwnd, LOWORD(htEx))) != NULL) { PTOOLTIPWND pttwnd = (PTOOLTIPWND)pdesk->spwndTooltip; ThreadLockAlways(pttwnd, &tlpwnd); xxxCreateTooltip(pttwnd, pstr); ThreadUnlock(&tlpwnd); pdesk->dwDTFlags |= DF_TOOLTIP; } else { PTOOLTIPWND pttwnd = (PTOOLTIPWND)pdesk->spwndTooltip; ThreadLockAlways(pttwnd, &tlpwnd); xxxResetTooltip(pttwnd); ThreadUnlock(&tlpwnd); } } /* if (htEx != HTCLIENT) */ ValidateThreadLocks(NULL, PtiCurrent()->ptl, (ULONG_PTR)&pwnd, TRUE); /* * Update new track window if needed. */ if (fNewpwndTrack) { PWND pwndActivate; Lock(&pdesk->spwndTrack, pwnd); /* * Active window tracking. * If there is non-zero timeout, get the window we're supposed to activate * and set the timer. Otherwise, set the queue flag so * xxxActiveWindowTracking can do its thing. */ if ((message == WM_MOUSEMOVE) && TestUP(ACTIVEWINDOWTRACKING)) { if (UP(ACTIVEWNDTRKTIMEOUT) != 0) { pwndActivate = GetActiveTrackPwnd(pwnd, NULL); if (pwndActivate != NULL) { InternalSetTimer(pwndActivate, IDSYS_WNDTRACKING, UP(ACTIVEWNDTRKTIMEOUT), xxxSystemTimerProc, TMRF_SYSTEM); } } else { PtiCurrent()->pq->QF_flags |= QF_ACTIVEWNDTRACKING; } /* if (TestUP(ACTIVEWNDTRKZORDER)) */ } /* if (TestUP(ACTIVEWINDOWTRACKING)) */ } /* * Save new hit test code */ pdesk->htEx = htEx; #if DBG --gcReEntered; #endif }
BOOL _RegisterHotKey( PWND pwnd, int id, UINT fsModifiers, UINT vk) { PHOTKEY phk; BOOL fKeysExist; PTHREADINFO ptiCurrent; PWINDOWSTATION pwinsta = _GetProcessWindowStation(NULL); ptiCurrent = PtiCurrent(); /* * Blow it off if the caller is not the windowstation init thread * and doesn't have the proper access rights */ if (grpwinstaList && !CheckWinstaWriteAttributesAccess()) { return FALSE; } /* * Can't register hotkey for a window of another queue. * Return FALSE in this case. */ if ((pwnd != PWND_FOCUS) && (pwnd != PWND_INPUTOWNER)) { if (GETPTI(pwnd) != ptiCurrent) { RIPERR0(ERROR_WINDOW_OF_OTHER_THREAD, RIP_VERBOSE, ""); return FALSE; } } phk = FindHotKey(ptiCurrent, pwnd, id, fsModifiers, vk, FALSE, &fKeysExist); /* * If the keys have already been registered, return FALSE. */ if (fKeysExist) { RIPERR0(ERROR_HOTKEY_ALREADY_REGISTERED, RIP_VERBOSE, ""); return FALSE; } if (phk == NULL) { /* * This hotkey doesn't exist yet. */ phk = (PHOTKEY)UserAllocPool(sizeof(HOTKEY), TAG_HOTKEY); /* * If the allocation failed, bail out. */ if (phk == NULL) { return FALSE; } phk->pti = ptiCurrent; if ((pwnd != PWND_FOCUS) && (pwnd != PWND_INPUTOWNER)) { phk->spwnd = NULL; Lock(&phk->spwnd, pwnd); } else { phk->spwnd = pwnd; } phk->fsModifiers = fsModifiers; phk->vk = vk; phk->id = id; /* * Link the new hotkey to the front of the list. */ phk->phkNext = gphkFirst; gphkFirst = phk; } else { /* * Hotkey already exists, reset the keys. */ phk->fsModifiers = fsModifiers; phk->vk = vk; } return TRUE; }
/***************************************************************************\ * * 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); }
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); } } }
/***************************************************************************\ * xxxMNStartMenuState * * This function is called when the menu bar is about to be activated (the * app's main menu). It makes sure that the threads involved are not in * menu mode already, finds the owner/notification window, initializes * pMenuState and sends the WM_ENTERMENULOOP message. * It successful, it returns a pointer to a pMenuState. If so, the caller * must call MNEndMenuState when done. * * History: * 4-25-91 Mikehar Port for 3.1 merge * 5-20-96 GerardoB Renamed and changed (Old name: xxxMNGetPopup) \***************************************************************************/ PMENUSTATE xxxMNStartMenuState(PWND pwnd) { PPOPUPMENU ppopupmenu; PTHREADINFO ptiCurrent, ptiNotify; PMENUSTATE pMenuState; TL tlpwnd; CheckLock(pwnd); /* * Bail if the current thread is already in menu mode */ ptiCurrent = PtiCurrent(); if (ptiCurrent->pMenuState != NULL) { return NULL; } /* * If the window doesn't have any children, return pwndActive. */ if (!TestwndChild(pwnd)) { pwnd = GETPTI(pwnd)->pq->spwndActive; } else { /* * Search up the parents for a window with a System Menu. */ while (TestwndChild(pwnd)) { if (TestWF(pwnd, WFSYSMENU)) break; pwnd = pwnd->spwndParent; } } if (pwnd == NULL) { return NULL; } if (!TestwndChild(pwnd) && (pwnd->spmenu != NULL)) { goto hasmenu; } if (!TestWF(pwnd, WFSYSMENU)) { return NULL; } hasmenu: /* * If the owner/notification window was created by another thread, * make sure that it's not in menu mode already * This can happen if PtiCurrent() is attached to other threads, one of * which created pwnd. */ ptiNotify = GETPTI(pwnd); if (ptiNotify->pMenuState != NULL) { return NULL; } /* * Allocate ppoupmenu and pMenuState */ ppopupmenu = MNAllocPopup(FALSE); if (ppopupmenu == NULL) { return NULL; } pMenuState = MNAllocMenuState(ptiCurrent, ptiNotify, ppopupmenu); if (pMenuState == NULL) { MNFreePopup(ppopupmenu); return NULL; } ppopupmenu->fIsMenuBar = TRUE; ppopupmenu->fHasMenuBar = TRUE; Lock(&(ppopupmenu->spwndNotify), pwnd); ppopupmenu->posSelectedItem = MFMWFP_NOITEM; Lock(&(ppopupmenu->spwndPopupMenu), pwnd); ppopupmenu->ppopupmenuRoot = ppopupmenu; /* * Notify the app we are entering menu mode. wParam is always 0 since this * procedure will only be called for menu bar menus not TrackPopupMenu * menus. */ ThreadLockAlways(pwnd, &tlpwnd); xxxSendMessage(pwnd, WM_ENTERMENULOOP, 0, 0L); ThreadUnlock(&tlpwnd); return pMenuState; }
/*****************************************************************************\ * * GetGUIThreadInfo() * * This gets GUI information out of context. If you pass in a NULL thread ID, * we will get the 'global' information, using the foreground thread. This * is guaranteed to be the real active window, focus window, etc. Yes, you * could do it yourself by calling GetForegroundWindow, getting the thread ID * of that window via GetWindowThreadProcessId, then passing the ID into * GetGUIThreadInfo(). However, that takes three calls and aside from being * a pain, anything could happen in the middle. So passing in NULL gets * you stuff in one call and hence also works right. * * This function returns FALSE if the thread doesn't have a queue or the * thread ID is bogus. * \*****************************************************************************/ BOOL WINAPI _GetGUIThreadInfo(PTHREADINFO pti, PGUITHREADINFO pgui) { PQ pq; /* * Validate threadinfo structure */ if (pgui->cbSize != sizeof(GUITHREADINFO)) { RIPERR1(ERROR_INVALID_PARAMETER, RIP_WARNING, "GUITHREADINFO.cbSize %d is wrong", pgui->cbSize); return FALSE; } /* * Is this a valid initialized GUI thread? */ if (pti != NULL) { if ((pq = pti->pq) == NULL) { // does this ever happen? RIPMSG1(RIP_ERROR, "GetGUIThreadInfo: No queue for pti %lx", pti); return FALSE; } } else { /* * Use the foreground queue. To get menu state information we must also * figure out the right pti. This matches _GetForegroundWindow() logic. */ if ((pq = gpqForeground) == NULL) { // this does sometimes happen... RIPMSG0(RIP_WARNING, "GetGUIThreadInfo: No foreground queue"); return FALSE; } if (pq->spwndActive && (GETPTI(pq->spwndActive)->pq == pq)) { pti = GETPTI(pq->spwndActive); if (PtiCurrentShared()->rpdesk != pti->rpdesk) { RIPERR0(ERROR_ACCESS_DENIED, RIP_VERBOSE, "Foreground window on different desktop"); return FALSE; } } } UserAssert(pq != NULL); /* * For C2 security, verify that pq and pti are on the current thread's desktop. * We can't directly determine which desktop pq belongs to, but we can at * least ensure that any caret info we return is not from another desktop */ if (pq->caret.spwnd && (GETPTI(pq->caret.spwnd)->rpdesk != PtiCurrentShared()->rpdesk)) { RIPERR0(ERROR_ACCESS_DENIED, RIP_VERBOSE, "Foreground caret on different desktop"); return FALSE; } if (pti && (pti->rpdesk != PtiCurrentShared()->rpdesk)) { RIPERR0(ERROR_ACCESS_DENIED, RIP_VERBOSE, "Foreground thread on different desktop"); return FALSE; } pgui->flags = 0; pgui->hwndMoveSize = NULL; pgui->hwndMenuOwner = NULL; /* * Get Menu information from the THREADINFO */ if (pti != NULL) { if (pti->pmsd && !pti->pmsd->fTrackCancelled && pti->pmsd->spwnd) { pgui->flags |= GUI_INMOVESIZE; pgui->hwndMoveSize = HWq(pti->pmsd->spwnd); } if (pti->pMenuState && pti->pMenuState->pGlobalPopupMenu) { pgui->flags |= GUI_INMENUMODE; if (pti->pMenuState->pGlobalPopupMenu->fHasMenuBar) { if (pti->pMenuState->pGlobalPopupMenu->fIsSysMenu) { pgui->flags |= GUI_SYSTEMMENUMODE; } } else { pgui->flags |= GUI_POPUPMENUMODE; } if (pti->pMenuState->pGlobalPopupMenu->spwndNotify) pgui->hwndMenuOwner = HWq(pti->pMenuState->pGlobalPopupMenu->spwndNotify); } } /* * Get the rest of the information from the queue */ pgui->hwndActive = HW(pq->spwndActive); pgui->hwndFocus = HW(pq->spwndFocus); pgui->hwndCapture = HW(pq->spwndCapture); pgui->hwndCaret = NULL; if (pq->caret.spwnd) { pgui->hwndCaret = HWq(pq->caret.spwnd); /* * These coords are always relative to the client of hwndCaret * of course. */ pgui->rcCaret.left = pq->caret.x; pgui->rcCaret.right = pgui->rcCaret.left + pq->caret.cx; pgui->rcCaret.top = pq->caret.y; pgui->rcCaret.bottom = pgui->rcCaret.top + pq->caret.cy; if (pq->caret.iHideLevel == 0) pgui->flags |= GUI_CARETBLINKING; } else if (pti && (pti->ppi->W32PF_Flags & W32PF_CONSOLEHASFOCUS)) { /* * The thread is running in the console window with focus. Pull * out the info from the console pseudo caret. */ pgui->hwndCaret = pti->rpdesk->cciConsole.hwnd; pgui->rcCaret = pti->rpdesk->cciConsole.rc; } else { SetRectEmpty(&pgui->rcCaret); } return TRUE; }