Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

5971 lines
187 KiB

/****************************** Module Header ******************************\
* Module Name: input.c
*
* Copyright (c) 1985-91, Microsoft Corporation
*
* This module contains the core functions of the input sub-system
*
* History:
* 10-18-90 DavidPe Created.
* 02-14-91 mikeke Added Revalidation code
\***************************************************************************/
#include "precomp.h"
#pragma hdrstop
//#define MARKPATH
#ifdef MARKPATH
BOOL gfMarkPath = 0;
#endif
#ifdef TRACESYSPEEK // is defined in userk.h
BOOL gbTraceSysPeek = FALSE;
int gnSysPeekSearch = 0;
void CheckPtiSysPeek(int where, PQ pq, DWORD newIdSysPeek)
{
PTHREADINFO ptiCurrent = PtiCurrent();
if (gbTraceSysPeek) {
KdPrint(("%d pti %lx sets id %lx to pq %lx ; old id %lx\n",
where, ptiCurrent, newIdSysPeek, pq, pq->idSysPeek));
if (newIdSysPeek > 1) {
PQMSG pqmsg = (PQMSG)newIdSysPeek;
KdPrint(("-> msg %lx hwnd %lx w %lx l %lx pti %lx\n",
pqmsg->msg.message, pqmsg->msg.hwnd, pqmsg->msg.wParam,
pqmsg->msg.lParam, pqmsg->pti));
}
}
}
void CheckSysLock(int where, PQ pq, PTHREADINFO ptiSysLock)
{
PTHREADINFO ptiCurrent = PtiCurrent();
if (gbTraceSysPeek) {
KdPrint(("%d pti %lx sets ptiSL %lx to pq %lx ; old ptiSL %lx\n",
where, ptiCurrent, ptiSysLock, pq, pq->ptiSysLock));
}
}
#endif //TRACESYSPEEK
#if DBG
BOOL gfLogPlayback = FALSE;
LPCSTR aszMouse[] = {
"WM_MOUSEMOVE",
"WM_LBUTTONDOWN",
"WM_LBUTTONUP",
"WM_LBUTTONDBLCLK",
"WM_RBUTTONDOWN",
"WM_RBUTTONUP",
"WM_RBUTTONDBLCLK",
"WM_MBUTTONDOWN",
"WM_MBUTTONUP",
"WM_MBUTTONDBLCLK"
"WM_MOUSEWHEEL"
};
LPCSTR aszKey[] = {
"WM_KEYDOWN",
"WM_KEYUP",
"WM_CHAR",
"WM_DEADCHAR",
"WM_SYSKEYDOWN",
"WM_SYSKEYUP",
"WM_SYSCHAR",
"WM_SYSDEADCHAR"
};
#endif // DBG
int LangToggleKeyState = 0;
PVOID KeyStateLookasideBase;
PVOID KeyStateLookasideBounds;
ZONE_HEADER KeyStateLookasideZone;
#if DBG
ULONG AllocKeyStateHiWater;
ULONG AllocKeyStateCalls;
ULONG AllocKeyStateSlowCalls;
ULONG DelKeyStateCalls;
ULONG DelKeyStateSlowCalls;
#endif // DBG
#define CANCEL_ACTIVESTATE 0
#define CANCEL_FOCUSSTATE 1
#define CANCEL_CAPTURESTATE 2
#define KEYSTATESIZE (CBKEYSTATE + CBKEYSTATERECENTDOWN)
/*
* xxxGetNextSysMsg return values
*/
#define PQMSG_PLAYBACK ((PQMSG)1)
BOOL xxxScanSysQueue(PTHREADINFO ptiCurrent, LPMSG lpMsg, PWND pwndFilter,
UINT msgMinFilter, UINT msgMaxFilter, DWORD flags, DWORD fsReason);
BOOL xxxReadPostMessage(PTHREADINFO pti, LPMSG lpMsg, PWND pwndFilter,
UINT msgMin, UINT msgMax, BOOL fRemoveMsg);
void CleanEventMessage(PQMSG pqmsg);
void FreeQEntry(PQMSG pqmsg);
UINT GetMouseKeyFlags(PQ pq);
/***************************************************************************\
* xxxWaitMessage (API)
*
* This API will block until an input message is received on
* the current queue.
*
* History:
* 10-25-90 DavidPe Created.
\***************************************************************************/
BOOL xxxWaitMessage(VOID)
{
return xxxSleepThread(QS_ALLINPUT | QS_EVENT, 0, TRUE);
}
/***************************************************************************\
* CheckProcessBackground/Foreground
*
* This checks to see if the process is at the right priority. If CSPINS is
* greater than CSPINBACKGROUND and the process isn't at the background
* priority, put it there. If it is less than this and should be foreground
* and isn't put it there.
*
* Need to put foreground spinning apps in the background (make it the same
* priority as all other background apps) so that bad apps can still communicate
* with apps in the background via dde, for example. There are other cases
* where spinning foreground apps affect the server, the printer spooler, and
* mstest scenarios. On Win3.1, calling PeekMessage() involves making a trip
* through the scheduler, forcing other apps to run. Processes run with
* priority on NT, where the foreground process gets foreground priority for
* greater responsiveness.
*
* If an app calls peek/getmessage without idling, count how many times this
* happens - if it happens CSPINBACKGROUND or more times, make the process
* background. This handles most of the win3.1 app compatibility spinning
* cases. If there is no priority contention, the app continues to run at
* full speed (no performance scenarios should be adversely affected by this).
*
* This solves these cases:
*
* - high speed timer not allowing app to go idle
* - post/peek loop (receiving a WM_ENTERIDLE, and posting a msg, for example)
* - peek no remove loop (winword "idle" state, most dde loops, as examples)
*
* But doesn't protect against these sort of cases:
*
* - app calls getmessage, then goes into a tight loop
* - non-gui threads in tight cpu loops
*
* 02-08-93 ScottLu Created.
\***************************************************************************/
void CheckProcessForeground(
PTHREADINFO pti)
{
PTHREADINFO ptiT;
/*
* Check to see if we need to move this process into foreground
* priority.
*/
pti->pClientInfo->cSpins = 0;
pti->TIF_flags &= ~TIF_SPINNING;
pti->pClientInfo->dwTIFlags = pti->TIF_flags;
if (pti->ppi->W32PF_Flags & W32PF_FORCEBACKGROUNDPRIORITY) {
/*
* See if any thread of this process is spinning. If none
* are, we can remove the force to background.
*/
for (ptiT = pti->ppi->ptiList; ptiT != NULL; ptiT = ptiT->ptiSibling) {
if (ptiT->TIF_flags & TIF_SPINNING)
return;
}
pti->ppi->W32PF_Flags &= ~W32PF_FORCEBACKGROUNDPRIORITY;
if (pti->ppi == gppiWantForegroundPriority) {
SetForegroundPriority(pti, TRUE);
}
}
}
/***************************************************************************\
* xxxInternalGetMessage
*
* This routine is the worker for both xxxGetMessage() and xxxPeekMessage()
* and is modelled after its win3.1 counterpart. From Win3.1:
*
* Get msg from the app queue or sys queue if there is one that matches
* hwndFilter and matches msgMin/msgMax. If no messages in either queue, check
* the QS_PAINT and QS_TIMER bits, call DoPaint or DoTimer to Post the
* appropriate message to the application queue, and then Read that message.
* Otherwise, if in GetMessage, Sleep until a wake bit is set indicating there
* is something we need to do. If in PeekMessage, return to caller. Before
* reading messages from the queues, check to see if the QS_SENDMESSAGE bit
* is set, and if so, call ReceiveMessage().
*
* 10-19-92 ScottLu Created.
\***************************************************************************/
BOOL xxxInternalGetMessage(
LPMSG lpMsg,
HWND hwndFilter,
UINT msgMin,
UINT msgMax,
UINT flags,
BOOL fGetMessage)
{
UINT fsWakeBits;
UINT fsWakeMask;
PTHREADINFO pti;
PW32PROCESS W32Process;
PWND pwndFilter;
BOOL fLockPwndFilter;
TL tlpwndFilter;
BOOL fRemove;
BOOL fExit;
PQ pq;
#ifdef MARKPATH
DWORD pathTaken = 0;
#define PATHTAKEN(x) pathTaken |= x
#define DUMPPATHTAKEN() if (gfMarkPath) DbgPrint("xxxInternalGetMessage path:%08x\n", pathTaken)
#else
#define PATHTAKEN(x)
#define DUMPPATHTAKEN()
#endif
CheckCritIn();
pti = PtiCurrent();
/*
* PeekMessage accepts NULL, 0x0000FFFF, and -1 as valid HWNDs.
* If hwndFilter is invalid we can't just return FALSE because that will
* hose existing badly behaved apps who might attempt to dispatch
* the random contents of pmsg.
*/
if ((hwndFilter == (HWND)-1) || (hwndFilter == (HWND)0x0000FFFF)) {
hwndFilter = (HWND)1;
}
if ((hwndFilter != NULL) && (hwndFilter != (HWND)1)) {
if ((pwndFilter = ValidateHwnd(hwndFilter)) == NULL) {
lpMsg->hwnd = NULL;
lpMsg->message = WM_NULL;
PATHTAKEN(1);
DUMPPATHTAKEN();
if (fGetMessage)
return -1;
else
return 0;
}
ThreadLockAlwaysWithPti(pti, pwndFilter, &tlpwndFilter);
fLockPwndFilter = TRUE;
} else {
pwndFilter = (PWND)hwndFilter;
fLockPwndFilter = FALSE;
}
/*
* Add one to our spin count. At this end of this routine we'll check
* to see if the spin count gets >= CSPINBACKGROUND. If so we'll put this
* process into the background.
*/
pti->pClientInfo->cSpins++;
/*
* Check to see if the startglass is on, and if so turn it off and update.
*/
W32Process = W32GetCurrentProcess();
if (W32Process->W32PF_Flags & W32PF_STARTGLASS) {
/*
* This app is no longer in "starting" mode. Recalc when to hide
* the app starting cursor.
*/
W32Process->W32PF_Flags &= ~W32PF_STARTGLASS;
CalcStartCursorHide(NULL, 0);
}
/*
* Next check to see if any .dlls need freeing in
* the context of this client (used for windows hooks).
*/
if (pti->ppi->cSysExpunge != gcSysExpunge) {
pti->ppi->cSysExpunge = gcSysExpunge;
if (pti->ppi->dwhmodLibLoadedMask & gdwSysExpungeMask)
xxxDoSysExpunge(pti);
}
/*
* Set up BOOL fRemove local variable from for ReadMessage()
*/
fRemove = flags & PM_REMOVE;
/*
* Unlock the system queue if it's owned by us.
*/
/*
* If we're currently processing a message, unlock the input queue
* because the sender, who is blocked, might be the owner, and in order
* to reply, the receiver may need to read keyboard / mouse input.
*/
/*
* If this thread has the input queue locked and the last message removed
* is the last message we looked at, then unlock - we're ready for anyone
* to get the next message.
*/
pq = pti->pq;
if ( (pti->psmsCurrent != NULL)
|| (pq->ptiSysLock == pti && pq->idSysLock == pti->idLast)
) {
CheckSysLock(1, pq, NULL);
pq->ptiSysLock = NULL;
PATHTAKEN(2);
} else if (pq->ptiSysLock && (pq->ptiSysLock->cVisWindows == 0) &&
(pti->pDeskInfo->asphkStart[WH_JOURNALPLAYBACK + 1] != NULL)) {
/*
* If the thread that has the system queue lock has no windows visible
* (can happen if it just hid its last window), don't expect it to call
* GetMessage() again! - unlock the system queue. --- ScottLu
* This condition creates a hole by which a second thread attached to
* the same queue as thread 1 can alter pq->idSysPeek during a callback
* made by thread 1 so that thread 1 will delete the wrong message
* (losing keystrokes - causing Shift to appear be stuck down editing a
* Graph5 caption embedded in Word32 document #5032. However, MSTEST
* requires this hole, so allow it if Journal Playback is occurring
* #8850 (yes, a hack) Chicago also has this behavior. --- IanJa
*/
CheckSysLock(2, pq, NULL);
pq->ptiSysLock = NULL;
PATHTAKEN(3);
}
if (pq->ptiSysLock != pti) {
pti->pcti->CTIF_flags &= ~CTIF_SYSQUEUELOCKED;
}
/*
* If msgMax == 0 then msgMax = -1: that makes our range checking only
* have to deal with msgMin < msgMax.
*/
if (msgMax == 0)
msgMax--;
/*
* Compute the QS* mask that corresponds to the message range.
*/
fsWakeMask = CalcWakeMask(msgMin, msgMax);
pti->fsChangeBitsRemoved = 0;
/*
* If we can yield and one or more events were skipped,
* set the wakebits for event
*/
if (!(flags & PM_NOYIELD) && pti->TIF_flags & TIF_DELAYEDEVENT) {
pti->pcti->fsWakeBits |= QS_EVENT;
pti->pcti->fsChangeBits |= QS_EVENT;
pti->TIF_flags &= ~TIF_DELAYEDEVENT;
}
while (TRUE) {
/*
* Restore any wake bits saved while journalling
*/
pti->pcti->fsWakeBits |= pti->pcti->fsWakeBitsJournal;
/*
* If we need to recalc queue attachments, do it here. Do it on the
* right desktop or else the queues will get created in the wrong
* heap.
*/
if (pti->rpdesk == gpdeskRecalcQueueAttach) {
gpdeskRecalcQueueAttach = NULL;
if (pti->rpdesk != NULL && !FJOURNALRECORD() && !FJOURNALPLAYBACK()) {
ReattachThreads(FALSE);
PATHTAKEN(4);
}
}
/*
* Remember what change bits we're clearing. This is important to
* fix a bug in the input model: If an app receives a sent message
* from within SleepThread(), then does PostMessage() (which sets
* QS_POSTMESSAGE), then does a PeekMessage(...) for some different
* posted message (clears QS_POSTMESSAGE in fsChangeBits), then returns
* back into SleepThread(), it won't wake up to retrieve that newly
* posted message because the change bits are cleared.
*
* What we do is remember the change bits that are being cleared.
* Then, when we return to SleepThread(), we put these remembered
* bits back into the change bits that also have corresponding
* bits in the wakebits (so we don't set changebits that represent
* input that isn't there anymore). This way, the app will retrieve
* the newly posted message refered to earlier.
* - scottlu
*/
pti->fsChangeBitsRemoved |= pti->pcti->fsChangeBits & fsWakeMask;
/*
* Clear the change bits that we're looking at, in order to detect
* incoming events that may occur the last time we checked the wake
* bits.
*/
pti->pcti->fsChangeBits &= ~fsWakeMask;
/*
* Check to see if we have any input we want.
*/
xxxReceiveMessages(pti);
if ((pti->pcti->fsWakeBits & fsWakeMask) == 0) {
PATHTAKEN(8);
goto NoMessages;
}
fsWakeBits = pti->pcti->fsWakeBits;
/*
* If the queue lock is != NULL (ptiSysLock) and it is this thread that
* locked it, then go get the message from the system queue. This is
* to prevent messages posted after a PeekMessage/no-remove from being
* seen before the original message from the system queue. (Aldus
* Pagemaker requires this) (bobgu 8/5/87).
*/
if (pti->pq->ptiSysLock == pti &&
(pti->pq->QF_flags & QF_LOCKNOREMOVE)) {
/*
* Does the caller want mouse / keyboard?
*/
if (fsWakeBits & fsWakeMask & (QS_INPUT | QS_EVENT)) {
if (xxxScanSysQueue(pti, lpMsg, pwndFilter,
msgMin, msgMax, flags,
fsWakeBits & fsWakeMask & (QS_INPUT | QS_EVENT))) {
PATHTAKEN(0x10);
break;
}
}
}
/*
* See if there's a message in the application queue.
*/
if (fsWakeBits & fsWakeMask & QS_POSTMESSAGE) {
if (xxxReadPostMessage(pti, lpMsg, pwndFilter,
msgMin, msgMax, fRemove)) {
PATHTAKEN(0x20);
break;
}
}
/*
* Time to scan the raw input queue for input. First check to see
* if the caller wants mouse / keyboard input.
*/
if (fsWakeBits & fsWakeMask & (QS_INPUT | QS_EVENT)) {
if (xxxScanSysQueue(pti, lpMsg, pwndFilter,
msgMin, msgMax, flags,
fsWakeBits & fsWakeMask & (QS_INPUT | QS_EVENT))) {
PATHTAKEN(0x40);
break;
}
}
/*
* Get new input bits, check for SendMsgs.
*/
xxxReceiveMessages(pti);
if ((pti->pcti->fsWakeBits & fsWakeMask) == 0) {
PATHTAKEN(0x80);
goto NoMessages;
}
fsWakeBits = pti->pcti->fsWakeBits;
/*
* Does the caller want paint messages? If so, try to find a paint.
*/
if (fsWakeBits & fsWakeMask & QS_PAINT) {
if (DoPaint(pwndFilter, lpMsg)) {
PATHTAKEN(0x100);
break;
}
}
/*
* We must yield for 16 bit apps before checking timers or an app
* that has a fast timer could chew up all the time and never let
* anyone else run.
*
* NOTE: This could cause PeekMessage() to yield TWICE, if the user
* is filtering with a window handle. If the DoTimer() call fails
* then we end up yielding again.
*/
if (!(flags & PM_NOYIELD)) {
/*
* This is the point where windows would yield. Here we wait to wake
* up any threads waiting for this thread to hit "idle state".
*/
WakeInputIdle(pti);
/*
* Yield and receive pending messages.
*/
xxxUserYield(pti);
/*
* Check new input buts and receive pending messages.
*/
xxxReceiveMessages(pti);
if ((pti->pcti->fsWakeBits & fsWakeMask) == 0) {
PATHTAKEN(0x200);
goto NoMessages;
}
fsWakeBits = pti->pcti->fsWakeBits;
}
/*
* Does the app want timer messages, and if there one pending?
*/
if (fsWakeBits & fsWakeMask & QS_TIMER) {
if (DoTimer(pwndFilter, msgMin, msgMax)) {
/*
* DoTimer() posted the message into the app's queue,
* so start over and we'll grab it from there.
*/
PATHTAKEN(0x400);
continue;
}
}
NoMessages:
/*
* Looks like we have no input. If we're being called from GetMessage()
* then go to sleep until we find something.
*/
if (!fGetMessage) {
/*
* This is one last check for pending sent messages. It also
* yields. Win3.1 does this.
*/
if (!(flags & PM_NOYIELD)) {
/*
* This is the point where windows yields. Here we wait to wake
* up any threads waiting for this thread to hit "idle state".
*/
WakeInputIdle(pti);
/*
* Yield and receive pending messages.
*/
xxxUserYield(pti);
}
PATHTAKEN(0x800);
goto FalseExit;
}
/*
* This is a getmessage not a peekmessage, so sleep. When we sleep,
* WakeInputIdle() is called to wake up any apps waiting on this
* app to go idle.
*/
if (!xxxSleepThread(fsWakeMask, 0, TRUE))
goto FalseExit;
}
/*
* If we're here then we have input for this queue. Call the
* GetMessage() hook with this input.
*/
if (IsHooked(pti, WHF_GETMESSAGE))
xxxCallHook(HC_ACTION, flags, (DWORD)lpMsg, WH_GETMESSAGE);
/*
* If called from PeekMessage(), return TRUE.
*/
if (!fGetMessage) {
PATHTAKEN(0x1000);
goto TrueExit;
}
/*
* Being called from GetMessage(): return FALSE if the message is WM_QUIT,
* TRUE otherwise.
*/
if (lpMsg->message == WM_QUIT) {
PATHTAKEN(0x2000);
goto FalseExit;
}
/*
* Fall through to TrueExit...
*/
TrueExit:
/*
* Update timeLastRead. We use this for hung app calculations.
*/
SET_TIME_LAST_READ(pti);
fExit = TRUE;
PATHTAKEN(0x4000);
goto Exit;
FalseExit:
fExit = FALSE;
Exit:
if (fLockPwndFilter)
ThreadUnlock(&tlpwndFilter);
/*
* see CheckProcessBackground() comment above
* Check to see if we need to move this process into background
* priority.
*/
if (pti->pClientInfo->cSpins >= CSPINBACKGROUND) {
pti->pClientInfo->cSpins = 0;
if (!(pti->TIF_flags & TIF_SPINNING)) {
pti->TIF_flags |= TIF_SPINNING;
pti->pClientInfo->dwTIFlags = pti->TIF_flags;
if (!(pti->ppi->W32PF_Flags & W32PF_FORCEBACKGROUNDPRIORITY)) {
pti->ppi->W32PF_Flags |= W32PF_FORCEBACKGROUNDPRIORITY;
if (pti->ppi == gppiWantForegroundPriority) {
SetForegroundPriority(pti, FALSE);
}
}
}
/*
* For spinning Message loops, we need to take the 16bit-thread out
* of the scheduler temporarily so that other processes can get a chance
* to run. This is appearent in OLE operations where a 16bit foreground
* thread starts an OLE activation on a 32bit process. The 32bit process
* gets starved of CPU while the 16bit thread spins.
*/
if (pti->TIF_flags & TIF_16BIT) {
/*
* Take the 16bit thread out of the scheduler. This wakes any
* other 16bit thread needing time, and takes the current thread
* out. We will do a brief sleep so that apps can respond in time.
* When done, we will reschedule the thread. The WakeInputIdle()
* should have been called in the no-messages section, so we have
* already set the Idle-Event.
*/
xxxSleepTask(FALSE, HEVENT_REMOVEME);
LeaveCrit();
ZwYieldExecution();
CheckForClientDeath();
EnterCrit();
xxxDirectedYield(DY_OLDYIELD);
}
}
PATHTAKEN(0x8000);
DUMPPATHTAKEN();
return fExit;
}
#undef PATHTAKEN
#undef DUMPPATHTAKEN
/***************************************************************************\
* xxxDispatchMessage (API)
*
* Calls the appropriate window procedure or function with pmsg.
*
* History:
* 10-25-90 DavidPe Created.
\***************************************************************************/
LONG xxxDispatchMessage(
LPMSG pmsg)
{
LONG lRet;
PWND pwnd;
WNDPROC_PWND lpfnWndProc;
TL tlpwnd;
pwnd = NULL;
if (pmsg->hwnd != NULL) {
if ((pwnd = ValidateHwnd(pmsg->hwnd)) == NULL)
return 0;
}
/*
* If this is a synchronous-only message (takes a pointer in wParam or
* lParam), then don't allow this message to go through since those
* parameters have not been thunked, and are pointing into outer-space
* (which would case exceptions to occur).
*
* (This api is only called in the context of a message loop, and you
* don't get synchronous-only messages in a message loop).
*/
if (TESTSYNCONLYMESSAGE(pmsg->message, pmsg->wParam)) {
/*
* Fail if 32 bit app is calling.
*/
if (!(PtiCurrent()->TIF_flags & TIF_16BIT)) {
RIPERR1(ERROR_INVALID_MESSAGE, RIP_WARNING, "xxxDispatchMessage: Sync only message 0x%lX",
pmsg->message);
return 0;
}
/*
* For wow apps, allow it to go through (for compatibility). Change
* the message id so our code doesn't understand the message - wow
* will get the message and strip out this bit before dispatching
* the message to the application.
*/
pmsg->message |= MSGFLAG_WOW_RESERVED;
}
ThreadLock(pwnd, &tlpwnd);
/*
* Is this a timer? If there's a proc address, call it,
* otherwise send it to the wndproc.
*/
if ((pmsg->message == WM_TIMER) || (pmsg->message == WM_SYSTIMER)) {
if (pmsg->lParam != (LONG)NULL) {
/*
* System timers must be executed on the server's context.
*/
if (pmsg->message == WM_SYSTIMER) {
/*
* Verify that it's a valid timer proc. If so,
* don't leave the critsect to call server-side procs
* and pass a PWND, not HWND.
*/
PTIMER ptmr;
lRet = 0;
for (ptmr = gptmrFirst; ptmr != NULL; ptmr = ptmr->ptmrNext) {
if (pmsg->lParam == (LONG)ptmr->pfn) {
lRet = ptmr->pfn(pwnd, WM_SYSTIMER, pmsg->wParam,
NtGetTickCount());
break;
}
}
goto Exit;
} else {
/*
* WM_TIMER is the same for Unicode/ANSI.
*/
PTHREADINFO pti = PtiCurrent();
if (pti->TIF_flags & TIF_SYSTEMTHREAD)
goto Exit;
lRet = CallClientProcA(pwnd, WM_TIMER,
pmsg->wParam, NtGetTickCount(), pmsg->lParam);
goto Exit;
}
}
}
/*
* Check to see if pwnd is NULL AFTER the timer check. Apps can set
* timers with NULL hwnd's, that's totally legal. But NULL hwnd messages
* don't get dispatched, so check here after the timer case but before
* dispatching - if it's NULL, just return 0.
*/
if (pwnd == NULL) {
lRet = 0;
goto Exit;
}
/*
* If we're dispatching a WM_PAINT message, set a flag to be used to
* determine whether it was processed properly.
*/
if (pmsg->message == WM_PAINT)
SetWF(pwnd, WFPAINTNOTPROCESSED);
/*
* If this window's proc is meant to be executed from the server side
* we'll just stay inside the semaphore and call it directly. Note
* how we don't convert the pwnd into an hwnd before calling the proc.
*/
if (TestWF(pwnd, WFSERVERSIDEPROC)) {
UINT fnMessageType;
fnMessageType = pmsg->message >= WM_USER ? (UINT)SfnDWORD :
(UINT)gapfnScSendMessage[pmsg->message];
/*
* Convert the WM_CHAR from ANSI to UNICODE if the source was ANSI
*/
if (fnMessageType == (UINT)SfnINWPARAMCHAR && TestWF(pwnd, WFANSIPROC)) {
UserAssert(PtiCurrent() == GETPTI(pwnd)); // use receiver's codepage
RtlMBMessageWParamCharToWCS(pmsg->message, (PDWORD)&pmsg->wParam);
}
lRet = pwnd->lpfnWndProc(pwnd, pmsg->message, pmsg->wParam,
pmsg->lParam);
goto Exit;
}
/*
* Cool people dereference any window structure members before they
* leave the critsect.
*/
lpfnWndProc = pwnd->lpfnWndProc;
{
/*
* If we're dispatching the message to an ANSI wndproc we need to
* convert the character messages from Unicode to Ansi.
*/
if (TestWF(pwnd, WFANSIPROC)) {
UserAssert(PtiCurrent() == GETPTI(pwnd)); // use receiver's codepage
RtlWCSMessageWParamCharToMB(pmsg->message, (PDWORD)&pmsg->wParam);
lRet = CallClientProcA(pwnd, pmsg->message,
pmsg->wParam, pmsg->lParam, (DWORD)lpfnWndProc);
} else {
lRet = CallClientProcW(pwnd, pmsg->message,
pmsg->wParam, pmsg->lParam, (DWORD)lpfnWndProc);
}
}
/*
* If we dispatched a WM_PAINT message and it wasn't properly
* processed, do the drawing here.
*/
if (pmsg->message == WM_PAINT && RevalidateHwnd(pmsg->hwnd) &&
TestWF(pwnd, WFPAINTNOTPROCESSED)) {
//RIPMSG0(RIP_WARNING,
// "Missing BeginPaint or GetUpdateRect/Rgn(fErase == TRUE) in WM_PAINT");
ClrWF(pwnd, WFWMPAINTSENT);
xxxSimpleDoSyncPaint(pwnd);
}
Exit:
ThreadUnlock(&tlpwnd);
return lRet;
}
/***************************************************************************\
* AdjustForCoalescing
*
* If message is in the coalesce message range, and it's message and hwnd
* equals the last message in the queue, then coalesce these two messages
* by simple deleting the last one.
*
* 11-12-92 ScottLu Created.
\***************************************************************************/
void AdjustForCoalescing(
PMLIST pml,
HWND hwnd,
UINT message)
{
/*
* First see if this message is in that range.
*/
if (!CheckMsgFilter(message, WM_COALESCE_FIRST, WM_COALESCE_LAST))
return;
if (pml->pqmsgWriteLast == NULL)
return;
if (pml->pqmsgWriteLast->msg.message != message)
return;
if (pml->pqmsgWriteLast->msg.hwnd != hwnd)
return;
/*
* The message and hwnd are the same, so delete this message and
* the new one will added later.
*/
DelQEntry(pml, pml->pqmsgWriteLast);
}
/***************************************************************************\
* _PostMessage (API)
*
* Writes a message to the message queue for pwnd. If pwnd == -1, the message
* is broadcasted to all windows.
*
* History:
* 11-06-90 DavidPe Created.
\***************************************************************************/
BOOL _PostMessage(
PWND pwnd,
UINT message,
DWORD wParam,
LONG lParam)
{
PQMSG pqmsg;
BOOL fPwndUnlock;
BOOL fRet;
DWORD dwPostCode;
TL tlpwnd;
PTHREADINFO pti;
/*
* First check to see if this message takes DWORDs only. If it does not,
* fail the post. Cannot allow an app to post a message with pointers or
* handles in it - this can cause the server to fault and cause other
* problems - such as causing apps in separate address spaces to fault.
* (or even an app in the same address space to fault!)
*/
if (TESTSYNCONLYMESSAGE(message, wParam)) {
RIPERR1(ERROR_INVALID_PARAMETER,
RIP_WARNING,
"Invalid parameter \"message\" (%ld) to _PostMessage",
message);
return FALSE;
}
/*
* Is this a BroadcastMsg()?
*/
if ((pwnd == (PWND)-1) || (pwnd == (PWND)0xFFFF)) {
xxxBroadcastMessage(NULL, message, wParam, lParam, BMSG_POSTMSG, NULL);
return TRUE;
}
pti = PtiCurrent();
/*
* Is this posting to the current thread info?
*/
if (pwnd == NULL) {
return _PostThreadMessage(pti, message, wParam, lParam);
}
fPwndUnlock = FALSE;
if (message >= WM_DDE_FIRST && message <= WM_DDE_LAST) {
ThreadLockAlwaysWithPti(pti, pwnd, &tlpwnd);
dwPostCode = xxxDDETrackPostHook(&message, pwnd, wParam, &lParam, FALSE);
if (dwPostCode != DO_POST) {
ThreadUnlock(&tlpwnd);
return(dwPostCode == FAKE_POST);
}
fPwndUnlock = TRUE;
}
pti = GETPTI(pwnd);
/*
* Check to see if this message is in the multimedia coalescing range.
* If so, see if it can be coalesced with the previous message.
*/
AdjustForCoalescing(&pti->mlPost, HWq(pwnd), message);
/*
* Allocate a key state update event if needed.
*/
if (message >= WM_KEYFIRST && message <= WM_KEYLAST) {
PostUpdateKeyStateEvent(pti->pq);
}
/*
* Put this message on the 'post' list.
*/
fRet = FALSE;
if ((pqmsg = AllocQEntry(&pti->mlPost)) != NULL) {
/*
* Set the QS_POSTMESSAGE bit so the thread knows it has a message.
*/
StoreQMessage(pqmsg, pwnd, message, wParam, lParam, 0, 0);
SetWakeBit(pti, QS_POSTMESSAGE | QS_ALLPOSTMESSAGE);
/*
* If it's a hotkey, set the QS_HOTKEY bit since we have a separate
* bit for those messages.
*/
if (message == WM_HOTKEY)
SetWakeBit(pti, QS_HOTKEY);
fRet = TRUE;
}
/*
* Are we posting to the thread currently reading from the input queue?
* If so, update idSysLock with this pqmsg so that the input queue will
* not be unlocked until this message is read.
*/
if (pti == pti->pq->ptiSysLock)
pti->pq->idSysLock = (DWORD)pqmsg;
if (fPwndUnlock)
ThreadUnlock(&tlpwnd);
return fRet;
}
/***************************************************************************\
* _PostQuitMessage (API)
*
* Writes a message to the message queue for pwnd. If pwnd == -1, the message
* is broadcasted to all windows.
*
* History:
* 11-06-90 DavidPe Created.
* 05-16-91 mikeke Changed to return BOOL
\***************************************************************************/
BOOL _PostQuitMessage(
int nExitCode)
{
PTHREADINFO pti;
pti = PtiCurrent();
pti->cQuit = 1;
pti->exitCode = nExitCode;
SetWakeBit(pti, QS_POSTMESSAGE | QS_ALLPOSTMESSAGE);
return TRUE;
}
/***************************************************************************\
* _PostThreadMessage (API)
*
* Given a thread ID, the function will post the specified message to this
* thread with pmsg->hwnd == NULL..
*
* History:
* 11-21-90 DavidPe Created.
\***************************************************************************/
BOOL _PostThreadMessage(
PTHREADINFO pti,
UINT message,
DWORD wParam,
LONG lParam)
{
PQMSG pqmsg;
if ((pti == NULL) ||
!(pti->TIF_flags & TIF_GUITHREADINITIALIZED) ||
(pti->TIF_flags & TIF_INCLEANUP)) {
RIPERR0(ERROR_INVALID_THREAD_ID, RIP_VERBOSE, "");
return FALSE;
}
/*
* First check to see if this message takes DWORDs only. If it does not,
* fail the post. Cannot allow an app to post a message with pointers or
* handles in it - this can cause the server to fault and cause other
* problems - such as causing apps in separate address spaces to fault.
* (or even an app in the same address space to fault!)
*/
if (TESTSYNCONLYMESSAGE(message, wParam)) {
RIPERR1(ERROR_INVALID_PARAMETER,
RIP_WARNING,
"Invalid parameter \"message\" (%ld) to _PostThreadMessage",
message);
return FALSE;
}
/*
* Check to see if this message is in the multimedia coalescing range.
* If so, see if it can be coalesced with the previous message.
*/
AdjustForCoalescing(&pti->mlPost, NULL, message);
/*
* Put this message on the 'post' list.
*/
if ((pqmsg = AllocQEntry(&pti->mlPost)) == NULL)
return FALSE;
/*
* Set the QS_POSTMESSAGE bit so the thread knows it has a message.
*/
StoreQMessage(pqmsg, NULL, message, wParam, lParam, 0, 0);
SetWakeBit(pti, QS_POSTMESSAGE | QS_ALLPOSTMESSAGE);
/*
* If it's a hotkey, set the QS_HOTKEY bit since we have a separate
* bit for those messages.
*/
if (message == WM_HOTKEY)
SetWakeBit(pti, QS_HOTKEY);
/*
* Are we posting to the thread currently reading from the input queue?
* If so, update idSysLock with this pqmsg so that the input queue will
* not be unlocked until this message is read.
*/
if (pti == pti->pq->ptiSysLock)
pti->pq->idSysLock = (DWORD)pqmsg;
return TRUE;
}
/***************************************************************************\
* _GetMessagePos (API)
*
* This API returns the cursor position when the last message was read from
* the current message queue.
*
* History:
* 11-19-90 DavidPe Created.
\***************************************************************************/
DWORD _GetMessagePos(VOID)
{
PTHREADINFO pti;
pti = PtiCurrent();
return MAKELONG((SHORT)pti->ptLast.x, (SHORT)pti->ptLast.y);
}
#ifdef SYSMODALWINDOWS
/***************************************************************************\
* _SetSysModalWindow (API)
*
* History:
* 01-25-91 DavidPe Created stub.
\***************************************************************************/
PWND APIENTRY _SetSysModalWindow(
PWND pwnd)
{
pwnd;
return NULL;
}
/***************************************************************************\
* _GetSysModalWindow (API)
*
* History:
* 01-25-91 DavidPe Created stub.
\***************************************************************************/
PWND APIENTRY _GetSysModalWindow(VOID)
{
return NULL;
}
#endif //LATER
/***************************************************************************\
* PostMove
*
* This routine gets called when it is detected that the QF_MOUSEMOVED bit
* is set in a particular queue.
*
* 11-03-92 ScottLu Created.
\***************************************************************************/
VOID PostMove(
PQ pq)
{
PostInputMessage(pq, NULL, WM_MOUSEMOVE, 0,
MAKELONG((SHORT)ptCursor.x, (SHORT)ptCursor.y),
dwMouseMoveExtraInfo);
pq->QF_flags &= ~QF_MOUSEMOVED;
}
/***************************************************************************\
* SetFMouseMoved
*
* Send a mouse move through the system. This usually occurs when doing
* window management to be sure that the mouse shape accurately reflects
* the part of the window it is currently over (window managment may have
* changed this).
*
* 11-02-92 ScottLu Created.
\***************************************************************************/
VOID SetFMouseMoved(
VOID)
{
PWND pwnd;
PWND pwndOldCursor;
PQ pq;
/*
* Need to first figure out what queue this mouse event is in. Do NOT
* check for mouse capture here !! Talk to scottlu.
*/
if ((pwnd = gspwndScreenCapture) == NULL) {
if ((pwnd = gspwndMouseOwner) == NULL) {
if ((pwnd = gspwndInternalCapture) == NULL) {
pwnd = SpeedHitTest(grpdeskRitInput->pDeskInfo->spwnd, ptCursor);
}
}
}
if (pwnd == NULL)
return;
/*
* This is apparently needed by the attach/unattach code for some
* reason. I'd like to get rid of it - scottlu.
*/
pwndOldCursor = Lock(&gspwndCursor, pwnd);
/*
* If we're giving a mouse move to a new queue, be sure the cursor
* image represents what this queue thinks it should be.
*/
pq = GETPTI(pwnd)->pq;
if (pq != gpqCursor) {
/*
* If the old queue had the mouse captured, let him know that
* the mouse moved first. Need this to fix tooltips in
* WordPerfect Office. Do the same for mouse tracking.
*/
if (gpqCursor != NULL) {
if (gpqCursor->spwndCapture != NULL) {
gpqCursor->QF_flags |= QF_MOUSEMOVED;
SetWakeBit(GETPTI(gpqCursor->spwndCapture), QS_MOUSEMOVE);
}
if (gpqCursor->spwndLastMouseMessage != NULL) {
gpqCursor->QF_flags |= QF_MOUSEMOVED;
SetWakeBit(GETPTI(gpqCursor->spwndLastMouseMessage), QS_MOUSEMOVE);
}
}
/*
* First re-assign gpqCursor so any SetCursor() calls
* will only take effect if done by the thread that
* owns the window the mouse is currently over.
*/
gpqCursor = pq;
/*
* Call UpdateCursorImage() so the new gpqCursor's
* notion of the current cursor is represented.
*/
UpdateCursorImage();
}
/*
* Set the mouse moved bit for this queue so we know later to post
* a move message to this queue.
*/
pq->QF_flags |= QF_MOUSEMOVED;
/*
* Reassign mouse input to this thread - this indicates which thread
* to wake up when new input comes in.
*/
pq->ptiMouse = GETPTI(pwnd);
/*
* Wake some thread within this queue to process this mouse event.
*/
WakeSomeone(pq, WM_MOUSEMOVE, NULL);
/*
* We're possibly generating a fake mouse move message - it has no
* extra info associated with it - so 0 it out.
*/
dwMouseMoveExtraInfo = 0;
}
/***************************************************************************\
* CancelForegroundActivate
*
* This routine cancels the foreground activate that we allow apps starting
* up to have. This means that if you make a request to start an app,
* if this routine is called before the app becomes foreground, it won't
* become foreground. This routine gets called if the user down clicks or
* makes a keydown event, with the idea being that if the user did this,
* the user is using some other app and doesn't want the newly starting
* application to appear on top and force itself into the foreground.
*
* 09-15-92 ScottLu Created.
\***************************************************************************/
void CancelForegroundActivate()
{
PPROCESSINFO ppiT;
if (gfAllowForegroundActivate) {
for (ppiT = gppiStarting; ppiT != NULL; ppiT = ppiT->ppiNext) {
/*
* Don't cancel activation if the app is being debugged - if
* the debugger stops the application before it has created and
* activated its first window, the app will come up behind all
* others - not what you want when being debugged.
*/
if (!ppiT->Process->DebugPort)
ppiT->W32PF_Flags &= ~W32PF_ALLOWFOREGROUNDACTIVATE;
}
gfAllowForegroundActivate = FALSE;
}
}
/***************************************************************************\
* RestoreForegroundActivate
*
* This routine re-enables an app's right to foreground activate (activate and
* come on top) if it is starting up. This is called when we minimize or when
* the last window of a thread goes away, for example.
*
* 01-26-93 ScottLu Created.
\***************************************************************************/
void RestoreForegroundActivate()
{
PPROCESSINFO ppiT;
extern BOOL gfAllowForegroundActivate;
for (ppiT = gppiStarting; ppiT != NULL; ppiT = ppiT->ppiNext) {
if (ppiT->W32PF_Flags & W32PF_APPSTARTING) {
ppiT->W32PF_Flags |= W32PF_ALLOWFOREGROUNDACTIVATE;
gfAllowForegroundActivate = TRUE;
}
}
}
/***************************************************************************\
* PostInputMessage
*
* Puts a message on the 'input' linked-list of message for the specified
* queue.
*
* History:
* 10-25-90 DavidPe Created.
* 01-21-92 DavidPe Rewrote to deal with OOM errors gracefully.
\***************************************************************************/
void PostInputMessage(
PQ pq,
PWND pwnd,
UINT message,
DWORD wParam,
LONG lParam,
DWORD dwExtraInfo)
{
PQMSG pqmsgInput, pqmsgPrev;
short sWheelDelta;
/*
* Grab the last written message before we start allocing new ones,
* so we're sure to point to the correct message.
*/
pqmsgPrev = pq->mlInput.pqmsgWriteLast;
/*
* Allocate a key state update event if needed.
*/
if (pq->QF_flags & QF_UPDATEKEYSTATE) {
PostUpdateKeyStateEvent(pq);
}
/*
* We want to coalesce sequential WM_MOUSEMOVE and WM_MOUSEWHEEL.
* WM_MOUSEMOVEs are coalesced by just storing the most recent
* event over the last one.
* WM_MOUSEWHEELs also add up the wheel rolls.
*/
if (pqmsgPrev != NULL &&
pqmsgPrev->msg.message == message &&
(message == WM_MOUSEMOVE || message == WM_MOUSEWHEEL)) {
if (message == WM_MOUSEWHEEL) {
sWheelDelta = (short)HIWORD(wParam) + (short)HIWORD(pqmsgPrev->msg.wParam);
#if 0
/*
* LATER: We can't remove a wheel message with zero delta
* unless we know it hasn't been peeked. Ideally,
* we would check idsyspeek for this, but we're too close
* to ship and idsyspeek is too fragile. Consider also
* checking to see if mouse move messages have been peeked.
*/
if (sWheelDelta == 0) {
if ((PQMSG)pq->idSysPeek == pqmsgPrev) {
RIPMSG0(RIP_VERBOSE,
"Coalescing of mouse wheel messages causing "
"idSysPeek to be reset to 0");
pq->idSysPeek = 0;
}
DelQEntry(&pq->mlInput, pqmsgPrev);
return;
}
#endif
wParam = MAKEWPARAM(LOWORD(wParam), sWheelDelta);
}
StoreQMessage(pqmsgPrev, pwnd, message, wParam, lParam, 0, dwExtraInfo);
WakeSomeone(pq, message, pqmsgPrev);
return;
}
/*
* Fill in pqmsgInput.
*/
pqmsgInput = AllocQEntry(&pq->mlInput);
if (pqmsgInput != NULL) {
StoreQMessage(pqmsgInput, pwnd, message, wParam, lParam, 0, dwExtraInfo);
WakeSomeone(pq, message, pqmsgInput);
}
}
/***************************************************************************\
* WakeSomeone
*
* Figures out which thread to wake up based on the queue and message.
* If the queue pointer is NULL, figures out a likely queue.
*
* 10-23-92 ScottLu Created.
\***************************************************************************/
void WakeSomeone(
PQ pq,
UINT message,
PQMSG pqmsg)
{
PTHREADINFO ptiT;
/*
* Set the appropriate wakebits for this queue.
*/
ptiT = NULL;
switch (message) {
case WM_SYSCHAR:
case WM_CHAR:
/* Freelance graphics seems to pass in WM_SYSCHARs and WM_CHARs into
* the journal playback hook, so we need to set an input bit for
* this case since that is what win3.1 does. VB2 "learning" demo does
* the same, as does Excel intro.
*
* On win3.1, the WM_CHAR would by default set the QS_MOUSEBUTTON bit.
* On NT, the WM_CHAR sets the QS_KEY bit. This is because
* ScanSysQueue() calls TransferWakeBit() with the QS_KEY bit when
* a WM_CHAR message is passed in. By using the QS_KEY bit on NT,
* we're more compatible with what win3.1 wants to be.
*
* This fixes a case where the mouse was over progman, the WM_CHAR
* would come in via journal playback, wakesomeone would be called,
* and set the mouse bit in progman. Progman would then get into
* ScanSysQueue(), callback the journal playback hook, get the WM_CHAR,
* and do it again, looping. This caught VB2 in a loop.
*/
/* fall through */
case WM_SYSKEYDOWN:
case WM_KEYDOWN:
CancelForegroundActivate();
/* fall through */
case WM_KEYUP:
case WM_SYSKEYUP:
case WM_MOUSEWHEEL:
/*
* Win3.1 first looks at what thread has the active status. This
* means that we don't depend on the thread owning ptiKeyboard
* to wake up and process this key in order to give it to the
* active window, which is potentially newly active. Case in
* point: excel bringing up CBT, cbt has an error, brings up
* a message box: since excel is filtering for CBT messages only,
* ptiKeyboard never gets reassigned to CBT so CBT doesn't get
* any key messages and appears hung.
*/
ptiT = pq->ptiKeyboard;
if (pq->spwndActive != NULL)
ptiT = GETPTI(pq->spwndActive);
SetWakeBit(ptiT, message == WM_MOUSEWHEEL ? QS_MOUSEBUTTON : QS_KEY);
break;
case WM_MOUSEMOVE:
/*
* Make sure we wake up the thread with the capture, if there is
* one. This fixes PC Tools screen capture program, which sets
* capture and then loops trying to remove messages from the
* queue.
*/
if (pq->spwndCapture != NULL)
ptiT = GETPTI(pq->spwndCapture);
else
ptiT = pq->ptiMouse;
SetWakeBit(ptiT, QS_MOUSEMOVE);
break;
default:
/*
* The default case in Win3.1 for this is QS_MOUSEBUTTON.
*/
/* fall through */
case WM_LBUTTONDOWN:
case WM_LBUTTONDBLCLK:
case WM_RBUTTONDOWN:
case WM_RBUTTONDBLCLK:
case WM_MBUTTONDOWN:
case WM_MBUTTONDBLCLK:
CancelForegroundActivate();
/* fall through */
case WM_LBUTTONUP:
case WM_RBUTTONUP:
case WM_MBUTTONUP:
/*
* Make sure we wake up the thread with the capture, if there is
* one. This fixes PC Tools screen capture program, which sets
* capture and then loops trying to remove messages from the
* queue.
*/
if (pq->spwndCapture != NULL &&
message >= WM_MOUSEFIRST && message <= WM_MOUSELAST)
ptiT = GETPTI(pq->spwndCapture);
else
ptiT = pq->ptiMouse;
SetWakeBit(ptiT, QS_MOUSEBUTTON);
break;
}
/*
* If a messaged was passed in, remember in it who we woke up for this
* message. We do this so each message is ownership marked. This way
* we can merge/unmerge message streams when AttachThreadInput() is
* called.
*/
if (ptiT != NULL && pqmsg != NULL) {
pqmsg->pti = ptiT;
UserAssert(!(ptiT->TIF_flags & TIF_INCLEANUP));
}
}
/***************************************************************************\
* InitKeyStateLookaside
*
* Initializes the keystate entry lookaside list. This improves keystate
* entry locality by keeping keystate entries in a single page
*
* 09-09-93 Markl Created.
\***************************************************************************/
NTSTATUS
InitKeyStateLookaside()
{
ULONG BlockSize;
ULONG InitialSegmentSize;
BlockSize = (KEYSTATESIZE + 7) & ~7;
InitialSegmentSize = 8 * BlockSize + sizeof(ZONE_SEGMENT_HEADER);
KeyStateLookasideBase = UserAllocPool(InitialSegmentSize, TAG_LOOKASIDE);
if ( !KeyStateLookasideBase ) {
return STATUS_NO_MEMORY;
}
KeyStateLookasideBounds = (PVOID)((PUCHAR)KeyStateLookasideBase + InitialSegmentSize);
return ExInitializeZone(&KeyStateLookasideZone,
BlockSize,
KeyStateLookasideBase,
InitialSegmentSize);
}
/***************************************************************************\
* AllocKeyState
*
* Allocates a message on a message list. DelKeyState deletes a message
* on a message list.
*
* 10-22-92 ScottLu Created.
\***************************************************************************/
PBYTE AllocKeyState(
VOID)
{
PBYTE pKeyState;
//
// Attempt to get a QMSG from the zone. If this fails, then
// LocalAlloc the QMSG
//
pKeyState = ExAllocateFromZone(&KeyStateLookasideZone);
if ( !pKeyState ) {
/*
* Allocate a Q message structure.
*/
#if DBG
AllocKeyStateSlowCalls++;
#endif // DBG
if ((pKeyState = (PBYTE)UserAllocPool(KEYSTATESIZE, TAG_KBDSTATE)) == NULL)
return NULL;
}
#if DBG
AllocKeyStateCalls++;
if (AllocKeyStateCalls-DelKeyStateCalls > AllocKeyStateHiWater ) {
AllocKeyStateHiWater = AllocKeyStateCalls-DelKeyStateCalls;
}
#endif // DBG
return pKeyState;
}
/***************************************************************************\
* FreeKeyState
*
* Returns a qmsg to the lookaside buffer or free the memory.
*
* 10-26-93 JimA Created.
\***************************************************************************/
void FreeKeyState(
PBYTE pKeyState)
{
#if DBG
DelKeyStateCalls++;
#endif // DBG
//
// If the pKeyState was from zone, then free to zone
//
if ( (PVOID)pKeyState >= KeyStateLookasideBase && (PVOID)pKeyState < KeyStateLookasideBounds ) {
ExFreeToZone(&KeyStateLookasideZone, pKeyState);
} else {
#if DBG
DelKeyStateSlowCalls++;
#endif // DBG
UserFreePool(pKeyState);
}
}
/***************************************************************************\
* PostUpdateKeyStateEvent
*
* This routine posts an event which updates the local thread's keystate
* table. This makes sure the thread's key state is always up-to-date.
*
* An example is: control-esc from cmd to taskman.
* Control goes to cmd, but taskman is activated. Control state is still down
* in cmd - switch back to cmd, start typing, nothing appears because it thinks
* the control state is still down.
*
* As events go into a particular queue (queue A), the async key state table is
* updated. As long as transition events are put into queue A, the key
* state at the logical "end of the queue" is up-to-date with the async key
* state. As soon as the user posts transition events (up/down msgs) into queue
* B, the queue A's end-of-queue key state is out of date with the user. If
* the user then again added messages to queue A, when those messages are read
* the thread specific key state would not be updated correctly unless we
* did some synchronization (which this routine helps to do).
*
* As soon as transition events change queues, we go mark all the other queues
* with the QF_UPDATEKEYSTATE flag. Before any input event is posted into
* a queue, this flag is checked, and if set, this routine is called. This
* routine makes a copy of the async key state, and a copy of the bits
* representing the keys that have changed since the last update (we need to
* keep track of which keys have changed so that any state set by the
* app with SetKeyboardState() doesn't get wiped out). We take this data
* and post a new event of the type QEVENT_UPDATEKEYSTATE, which points to this
* key state and transition information. When this message is read out of the
* queue, this key state copy is copied into the thread specific key state
* table for those keys that have changed, and the copy is deallocated.
*
* This ensures all queues are input-synchronized with key transitions no matter
* where they occur. The side affect of this is that an application may suddenly
* have a key be up without seeing the up message. If this causes any problems
* we may have to generate false transition messages (this could have more nasty
* side affects as well, so it needs to be considered closely before being
* implemented.)
*
* 06-07-91 ScottLu Created.
\***************************************************************************/
void PostUpdateKeyStateEvent(
PQ pq)
{
BYTE *pb;
if (!(pq->QF_flags & QF_UPDATEKEYSTATE))
return;
/*
* Exclude the RIT - it's queue is never read, so don't waste memory
*/
if (pq->ptiKeyboard == gptiRit) {
return;
}
if (pq->mlInput.pqmsgWriteLast != NULL) {
int i;
PQMSG pqmsg = pq->mlInput.pqmsgWriteLast;
DWORD *pdw;
/*
* If the last input message is an UPDATEKEYSTATE event, merge
*/
if (pqmsg->dwQEvent == QEVENT_UPDATEKEYSTATE) {
pb = (PBYTE)(pqmsg->msg.wParam);
pdw = (DWORD *) (pb + CBKEYSTATE);
/*
* Copy in the new key state
*/
RtlCopyMemory(pb, gafAsyncKeyState, CBKEYSTATE);
/*
* Or in the recent key down state (DWORD at a time)
*/
#if (CBKEYSTATERECENTDOWN % 4) != 0
#error "CBKEYSTATERECENTDOWN assumed to be an integral number of DWORDs"
#endif
for (i = 0; i < CBKEYSTATERECENTDOWN / sizeof(*pdw); i++) {
*pdw++ |= ((DWORD *)(pq->afKeyRecentDown))[i];
}
/*
* Set QS_EVENTSET as in PostEventMessage, although this is
* usually, but not always already set
*/
SetWakeBit(pq->ptiKeyboard, QS_EVENTSET);
goto SyncQueue;
}
}
/*
* Make a copy of the async key state buffer, point to it, and add an
* event to the end of the input queue.
*/
if ((pb = AllocKeyState()) == NULL) {
return;
}
RtlCopyMemory(pb, gafAsyncKeyState, CBKEYSTATE);
RtlCopyMemory(pb + CBKEYSTATE, pq->afKeyRecentDown, CBKEYSTATERECENTDOWN);
if (!PostEventMessage(pq->ptiKeyboard, pq, QEVENT_UPDATEKEYSTATE,
NULL, 0 , (DWORD)pb, 0)) {
FreeKeyState(pb);
return;
}
/*
* The key state of the queue is input-synchronized with the user. Erase
* all 'recent down' flags.
*/
SyncQueue:
RtlZeroMemory(pq->afKeyRecentDown, CBKEYSTATERECENTDOWN);
pq->QF_flags &= ~QF_UPDATEKEYSTATE;
}
/***************************************************************************\
* ProcessUpdateKeyStateEvent
*
* This is part two of the above routine, called when the QEVENT_UPDATEKEYSTATE
* message is read out of the input queue.
*
* 06-07-91 ScottLu Created.
\***************************************************************************/
void ProcessUpdateKeyStateEvent(
PQ pq,
PBYTE pbKeyState)
{
int i, j;
BYTE *pbChange;
BYTE *pbRecentDown;
int vk, vkSwap;
pbRecentDown = pbChange = pbKeyState + CBKEYSTATE;
for (i = 0; i < CBKEYSTATERECENTDOWN; i++, pbChange++) {
/*
* Find some keys that have changed.
*/
if (*pbChange == 0)
continue;
/*
* Some keys have changed in this byte. find out which key it is.
*/
for (j = 0; j < 8; j++) {
/*
* Convert our counts to a virtual key index and check to see
* if this key has changed.
*/
vkSwap = vk = (i << 3) + j;
if (!TestKeyRecentDownBit(pbRecentDown, vk))
continue;
/*
* This key has changed. Update it's state in the thread key
* state table.
* Swap the mouse buttons if necessary: The AsyncKeyState contains
* physical (unswapped) mouse VKs, but the queue's synch key state
* needs the swapped mouse VKs (if fSwapButtons)
*/
UserAssert(VK_LBUTTON == 1 && VK_RBUTTON == 2);
if ((vk <= VK_RBUTTON) && (vk >= VK_LBUTTON) && SYSMET(SWAPBUTTON)){
vkSwap ^= 3; // Use XOR to swap buttons
}
if (TestKeyDownBit(pbKeyState, vk)) {
SetKeyStateDown(pq, vkSwap);
} else {
ClearKeyStateDown(pq, vkSwap);
}
if (TestKeyToggleBit(pbKeyState, vk)) {
SetKeyStateToggle(pq, vkSwap);
} else {
ClearKeyStateToggle(pq, vkSwap);
}
}
}
/*
* Update the key cache index.
*/
gpsi->dwKeyCache++;
/*
* All updated. Free the key state table.
*/
FreeKeyState(pbKeyState);
}
/***************************************************************************\
* PostEventMessage
*
*
* History:
* 03-04-91 DavidPe Created.
\***************************************************************************/
BOOL PostEventMessage(
PTHREADINFO pti,
PQ pq,
DWORD dwQEvent,
PWND pwnd,
UINT message,
DWORD wParam,
LONG lParam)
{
PQMSG pqmsgEvent;
CheckCritIn();
if ((pqmsgEvent = AllocQEntry(&pq->mlInput)) == NULL)
return FALSE;
StoreQMessage(pqmsgEvent, pwnd, message, wParam, lParam, dwQEvent, 0);
pqmsgEvent->pti = pti;
/*
* If the thread is in cleanup, then it's possible the queue has
* already been removed for this thread. If this is the case, then
* we should fail to post the event to a dying thread.
*/
if (pti && (pti->TIF_flags & TIF_INCLEANUP))
return FALSE;
/*
* Let this thread know it has an event message to process.
*/
if (pti == NULL) {
UserAssert(pti);
SetWakeBit(pq->ptiMouse, QS_EVENTSET);
SetWakeBit(pq->ptiKeyboard, QS_EVENTSET);
} else {
SetWakeBit(pti, QS_EVENTSET);
}
return TRUE;
}
/***************************************************************************\
* CheckOnTop
*
* Usually windows come to the top and activate all at once. Occasionally, a
* starting app will create a window, pause for awhile, then make itself
* visible. During that pause, if the user clicks down, the window won't be
* allowed to activate (because of our foreground activation model). But this
* still leaves the new window on top of the active window. When this click
* happens, we get here: if this window is active and is not on top, bring it
* to the top.
*
* Case in point: start winquote, click down. The window
* you clicked on is active, but winquote is on top.
*
* This rarely does anything because 99.99% of the time the active
* window is already where it should be - on top. Note that
* CalcForegroundInsertAfter() takes into account owner-based zordering.
*
*
* NOTE: the following was the original function written. However, this
* function has been disabled for now. in WinWord and Excel especially,
* this tends to cause savebits to be blown-away on mouse-activation of
* its dialog-boxes. This could be a problem with GW_HWNDPREV and/or
* CalcForground not being the same, which causes a SetWindowPos to be
* called, resulting in the freeing of the SPB. This also solves a
* problem with the ComboBoxes in MsMoney hiding/freeing the dropdown
* listbox on activation as well.
*
* We need this for ActiveWindowTracking support. xxxBringWindowToTop used to be a call
* to SetWindowPos but now it's gone.
*
* It returns TRUE if the window was brought the top; if no z-order change
* took place, it returns FALSE.
*
* 05-20-93 ScottLu Created.
* 10-17-94 ChrisWil Made into stub-macro.
* 05-30-96 GerardoB Brought it back to live for AWT
\***************************************************************************/
BOOL CheckOnTop(PTHREADINFO pti, PWND pwndTop, UINT message)
{
if (pwndTop != pti->pq->spwndActive)
return FALSE;
switch (message) {
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
if ( TestWF(pwndTop, WEFTOPMOST)
|| (_GetWindow(pwndTop, GW_HWNDPREV) != CalcForegroundInsertAfter(pwndTop))) {
return xxxSetWindowPos(pwndTop, PWND_TOP, 0, 0, 0, 0,
SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
}
break;
}
return FALSE;
}
#define MA_PASSTHRU 0
#define MA_SKIP 1
#define MA_REHITTEST 2
/***************************************************************************\
* xxxActivateWindowTracking
*
* Activates a window without z-ordering it to the top
*
* 06/05/96 GerardoB Created
\***************************************************************************/
int xxxActiveWindowTracking(
PWND pwnd,
UINT uMsg,
int iHitTest)
{
static POINT gptLastTrack = {0, 0};
BOOL fSuccess;
PWND pwndActivate;
TL tlpwndActivate;
int iRet;
Q *pq;
CheckLock(pwnd);
UserAssert(SPI_UP(ACTIVEWINDOWTRACKING));
/*
* Some times we fake mouse moves to let apps know where the
* mouse is or to force them to wake up.
* We don't have a way to detect these so here is my hack
* to ignore them.
* This is to prevent activation from returning to the window
* the mouse is on after a window has been activated using
* the keyboard (alt-tab or ctrl-esc-run; xxxCapture calls
* SetFMouseMoved).
* LATER: GerardoB
* Note that as soon as you move the mouse, then activation
* will change anyway. In the future, we could add a
* snap-mouse-to-foreground-queue (or active window) option
* so we'll move the mouse into the window activated by the
* keyboard.
*/
if ((ptCursor.x == gptLastTrack.x) && (ptCursor.y == gptLastTrack.y)) {
return MA_PASSTHRU;
}
gptLastTrack = ptCursor;
/*
* If someone is in menu mode, bail
*/
if (IsSomeOneInMenuMode()) {
return MA_PASSTHRU;
}
pwndActivate = pwnd;
/*
* Find the top parent
*/
while (TestwndChild(pwndActivate)) {
pwndActivate = pwndActivate->spwndParent;
}
/*
* If disabled, get a enabled popup owned by it.
* This test is the same as calling FBadWindow (05/31/96)
* because we already know it's not NULL and it must
* be visible (the mouse is on it)
*/
UserAssert(TestWF(pwndActivate, WFVISIBLE));
if (TestWF(pwndActivate, WFDISABLED)) {
/*
* This is what we do elsewhere when someone clicks on a
* disabled non-active window. It might be cheaper to check
* pwnd->spwndLastActive first (we might need to walk up
* the owner chain though, as this is where we set spwndLastAcitve
* when activating a new window. see xxxActivateThisWindow).
* But let's do the same here; this should be fixed/improved
* in DWP_GetEnabledPopup anyway. There might be a reason
* why we don't grab spwndLastActive if OK.... perhaps it has
* something to do with nested owner windows
*/
pwndActivate = DWP_GetEnabledPopup(pwndActivate);
}
/*
* Bail if we didn't find one
*/
if (pwndActivate == NULL) {
return MA_PASSTHRU;
}
/*
* If already active in the foreground queue, nothing to do
*/
pq = GETPTI(pwndActivate)->pq;
if ((pq == gpqForeground) && (pwndActivate == pq->spwndActive)) {
return MA_PASSTHRU;
}
/*
* Don't activate the desktop
*/
if (pwndActivate == PWNDDESKTOP(pwndActivate)) {
return MA_PASSTHRU;
}
/*
* We're going to callback, so lock if needed
*/
if (pwnd != pwndActivate) {
ThreadLockAlways(pwndActivate, &tlpwndActivate);
}
/*
* Let's ask if it's OK to do this
*/
iRet = (int)xxxSendMessage(pwnd, WM_MOUSEACTIVATE,
(DWORD)(HW(pwndActivate)), MAKELONG((SHORT)iHitTest, uMsg));
switch (iRet) {
case MA_ACTIVATE:
case MA_ACTIVATEANDEAT:
/*
* Activate the window without bring it to the top.
* LATER: GerardoB
* What about an option that would do the window tracking
* bringing the window to the top? As easy as not passing
* the *_NOZORDER flag (I guess...)
*/
if (pq == gpqForeground) {
fSuccess = xxxActivateThisWindow(pwndActivate, 0, ATW_NOZORDER);
} else {
fSuccess = xxxSetForegroundWindow2(pwndActivate, NULL, SFW_SWITCH | SFW_NOZORDER);
}
/*
* Eat the message if activation failed.
*/
if (!fSuccess) {
iRet = MA_SKIP;
} else if (iRet == MA_ACTIVATEANDEAT) {
iRet = MA_SKIP;
}
break;
case MA_NOACTIVATEANDEAT:
iRet = MA_SKIP;
break;
case MA_NOACTIVATE:
default:
iRet = MA_PASSTHRU;
break;
}
if (pwnd != pwndActivate) {
ThreadUnlock(&tlpwndActivate);
}
return iRet;
}
/***************************************************************************\
* xxxMouseActivate
*
* This is where activation due to mouse clicks occurs.
*
* IMPLEMENTATION:
* The message is sent to the specified window. In xxxDefWindowProc, the
* message is sent to the window's parent. The receiving window may
* a) process the message,
* b) skip the message totally, or
* c) re-hit test message
*
* A WM_SETCURSOR message is also sent through the system to set the cursor.
*
* History:
* 11-22-90 DavidPe Ported.
\***************************************************************************/
int xxxMouseActivate(
PTHREADINFO pti,
PWND pwnd,
UINT message,
LPPOINT lppt,
int ht)
{
UINT x, y;
PWND pwndTop;
int result;
TL tlpwndTop;
BOOL fSend;
CheckLock(pwnd);
/*
* No mouse activation if the mouse is captured. Must check for the capture
* ONLY here. 123W depends on it - create a graph, select Rearrange..
* flip horizontal, click outside the graph. If this code checks for
* anything beside just capture, 123w will get the below messages and
* get confused.
*/
if (pti->pq->spwndCapture != NULL) {
return MA_PASSTHRU;
}
result = MA_PASSTHRU;
pwndTop = pwnd;
ThreadLockWithPti(pti, pwndTop, &tlpwndTop);
/*
* B#1404
* Don't send WM_PARENTNOTIFY messages if the child has
* WS_EX_NOPARENTNOTIFY style.
*
* Unfortunately, this breaks people who create controls in
* MDI children, like WinMail. They don't get WM_PARENTNOTIFY
* messages, which don't get passed to DefMDIChildProc(), which
* then can't update the active MDI child. Grrr.
*/
fSend = (!TestWF(pwnd, WFWIN40COMPAT) || !TestWF(pwnd, WEFNOPARENTNOTIFY));
/*
* If it's a buttondown event, send WM_PARENTNOTIFY.
*/
switch (message) {
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
while (TestwndChild(pwndTop)) {
pwndTop = pwndTop->spwndParent;
if (fSend) {
ThreadUnlock(&tlpwndTop);
ThreadLockWithPti(pti, pwndTop, &tlpwndTop);
x = (UINT)(lppt->x - pwndTop->rcClient.left);
y = (UINT)(lppt->y - pwndTop->rcClient.top);
xxxSendMessage(pwndTop, WM_PARENTNOTIFY, message, MAKELONG(x, y));
}
}
if (!fSend) {
ThreadUnlock(&tlpwndTop);
ThreadLockAlwaysWithPti(pti, pwndTop, &tlpwndTop);
}
/*
* NOTE: We break out of this loop with pwndTop locked.
*/
break;
}
/*
* The mouse was moved onto this window: make it foreground
*/
if (SPI_UP(ACTIVEWINDOWTRACKING) && (message == WM_MOUSEMOVE)) {
result = xxxActiveWindowTracking(pwnd, WM_MOUSEMOVE, ht);
}
/*
* Are we hitting an inactive top-level window WHICH ISN'T THE DESKTOP(!)?
*
* craigc 7-14-89 hitting either inactive top level or any child window,
* to be compatible with 2.X. Apps apparently needs this message.
*/
else if ((pti->pq->spwndActive != pwnd || pti->pq->QF_flags & QF_EVENTDEACTIVATEREMOVED) &&
(pwndTop != PWNDDESKTOP(pwndTop))) {
switch (message) {
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
/*
* Send the MOUSEACTIVATE message.
*/
result = (int)xxxSendMessage(pwnd, WM_MOUSEACTIVATE,
(DWORD)(HW(pwndTop)), MAKELONG((SHORT)ht, message));
switch (result) {
case 0:
case MA_ACTIVATE:
case MA_ACTIVATEANDEAT:
/*
* If activation fails, swallow the message.
*/
if ((pwndTop != pti->pq->spwndActive ||
pti->pq->QF_flags & QF_EVENTDEACTIVATEREMOVED) &&
!xxxActivateWindow(pwndTop,
(UINT)((pti->pq->codeCapture == NO_CAP_CLIENT) ?
AW_TRY2 : AW_TRY))) {
result = MA_SKIP;
} else if (TestWF(pwndTop, WFDISABLED)) {
#ifdef NEVER
/*
* Althoug this is what win3 does, it is braindead: it
* can easily cause infinite loops. Returning "rehittest"
* means process this event over again - nothing causes
* anything different to happen, and we get an infinite
* loop. This case never gets executed on win3 because if
* the window is disabled, it got the HTERROR hittest
* code. This can only be done on Win32 where input is
* assigned to a window BEFORE process occurs to pull
* it out of the queue.
*/
result = MA_REHITTEST;
#endif
/*
* Someone clicked on a window before it was disabled...
* Since it is disabled now, don't send this message to
* it: instead eat it.
*/
result = MA_SKIP;
} else if (result == MA_ACTIVATEANDEAT) {
result = MA_SKIP;
} else {
result = MA_PASSTHRU;
goto ItsActiveJustCheckOnTop;
}
break;
case MA_NOACTIVATEANDEAT:
result = MA_SKIP;
break;
}
}
} else {
ItsActiveJustCheckOnTop:
/*
* Make sure this active window is on top (see comment
* in CheckOnTop).
*/
if (SPI_UP(ACTIVEWINDOWTRACKING)) {
if (CheckOnTop(pti, pwndTop, message)) {
/*
* The window was z-ordered to the top.
* If it is a console window, skip the message
* so it won't go into "selecting" mode
* Hard error boxes are created by csrss as well
* If we have topmost console windows someday, this
* will need to change
*/
if ((GETPTI(pwndTop)->TIF_flags & TIF_CSRSSTHREAD)
&& !(TestWF(pwndTop, WEFTOPMOST))) {
RIPMSG2(RIP_WARNING, "xxxMouseActivate: Skipping msg %#lx for pwnd %#lx",
message, pwndTop);
result = MA_SKIP;
}
}
} /* if (gbActiveWindowTracking) */
}
/*
* Now set the cursor shape.
*/
if (pti->pq->spwndCapture == NULL) {
xxxSendMessage(pwnd, WM_SETCURSOR, (DWORD)HW(pwnd),
MAKELONG((SHORT)ht, message));
}
ThreadUnlock(&tlpwndTop);
return result;
}
/***************************************************************************\
* xxxMouseHoverTimer()
*
* Timer callback for WM_MOUSEHOVER/WM_NCMOUSEHOVER generation.
*
* 11/03/95 francish created.
\***************************************************************************/
LONG xxxMouseHoverTimer(
PWND pwnd,
UINT msg,
DWORD id,
LONG lParam)
{
POINT pt;
int part;
UINT wParam;
HWND hwnd;
PWND pwndDesktop = _GetDesktopWindow();
PQ pq = PtiCurrent()->pq;
if (pq->QF_flags & (QF_TRACKMOUSEHOVER | QF_TRACKMOUSEFIRING) != QF_TRACKMOUSEHOVER) {
return 0;
}
if (pq->spwndLastMouseMessage != pwnd) {
return 0;
}
/*
* remember that we're processing the timer
*/
pq->QF_flags |= QF_TRACKMOUSEFIRING;
/*
* hit test the current mouse position to verify our hover.
*/
pt = FJOURNALPLAYBACK() ? GETPTI(pwnd)->ptLast : ptCursor;
part = HTCLIENT; // Assume we have a capture.
pwnd = pq->spwndCapture;
if (!pwnd) { // If not, do the hit-testing...
// YIELD!
hwnd = xxxWindowHitTest(pwndDesktop, pt, &part, TRUE);
if (!hwnd || !(pwnd = ValidateHwnd(hwnd)))
pwnd = pwndDesktop;
}
/*
* get out if somebody else has taken over hovering.
*/
if (!(pq->QF_flags & QF_TRACKMOUSEFIRING)) {
return 0;
}
pq->QF_flags &= ~QF_TRACKMOUSEFIRING;
/*
* this must be us, make sure we are still over the right thing.
*/
if ((pwnd != pq->spwndLastMouseMessage) || (part != HTCLIENT)) {
goto cancel;
}
/*
* set up to check the tolerance
*/
wParam = GetMouseKeyFlags(pq);
if (pq->codeCapture != SCREEN_CAPTURE) {
pt.x -= pwnd->rcClient.left;
pt.y -= pwnd->rcClient.top;
}
if (!PtInRect(&pq->rcMouseHover, pt))
return 0;
_PostMessage(pwnd, WM_MOUSEHOVER, wParam, MAKELPARAM(pt.x, pt.y));
cancel:
CancelMouseHover(pq);
}
/***************************************************************************\
* ResetMouseHover()
*
* Resets mouse hover state information.
*
* 11/03/95 francish created.
\***************************************************************************/
void ResetMouseHover(
POINT pt)
{
PQ pq = PtiCurrent()->pq;
/*
* make sure we have something to do before we go and try to do it.
*/
if (!(pq->QF_flags & QF_TRACKMOUSEHOVER))
return;
if (pq->spwndLastMouseMessage == NULL) {
ResetMouseTracking(pq, NULL);
return;
}
/*
* set/reset the timer.
*/
_SetSystemTimer(pq->spwndLastMouseMessage, IDSYS_MOUSEHOVER,
pq->dwMouseHoverTime, xxxMouseHoverTimer);
/*
* update the tolerance rectangle for the hover window.
*/
SetRect(&pq->rcMouseHover,
pt.x - gcxMouseHover / 2,
pt.y - gcyMouseHover / 2,
pt.x + gcxMouseHover / 2,
pt.y + gcyMouseHover / 2);
}
/***************************************************************************\
* CancelMouseHover()
*
* Cancels mouse hover tracking.
*
* 11/03/95 francish created.
\***************************************************************************/
void CancelMouseHover(PQ pq)
{
if (pq->QF_flags & QF_TRACKMOUSEHOVER) {
if (pq->spwndLastMouseMessage != NULL)
_KillSystemTimer(pq->spwndLastMouseMessage, IDSYS_MOUSEHOVER);
pq->QF_flags &= ~(QF_TRACKMOUSEHOVER | QF_TRACKMOUSEFIRING);
}
}
/***************************************************************************\
* MouseEnter()
*
* Called when xxxScanSysQueue sees a window being entered or left.
*
* 11/03/95 francish created.
\***************************************************************************/
void MouseEnter(
PQ pq,
PWND pwnd)
{
/*
* make sure something has changed...
*/
if (pwnd == pq->spwndLastMouseMessage) {
return;
}
/*
* we know the mouse left the old window so cancel its stuff
*/
CancelMouseHover(pq);
if (pq->QF_flags & QF_TRACKMOUSELEAVE) {
_PostMessage(pq->spwndLastMouseMessage, WM_MOUSELEAVE, 0, 0);
pq->QF_flags &= ~QF_TRACKMOUSELEAVE;
}
/*
* save the new tracking window
*/
Lock(&pq->spwndLastMouseMessage, pwnd);
}
/***************************************************************************\
* ResetMouseTracking()
*
* Resets the mouse tracking for the specified window or Q...
*
* 11/03/95 francish created.
\***************************************************************************/
void ResetMouseTracking(
PQ pq,
PWND pwnd)
{
if ((pwnd != NULL) && (pwnd != pq->spwndLastMouseMessage))
return;
MouseEnter(pq, NULL);
}
/***************************************************************************\
* QueryTrackMouseEvent()
*
* Fills in a TRACKMOUSEEVENT structure describing current tracking state.
*
* 11/03/95 francish created.
\***************************************************************************/
BOOL QueryTrackMouseEvent(
LPTRACKMOUSEEVENT lpTME)
{
PQ pq = PtiCurrent()->pq;
/*
* initialize the struct
*/
RtlZeroMemory(lpTME, sizeof(*lpTME));
lpTME->cbSize = sizeof(*lpTME);
/*
* if there isn't anything being tracked get out
*/
if (!(pq->QF_flags & (QF_TRACKMOUSELEAVE | QF_TRACKMOUSEHOVER)) || !pq->spwndLastMouseMessage)
return TRUE;
/*
* fill in the requested information
*/
if (pq->QF_flags & QF_TRACKMOUSELEAVE)
lpTME->dwFlags |= TME_LEAVE;
if (pq->QF_flags & QF_TRACKMOUSEHOVER) {
lpTME->dwFlags |= TME_HOVER;
lpTME->dwHoverTime = pq->dwMouseHoverTime;
}
lpTME->hwndTrack = HWq(pq->spwndLastMouseMessage);
return TRUE;
}
/***************************************************************************\
* TrackMouseEvent()
*
* API for requesting extended mouse notifications (hover, leave, ...)
*
* 11/03/95 francish created.
\***************************************************************************/
BOOL TrackMouseEvent(
LPTRACKMOUSEEVENT lpTME)
{
PWND pwnd;
UINT dwFlags;
BOOL fValidRequest;
PQ pq = PtiCurrent()->pq;
/*
* make sure we have a valid window
*/
if ((pwnd = ValidateHwnd(lpTME->hwndTrack)) == NULL)
return FALSE;
/*
* are we going to give them what they asked for?
*/
dwFlags = lpTME->dwFlags;
fValidRequest = (pwnd == pq->spwndLastMouseMessage);
/*
* does the caller want hover tracking?
*/
if (fValidRequest && (dwFlags & TME_HOVER)) {
if (!(dwFlags & TME_CANCEL)) {
POINT pt;
pq->QF_flags |= QF_TRACKMOUSEHOVER;
pq->QF_flags &= ~QF_TRACKMOUSEFIRING;
pq->dwMouseHoverTime = lpTME->dwHoverTime;
if (!pq->dwMouseHoverTime || (pq->dwMouseHoverTime == HOVER_DEFAULT))
pq->dwMouseHoverTime = gdtMouseHover;
pt = GETPTI(pwnd)->ptLast;
if (pq->codeCapture != SCREEN_CAPTURE) {
pt.x -= pwnd->rcClient.left;
pt.y -= pwnd->rcClient.top;
}
ResetMouseHover(pt);
} else {
CancelMouseHover(pq);
}
}
/*
* does the caller want leave tracking?
*/
if (dwFlags & TME_LEAVE) {
if (!(dwFlags & TME_CANCEL)) {
if (fValidRequest) {
pq->QF_flags |= QF_TRACKMOUSELEAVE;
} else {
_PostMessage(pwnd, WM_MOUSELEAVE, 0, 0);
}
} else if (fValidRequest) {
pq->QF_flags &= ~QF_TRACKMOUSELEAVE;
}
}
return TRUE;
}
/***************************************************************************\
* xxxGetNextSysMsg
*
* Returns the queue pointer of the next system message or
* NULL - no more messages (may be a journal playback delay)
* PQMSG_PLAYBACK - got a journal playback message
* (Anything else is a real pointer)
*
* 10-23-92 ScottLu Created.
\***************************************************************************/
PQMSG xxxGetNextSysMsg(
PTHREADINFO pti,
PQMSG pqmsgPrev,
PQMSG pqmsg)
{
DWORD dt;
PMLIST pml;
PQMSG pqmsgT;
/*
* If there is a journal playback hook, call it to get the next message.
*/
if (pti->pDeskInfo->asphkStart[WH_JOURNALPLAYBACK + 1] != NULL &&
pti->rpdesk == grpdeskRitInput) {
/*
* We can't search through journal messages: we only get the current
* journal message. So if the caller has already called us once
* before, then exit with no messages.
*/
if (pqmsgPrev != 0)
return NULL;
/*
* Tell the journal playback hook that we're done
* with this message now.
*/
dt = xxxCallJournalPlaybackHook(pqmsg);
if (dt == 0xFFFFFFFF)
return NULL;
/*
* If dt == 0, then we don't need to wait: set the right wake
* bits and return this message.
*/
if (dt == 0) {
WakeSomeone(pti->pq, pqmsg->msg.message, NULL);
return PQMSG_PLAYBACK;
} else {
/*
* There is logically no more input in the "queue", so clear the
* bits so that we will sleep when GetMessage is called.
*/
pti->pcti->fsWakeBits &= ~QS_INPUT;
pti->pcti->fsChangeBits &= ~QS_INPUT;
/*
* Need to wait before processing this next message. Set
* a journal timer.
*/
SetJournalTimer(dt, pqmsg->msg.message);
return NULL;
}
}
/*
* No journalling going on... return next message in system queue.
*/
/*
* Queue up a mouse move if the mouse has moved.
*/
if (pti->pq->QF_flags & QF_MOUSEMOVED) {
PostMove(pti->pq);
}
/*
* If no messages in the input queue, return with 0.
*/
pml = &pti->pq->mlInput;
if (pml->cMsgs == 0)
return NULL;
/*
* If this is the first call to xxxGetNextSysMsg(), return the
* first message.
*/
if (pqmsgPrev == NULL || pti->pq->idSysPeek <= (DWORD)PQMSG_PLAYBACK) {
pqmsgT = pml->pqmsgRead;
} else {
/*
* Otherwise return the next message in the queue. Index with
* idSysPeek, because that is updated by recursive calls through
* this code.
*/
pqmsgT = ((PQMSG)(pti->pq->idSysPeek))->pqmsgNext;
}
/*
* Fill in the structure passed, and return the pointer to the
* current message in the message list. This will become the new
* pq->idSysPeek.
*/
if (pqmsgT != NULL)
*pqmsg = *pqmsgT;
return pqmsgT;
}
/***************************************************************************\
* UpdateKeyState
*
* Updates queue key state tables.
*
* 11-11-92 ScottLu Created.
\***************************************************************************/
void UpdateKeyState(
PQ pq,
UINT vk,
BOOL fDown)
{
if (vk != 0) {
/*
* If we're going down, toggle only if the key isn't
* already down.
*/
if (fDown && !TestKeyStateDown(pq, vk)) {
if (TestKeyStateToggle(pq, vk)) {
ClearKeyStateToggle(pq, vk);
} else {
SetKeyStateToggle(pq, vk);
}
}
/*
* Now set/clear the key down state.
*/
if (fDown) {
SetKeyStateDown(pq, vk);
} else {
ClearKeyStateDown(pq, vk);
}
/*
* If this is one of the keys we cache, update the key cache index.
*/
if (vk < CVKKEYCACHE) {
gpsi->dwKeyCache++;
}
}
}
/***************************************************************************\
* EqualMsg
*
* This routine is called in case that idSysPeek points to a message
* and we are trying to remove a different message
*
* 04-25-96 CLupu Created.
\***************************************************************************/
BOOL EqualMsg(PQMSG pqmsg1, PQMSG pqmsg2)
{
if (pqmsg1->msg.hwnd != pqmsg2->msg.hwnd ||
pqmsg1->msg.message != pqmsg2->msg.message)
return FALSE;
/*
* This might be a coalesced WM_MOUSEMOVE
*/
if (pqmsg1->msg.message == WM_MOUSEMOVE)
return TRUE;
if (pqmsg1->pti != pqmsg2->pti ||
pqmsg1->msg.time != pqmsg2->msg.time)
return FALSE;
return TRUE;
}
/***************************************************************************\
* xxxSkipSysMsg
*
* This routine "skips" an input message: either by calling the journal
* hooks if we're journalling or by "skipping" the message in the input
* queue. Internal keystate tables are updated as well.
*
* 10-23-92 ScottLu Created.
\***************************************************************************/
void xxxSkipSysMsg(
PTHREADINFO pti,
PQMSG pqmsg)
{
PQMSG pqmsgT;
BOOL fDown;
BYTE vk;
/*
* If idSysPeek is 0, then the pqmsg that we were looking at has been
* deleted, probably because of a callout from ScanSysQueue, and that
* callout then called PeekMessage(fRemove == TRUE), and then returned.
*/
if (pti->pq->idSysPeek == 0)
return;
if (pti->pDeskInfo->asphkStart[WH_JOURNALPLAYBACK + 1] != NULL) {
/*
* Tell the journal playback hook that we're done
* with this message now.
*/
PHOOK phk = PhkFirst(PtiCurrent(), WH_JOURNALPLAYBACK);
UserAssert(phk);
phk->flags |= HF_NEEDHC_SKIP;
} else {
if (pti->pDeskInfo->asphkStart[WH_JOURNALRECORD + 1] != NULL) {
/*
* We've processed a new message: tell the journal record
* hook what the message is.
*/
xxxCallJournalRecordHook(pqmsg);
}
/*
* If idSysPeek is 0 now, it means we've been recursed into yet
* again. This would confuse a journalling app, but it would confuse
* us more because we'd fault. Return if idSysPeek is 0.
*/
if ((pqmsgT = (PQMSG)pti->pq->idSysPeek) == NULL)
return;
/*
* Delete this message from the input queue. Make sure pqmsgT isn't
* 1: this could happen if an app unhooked a journal record hook
* during a callback from xxxScanSysQueue.
*/
if (pqmsgT != PQMSG_PLAYBACK) {
/*
* There are cases when idSysPeek points to a different message
* than the one we are trying to remove. This can happen if
* two threads enters in xxxScanSysQueue, sets the idSysPeek and
* after this their queues got redistributed. The first thread
* will have the idSysPeek preserved but the second one has to
* search the queue for its message. - ask CLupu
*/
if (!EqualMsg(pqmsgT, pqmsg)) {
PQMSG pqmsgS;
#ifdef TRACESYSPEEK
gnSysPeekSearch++;
#endif
#ifdef DEBUG
KdPrint((
"\nDifferent message than idSysPeek\n\n"
"pqmsg = %08lx idSysPeek = %08lx\n"
"pti = %08lx pti = %08lx\n"
"msg = %08lx msg = %08lx\n"
"hwnd = %08lx hwnd = %08lx\n"
"wParam = %08lx wParam = %08lx\n"
"lParam = %08lx lParam = %08lx\n"
"time = %08lx time = %08lx\n"
"Extra = %08lx Extra = %08lx\n"
"\npqmsgT = %08lx\n\n",
pqmsg, pqmsgT,
pqmsg->pti, pqmsgT->pti,
pqmsg->msg.message, pqmsgT->msg.message,
pqmsg->msg.hwnd, pqmsgT->msg.hwnd,
pqmsg->msg.wParam, pqmsgT->msg.wParam,
pqmsg->msg.lParam, pqmsgT->msg.lParam,
pqmsg->msg.time, pqmsgT->msg.time,
pqmsg->ExtraInfo, pqmsgT->ExtraInfo,
pqmsgT));
#endif
/*
* Begin to search for this message
*/
pqmsgS = pti->pq->mlInput.pqmsgRead;
while (pqmsgS != NULL) {
if (EqualMsg(pqmsgS, pqmsg)) {
#ifdef DEBUG
KdPrint(("Deleting pqmsg %lx, pti %lx\n",
pqmsgS, pqmsgS->pti));
KdPrint(("m %04lx, w %lx, l %lx, t %lx\n",
pqmsgS->msg.message, pqmsgS->msg.hwnd,
pqmsgS->msg.lParam, pqmsgS->msg.time));
#endif
pqmsgT = pqmsgS;
break;
}
pqmsgS = pqmsgS->pqmsgNext;
}
if (pqmsgS == NULL) {
#ifdef DEBUG
KdPrint(("Didn't find a matching message. No message removed\n"));
#endif
return;
}
}
if (pqmsgT == (PQMSG)pti->pq->idSysPeek) {
/*
* We'll remove this message from the input queue
* so set idSysPeek to 0.
*/
CheckPtiSysPeek(1, pti->pq, 0);
pti->pq->idSysPeek = 0;
}
DelQEntry(&pti->pq->mlInput, pqmsgT);
}
}
fDown = TRUE;
vk = 0;
switch (pqmsg->msg.message) {
case WM_MOUSEMOVE:
case WM_QUEUESYNC:
default:
/*
* No state change.
*/
break;
case WM_KEYUP:
case WM_SYSKEYUP:
fDown = FALSE;
/*
* Fall through.
*/
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
vk = LOBYTE(LOWORD(pqmsg->msg.wParam));
break;
case WM_LBUTTONUP:
fDown = FALSE;
/*
* Fall through.
*/
case WM_LBUTTONDOWN:
vk = VK_LBUTTON;
break;
case WM_RBUTTONUP:
fDown = FALSE;
/*
* Fall through.
*/
case WM_RBUTTONDOWN:
vk = VK_RBUTTON;
break;
case WM_MBUTTONUP:
fDown = FALSE;
/*
* Fall through.
*/
case WM_MBUTTONDOWN:
vk = VK_MBUTTON;
break;
}
/*
* Set toggle and down bits appropriately.
*/
if ((vk == VK_SHIFT) || (vk == VK_MENU) || (vk == VK_CONTROL)) {
BYTE vkHanded, vkOtherHand;
/*
* Convert this virtual key into a differentiated (Left/Right) key
* depending on the extended key bit.
*/
vkHanded = (vk - VK_SHIFT) * 2 + VK_LSHIFT +
((pqmsg->msg.lParam & EXTENDED_BIT) ? 1 : 0);
vkOtherHand = vkHanded ^ 1;
if (vk == VK_SHIFT) {
/*
* Clear extended bit for r.h. Shift, since it isn't really
* extended (bit was set to indicate right-handed)
*/
pqmsg->msg.lParam &= ~EXTENDED_BIT;
}
/*
* Update the key state for the differentiated (Left/Right) key.
*/
UpdateKeyState(pti->pq, vkHanded, fDown);
/*
* Update key state for the undifferentiated (logical) key.
*/
if (fDown || !TestKeyStateDown(pti->pq, vkOtherHand)) {
UpdateKeyState(pti->pq, vk, fDown);
}
} else {
UpdateKeyState(pti->pq, vk, fDown);
}
}
/***************************************************************************\
* CloseScrennSaverWindow
*
*
* 01-04-96 CLupu Created.
\***************************************************************************/
BOOL CloseScrennSaverWindow(
PTHREADINFO pti)
{
PWND pwndDesktop, pwndChild;
PDESKTOPINFO pdi;
pdi = pti->pDeskInfo;
if (!pdi)
return FALSE;
pwndDesktop = pdi->spwnd;
if (!pwndDesktop)
return FALSE;
pwndChild = pwndDesktop->spwndChild;
while (pwndChild) {
if (pwndChild->head.pti == pti) {
/*
* pwndChild is now the screen saver's main window
*/
_PostMessage(pwndChild, WM_CLOSE, 0, 0);
return TRUE;
}
pwndChild = pwndChild->spwndNext;
}
return FALSE;
}
/***************************************************************************\
* ScreenSaverCheck
*
* Check to see if the screen saver should be set back to normal priority
* so it can process the mouse/key input and go away.
*
* 03-29-93 ScottLu Created.
\***************************************************************************/
#define THRESHOLD 3
void ScreenSaverCheck(
PTHREADINFO pti)
{
UINT wWakeBit;
extern POINT gptSSCursor;
/*
* We've hit a bug where the screen blanker (a TOPMOST window) doesn't
* ever go away if a background process is using all available CPU cycles.
* This is because the screen-saver's priority is lowered upon creation
* (so it doesn't slow down background operations) and it never gets a
* chance to look for input. The fix is to raise its priority back to
* normal when it receives input plus give it the highest boost to make
* sure it wakes up.
*/
wWakeBit = pti->pcti->fsWakeBits;
if ((pti->TIF_flags & TIF_SCREENSAVER) && (wWakeBit & (QS_KEY | QS_MOUSE))) {
/*
* If this is a mouse-move event, make sure the mouse has
* actually moved and this isn't some SetFMouseMoved()
* generated by the window manager.
*/
if (!(wWakeBit & QS_MOUSEMOVE) ||
((abs(ptCursor.x - gptSSCursor.x) > THRESHOLD) ||
(abs(ptCursor.y - gptSSCursor.y) > THRESHOLD))) {
/*
* This will set this processes priority to the foreground
* priority saved in the EPROCESS structure. In the case
* of the screen saver, this priority will be the one it
* started with, which will be foreground of normal. This
* will give it enough juice to exit ok.
*/
pti->TIF_flags &= ~TIF_SCREENSAVER;
SetForegroundPriority(pti, TRUE);
if (!CloseScrennSaverWindow( pti )) {
/*
* Sometimes the screen saver app has not created a window yet,
* and will not receive this mouse / key input. That is why
* we ensure that it quits.
*/
pti->cQuit = 1;
pti->pcti->fsWakeBits |= (QS_POSTMESSAGE | QS_ALLPOSTMESSAGE);
pti->pcti->fsChangeBits |= (QS_POSTMESSAGE | QS_ALLPOSTMESSAGE);
}
}
}
}
#if DBG
/***************************************************************************\
* LogPlayback
*
*
* History:
* 02-13-95 JimA Created.
\***************************************************************************/
void LogPlayback(
PWND pwnd,
PMSG lpmsg)
{
static PWND pwndM = NULL, pwndK = NULL;
LPCSTR lpszMsg;
CHAR achBuf[20];
if ((lpmsg->message >= WM_MOUSEFIRST) && (lpmsg->message <= WM_MOUSELAST)) {
lpszMsg = aszMouse[lpmsg->message - WM_MOUSEFIRST];
if (pwnd != pwndM) {
DbgPrint("*** Mouse input to window \"%ws\" of class \"%s\"\n",
pwnd->strName.Length ? pwnd->strName.Buffer : L"",
pwnd->pcls->lpszAnsiClassName);
pwndM = pwnd;
}
} else if ((lpmsg->message >= WM_KEYFIRST) && (lpmsg->message <= WM_KEYLAST)) {
lpszMsg = aszKey[lpmsg->message - WM_KEYFIRST];
if (pwnd != pwndK) {
DbgPrint("*** Kbd input to window \"%ws\" of class \"%s\"\n",
pwnd->strName.Length ? pwnd->strName.Buffer : L"",
pwnd->pcls->lpszAnsiClassName);
pwndK = pwnd;
}
} else if (lpmsg->message == WM_QUEUESYNC) {
lpszMsg = "WM_QUEUESYNC";
} else {
wsprintfA(achBuf, "0x%4x", lpmsg->message);
lpszMsg = achBuf;
}
DbgPrint("msg = %s, wP = %x, lP = %x\n", lpszMsg,
lpmsg->wParam, lpmsg->lParam);
}
#endif // DBG
/***************************************************************************\
*
* GetMouseKeyFlags()
*
* Computes MOST of the MK_ flags given a Q.
* Does not compute MK_MOUSEENTER.
*
\***************************************************************************/
UINT GetMouseKeyFlags(
PQ pq)
{
UINT wParam = 0;
if (TestKeyStateDown(pq, VK_LBUTTON))
wParam |= MK_LBUTTON;
if (TestKeyStateDown(pq, VK_RBUTTON))
wParam |= MK_RBUTTON;
if (TestKeyStateDown(pq, VK_MBUTTON))
wParam |= MK_MBUTTON;
if (TestKeyStateDown(pq, VK_SHIFT))
wParam |= MK_SHIFT;
if (TestKeyStateDown(pq, VK_CONTROL))
wParam |= MK_CONTROL;
return wParam;
}
/***************************************************************************\
* xxxScanSysQueue
*
* This routine looks at the hardware message, determines what
* window it will be in, determines what the input message will
* be, and then checks the destination window against hwndFilter,
* and the input message against msgMinFilter and msgMaxFilter.
*
* It also updates various input synchronized states like keystate info.
*
* This is almost verbatim from Win3.1.
*
* 10-20-92 ScottLu Created.
\***************************************************************************/
BOOL xxxScanSysQueue(
PTHREADINFO ptiCurrent,
LPMSG lpMsg,
PWND pwndFilter,
UINT msgMinFilter,
UINT msgMaxFilter,
DWORD flags,
DWORD fsReason)
{
QMSG qmsg;
HWND hwnd;
PWND pwnd;
UINT message;
UINT wParam;
LONG lParam;
PTHREADINFO ptiKeyWake, ptiMouseWake, ptiEventWake;
POINT pt;
UINT codeMouseDown;
BOOL fMouseHookCalled;
BOOL fKbdHookCalled;
BOOL fOtherApp;
int part;
MOUSEHOOKSTRUCT mhs;
PWND pwndT;
BOOL fPrevDown;
BOOL fDown;
BOOL fAlt;
UINT key;
TL tlpwnd;
TL tlpwndT;
BOOL fRemove = (flags & PM_REMOVE);
PWND pwndMouse;
#ifdef FE_IME
DWORD dwImmRet = 0;
#endif
#ifdef MARKPATH
DWORD pathTaken = 0;
DWORD pathTaken2 = 0;
DWORD pathTaken3 = 0;
#define PATHTAKEN(x) pathTaken |= x
#define PATHTAKEN2(x) pathTaken2 |= x
#define PATHTAKEN3(x) pathTaken3 |= x
#define DUMPPATHTAKEN() if (gfMarkPath) DbgPrint("xxxScanSysQueue path:%08x %08x %08x\n", pathTaken, pathTaken2, pathTaken3)
#define DUMPSUBPATHTAKEN(p, x) if (gfMarkPath && p & x) { DbgPrint(" %08x %08x %08x\n", pathTaken, pathTaken2, pathTaken3); pathTaken = pathTaken2 = pathTaken3 = 0; }
#else
#define PATHTAKEN(x)
#define PATHTAKEN2(x)
#define PATHTAKEN3(x)
#define DUMPPATHTAKEN()
#define DUMPSUBPATHTAKEN(p, x)
#endif
UserAssert((fsReason & ~(QS_EVENT | QS_INPUT)) == 0 &&
(fsReason & (QS_EVENT | QS_INPUT)) != 0);
/*
* If we are looking at a peeked message currently (recursion into this
* routine) and the only reason we got here was because of an event
* message (an app was filtering for a non-input message), then just
* return so we don't screw up idSysPeek. If we do enter this code
* idSysPeek will get set back to 0, and when we return back into
* the previous xxxScanSysQueue(), SkipSysMsg() will do nothing, so the
* message won't get removed. (MS Publisher 2.0 does this).
*/
if (fsReason == QS_EVENT) {
if (ptiCurrent->pq->idSysPeek != 0) {
PATHTAKEN(1);
DUMPPATHTAKEN();
return FALSE;
}
}
fDown = FALSE;
fMouseHookCalled = FALSE;
fKbdHookCalled = FALSE;
/*
* Lock the queue if it's currently unlocked.
*/
if (ptiCurrent->pq->ptiSysLock == NULL) {
CheckSysLock(3, ptiCurrent->pq, ptiCurrent);
ptiCurrent->pq->ptiSysLock = ptiCurrent;
ptiCurrent->pcti->CTIF_flags |= CTIF_SYSQUEUELOCKED;
}
/*
* Flag to tell if locker was removing messages. If not, then next time
* Get/PeekMessage is called, the input message list is scanned before the
* post msg list.
*
* Under Win3.1, this flag only gets modified for key and mouse messages.
* Since under NT ScanSysQueue() can be called to execute event messages,
* we make this check to be compatible.
*/
if (fsReason & QS_INPUT) {
if (fRemove) {
PATHTAKEN(2);
ptiCurrent->pq->QF_flags &= ~QF_LOCKNOREMOVE;
} else {
PATHTAKEN(4);
ptiCurrent->pq->QF_flags |= QF_LOCKNOREMOVE;
}
}
/*
* Return FALSE if the current thread is not the one that lock this queue.
*/
if (ptiCurrent->pq->ptiSysLock != ptiCurrent) {
PATHTAKEN(8);
DUMPPATHTAKEN();
return FALSE;
}
ptiEventWake = ptiKeyWake = ptiMouseWake = NULL;
/*
* Initialize the thread lock structure here so we can unlock/lock in
* the main loop.
*/
pwnd = NULL;
ThreadLockWithPti(ptiCurrent, pwnd, &tlpwnd);
RestartScan:
CheckPtiSysPeek(2, ptiCurrent->pq, 0);
ptiCurrent->pq->idSysPeek = 0;
ContinueScan:
while (TRUE) {
DWORD idSysPeek;
DUMPSUBPATHTAKEN(pathTaken, 0xf0);
/*
* Store idSysPeek in a local which forces pq to be reloaded
* in case it changed during the xxx call (the compiler can
* evaluate the LValue at any time)
*/
idSysPeek = (DWORD)xxxGetNextSysMsg(ptiCurrent,
(PQMSG)ptiCurrent->pq->idSysPeek, &qmsg);
CheckPtiSysPeek(3, ptiCurrent->pq, idSysPeek);
ptiCurrent->pq->idSysPeek = idSysPeek;
if (ptiCurrent->pq->idSysPeek == 0) {
/*
* If we are only looking for event messages and we didn't
* find any, then clear the QS_EVENT bit
*/
if (fsReason == QS_EVENT)
ClearWakeBit(ptiCurrent, QS_EVENT, FALSE);
PATHTAKEN(0x10);
goto NoMessages;
}
/*
* pwnd should be locked for the duration of this routine.
* For most messages right out of GetNextSysMsg, this is
* NULL.
*/
ThreadUnlock(&tlpwnd);
pwnd = RevalidateHwnd(qmsg.msg.hwnd);
ThreadLockWithPti(ptiCurrent, pwnd, &tlpwnd);
/*
* See if this is an event message. If so, execute it regardless
* of message and window filters, but only if it is the first element
* of the input queue.
*/
if (qmsg.dwQEvent != 0) {
PTHREADINFO pti;
PATHTAKEN(0x20);
/*
* Most event messages can be executed out of order relative to
* its place in the queue. There are some examples were this is
* not allowed, and we check that here. For example, we would not
* want a keystate synchronization event to be processed before
* the keystrokes that came before it in the queue!
*
* We need to have most event messages be able to get processed
* out of order because apps can be filtering for message ranges
* that don't include input (like dde) - those scenarios still
* need to process events such as deactivate event messages even
* if there is input in the input queue.
*/
switch (qmsg.dwQEvent) {
case QEVENT_UPDATEKEYSTATE:
/*
* If the message is not the next message in the queue, don't
* process it.
*/
if (ptiCurrent->pq->idSysPeek !=
(DWORD)ptiCurrent->pq->mlInput.pqmsgRead) {
PATHTAKEN(0x40);
continue;
}
break;
}
/*
* If this event isn't for this thread, wake the thread it is
* for. A NULL qmsg.hti means that any thread can process
* the event.
*/
if (qmsg.pti != NULL && (pti = qmsg.pti) != ptiCurrent) {
/*
* If somehow this event message got into the wrong queue,
* then ignore it.
*/
UserAssert(pti->pq == ptiCurrent->pq);
if (pti->pq != ptiCurrent->pq) {
CleanEventMessage((PQMSG)ptiCurrent->pq->idSysPeek);
DelQEntry(&ptiCurrent->pq->mlInput,
(PQMSG)ptiCurrent->pq->idSysPeek);
PATHTAKEN(0x80);
goto RestartScan;
}
/*
* If ptiEventWake is already set, it means we've already
* found a thread to wake for event.
*/
if (ptiEventWake == NULL)
ptiEventWake = pti;
/*
* Clear idSysPeek so that the targeted thread
* can always get it. Look at the test at the
* start of this routine for more info.
*/
CheckPtiSysPeek(4, ptiCurrent->pq, 0);
ptiCurrent->pq->idSysPeek = 0;
PATHTAKEN(0x100);
goto NoMessages;
}
/*
* If this is called with PM_NOYIELD from a 16-bit app, skip
* processing any event that can generate activation messages. An
* example is printing from PageMaker 5.0. Bug #12662.
*/
if ((flags & PM_NOYIELD) && (ptiCurrent->TIF_flags & TIF_16BIT)) {
PATHTAKEN(0x200);
switch (qmsg.dwQEvent) {
/*
* The following events are safe to process if no yield
* is to occur.
*/
case QEVENT_UPDATEKEYSTATE:
case QEVENT_ASYNCSENDMSG:
break;
/*
* Skip all other events.
*/
default:
ptiCurrent->TIF_flags |= TIF_DELAYEDEVENT;
PATHTAKEN(0x400);
goto ContinueScan;
}
}
/*
* Delete this before it gets processed so there are no
* recursion problems.
*/
DelQEntry(&ptiCurrent->pq->mlInput,
(PQMSG)ptiCurrent->pq->idSysPeek);
/*
* Clear idSysPeek before processing any events messages, because
* they may recurse and want to use idSysPeek.
*/
CheckPtiSysPeek(5, ptiCurrent->pq, 0);
ptiCurrent->pq->idSysPeek = 0;
xxxProcessEventMessage(ptiCurrent, &qmsg);
/*
* Restart the scan from the start so we start with 0 in
* pq->idSysPeek (since that message is now gone!).
*/
PATHTAKEN(0x800);
goto RestartScan;
}
/*
* If the reason we called was just to process event messages, don't
* enumerate any other mouse or key messages!
*/
if (fsReason == QS_EVENT) {
PATHTAKEN(0x1000);
continue;
}
switch (message = qmsg.msg.message) {
case WM_QUEUESYNC:
PATHTAKEN(0x2000);
/*
* This message is for CBT. Its parameters should already be
* set up correctly.
*/
wParam = 0;
lParam = qmsg.msg.lParam;
/*
* Check if this is intended for the current app. Use the mouse
* bit for WM_QUEUESYNC.
*/
if (pwnd != NULL && GETPTI(pwnd) != ptiCurrent) {
/*
* If this other app isn't going to read from this
* queue, then skip this message. This can happen with
* WM_QUEUESYNC if the app passed a window handle
* to the wrong queue. This isn't likely to happen in
* this case because WM_QUEUESYNCs come in while journalling,
* which has all threads sharing the same queue.
*/
if (GETPTI(pwnd)->pq != ptiCurrent->pq) {
PATHTAKEN(0x4000);
goto SkipMessage;
}
if (ptiMouseWake == NULL)
ptiMouseWake = GETPTI(pwnd);
PATHTAKEN(0x8000);
goto NoMessages;
}
if (!CheckMsgFilter(message, msgMinFilter, msgMaxFilter)) {
PATHTAKEN(0x10000);
goto NoMessages;
}
/*
* Eat the message.
*/
if (fRemove) {
xxxSkipSysMsg(ptiCurrent, &qmsg);
}
/*
* !!HARDWARE HOOK!! goes here.
*/
/*
* Return the message.
*/
PATHTAKEN(0x20000);
goto ReturnMessage;
break;
/*
* Mouse message or generic hardware messages
* Key messages are handled in case statements
* further down in this switch.
*/
default:
ReprocessMsg:
DUMPSUBPATHTAKEN(pathTaken, 0x40000);
PATHTAKEN(0x40000);
/*
* !!GENERIC HARDWARE MESSAGE!! support goes here.
*/
/*
* Take the mouse position out of the message.
*/
pt.x = (int)(short)LOWORD(qmsg.msg.lParam);
pt.y = (int)(short)HIWORD(qmsg.msg.lParam);
/*
* Assume we have a capture.
*/
part = HTCLIENT;
/*
* We have a special global we use for when we're full screen.
* All mouse input will go to this window.
*/
if (gspwndScreenCapture != NULL) {
/*
* Change the mouse coordinates to full screen.
*/
pwnd = gspwndScreenCapture;
lParam = MAKELONG((WORD)qmsg.msg.pt.x,
(WORD)qmsg.msg.pt.y);
PATHTAKEN(0x80000);
} else if ((pwnd = ptiCurrent->pq->spwndCapture) == NULL) {
PATHTAKEN(0x100000);
/*
* We don't have the capture. Figure out which window owns
* this message.
*
* NOTE: Use gptiRit and not ptiCurrent to get the desktop
* window because if ptiCurrent is the thread that created
* the main desktop, it's associated desktop is the logon
* desktop - don't want to hittest against the logon desktop
* while switched into the main desktop!
*/
pwndT = gptiRit->rpdesk->pDeskInfo->spwnd;
ThreadLockWithPti(ptiCurrent, pwndT, &tlpwndT);
hwnd = xxxWindowHitTest(pwndT, pt, &part, TRUE);
ThreadUnlock(&tlpwndT);
if ((pwnd = RevalidateHwnd(hwnd)) == NULL) {
pwnd = ptiCurrent->rpdesk->pDeskInfo->spwnd;
PATHTAKEN(0x200000);
}
if (part == HTCLIENT) {
/*
* Part of the client... normal mouse message.
* NO_CAP_CLIENT means "not captured, in client area
* of window".
*/
ptiCurrent->pq->codeCapture = NO_CAP_CLIENT;
PATHTAKEN(0x400000);
} else {
/*
* Not part of the client... must be an NCMOUSEMESSAGE.
* NO_CAP_SYS is a creative name by raor which means
* "not captured, in system area of window."
*/
ptiCurrent->pq->codeCapture = NO_CAP_SYS;
PATHTAKEN(0x800000);
}
}
/*
* We've reassigned pwnd, so lock it.
*/
ThreadUnlock(&tlpwnd);
ThreadLockWithPti(ptiCurrent, pwnd, &tlpwnd);
if (fOtherApp = (GETPTI(pwnd) != ptiCurrent)) {
PATHTAKEN(0x1000000);
/*
* If this other app isn't going to read from this
* queue, then skip this message. This can happen if
* the RIT queues up a message thinking it goes to
* a particular hwnd, but then by the time GetMessage()
* is called for that thread, it doesn't go to that hwnd
* (like in the case of mouse messages, window rearrangement
* happens which changes which hwnd the mouse hits on).
*/
if (GETPTI(pwnd)->pq != ptiCurrent->pq) {
_SetCursor(SYSCUR(ARROW));
ResetMouseTracking(ptiCurrent->pq, NULL);
PATHTAKEN(0x2000000);
goto SkipMessage;
}
/*
* If we haven't already found a message that is intended
* for another app, remember that we have one.
*/
if (ptiMouseWake == NULL) {
ptiMouseWake = GETPTI(pwnd);
PATHTAKEN(0x4000000);
}
}
/*
* Map mouse coordinates based on hit test area code.
*/
switch (ptiCurrent->pq->codeCapture) {
case CLIENT_CAPTURE:
case NO_CAP_CLIENT:
pt.x -= pwnd->rcClient.left;
pt.y -= pwnd->rcClient.top;
PATHTAKEN2(2);
break;
case WINDOW_CAPTURE:
pt.x -= pwnd->rcWindow.left;
pt.y -= pwnd->rcWindow.top;
PATHTAKEN2(4);
break;
}
/*
* Do mouse hover processing.
*/
pwndMouse = (part == HTCLIENT) ? pwnd : NULL;
if (pwndMouse != ptiCurrent->pq->spwndLastMouseMessage) {
MouseEnter(ptiCurrent->pq, pwndMouse);
} else if (ptiCurrent->pq->QF_flags & QF_TRACKMOUSEHOVER) {
switch (message) {
default:
if (PtInRect(&ptiCurrent->pq->rcMouseHover, pt))
break;
// FALL THROUGH
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
ResetMouseHover(pt);
}
}
/*
* Now see if it matches the window handle filter. If not,
* get the next message.
*/
if (!CheckPwndFilter(pwnd, pwndFilter)) {
PATHTAKEN(0x8000000);
continue;
}
/*
* See if we need to map to a double click.
*/
codeMouseDown = 0;
switch (message) {
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
if (TestCF(pwnd, CFDBLCLKS) ||
ptiCurrent->pq->codeCapture == NO_CAP_SYS ||
IsMenuStarted(ptiCurrent)) {
codeMouseDown++;
PATHTAKEN(0x10000000);
if (qmsg.msg.time <= ptiCurrent->pq->timeDblClk &&
pwnd == PW(ptiCurrent->pq->hwndDblClk) &&
PtInRect(&ptiCurrent->pq->rcDblClk, qmsg.msg.pt) &&
message == ptiCurrent->pq->msgDblClk) {
message += (WM_LBUTTONDBLCLK - WM_LBUTTONDOWN);
codeMouseDown++;
PATHTAKEN(0x20000000);
}
}
// FALL THROUGH!!!
case WM_LBUTTONUP:
case WM_RBUTTONUP:
case WM_MBUTTONUP:
/*
* Note that the mouse button went up or down if we were
* in menu status mode of alt-key down
*/
PATHTAKEN(0x40000000);
if (ptiCurrent->pq->QF_flags & QF_FMENUSTATUS) {
ptiCurrent->pq->QF_flags |= QF_FMENUSTATUSBREAK;
PATHTAKEN(0x80000000);
}
}
/*
* Map message number based on hit test area code.
*/
if (ptiCurrent->pq->codeCapture == NO_CAP_SYS) {
message += (UINT)(WM_NCMOUSEMOVE - WM_MOUSEMOVE);
wParam = (UINT)part;
PATHTAKEN2(1);
}
/*
* Message number has been mapped: see if it fits the filter.
* If not, get the next message.
*/
if (!CheckMsgFilter(message, msgMinFilter, msgMaxFilter)) {
PATHTAKEN2(8);
continue;
}
/*
* If message is for another app but it fits our filter, then
* we should stop looking for messages: this will ensure that
* we don't keep looking and find and process a message that
* occured later than the one that should be processed by the
* other guy.
*/
if (fOtherApp) {
PATHTAKEN2(0x10);
goto NoMessages;
}
/*
* If we're doing full drag, the mouse messages should go to
* the xxxMoveSize PeekMessage loop. So we get the next message.
* This can happen when an application does a PeekMessage in
* response to a message sent inside the movesize dragging loop.
* This causes the dragging loop to not get the WM_LBUTTONUP
* message and dragging continues after the button is up
* (fix for Micrografx Draw). -johannec
*/
if (message >= WM_MOUSEFIRST && message <= WM_MOUSELAST &&
ptiCurrent->TIF_flags & TIF_MOVESIZETRACKING) {
PATHTAKEN2(0x20);
continue;
}
/*
* Let us call the mouse hook to find out if this click is
* permitted by it.
*
* We want to inform the mouse hook before we test for
* HTNOWHERE and HTERROR; Otherwise, the mouse hook won't
* get these messages (sankar 12/10/91).
*/
if (IsHooked(ptiCurrent, WHF_MOUSE)) {
fMouseHookCalled = TRUE;
mhs.pt = qmsg.msg.pt;
mhs.hwnd = HW(pwnd);
mhs.wHitTestCode = (UINT)part;
mhs.dwExtraInfo = qmsg.ExtraInfo;
if (xxxCallMouseHook(message, &mhs, fRemove)) {
/*
* Not allowed by mouse hook; so skip it.
*/
PATHTAKEN2(0x40);
goto SkipMessage;
}
PATHTAKEN2(0x80);
}
/*
* If a HTERROR or HTNOWHERE occured, send the window the
* WM_SETCURSOR message so it can beep or whatever. Then skip
* the message and try the next one.
*/
switch (part) {
case HTERROR:
case HTNOWHERE:
/*
* Now set the cursor shape.
*/
xxxSendMessage(pwnd, WM_SETCURSOR, (DWORD)HW(pwnd),
MAKELONG(part, qmsg.msg.message));
/*
* Skip the message.
*/
PATHTAKEN2(0x100);
goto SkipMessage;
break;
}
if (fRemove) {
PATHTAKEN2(0x200);
/*
* Since the processing of a down click may cause the next
* message to be interpreted as a double click, we only want
* to do the double click setup if we're actually going to
* remove the message. Otherwise, the next time we read the
* same message it would be interpreted as a double click.
*/
switch (codeMouseDown) {
case 1:
/*
* Down clock: set up for later possible double click.
*/
ptiCurrent->pq->msgDblClk = qmsg.msg.message;
ptiCurrent->pq->timeDblClk = qmsg.msg.time + dtDblClk;
ptiCurrent->pq->hwndDblClk = HW(pwnd);
SetRect(&ptiCurrent->pq->rcDblClk,
qmsg.msg.pt.x - SYSMET(CXDOUBLECLK) / 2,
qmsg.msg.pt.y - SYSMET(CYDOUBLECLK) / 2,
qmsg.msg.pt.x + SYSMET(CXDOUBLECLK) / 2,
qmsg.msg.pt.y + SYSMET(CYDOUBLECLK) / 2);
PATHTAKEN2(0x400);
break;
case 2:
/*
* Double click: finish processing.
*/
ptiCurrent->pq->timeDblClk = 0L;
PATHTAKEN2(0x800);
break;
default:
PATHTAKEN2(0x1000);
break;
}
/*
* Set mouse cursor and allow app to activate window
* only if we're removing the message.
*/
switch (xxxMouseActivate(ptiCurrent, pwnd,
qmsg.msg.message, &qmsg.msg.pt, part)) {
SkipMessage:
case MA_SKIP:
DUMPSUBPATHTAKEN(pathTaken2, 0x2000);
PATHTAKEN2(0x2000);
xxxSkipSysMsg(ptiCurrent, &qmsg);
/*
* Inform the CBT hook that we skipped a mouse click.
*/
if (fMouseHookCalled) {
if (IsHooked(ptiCurrent, WHF_CBT)) {
xxxCallHook(HCBT_CLICKSKIPPED, message,
(DWORD)&mhs, WH_CBT);
PATHTAKEN2(0x4000);
}
fMouseHookCalled = FALSE;
}
/*
* Inform the CBT hook that we skipped a key
*/
if (fKbdHookCalled) {
if (IsHooked(ptiCurrent, WHF_CBT)) {
xxxCallHook(HCBT_KEYSKIPPED, wParam, lParam,
WH_CBT);
PATHTAKEN2(0x8000);
}
fKbdHookCalled = FALSE;
}
/*
* If we aren't removing messages, don't reset idSysPeek
* otherwise we will go into an infinite loop if
* the keyboard hook says to ignore the message.
* (bobgu 4/7/87).
*/
if (!fRemove) {
PATHTAKEN2(0x10000);
goto ContinueScan;
} else {
PATHTAKEN2(0x20000);
goto RestartScan;
}
break;
case MA_REHITTEST:
/*
* Reprocess the message.
*/
PATHTAKEN2(0x40000);
goto ReprocessMsg;
}
}
/*
* Eat the message from the input queue (and set the keystate
* table).
*/
PATHTAKEN2(0x80000);
if (fRemove) {
xxxSkipSysMsg(ptiCurrent, &qmsg);
}
if (fRemove && fMouseHookCalled && IsHooked(ptiCurrent, WHF_CBT)) {
xxxCallHook(HCBT_CLICKSKIPPED, message,
(DWORD)&mhs, WH_CBT);
}
fMouseHookCalled = FALSE;
/*
* Calculate virtual key state bitmask for wParam.
*/
if (message >= WM_MOUSEFIRST) {
/*
* This is a USER mouse message. Calculate the bit mask for the
* virtual key state.
*/
wParam = GetMouseKeyFlags(ptiCurrent->pq);
PATHTAKEN2(0x100000);
}
lParam = MAKELONG((short)pt.x, (short)pt.y);
PATHTAKEN2(0x200000);
goto ReturnMessage;
break;
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
fDown = TRUE;
/*
* If we are sending keyboard input to an app that has been
* spinning then boost it back up. If we don't you use spinning
* apps like Write or Project and do two builds in the
* background. Note the app will also be unboosted again shortly
* after you stop typing by the old logic. #11188
*/
if (ptiCurrent->TIF_flags & TIF_SPINNING)
CheckProcessForeground(ptiCurrent);
/*
* Apps doing journal playback sometimes put trash in the hiword
* of wParam... zero it out here.
*/
wParam = qmsg.msg.wParam & 0xFF;
/*
* Clear QF_FMENUSTATUS if a key other than Alt it hit
* since this means the break of the Alt wouldn't be a
* menu key anymore.
*/
if (wParam != VK_MENU)
ptiCurrent->pq->QF_flags &= ~(QF_FMENUSTATUS|QF_FMENUSTATUSBREAK);
/*
* Check if it is the PrintScrn key.
*/
fAlt = TestKeyStateDown(ptiCurrent->pq, VK_MENU);
if (wParam == VK_SNAPSHOT &&
((fAlt && !(ptiCurrent->fsReserveKeys & CONSOLE_ALTPRTSC)) ||
(!fAlt && !(ptiCurrent->fsReserveKeys & CONSOLE_PRTSC)))) {
/*
* Remove this message from the input queue.
*/
PATHTAKEN2(0x400000);
xxxSkipSysMsg(ptiCurrent, &qmsg);
/*
* PRINTSCREEN -> Snap the whole screen.
* ALT-PRINTSCREEN -> Snap the current window.
*/
pwndT = ptiCurrent->pq->spwndActive;
if (!fAlt) {
pwndT = ptiCurrent->rpdesk->pDeskInfo->spwnd;
}
if (pwndT != NULL) {
ThreadLockAlwaysWithPti(ptiCurrent, pwndT, &tlpwndT);
xxxSnapWindow(pwndT);
ThreadUnlock(&tlpwndT);
}
PATHTAKEN2(0x800000);
goto RestartScan;
}
/*
* Check for keyboard language toggle. Build the key state
* here for use during key up processing (where the layout
* switching takes place. This code is skipped if layout
* switching via the keyboard is disabled.
*/
if (LangToggle[0].bVkey && (LangToggleKeyState < 8)) {
DWORD i;
BYTE scancode = LOBYTE(HIWORD(qmsg.msg.lParam));
BYTE vkey = LOBYTE(qmsg.msg.wParam);
for (i = 0; i < cLangToggleKeys; i++) {
if (LangToggle[i].bScan) {
if (LangToggle[i].bScan == scancode) {
LangToggleKeyState |= LangToggle[i].iBitPosition;
break;
}
} else {
if (LangToggle[i].bVkey == vkey) {
LangToggleKeyState |= LangToggle[i].iBitPosition;
break;
}
}
}
if (i == cLangToggleKeys) {
LangToggleKeyState = 8; // not a language toggle combination
}
}
/*
* Check for hot keys being hit if any are defined.
*/
if (gcHotKey != 0) {
key = wParam;
if (TestKeyStateDown(ptiCurrent->pq, VK_MENU))
key |= 0x0400;
if (TestKeyStateDown(ptiCurrent->pq, VK_CONTROL))
key |= 0x0200;
if (TestKeyStateDown(ptiCurrent->pq, VK_SHIFT))
key |= 0x0100;
pwndT = HotKeyToWindow(key);
if (pwndT != NULL) {
_PostMessage(ptiCurrent->pq->spwndActive, WM_SYSCOMMAND,
(WPARAM)SC_HOTKEY, (LONG)HWq(pwndT));
/*
* Remove this message from the input queue.
*/
xxxSkipSysMsg(ptiCurrent, &qmsg);
PATHTAKEN2(0x1000000);
goto RestartScan;
}
PATHTAKEN2(0x2000000);
}
/*
* Fall through.
*/
case WM_SYSKEYUP:
case WM_KEYUP:
wParam = qmsg.msg.wParam & 0xFF;
/*
* Process keyboard toggle keys only if this is
* a break event and fRemove == TRUE. Some apps,
* for instance Word 95, call PeekMessage with
* PM_NOREMOVE followed by a call with PM_REMOVE.
* We only want to process this once. Skip all
* of this is layout switching via the keyboard
* is disabled.
*/
if (!fDown && fRemove && LangToggle[0].bVkey) {
BOOL bDropToggle = FALSE;
DWORD dwDirection = 0;
PKL pkl;
// PWND pwndTop;
pwnd = ptiCurrent->pq->spwndFocus;
if (pwnd == NULL) {
pwnd = ptiCurrent->pq->spwndActive;
if (!pwnd) {
goto NoLayoutSwitch;
}
}
pkl = GETPTI(pwnd)->spklActive;
UserAssert(pkl != NULL);
switch (LangToggleKeyState) {
case KLT_LEFTSHIFT:
bDropToggle = TRUE;
dwDirection = LANGCHANGE_BACKWARD;
pkl = HKLtoPKL((HKL)HKL_NEXT);
break;
case KLT_RIGHTSHIFT:
bDropToggle = TRUE;
dwDirection = LANGCHANGE_FORWARD;
pkl = HKLtoPKL((HKL)HKL_PREV);
break;
case KLT_BOTHSHIFTS:
pkl = gspklBaseLayout;
break;
default:
goto NoLayoutSwitch;
break;
}
if (pkl == NULL) {
pkl = GETPTI(pwnd)->spklActive;
}
UserAssert(gspklBaseLayout != NULL);
// One day Memphis and Windows NT should come up with a better
// strategy. This one goes up too far, bypassing Word when
// using wordmail. - IanJa
// if ((pwndTop = GetTopLevelWindow(pwnd)) != NULL) {
// pwnd = pwndTop;
// }
_PostMessage(
pwnd,
WM_INPUTLANGCHANGEREQUEST,
(DWORD)(((pkl->bCharsets & gSystemCPB) ? TRUE : FALSE) | dwDirection),
(LONG)pkl->hkl
);
NoLayoutSwitch:
if (bDropToggle) {
/*
* Clear this key from the key state so that multiple key
* presses will work (i.e., Alt+Shft+Shft). We don't do
* this when both shift keys are pressed simultaneously to
* avoid two activates.
*/
DWORD i;
BYTE scancode = LOBYTE(HIWORD(qmsg.msg.lParam));
BYTE vkey = LOBYTE(qmsg.msg.wParam);
for (i = 0; i < cLangToggleKeys; i++) {
if (LangToggle[i].bScan) {
if (LangToggle[i].bScan == scancode) {
LangToggleKeyState &= ~(LangToggle[i].iBitPosition);
}
} else {
if (LangToggle[i].bVkey == vkey) {
LangToggleKeyState &= ~(LangToggle[i].iBitPosition);
}
}
}
} else {
LangToggleKeyState = 0;
}
}
/*
* Convert F10 to syskey for new apps.
*/
if (wParam == VK_F10)
message |= (WM_SYSKEYDOWN - WM_KEYDOWN);
if (TestKeyStateDown(ptiCurrent->pq, VK_CONTROL) &&
wParam == VK_ESCAPE) {
message |= (WM_SYSKEYDOWN - WM_KEYDOWN);
}
/*
* Clear the 'simulated keystroke' bit for all applications except
* console so it can pass it to 16-bit vdms. VDM keyboards need to
* distinguish between AltGr (where Ctrl keystroke is simulated)
* and a real Ctrl+Alt. Check TIF_CSRSSTHREAD for the console
* input thread because it lives in the server. This is a cheap
* way to check for it.
*/
if (!(ptiCurrent->TIF_flags & TIF_CSRSSTHREAD))
qmsg.msg.lParam &= ~FAKE_KEYSTROKE;
PATHTAKEN2(0x4000000);
/*
* Fall through.
*/
/*
* Some apps want to be able to feed WM_CHAR messages through
* the playback hook. Why? Because they want to be able to
* convert a string of characters info key messages
* and feed them to themselves or other apps. Unfortunately,
* there are no machine independent virtual key codes for
* some characters (for example '$'), so they need to send
* those through as WM_CHARs. (6/10/87).
*/
case WM_CHAR:
wParam = qmsg.msg.wParam & 0xFF;
/*
* Assign the input to the focus window. If there is no focus
* window, assign it to the active window as a SYS message.
*/
pwnd = ptiCurrent->pq->spwndFocus;
if (ptiCurrent->pq->spwndFocus == NULL) {
if ((pwnd = ptiCurrent->pq->spwndActive) != NULL) {
if (CheckMsgFilter(message, WM_KEYDOWN, WM_DEADCHAR)) {
message += (WM_SYSKEYDOWN - WM_KEYDOWN);
PATHTAKEN2(0x8000000);
}
} else {
PATHTAKEN2(0x10000000);
goto SkipMessage;
}
}
/*
* If there is no active window or focus window, eat this
* message.
*/
if (pwnd == NULL) {
PATHTAKEN2(0x20000000);
goto SkipMessage;
}
ThreadUnlock(&tlpwnd);
ThreadLockAlwaysWithPti(ptiCurrent, pwnd, &tlpwnd);
/*
* Check if this is intended for the current app.
*/
if (fOtherApp = (GETPTI(pwnd) != ptiCurrent)) {
/*
* If this other app isn't going to read from this
* queue, then skip this message. This can happen if
* the RIT queues up a message thinking it goes to
* a particular hwnd, but then by the time GetMessage()
* is called for that thread, it doesn't go to that hwnd
* (like in the case of mouse messages, window rearrangement
* happens which changes which hwnd the mouse hits on).
*/
if (GETPTI(pwnd)->pq != ptiCurrent->pq) {
PATHTAKEN2(0x40000000);
goto SkipMessage;
}
/*
* If the current thread is in the menu loop then we need
* to give it the input
*/
if (IsInsideMenuLoop(ptiCurrent)) {
pwnd = ptiCurrent->pMenuState->pGlobalPopupMenu->spwndNotify;
fOtherApp = (GETPTI(pwnd) != ptiCurrent);
/*
* We've reassigned pwnd, so lock it.
*/
ThreadUnlock(&tlpwnd);
ThreadLockWithPti(ptiCurrent, pwnd, &tlpwnd);
PATHTAKEN2(0x80000000);
}
/*
* If not for us, then remember who it is for.
*/
if (ptiKeyWake == NULL) {
PATHTAKEN3(1);
ptiKeyWake = GETPTI(pwnd);
}
}
/*
* See if this thing matches our filter.
*/
if (!CheckMsgFilter(message, msgMinFilter, msgMaxFilter) ||
!CheckPwndFilter(pwnd, pwndFilter)) {
PATHTAKEN3(2);
continue;
}
/*
* This message matches our filter. If it is not for us then
* stop searching to make sure the real owner processes this
* message first.
*/
if (fOtherApp) {
PATHTAKEN3(4);
goto NoMessages;
}
/*
* Generate some special messages if we are removing and we are
* not inside the menu loop.
*/
if (fRemove && !IsInsideMenuLoop(ptiCurrent)) {
/*
* Generate a WM_CONTEXTMENU for the VK_APPS key
*/
if ((wParam == VK_APPS) && (message == WM_KEYUP)) {
_PostMessage(pwnd, WM_CONTEXTMENU, (WPARAM)PtoH(pwnd), -1);
}
/*
* If this is a WM_KEYDOWN message for F1 key then we must generate
* the WM_KEYF1 message.
*/
if ((wParam == VK_F1) && (message == WM_KEYDOWN)) {
_PostMessage(pwnd, WM_KEYF1, 0, 0);
}
}
/*
* If one Shift key is released while the other Shift key is held
* down, this keystroke is normally skipped, presumably to prevent
* applications from thinking that the shift condition no longer
* applies.
*/
if (wParam == VK_SHIFT) {
BYTE vkHanded, vkOtherHand;
if (qmsg.msg.lParam & EXTENDED_BIT) {
vkHanded = VK_RSHIFT;
} else {
vkHanded = VK_LSHIFT;
}
vkOtherHand = vkHanded ^ 1;
if (!fDown && TestKeyStateDown(ptiCurrent->pq, vkOtherHand)) {
/*
* Unlike normal apps, Console MUST be sent a Shift break
* even when the other Shift key is still down, since it
* has to be passed on to VDM, which maintains it's own
* state. Check TIF_CSRSSTHREAD for the console input
* thread because it lives in the server. This is a cheap
* way to check for it.
*/
if ((ptiCurrent->TIF_flags & TIF_CSRSSTHREAD) == 0) {
/*
* We ignore this key event, so we must update
* it's key state whether fRemove is TRUE or not.
* (ignoring an key event is same as removing it)
*/
qmsg.msg.wParam = vkHanded;
xxxSkipSysMsg(ptiCurrent, &qmsg);
PATHTAKEN3(8);
goto RestartScan;
}
PATHTAKEN3(0x10);
}
}
/*
* Get the previous up/down state of the key here since
* SkipSysMsg() sets the key state table and destroys
* the previous state info.
*/
fPrevDown = FALSE;
if (TestKeyStateDown(ptiCurrent->pq, wParam))
fPrevDown = TRUE;
/*
* Eat the message from the input queue and set the keystate
* table.
*/
PATHTAKEN3(0x20);
if (fRemove) {
xxxSkipSysMsg(ptiCurrent, &qmsg);
}
/*
* This gets us the LOWORD of lParam, the repeat count,
* the bit in the hi byte indicating whether this is an extended
* key, and the scan code. We also need to re-get the wParam in
* case xxxSkipSysMsg called a hook which modified the message.
* AfterDark's password protection does this.
*/
lParam = qmsg.msg.lParam;
wParam = qmsg.msg.wParam;
/*
* Indicate if it was previously down.
*/
if (fPrevDown)
lParam |= 0x40000000; // KF_REPEAT
/*
* Set the transition bit.
*/
switch (message) {
case WM_KEYUP:
case WM_SYSKEYUP:
lParam |= 0x80000000; // KF_UP
break;
}
/*
* Set the alt key down bit.
*/
if (TestKeyStateDown(ptiCurrent->pq, VK_MENU)) {
lParam |= 0x20000000; // KF_ALTDOWN
}
/*
* Set the menu state flag.
*/
if (IsMenuStarted(ptiCurrent)) {
lParam |= 0x10000000; // KF_MENUMODE
}
/*
* Set the dialog state flag.
*/
if (ptiCurrent->pq->QF_flags & QF_DIALOGACTIVE) {
lParam |= 0x08000000; // KF_DLGMODE
}
/*
* 0x80000000 is set if up, clear if down
* 0x40000000 is previous up/down state of key
* 0x20000000 is whether the alt key is down
* 0x10000000 is whether currently in menumode.
* 0x08000000 is whether in dialog mode
* 0x04000000 is not used
* 0x02000000 is not used
* 0x01000000 is whether this is an extended keyboard key
*
* Low word is repeat count, low byte hiword is scan code,
* hi byte hiword is all these bits.
*/
#ifdef FE_IME
/*
* Callback the client IME before calling the keyboard hook.
* If the vkey is one of the IME hotkeys, the vkey will not
* be passed to the keyboard hook.
* If IME needs this vkey, VK_PROCESSKEY will be put into the
* application queue instead of real vkey.
*/
if ( fRemove &&
! IsMenuStarted(ptiCurrent) &&
! (ptiCurrent->TIF_flags & TIF_DISABLEIME) &&
ptiCurrent->pq->spwndFocus != NULL
)
{
dwImmRet = xxxImmProcessKey( ptiCurrent->pq,
ptiCurrent->pq->spwndFocus,
message,
wParam,
lParam);
if ( dwImmRet & IPHK_HOTKEY ) {
dwImmRet = 0;
goto SkipMessage;
}
}
#endif
/*
* If we are removing the message, call the keyboard hook
* with HC_ACTION, otherwise call the hook with HC_NOREM
* to let it know that the message is not being removed.
*/
if (IsHooked(ptiCurrent, WHF_KEYBOARD)) {
fKbdHookCalled = TRUE;
if (xxxCallHook(fRemove ? HC_ACTION : HC_NOREMOVE,
wParam, lParam, WH_KEYBOARD)) {
PATHTAKEN3(0x40);
goto SkipMessage;
}
}
if (fKbdHookCalled && fRemove && IsHooked(ptiCurrent, WHF_CBT)) {
xxxCallHook(HCBT_KEYSKIPPED, wParam, lParam, WH_CBT);
PATHTAKEN3(0x80);
}
fKbdHookCalled = FALSE;
PATHTAKEN3(0x100);
goto ReturnMessage;
case WM_MOUSEWHEEL:
/*
* If we are sending keyboard input to an app that has been
* spinning then boost it back up. If we don't you use spinning
* apps like Write or Project and do two builds in the
* background. Note the app will also be unboosted again shortly
* after you stop typing by the old logic. #11188
*/
if (ptiCurrent->TIF_flags & TIF_SPINNING)
CheckProcessForeground(ptiCurrent);
/*
* Assign the input to the focus window. If there is no focus
* window, or we are in a menu loop, eat this message.
*/
pwnd = ptiCurrent->pq->spwndFocus;
if (pwnd == NULL || IsInsideMenuLoop(ptiCurrent)) {
PATHTAKEN2(0x20000000);
goto SkipMessage;
}
ThreadUnlock(&tlpwnd);
ThreadLockAlwaysWithPti(ptiCurrent, pwnd, &tlpwnd);
/*
* Check if this is intended for the current app.
*/
if (fOtherApp = (GETPTI(pwnd) != ptiCurrent)) {
/*
* If this other app isn't going to read from this
* queue, then skip this message. This can happen if
* the RIT queues up a message thinking it goes to
* a particular hwnd, but then by the time GetMessage()
* is called for that thread, it doesn't go to that hwnd
* (like in the case of mouse messages, window rearrangement
* happens which changes which hwnd the mouse hits on).
*/
if (GETPTI(pwnd)->pq != ptiCurrent->pq) {
PATHTAKEN2(0x40000000);
goto SkipMessage;
}
/*
* If not for us, then remember who it is for.
*/
if (ptiKeyWake == NULL) {
PATHTAKEN3(1);
ptiKeyWake = GETPTI(pwnd);
}
}
/*
* See if this thing matches our filter.
* NOTE: We need to check whether the caller is filtering
* for all mouse messages - if so, we assume the caller
* wants mouse wheel messages too.
*/
if ( !CheckMsgFilter(WM_MOUSEWHEEL, msgMinFilter, msgMaxFilter) ||
!CheckPwndFilter(pwnd, pwndFilter)) {
PATHTAKEN3(2);
continue;
}
/*
* This message matches our filter. If it is not for us then
* stop searching to make sure the real owner processes this
* message first.
*/
if (fOtherApp) {
PATHTAKEN3(4);
goto NoMessages;
}
/*
* Eat the message from the input queue and set the keystate
* table.
*/
PATHTAKEN3(0x20);
if (fRemove) {
xxxSkipSysMsg(ptiCurrent, &qmsg);
}
wParam = GetMouseKeyFlags(ptiCurrent->pq);
UserAssert(LOWORD(qmsg.msg.wParam) == 0);
UserAssert(HIWORD(wParam) == 0);
wParam |= qmsg.msg.wParam;
lParam = qmsg.msg.lParam;
/*
* If we are removing the message, call the mouse hook
* with HC_ACTION, otherwise call the hook with HC_NOREM
* to let it know that the message is not being removed.
*/
if (IsHooked(ptiCurrent, WHF_MOUSE)) {
fMouseHookCalled = TRUE;
mhs.pt = qmsg.msg.pt;
mhs.hwnd = HW(pwnd);
mhs.wHitTestCode = HTNOWHERE;
mhs.dwExtraInfo = qmsg.ExtraInfo;
if (xxxCallMouseHook(message, &mhs, fRemove)) {
/*
* Not allowed by mouse hook; so skip it.
*/
PATHTAKEN3(0x40);
goto SkipMessage;
}
}
if (fMouseHookCalled && fRemove && IsHooked(ptiCurrent, WHF_CBT)) {
/*
* CONSIDER: Add new HCBT_ constant for the mouse wheel?
*/
xxxCallHook(HCBT_CLICKSKIPPED, message, (DWORD) &mhs, WH_CBT);
PATHTAKEN3(0x80);
}
fMouseHookCalled = FALSE;
PATHTAKEN3(0x100);
goto ReturnMessage;
} /* End of switch (message = qmsg.msg.message) */
} /* End of the GetNextSysMsg() loop */
ReturnMessage:
ptiCurrent->ptLast = qmsg.msg.pt;
ptiCurrent->timeLast = qmsg.msg.time;
ptiCurrent->pq->ExtraInfo = qmsg.ExtraInfo;
/*
* idSysLock value of 1 indicates that the message came from the input
* queue.
*/
ptiCurrent->idLast = ptiCurrent->pq->idSysLock = 1;
/*
* Now see if our input bit is set for this input. If it isn't, set ours
* and clear the guy who had it previously.
*/
TransferWakeBit(ptiCurrent, message);
/*
* Clear the input bits if no messages in the input queue.
*/
ClearWakeBit(ptiCurrent, QS_MOUSE | QS_KEY | QS_EVENT | QS_TRANSFER, TRUE);
/*
* Get the message and split.
*/
lpMsg->hwnd = HW(pwnd);
lpMsg->message = message;
#ifdef FE_IME
/*
* If the IME claims that it needs this vkey, replace it
* with VK_PROCESSKEY. The real vkey has been saved in
* the input context in the client side.
*/
lpMsg->wParam = ( dwImmRet & IPHK_PROCESSBYIME ) ? VK_PROCESSKEY : wParam;
#else
lpMsg->wParam = wParam;
#endif
lpMsg->lParam = lParam;
lpMsg->time = qmsg.msg.time;
lpMsg->pt = qmsg.msg.pt;
#if DBG
if (gfLogPlayback && ptiCurrent->pq->idSysPeek == (int)PQMSG_PLAYBACK)
LogPlayback(pwnd, lpMsg);
#endif // DBG
ThreadUnlock(&tlpwnd);
PATHTAKEN3(0x200);
DUMPPATHTAKEN();
return TRUE;
NoMessages:
/*
* The message was for another app, or none were found that fit the
* filter.
*/
/*
* Unlock the system queue.
*/
ptiCurrent->pq->idSysLock = 0;
CheckSysLock(4, ptiCurrent->pq, NULL);
ptiCurrent->pq->ptiSysLock = NULL;
ptiCurrent->pcti->CTIF_flags &= ~CTIF_SYSQUEUELOCKED;
/*
* Wake up someone else if we found a message for him. QS_TRANSFER
* signifies that the thread was woken due to input transfer
* from another thread, rather than from a real input event.
*/
if (ptiKeyWake != NULL || ptiMouseWake != NULL || ptiEventWake != NULL) {
PATHTAKEN3(0x400);
if (ptiKeyWake != NULL) {
SetWakeBit(ptiKeyWake, QS_KEY | QS_TRANSFER);
ClearWakeBit(ptiCurrent, QS_KEY | QS_TRANSFER, FALSE);
PATHTAKEN3(0x800);
}
if (ptiMouseWake != NULL) {
SetWakeBit(ptiMouseWake, QS_MOUSE | QS_TRANSFER);
ClearWakeBit(ptiCurrent, QS_MOUSE | QS_TRANSFER, FALSE);
PATHTAKEN3(0x1000);
}
if (ptiEventWake != NULL) {
SetWakeBit(ptiEventWake, QS_EVENTSET);
ClearWakeBit(ptiCurrent, QS_EVENT, FALSE);
PATHTAKEN3(0x2000);
} else if (FJOURNALPLAYBACK()) {
/*
* If journal playback is occuring, clear the input bits. This will
* help prevent a race condition between two threads that call
* WaitMessage/PeekMessage. This can occur when embedding an OLE
* object. An example is inserting a Word object into an Excel
* spreadsheet.
* Also clear change bits else this thread might not xxxSleepThread.
*/
ptiCurrent->pcti->fsWakeBitsJournal |= (ptiCurrent->pcti->fsWakeBits &
(QS_MOUSE | QS_KEY | QS_TRANSFER));
ClearWakeBit(ptiCurrent, QS_MOUSE | QS_KEY | QS_TRANSFER, FALSE);
ptiCurrent->pcti->fsChangeBits &= ~(QS_MOUSE | QS_KEY | QS_TRANSFER);
}
} else {
/*
* Clear the input bits if no messages in the input queue.
*/
ptiCurrent->pcti->fsWakeBitsJournal = 0;
ClearWakeBit(ptiCurrent, QS_MOUSE | QS_KEY | QS_EVENT |
QS_TRANSFER, TRUE);
PATHTAKEN3(0x4000);
}
ThreadUnlock(&tlpwnd);
PATHTAKEN3(0x8000);
DUMPPATHTAKEN();
return FALSE;
}
#undef PATHTAKEN
#undef PATHTAKEN2
#undef PATHTAKEN3
#undef DUMPPATHTAKEN
#undef DUMPSUBPATHTAKEN
/***************************************************************************\
* IdleTimerProc
*
* This will start the screen saver app
*
* History:
* 09-06-91 mikeke Created.
* 03-26-92 DavidPe Changed to be run from hungapp timer on RIT.
\***************************************************************************/
VOID IdleTimerProc(VOID)
{
CheckCritIn();
if (_GetAsyncKeyState(VK_LBUTTON) & 0x8000)
return;
if (_GetAsyncKeyState(VK_RBUTTON) & 0x8000)
return;
if (_GetAsyncKeyState(VK_MBUTTON) & 0x8000)
return;
if ((iScreenSaveTimeOut > 0) && (timeLastInputMessage != 0)) {
/*
* Should we screen save?
*/
if ((NtGetTickCount() - timeLastInputMessage) >
(DWORD)(iScreenSaveTimeOut * 1000)) {
/*
* Set this to 0 so that we don't try to screen save again till
* we get another input message.
*/
timeLastInputMessage = 0;
if (gpqForeground != NULL && gpqForeground->spwndActive != NULL) {
_PostMessage(gpqForeground->spwndActive,
WM_SYSCOMMAND, SC_SCREENSAVE, 0L);
} else {
StartScreenSaver();
}
}
}
}
/***************************************************************************\
* WakeInputIdle
*
* The calling thread is going "idle". Wake up any thread waiting for this.
*
* 09-24-91 ScottLu Created.
\***************************************************************************/
void WakeInputIdle(
PTHREADINFO pti)
{
PW32PROCESS W32Process = W32GetCurrentProcess();
/*
* clear out the TIF_FIRSTIDLE since here we are
*/
pti->TIF_flags &= ~TIF_FIRSTIDLE;
/*
* If this is a screen saver, set its priority - this will force it
* to go idle.
*/
if (pti->TIF_flags & TIF_SCREENSAVER)
SetForegroundPriority(pti, FALSE);
/*
* Shared Wow Apps use the per thread idle event for synchronization.
* Separate Wow VDMs use the regular mechanism.
*/
if (pti->TIF_flags & TIF_SHAREDWOW) {
UserAssert(pti->TIF_flags & TIF_16BIT);
if (pti->ptdb->pwti) {
SET_PSEUDO_EVENT(&pti->ptdb->pwti->pIdleEvent);
}
} else {
/*
* If the main thread is NULL, set it to this queue: it is calling
* GetMessage().
*/
if (pti->ppi->ptiMainThread == NULL)
pti->ppi->ptiMainThread = pti;
/*
* Wake up anyone waiting on this event.
*/
if (pti->ppi->ptiMainThread == pti) {
SET_PSEUDO_EVENT(&W32Process->InputIdleEvent);
}
}
/*
* Check to see if the startglass is on, and if so turn it off and update.
*/
if (W32Process->W32PF_Flags & W32PF_STARTGLASS) {
/*
* This app is no longer in "starting" mode. Recalc when to hide
* the app starting cursor.
*/
W32Process->W32PF_Flags &= ~W32PF_STARTGLASS;
CalcStartCursorHide(NULL, 0);
}
}
void SleepInputIdle(
PTHREADINFO pti)
{
PW32PROCESS W32Process;
/*
* Shared Wow Apps use the per thread idle event for synchronization.
* Separate Wow VDMs use the regular mechanism.
*/
if (pti->TIF_flags & TIF_SHAREDWOW) {
UserAssert(pti->TIF_flags & TIF_16BIT);
if (pti->ptdb->pwti) {
RESET_PSEUDO_EVENT(&pti->ptdb->pwti->pIdleEvent);
}
} else {
/*
* If the main thread is NULL, set it to this queue: it is calling
* GetMessage().
*/
if (pti->ppi->ptiMainThread == NULL)
pti->ppi->ptiMainThread = pti;
/*
* Put to sleep up anyone waiting on this event.
*/
if (pti->ppi->ptiMainThread == pti) {
W32Process = W32GetCurrentProcess();
RESET_PSEUDO_EVENT(&W32Process->InputIdleEvent);
}
}
}
/***************************************************************************\
* RecalcThreadAttachment
* Recalc2
* AddAttachment
* CheckAttachment
*
* Runs through all the attachinfo fields for all threads and calculates
* which threads share which queues. Puts calculated result in pqAttach
* field in each threadinfo structure. This is a difficult problem
* whose only solution in iterative. The basic algorithm is:
*
* 0. Find next unattached thread and attach a queue to it. If none, stop.
* 1. Loop through all threads: If thread X assigned to this queue or any
* of X's attach requests assigned to this queue, assign X and all X's
* attachments to this queue. Remember if we ever attach a 16 bit thread.
* 2. If thread X is a 16 bit thread and we've already attached another
* 16 bit thread, assign X and all X's attachments to this queue.
* 3. If any change found in 1-2, goto 1
* 4. Goto 0
*
* 12-11-92 ScottLu Created.
* 01-Oct-1993 mikeke Fixed to work with MWOWs
\***************************************************************************/
void AddAttachment(
PTHREADINFO pti,
PQ pqAttach,
LPBOOL pfChanged)
{
if (pti->pqAttach != pqAttach) {
/*
* LATER
* !!! This is totally screwed up, The only reason that this thing
* could be non null is because two threads are going through
* attachthreadintput() at the same time. No one can predict
* what kind of problems are going to be caused by that.
* We leave the critical section in one place where we send
* WM_CANCELMODE below. We should figure out how to remove
* the sendmessage.
*
* If there already is a queue there, as there may be, destroy it.
* Note that DestroyQueue() will only get rid of the queue if the
* thread reference count goes to 0.
*/
PQ pqDestroy = pti->pqAttach;
pti->pqAttach = pqAttach;
if (pqDestroy != NULL)
DestroyQueue(pqDestroy, pti);
pqAttach->cThreads++;
*pfChanged = TRUE;
}
}
void Recalc2(
PQ pqAttach)
{
PATTACHINFO pai;
PTHREADINFO pti;
BOOL fChanged;
PLIST_ENTRY pHead, pEntry;
/*
* Keep adding attachments until everything that should be attached to this
* queue is attached
*/
do {
fChanged = FALSE;
/*
* If a thread is attached to this Q attach all of it's attachments
* and MWOW buddies if they aren't already attached.
*/
pHead = &PtiCurrent()->rpdesk->PtiList;
for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink) {
pti = CONTAINING_RECORD(pEntry, THREADINFO, PtiLink);
if (pti->pqAttach == pqAttach) {
/*
* check each of the attachments to see if this thread is attached
* to any other threads
*/
for (pai = gpai; pai != NULL; pai = pai->paiNext) {
/*
* if they weren't attached already, attach them
*/
if (pai->pti1 == pti || pai->pti2 == pti) {
AddAttachment(
(pai->pti1 == pti) ? pai->pti2 : pai->pti1,
pqAttach,
&fChanged);
}
}
/*
* If this is a 16bit thread attach to all other threads in
* it's MWOW
*/
if (pti->TIF_flags & TIF_16BIT) {
PTHREADINFO ptiAttach;
PLIST_ENTRY pHeadAttach, pEntryAttach;
pHeadAttach = &pti->rpdesk->PtiList;
for (pEntryAttach = pHeadAttach->Flink;
pEntryAttach != pHeadAttach;
pEntryAttach = pEntryAttach->Flink) {
ptiAttach = CONTAINING_RECORD(pEntryAttach, THREADINFO, PtiLink);
if (ptiAttach->TIF_flags & TIF_16BIT &&
ptiAttach->ppi == pti->ppi) {
AddAttachment(ptiAttach, pqAttach, &fChanged);
}
}
}
}
}
} while (fChanged);
}
void RecalcThreadAttachment()
{
PTHREADINFO pti;
PLIST_ENTRY pHead, pEntry;
/*
* For all threads, start an attach queue if a thread hasn't been
* attached yet.
*/
pHead = &PtiCurrent()->rpdesk->PtiList;
for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink) {
pti = CONTAINING_RECORD(pEntry, THREADINFO, PtiLink);
/*
* If the thread does not have a queue yet, don't
* attach.
*/
if (pti->pqAttach == NULL) {
/*
* Allocate a new queue for this thread if more than
* one thread references it.
*/
if (pti->pq->cThreads > 1) {
pti->pqAttach = AllocQueue(NULL, NULL);
if (pti->pqAttach == NULL) {
break;
}
pti->pqAttach->cThreads++;
} else {
pti->pqAttach = pti->pq;
}
/*
* Attach every thread that is directly or indirectly attached
* to this thread.
*/
Recalc2(pti->pqAttach);
}
}
}
/***************************************************************************\
* RedistributeInput
*
* This routine takes a input stream from the queue being left, and
* redistributes it. This effectively filters out the messages destined
* to the thread that left the queue.
*
* 12-10-92 ScottLu Created.
\***************************************************************************/
void RedistributeInput(
PQMSG pqmsgS,
PQ pqRedist)
{
PTHREADINFO ptiSave;
PTHREADINFO ptiT;
PQMSG *ppqmsgD;
PQMSG pqmsgT;
PMLIST pmlInput;
/*
* Since the thread attaching or unattaching may have left a queue
* shared by other threads, the messages we are going to requeue
* may have multiple destinations. On top of this, once we find
* a home queue for a message, it needs to be inserted in the
* list ordered by its time stamp (older messages go at the end).
*/
/*
* Loop through a given dest's messages to find where to insert
* the source messages, based on message time stamp. Be sure
* to deal with empty message lists (meaning, check for NULL).
*/
ptiT = NULL;
ppqmsgD = NULL;
pmlInput = NULL;
while (pqmsgS != NULL) {
/*
* Find out where this message should go.
*/
ptiSave = ptiT;
ptiT = pqmsgS->pti;
/*
* Get rid of some event messages.
*
* QEVENT_UPDATEKEYSTATE: key state already up to date
*/
if (pqmsgS->dwQEvent == QEVENT_UPDATEKEYSTATE) {
ptiT = NULL;
}
if (ptiT == NULL) {
/*
* Unlink it. pqmsgS should be the first in the list
*/
UserAssert(!pqmsgS->pqmsgPrev);
if (pqmsgS->pqmsgNext != NULL) {
pqmsgS->pqmsgNext->pqmsgPrev = NULL;
}
pqmsgT = pqmsgS;
pqmsgS = pqmsgS->pqmsgNext;
/*
* Clean it / free it.
*/
CleanEventMessage(pqmsgT);
FreeQEntry(pqmsgT);
ptiT = ptiSave;
continue;
}
/*
* Point to the pointer that points to the first message
* that this message should go to, so that pointer is easy to
* update, no matter where it is.
*/
if (ppqmsgD == NULL || ptiSave != ptiT) {
/*
* If the source is younger than the last message in the
* destination, go to the end. Otherwise, start at the
* head of the desination list and find a place to insert
* the message.
*/
if (ptiT->pq->mlInput.pqmsgWriteLast != NULL &&
pqmsgS->msg.time >= ptiT->pq->mlInput.pqmsgWriteLast->msg.time) {
ppqmsgD = &ptiT->pq->mlInput.pqmsgWriteLast->pqmsgNext;
} else {
ppqmsgD = &ptiT->pq->mlInput.pqmsgRead;
}
pmlInput = &ptiT->pq->mlInput;
}
/*
* If we're not at the end of the destination AND the destination
* message time is younger than the source time, go on to
* the next message.
*/
while (*ppqmsgD != NULL && ((*ppqmsgD)->msg.time <= pqmsgS->msg.time)) {
ppqmsgD = &((*ppqmsgD)->pqmsgNext);
}
/*
* Link in the source before the dest message. Update
* it's next and prev pointers. Update the dest prev
* pointer.
*/
pqmsgT = pqmsgS;
pqmsgS = pqmsgS->pqmsgNext;
pqmsgT->pqmsgNext = *ppqmsgD;
if (*ppqmsgD != NULL) {
pqmsgT->pqmsgPrev = (*ppqmsgD)->pqmsgPrev;
(*ppqmsgD)->pqmsgPrev = pqmsgT;
} else {
pqmsgT->pqmsgPrev = pmlInput->pqmsgWriteLast;
pmlInput->pqmsgWriteLast = pqmsgT;
}
*ppqmsgD = pqmsgT;
ppqmsgD = &pqmsgT->pqmsgNext;
pmlInput->cMsgs++;
/*
* Preserve the 'idSysPeek' from the old queue.
* Carefull if the redistributed queue is the same as ptiT->pq.
*/
if (pqmsgT == (PQMSG)(pqRedist->idSysPeek) && (pqRedist != ptiT->pq)) {
if (ptiT->pq->idSysPeek == 0) {
CheckPtiSysPeek(6, ptiT->pq, pqRedist->idSysPeek);
ptiT->pq->idSysPeek = pqRedist->idSysPeek;
}
#if DBG
else
KdPrint(("idSysPeek %lx already set in pq %lx\n",
ptiT->pq->idSysPeek, ptiT->pq));
#endif
/*
* Set the 'idSysPeek' of this queue to 0 since
* we moved the idSysPeek to other queue
*/
CheckPtiSysPeek(7, pqRedist, 0);
pqRedist->idSysPeek = 0;
/*
* Preserve also 'ptiSysLock'
*/
if (ptiT->pq->ptiSysLock == NULL) {
CheckSysLock(4, ptiT->pq, pqRedist->ptiSysLock);
ptiT->pq->ptiSysLock = pqRedist->ptiSysLock;
CheckSysLock(5, pqRedist, NULL);
pqRedist->ptiSysLock = NULL;
}
#if DBG
else
KdPrint(("ptiSysLock %lx already set in pq %lx\n",
ptiT->pq->ptiSysLock, ptiT->pq));
#endif
}
/*
* Don't want the prev pointer on our message list to point
* to this message which is on a different list (doesn't
* really matter because we're about to link it anyway,
* but completeness shouldn't hurt).
*/
if (pqmsgS != NULL) {
pqmsgS->pqmsgPrev = NULL;
}
}
}
/***************************************************************************\
* CancelInputState
*
* This routine takes a queue and "cancels" input state in it - i.e., if the
* app thinks it is active, make it think it is not active, etc.
*
* 12-10-92 ScottLu Created.
\***************************************************************************/
VOID CancelInputState(
PTHREADINFO pti,
DWORD cmd)
{
PTHREADINFO ptiCurrent = PtiCurrent();
PWND pwndT;
TL tlpwndT;
TL tlpwndChild;
AAS aas;
/*
* In all cases, do not leave do any send messages or any callbacks!
* This is because this code is called from
* SetWindowsHook(WH_JOURNALPLAYBACK | WH_JOURNALRECORD). No app currently
* calling this routine expects to be called before this routine returns.
* (If you do callback before it returns, you'll break at least Access
* for Windows). - scottlu
*/
switch (cmd) {
case CANCEL_ACTIVESTATE:
/*
* Active state.
*/
pwndT = pti->pq->spwndActive;
ThreadLockWithPti(ptiCurrent, pwndT, &tlpwndT);
QueueNotifyMessage(pwndT, WM_NCACTIVATE, FALSE, 0);
QueueNotifyMessage(pwndT, WM_ACTIVATE,
MAKELONG(WA_INACTIVE, TestWF(pwndT, WFMINIMIZED)),
(LONG)NULL);
if (pwndT == pti->pq->spwndActive)
Unlock(&pti->pq->spwndActive);
aas.ptiNotify = GETPTI(pwndT);
aas.tidActDeact = (DWORD)GETPTI(pwndT)->Thread->Cid.UniqueThread;
aas.fActivating = FALSE;
aas.fQueueNotify = TRUE;
/*
* Even though this in an xxx call, it does NOT leave any critical
* sections (because fQueueNotify is TRUE).
*/
ThreadLockWithPti(ptiCurrent, GETPTI(pwndT)->rpdesk->pDeskInfo->spwnd->spwndChild, &tlpwndChild);
xxxInternalEnumWindow(GETPTI(pwndT)->rpdesk->pDeskInfo->spwnd->spwndChild,
(WNDENUMPROC_PWND)xxxActivateApp, (LONG)&aas, BWL_ENUMLIST);
ThreadUnlock(&tlpwndChild);
ThreadUnlock(&tlpwndT);
break;
case CANCEL_FOCUSSTATE:
/*
* Focus state.
*/
pwndT = pti->pq->spwndFocus;
ThreadLockWithPti(ptiCurrent, pwndT, &tlpwndT);
QueueNotifyMessage(pwndT, WM_KILLFOCUS, 0, 0);
if (pwndT == pti->pq->spwndFocus)
Unlock(&pti->pq->spwndFocus);
ThreadUnlock(&tlpwndT);
break;
case CANCEL_CAPTURESTATE:
/*
* Capture state.
*/
pwndT = pti->pq->spwndCapture;
ThreadLockWithPti(ptiCurrent, pwndT, &tlpwndT);
QueueNotifyMessage(pwndT, WM_CANCELMODE, 0, 0);
if (pwndT == pti->pq->spwndCapture)
Unlock(&pti->pq->spwndCapture);
ThreadUnlock(&tlpwndT);
break;
}
return;
}
/***************************************************************************\
* _AttachThreadInput (API)
* ReattachThreads
* AttachToQueue
* CheckTransferState
*
* Attaches a given thread to another input queue, either by attaching to
* a queue (referenced by another thread id), or detaching from one.
*
* 12-09-92 ScottLu Created.
\***************************************************************************/
#define CTS_DONOTHING 0
#define CTS_CANCELOLD 1
#define CTS_TRANSFER 2
DWORD CheckTransferState(
PTHREADINFO pti,
PQ pqAttach,
LONG offset,
BOOL fJoiningForeground)
{
PWND pwndOld, pwndNew, pwndForegroundState;
/*
* return 0: do nothing.
* return 1: cancel the old state.
* return 2: transfer the old state to the new state
*/
pwndOld = *(PWND *)(((BYTE *)pti->pq) + offset);
pwndNew = *(PWND *)(((BYTE *)pqAttach) + offset);
/*
* Make sure the old state even exists, and that the old state is
* owned by this thread. If not, nothing happens.
*/
if (pwndOld == NULL || GETPTI(pwndOld) != pti)
return CTS_DONOTHING;
/*
* If the new state already exists, cancel the old state.
*/
if (pwndNew != NULL)
return CTS_CANCELOLD;
/*
* Transfer this old state if this thread is not joining the foreground.
*/
if (gpqForeground == NULL || !fJoiningForeground)
return CTS_TRANSFER;
/*
* We're joining the foreground - only transfer the old state if we own
* that foreground state or if there is no foreground state.
*/
pwndForegroundState = *(PWND *)(((BYTE *)gpqForeground) + offset);
if (pwndForegroundState == NULL || pwndOld == pwndForegroundState)
return CTS_TRANSFER;
/*
* We're joining the foreground but we didn't set that foreground state.
* Don't allow the transfer of that state.
*/
return CTS_CANCELOLD;
}
void AttachToQueue(
PTHREADINFO pti,
PQ pqAttach,
PQ pqJournal,
BOOL fJoiningForeground)
{
PQMSG pqmsgT;
PQ pqDestroy;
/*
* Check active state.
*/
switch (CheckTransferState(pti, pqAttach,
FIELD_OFFSET(Q, spwndActive), fJoiningForeground)) {
case CTS_CANCELOLD:
CancelInputState(pti, CANCEL_ACTIVESTATE);
break;
case CTS_TRANSFER:
Lock(&pqAttach->spwndActive, pti->pq->spwndActive);
/*
* The caret usually follows the focus window, which follows
* the active window...
*/
if (pti->pq->caret.spwnd != NULL) {
if (GETPTI(pti->pq->caret.spwnd) == pti) {
/*
* Just copy the entire caret structure... that way we
* don't need to deal with locking/unlocking the spwnd.
*/
if (pqAttach->caret.spwnd == NULL) {
pqAttach->caret = pti->pq->caret;
pti->pq->caret.spwnd = NULL;
}
}
}
break;
}
/*
* Check focus state.
*/
switch (CheckTransferState(pti, pqAttach,
FIELD_OFFSET(Q, spwndFocus), fJoiningForeground)) {
case CTS_CANCELOLD:
CancelInputState(pti, CANCEL_FOCUSSTATE);
break;
case CTS_TRANSFER:
Lock(&pqAttach->spwndFocus, pti->pq->spwndFocus);
break;
}
/*
* Check capture state.
*/
switch (CheckTransferState(pti, pqAttach,
FIELD_OFFSET(Q, spwndCapture), fJoiningForeground)) {
case CTS_CANCELOLD:
CancelInputState(pti, CANCEL_CAPTURESTATE);
break;
case CTS_TRANSFER:
Lock(&pqAttach->spwndCapture, pti->pq->spwndCapture);
pqAttach->codeCapture = pti->pq->codeCapture;
break;
}
/*
* Check mouse tracking state.
*/
switch (CheckTransferState(pti, pqAttach,
FIELD_OFFSET(Q, spwndLastMouseMessage), fJoiningForeground)) {
case CTS_CANCELOLD:
ResetMouseTracking(pti->pq, NULL);
break;
case CTS_TRANSFER:
Lock(&pqAttach->spwndLastMouseMessage, pti->pq->spwndLastMouseMessage);
pqAttach->QF_flags |= pti->pq->QF_flags & (QF_TRACKMOUSEHOVER | QF_TRACKMOUSELEAVE);
break;
}
/*
* Check spwndActivePrev state. This check has some considerations.
* If the CTS_TRANSFER is returned, it usually means there was no
* prev-active in the attach-queue, and it will use the first
* window it encounters. Since we walk the thread-list, a out-of-zorder
* window could be chosen. So, to counter this, we'll check the
* attach-queue-next-prev against the thread-previous window to see
* if it is truly the next-zorder window.
*/
switch (CheckTransferState(pti, pqAttach,
FIELD_OFFSET(Q, spwndActivePrev), fJoiningForeground)) {
case CTS_TRANSFER:
Lock(&pqAttach->spwndActivePrev, pti->pq->spwndActivePrev);
break;
case CTS_CANCELOLD:
/*
* Check to see if the previous window is what we would expect it
* to be.
*/
if (pqAttach->spwndActive &&
(pqAttach->spwndActivePrev && pti->pq->spwndActivePrev) &&
(pqAttach->spwndActive->spwndNext == pti->pq->spwndActivePrev)) {
Lock(&pqAttach->spwndActivePrev, pti->pq->spwndActivePrev);
}
break;
}
if (pti == pti->pq->ptiSysLock) {
pqAttach->QF_flags = pti->pq->QF_flags;
/*
* Fix for 29967 "Start menu disappears when clicked and Office
* taskbar has focus!". Win95 uses a global counter instead of this
* flag. In NT when we click on Office taskbar and then on the Start
* Menu, MSoffice calls AttachThreadInput() which changes the Start
* Menu's queue and the new queue has the QF_ACTIVATIONCHANGE flag on.
* Inside the xxxMNLoop we test if this flag is on and if it is we
* exit the menu.
*/
if (!IsInsideMenuLoop(pti)) {
pqAttach->QF_flags &= ~QF_ACTIVATIONCHANGE;
}
}
if (gspwndCursor != NULL && pti == GETPTI(gspwndCursor)) {
LockQCursor(pqAttach, pti->pq->spcurCurrent);
}
/*
* Each thread has its own cursor level, which is a count of the number
* of times that app has called show/hide cursor. This gets added into
* the queue's count for a completely accurate count every time this
* queue recalculation is done.
*/
pqAttach->iCursorLevel += pti->iCursorLevel;
/*
* Pump up the new queue with the right input variables.
*/
pqAttach->ptiMouse = pti;
pqAttach->ptiKeyboard = pti;
/*
* Grab the alt-tab window if it exists and we don't already have
* one. Queues other than the RIT can have alt-tab windows, so
* we must make sure we clean up the extra windows.
*/
if (pti->pq->spwndAltTab != NULL && GETPTI(pti->pq->spwndAltTab) == pti &&
pqAttach->spwndAltTab == NULL) {
Lock(&pqAttach->spwndAltTab, pti->pq->spwndAltTab);
Unlock(&pti->pq->spwndAltTab);
pqAttach->QF_flags |= QF_INALTTAB;
pti->pq->QF_flags &= ~QF_INALTTAB;
}
pqDestroy = pti->pq;
/*
* Don't increment the thread count here because we already incremented
* it when we put it in pti->pqAttach. Since we're moving it from pqAttach
* to pq, we don't mess with the reference count.
*/
pti->pq = pqAttach;
/*
* If the thread is using the journal queue, leave the message list
* alone. Otherwise, redistribute the messages.
*/
if (pqDestroy != pqJournal) {
/*
* Remember the current message list so it can get redistributed taking
* into account ptiAttach's new queue.
*/
pqmsgT = pqDestroy->mlInput.pqmsgRead;
pqDestroy->mlInput.pqmsgRead = NULL;
pqDestroy->mlInput.pqmsgWriteLast = NULL;
pqDestroy->mlInput.cMsgs = 0;
/*
* Now redistribute the input messages from the old queue they go into the
* right queues.
*
* Preserve the 'idSysPeek' when redistributing the queue
*/
RedistributeInput(pqmsgT, pqDestroy);
/*
* Officially attach the new queue to this thread. Note that DestroyQueue()
* doesn't actually destroy anything until the thread reference count goes
* to 0.
*/
DestroyQueue(pqDestroy, pti);
} else {
UserAssert(pqDestroy->cThreads);
pqDestroy->cThreads--;
}
}
BOOL ReattachThreads(
BOOL fJournalAttach)
{
PTHREADINFO ptiCurrent = PtiCurrent();
PTHREADINFO pti;
PQ pqForegroundPrevNew;
PQ pqForegroundNew;
PQ pqAttach;
PQ pqJournal;
PLIST_ENTRY pHead, pEntry;
/*
* In all cases, do not leave do any send messages or any callbacks!
* This is because this code is called from
* SetWindowsHook(WH_JOURNALPLAYBACK | WH_JOURNALRECORD). No app currently
* calling this routine expects to be called before this routine returns.
* (If you do callback before it returns, you'll break at least Access
* for Windows). - scottlu
*/
/*
* Don't recalc attach info if this is a journal attach, because
* the journal attach code has already done this for us.
*/
if (!fJournalAttach) {
/*
* Now recalculate all the different queue groups, based on the
* attach requests. This fills in the pqAttach of each thread info
* with the new queue this thread belongs to. Always takes into
* account all attachment requests.
*/
RecalcThreadAttachment();
/*
* Make a guess about which queue is the journal queue.
*/
pqJournal = gpqForeground;
if (pqJournal == NULL)
pqJournal = ptiCurrent->pq;
/*
* If the queue is only used by one thread, perform normal processing.
*/
if (pqJournal->cThreads == 1) {
pqJournal = NULL;
} else {
/*
* Lock the queue to ensure that it stays valid
* until we have redistributed the input.
*/
(pqJournal->cLockCount)++;
}
} else {
pqJournal = NULL;
}
/*
* What will be the new foreground queue?
*/
pqForegroundNew = NULL;
if (gpqForeground != NULL && gpqForeground->spwndActive != NULL) {
pqForegroundNew = GETPTI(gpqForeground->spwndActive)->pqAttach;
}
pqForegroundPrevNew = NULL;
if (gpqForegroundPrev != NULL && gpqForegroundPrev->spwndActivePrev != NULL) {
pqForegroundPrevNew = GETPTI(gpqForegroundPrev->spwndActivePrev)->pqAttach;
}
while (TRUE) {
/*
* We need to leave the critical section in this code, so just
* find the next threadinfo to attach, then leave the loop before
* leaving the critical section.
*/
pHead = &ptiCurrent->rpdesk->PtiList;
for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink) {
pti = CONTAINING_RECORD(pEntry, THREADINFO, PtiLink);
if(pti->pqAttach == pti->pq) {
pti->pqAttach = NULL;
}
else if(pti->pqAttach != NULL) {
break;
}
}
/*
* If all threads have been attached, we're all done!
*/
if (pEntry == pHead)
break;
/*
* It is crucial that we NULL out pqAttach for this queue once
* we have it in a local variable because the NULL-ness of this
* field is checked in attach operations.
*/
pqAttach = pti->pqAttach;
pti->pqAttach = NULL;
AttachToQueue(pti, pqAttach, pqJournal, pqForegroundNew == pqAttach);
}
/*
* If we are doing a journal detach, redistribute the input messages
* from the old queue.
*/
if (pqJournal != NULL) {
PQMSG pqmsgRedist;
UserAssert(pqJournal->cLockCount);
(pqJournal->cLockCount)--;
pqmsgRedist = pqJournal->mlInput.pqmsgRead;
pqJournal->mlInput.pqmsgRead = NULL;
pqJournal->mlInput.pqmsgWriteLast = NULL;
pqJournal->mlInput.cMsgs = 0;
RedistributeInput(pqmsgRedist, pqJournal);
/*
* Only destroy the queue if it is no longer is use.
*/
if (pqJournal->cThreads == 0) {
pqJournal->cThreads = 1; // prevent underflow
DestroyQueue(pqJournal, pti);
}
}
/*
* If the current thread is not on the active desktop, do not
* change the global foreground state.
*/
if (PtiCurrent()->rpdesk != grpdeskRitInput)
return TRUE;
/*
* We're done attaching. gptiForeground hasn't changed... but
* gpqForeground has! Try not to leave NULL as the foreground.
*/
gpqForeground = pqForegroundNew;
gpqForegroundPrev = pqForegroundPrevNew;
if (gpqForeground == NULL) {
PWND pwndNewForeground;
PTHREADINFO pti = PtiCurrent();
pwndNewForeground = _GetNextQueueWindow(pti->rpdesk->pDeskInfo->spwnd->spwndChild, 0, FALSE);
/*
* Don't use xxxSetForegroundWindow2 because we must not leave
* the critical section. There is no currently active foreground
* so all that is needed is to post an activate event to the
* new foreground queue.
*/
if (pwndNewForeground != NULL) {
PostEventMessage(GETPTI(pwndNewForeground),
GETPTI(pwndNewForeground)->pq,QEVENT_ACTIVATE, NULL, 0,
0 , (DWORD)HWq(pwndNewForeground));
}
}
SetFMouseMoved();
return TRUE;
}
BOOL _AttachThreadInput(
PTHREADINFO ptiAttach,
PTHREADINFO ptiAttachTo,
BOOL fAttach)
{
CheckCritIn();
/*
* Attaching to yourself doesn't make any sense.
*/
if (ptiAttach == ptiAttachTo)
return FALSE;
/*
* Will this thread allow attaching? Shell threads and system threads
* won't allow attaching.
*/
if (ptiAttachTo->TIF_flags & TIF_DONTATTACHQUEUE)
return FALSE;
if (ptiAttach->TIF_flags & TIF_DONTATTACHQUEUE)
return FALSE;
/*
* Don't allow attaching across desktops, either.
*/
if (ptiAttachTo->rpdesk != ptiAttach->rpdesk)
return FALSE;
/*
* If attaching, make a new attachinfo structure for this thread.
* If not attaching, remove an existing attach reference.
*/
if (fAttach) {
PATTACHINFO pai;
/*
* Alloc a new attachinfo struct, fill it in, link it in.
*/
if ((pai = (PATTACHINFO)UserAllocPool(sizeof(ATTACHINFO), TAG_ATTACHINFO)) == NULL)
return FALSE;
pai->pti1 = ptiAttach;
pai->pti2 = ptiAttachTo;;
pai->paiNext = gpai;
gpai = pai;
} else {
PATTACHINFO *ppai;
BOOL fFound = FALSE;
/*
* Search for this attachinfo struct. If we can't find it, fail.
* If we do find it, unlink it and free it.
*/
for (ppai = &gpai; (*ppai) != NULL; ppai = &(*ppai)->paiNext) {
if (((*ppai)->pti2 == ptiAttachTo) && ((*ppai)->pti1 == ptiAttach)) {
PATTACHINFO paiKill = *ppai;
fFound = TRUE;
*ppai = (*ppai)->paiNext;
UserFreePool((HLOCAL)paiKill);
break;
}
}
/*
* If we couldn't find this reference, then fail.
*/
if (!fFound) {
return FALSE;
}
}
/*
* Now do the actual reattachment work for all threads - unless we're
* journalling. If we did by mistake do attachment while journalling
* was occuring, journalling would be hosed because journalling requires
* all threads to be attached - but it is also treated as a special
* case so it doesn't affect the ATTACHINFO structures. Therefore
* recalcing attach info based on ATTACHINFO structures would break
* the attachment required for journalling.
*/
if (!FJOURNALRECORD() && !FJOURNALPLAYBACK())
return ReattachThreads(FALSE);
return TRUE;
}
/***************************************************************************\
* _SetMessageExtraInfo (API)
*
* History:
* 1-May-1995 FritzS
\***************************************************************************/
LONG _SetMessageExtraInfo(LONG lData)
{
LONG lRet;
PTHREADINFO pti = PtiCurrent();
lRet = pti->pq->ExtraInfo;
pti->pq->ExtraInfo = lData;
return lRet;
}