test: input.c Source File

Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

# input.c

Go to the documentation of this file.

    00001 /****************************** Module Header ******************************\
    00002 * Module Name: input.c
    00003 *
    00004 * Copyright (c) 1985 - 1999, Microsoft Corporation
    00005 *
    00006 * This module contains the core functions of the input sub-system
    00007 *
    00008 * History:
    00009 * 10-18-90 DavidPe      Created.
    00010 * 02-14-91 mikeke       Added Revalidation code
    00011 \***************************************************************************/
    00012
    00013 #include "precomp.h"
    00014 #pragma hdrstop
    00015
    00016 //#define MARKPATH
    00017 #ifdef MARKPATH
    00018 BOOL gfMarkPath;
    00019 #endif
    00020
    00021 #if DEBUGTAGS
    00022
    00023 int  gnSysPeekSearch;
    00024
    00025 void CheckPtiSysPeek(int where, PQ pq, ULONG_PTR newIdSysPeek)
    00026 {
    00027     PTHREADINFO ptiCurrent = PtiCurrent();
    00028     DWORD       dwRip;
    00029
    00030     dwRip = (newIdSysPeek > 1) ? RIP_THERESMORE : 0;
    00031     TAGMSG5(DBGTAG_SysPeek | dwRip,
    00032             "%d pti %#p sets id %#p to pq %#p ; old id %#p",
    00033             where, ptiCurrent, newIdSysPeek, pq, pq->idSysPeek);
    00034
    00035     if (newIdSysPeek > 1) {
    00036         PQMSG pqmsg = (PQMSG)newIdSysPeek;
    00037         TAGMSG5(DBGTAG_SysPeek | RIP_NONAME,
    00038                 "-> msg %lx hwnd %#p w %#p l %#p pti %#p",
    00039                 pqmsg->msg.message, pqmsg->msg.hwnd,  pqmsg->msg.wParam,
    00040                 pqmsg->msg.lParam,  pqmsg->pti);
    00041     }
    00042 }
    00043
    00044 void CheckSysLock(int where, PQ pq, PTHREADINFO ptiSysLock)
    00045 {
    00046     PTHREADINFO ptiCurrent = PtiCurrent();
    00047     TAGMSG5(DBGTAG_SysPeek,
    00048             "%d pti %#p sets ptiSL %#p to pq %#p ; old ptiSL %#p\n",
    00049             where, ptiCurrent, ptiSysLock, pq, pq->ptiSysLock);
    00050 }
    00051 #endif //DEBUGTAGS
    00052
    00053 #if DBG
    00054 BOOL gfLogPlayback;
    00055
    00056 LPCSTR aszMouse[] = {
    00057     "WM_MOUSEMOVE",
    00058     "WM_LBUTTONDOWN",
    00059     "WM_LBUTTONUP",
    00060     "WM_LBUTTONDBLCLK",
    00061     "WM_RBUTTONDOWN",
    00062     "WM_RBUTTONUP",
    00063     "WM_RBUTTONDBLCLK",
    00064     "WM_MBUTTONDOWN",
    00065     "WM_MBUTTONUP",
    00066     "WM_MBUTTONDBLCLK"
    00067     "WM_MOUSEWHEEL",
    00068     "WM_XBUTTONDOWN",
    00069     "WM_XBUTTONUP",
    00070     "WM_XBUTTONDBLCLK",
    00071 };
    00072 LPCSTR aszKey[] = {
    00073     "WM_KEYDOWN",
    00074     "WM_KEYUP",
    00075     "WM_CHAR",
    00076     "WM_DEADCHAR",
    00077     "WM_SYSKEYDOWN",
    00078     "WM_SYSKEYUP",
    00079     "WM_SYSCHAR",
    00080     "WM_SYSDEADCHAR"
    00081 };
    00082 #endif  // DBG
    00083
    00084 #define CANCEL_ACTIVESTATE  0
    00085 #define CANCEL_FOCUSSTATE   1
    00086 #define CANCEL_CAPTURESTATE 2
    00087
    00088 #define KEYSTATESIZE    (CBKEYSTATE + CBKEYSTATERECENTDOWN)
    00089
    00090
    00091 /*
    00092  * xxxGetNextSysMsg return values
    00093  */
    00094 #define PQMSG_PLAYBACK       ((PQMSG)1)
    00095
    00096 BOOL xxxScanSysQueue(PTHREADINFO ptiCurrent, LPMSG lpMsg, PWND pwndFilter,
    00097         UINT msgMinFilter, UINT msgMaxFilter, DWORD flags, DWORD fsReason);
    00098 BOOL xxxReadPostMessage(PTHREADINFO pti, LPMSG lpMsg, PWND pwndFilter,
    00099         UINT msgMin, UINT msgMax, BOOL fRemoveMsg);
    00100 void CleanEventMessage(PQMSG pqmsg);
    00101
    00102 /***************************************************************************\
    00103 * xxxWaitMessage (API)
    00104 *
    00105 * This API will block until an input message is received on
    00106 * the current queue.
    00107 *
    00108 * History:
    00109 * 10-25-90 DavidPe      Created.
    00110 \***************************************************************************/
    00111
    00112 BOOL xxxWaitMessage(VOID)
    00113 {
    00114     return xxxSleepThread(QS_ALLINPUT | QS_EVENT, 0, TRUE);
    00115 }
    00116
    00117
    00118 /***************************************************************************\
    00119 * CheckProcessBackground/Foreground
    00120 *
    00121 * This checks to see if the process is at the right priority. If CSPINS is
    00122 * greater than CSPINBACKGROUND and the process isn't at the background
    00123 * priority, put it there. If it is less than this and should be foreground
    00124 * and isn't put it there.
    00125 *
    00126 * Need to put foreground spinning apps in the background (make it the same
    00127 * priority as all other background apps) so that bad apps can still communicate
    00128 * with apps in the background via dde, for example. There are other cases
    00129 * where spinning foreground apps affect the server, the printer spooler, and
    00130 * mstest scenarios. On Win3.1, calling PeekMessage() involves making a trip
    00131 * through the scheduler, forcing other apps to run. Processes run with
    00132 * priority on NT, where the foreground process gets foreground priority for
    00133 * greater responsiveness.
    00134 *
    00135 * If an app calls peek/getmessage without idling, count how many times this
    00136 * happens - if it happens CSPINBACKGROUND or more times, make the process
    00137 * background. This handles most of the win3.1 app compatibility spinning
    00138 * cases. If there is no priority contention, the app continues to run at
    00139 * full speed (no performance scenarios should be adversely affected by this).
    00140 *
    00141 * This solves these cases:
    00142 *
    00143 * - high speed timer not allowing app to go idle
    00144 * - post/peek loop (receiving a WM_ENTERIDLE, and posting a msg, for example)
    00145 * - peek no remove loop (winword "idle" state, most dde loops, as examples)
    00146 *
    00147 * But doesn't protect against these sort of cases:
    00148 *
    00149 * - app calls getmessage, then goes into a tight loop
    00150 * - non-gui threads in tight cpu loops
    00151 *
    00152 * 02-08-93 ScottLu      Created.
    00153 \***************************************************************************/
    00154
    00155 void CheckProcessForeground(
    00156     PTHREADINFO pti)
    00157 {
    00158     PTHREADINFO ptiT;
    00159
    00160     /*
    00161      * Check to see if we need to move this process into foreground
    00162      * priority.
    00163      */
    00164     pti->pClientInfo->cSpins = 0;
    00165     pti->TIF_flags &= ~TIF_SPINNING;
    00166     pti->pClientInfo->dwTIFlags = pti->TIF_flags;
    00167
    00168     if (pti->ppi->W32PF_Flags & W32PF_FORCEBACKGROUNDPRIORITY) {
    00169         /*
    00170          * See if any thread of this process is spinning. If none
    00171          * are, we can remove the force to background.
    00172          */
    00173         for (ptiT = pti->ppi->ptiList; ptiT != NULL; ptiT = ptiT->ptiSibling) {
    00174             if (ptiT->TIF_flags & TIF_SPINNING)
    00175                 return;
    00176         }
    00177
    00178         pti->ppi->W32PF_Flags &= ~W32PF_FORCEBACKGROUNDPRIORITY;
    00179         if (pti->ppi == gppiWantForegroundPriority) {
    00180             SetForegroundPriority(pti, TRUE);
    00181         }
    00182     }
    00183 }
    00184
    00185 /***************************************************************************\
    00186 * xxxInternalGetMessage
    00187 *
    00188 * This routine is the worker for both xxxGetMessage() and xxxPeekMessage()
    00189 * and is modelled after its win3.1 counterpart. From Win3.1:
    00190 *
    00191 * Get msg from the app queue or sys queue if there is one that matches
    00192 * hwndFilter and matches msgMin/msgMax. If no messages in either queue, check
    00193 * the QS_PAINT and QS_TIMER bits, call DoPaint or DoTimer to Post the
    00194 * appropriate message to the application queue, and then Read that message.
    00195 * Otherwise, if in GetMessage, Sleep until a wake bit is set indicating there
    00196 * is something we need to do.  If in PeekMessage, return to caller. Before
    00197 * reading messages from the queues, check to see if the QS_SENDMESSAGE bit
    00198 * is set, and if so, call ReceiveMessage().
    00199 *
    00200 * New for NT5: HIWORD(flags) contains a wake mask provided by the caller.
    00201 *   This mask is passed to CalcWakeMask to be combined with the mask generated
    00202 *   from msgMin and MsgMax. The default mask includes QS_SENDMESSAGE
    00203 *   now; we won't call xxxReceiveMessages (directly) unless this bit is set;
    00204 *   however, to avoid potentail deadlocks and maintain NT4 compatibility as
    00205 *   much as possible, we fail the call if QS_SENDMESSAGE is set in fsWakeBits
    00206 *   but not requested by the caller. The same applies to QS_EVENT which we would
    00207 *   always process in NT4.
    00208 *
    00209 *
    00210 * 10-19-92 ScottLu      Created.
    00211 \***************************************************************************/
    00212
    00213 BOOL xxxInternalGetMessage(
    00214     LPMSG lpMsg,
    00215     HWND hwndFilter,
    00216     UINT msgMin,
    00217     UINT msgMax,
    00218     UINT flags,
    00219     BOOL fGetMessage)
    00220 {
    00221     UINT fsWakeBits;
    00222     UINT fsWakeMask;
    00223     UINT fsRemoveBits;
    00224     PTHREADINFO ptiCurrent;
    00225     PW32PROCESS W32Process;
    00226     PWND pwndFilter;
    00227     BOOL fLockPwndFilter;
    00228     TL tlpwndFilter;
    00229     BOOL fRemove;
    00230     BOOL fExit;
    00231     PQ pq;
    00232 #ifdef MARKPATH
    00233     DWORD pathTaken = 0;
    00234
    00235 #define PATHTAKEN(x)  pathTaken  |= x
    00236 #define DUMPPATHTAKEN() if (gfMarkPath) DbgPrint("xxxInternalGetMessage path:%08x\n", pathTaken)
    00237 #else
    00238 #define PATHTAKEN(x)
    00239 #define DUMPPATHTAKEN()
    00240 #endif
    00241
    00242     CheckCritIn();
    00243     UserAssert(IsWinEventNotifyDeferredOK());
    00244
    00245     ptiCurrent = PtiCurrent();
    00246
    00247     /*
    00248      * PeekMessage accepts NULL, 0x0000FFFF, and -1 as valid HWNDs.
    00249      * If hwndFilter is invalid we can't just return FALSE because that will
    00250      * hose existing badly behaved apps who might attempt to dispatch
    00251      * the random contents of pmsg.
    00252      */
    00253     if ((hwndFilter == (HWND)-1) || (hwndFilter == (HWND)0x0000FFFF)) {
    00254         hwndFilter = (HWND)1;
    00255     }
    00256
    00257     if ((hwndFilter != NULL) && (hwndFilter != (HWND)1)) {
    00258         if ((pwndFilter = ValidateHwnd(hwndFilter)) == NULL) {
    00259             lpMsg->hwnd = NULL;
    00260             lpMsg->message = WM_NULL;
    00261             PATHTAKEN(1);
    00262             DUMPPATHTAKEN();
    00263             if (fGetMessage)
    00264                 return -1;
    00265             else
    00266                 return 0;
    00267         }
    00268
    00269         ThreadLockAlwaysWithPti(ptiCurrent, pwndFilter, &tlpwndFilter);
    00270         fLockPwndFilter = TRUE;
    00271
    00272     } else {
    00273         pwndFilter = (PWND)hwndFilter;
    00274         fLockPwndFilter = FALSE;
    00275     }
    00276
    00277     /*
    00278      * Add one to our spin count. At this end of this routine we'll check
    00279      * to see if the spin count gets >= CSPINBACKGROUND. If so we'll put this
    00280      * process into the background.
    00281      */
    00282     ptiCurrent->pClientInfo->cSpins++;
    00283
    00284     /*
    00285      * Check to see if the startglass is on, and if so turn it off and update.
    00286      */
    00287     W32Process = W32GetCurrentProcess();
    00288     if (W32Process->W32PF_Flags & W32PF_STARTGLASS) {
    00289
    00290         /*
    00291          * This app is no longer in "starting" mode. Recalc when to hide
    00292          * the app starting cursor.
    00293          */
    00294         W32Process->W32PF_Flags &= ~W32PF_STARTGLASS;
    00295         /*
    00296          * Don't need DeferWinEventNotify() - xxxDoSysExpunge below doesn't
    00297          */
    00298         zzzCalcStartCursorHide(NULL, 0);
    00299     }
    00300
    00301     /*
    00302      * Next check to see if any .dlls need freeing in
    00303      * the context of this client (used for windows hooks).
    00304      */
    00305     if (ptiCurrent->ppi->cSysExpunge != gcSysExpunge) {
    00306         ptiCurrent->ppi->cSysExpunge = gcSysExpunge;
    00307         if (ptiCurrent->ppi->dwhmodLibLoadedMask & gdwSysExpungeMask)
    00308             xxxDoSysExpunge(ptiCurrent);
    00309     }
    00310
    00311     /*
    00312      * Set up BOOL fRemove local variable from for ReadMessage()
    00313      */
    00314     fRemove = flags & PM_REMOVE;
    00315
    00316     /*
    00317      * Unlock the system queue if it's owned by us.
    00318      */
    00319     /*
    00320      * If we're currently processing a message, unlock the input queue
    00321      * because the sender, who is blocked, might be the owner, and in order
    00322      * to reply, the receiver may need to read keyboard / mouse input.
    00323      */
    00324     /*
    00325      * If this thread has the input queue locked and the last message removed
    00326      * is the last message we looked at, then unlock - we're ready for anyone
    00327      * to get the next message.
    00328      */
    00329     pq = ptiCurrent->pq;
    00330     if (   (ptiCurrent->psmsCurrent != NULL)
    00331         || (pq->ptiSysLock == ptiCurrent && pq->idSysLock == ptiCurrent->idLast)
    00332        ) {
    00333         CheckSysLock(1, pq, NULL);
    00334         pq->ptiSysLock = NULL;
    00335         PATHTAKEN(2);
    00336     } else if (pq->ptiSysLock
    00337                 && (pq->ptiSysLock->cVisWindows == 0)
    00338                 && (PhkFirstGlobalValid(ptiCurrent, WH_JOURNALPLAYBACK) != NULL)) {
    00339         /*
    00340          * If the thread that has the system queue lock has no windows visible
    00341          * (can happen if it just hid its last window), don't expect it to call
    00342          * GetMessage() again! - unlock the system queue. --- ScottLu
    00343          * This condition creates a hole by which a second thread attached to
    00344          * the same queue as thread 1 can alter pq->idSysPeek during a callback
    00345          * made by thread 1 so that thread 1 will delete the wrong message
    00346          * (losing keystrokes - causing Shift to appear be stuck down editing a
    00347          * Graph5 caption embedded in Word32 document #5032.  However, MSTEST
    00348          * requires this hole, so allow it if Journal Playback is occurring
    00349          * #8850 (yes, a hack)  Chicago also has this behavior.  --- IanJa
    00350          */
    00351         CheckSysLock(2, pq, NULL);
    00352         pq->ptiSysLock = NULL;
    00353         PATHTAKEN(3);
    00354     }
    00355     if (pq->ptiSysLock != ptiCurrent) {
    00356         ptiCurrent->pcti->CTIF_flags &= ~CTIF_SYSQUEUELOCKED;
    00357     }
    00358
    00359     /*
    00360      * If msgMax == 0 then msgMax = -1: that makes our range checking only
    00361      * have to deal with msgMin < msgMax.
    00362      */
    00363     if (msgMax == 0)
    00364         msgMax--;
    00365
    00366     /*
    00367      * Compute the QS* mask that corresponds to the message range
    00368      *  and the wake mask filter (HIWORD(flags))
    00369      */
    00370     fsWakeMask = CalcWakeMask(msgMin, msgMax, HIWORD(flags));
    00371     ptiCurrent->fsChangeBitsRemoved = 0;
    00372
    00373     /*
    00374      * If we can yield and one or more events were skipped,
    00375      * set the wakebits for event
    00376      */
    00377     if (!(flags & PM_NOYIELD) && ptiCurrent->TIF_flags & TIF_DELAYEDEVENT) {
    00378         ptiCurrent->pcti->fsWakeBits |= QS_EVENT;
    00379         ptiCurrent->pcti->fsChangeBits |= QS_EVENT;
    00380         ptiCurrent->TIF_flags &= ~TIF_DELAYEDEVENT;
    00381         ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags;
    00382     }
    00383
    00384     while (TRUE) {
    00385
    00386         /*
    00387          * Restore any wake bits saved while journalling
    00388          */
    00389         ptiCurrent->pcti->fsWakeBits |= ptiCurrent->pcti->fsWakeBitsJournal;
    00390
    00391         /*
    00392          * If we need to recalc queue attachments, do it here. Do it on the
    00393          * right desktop or else the queues will get created in the wrong
    00394          * heap.
    00395          */
    00396         if (ptiCurrent->rpdesk == gpdeskRecalcQueueAttach) {
    00397             gpdeskRecalcQueueAttach = NULL;
    00398
    00399             if (ptiCurrent->rpdesk != NULL && !FJOURNALRECORD() && !FJOURNALPLAYBACK()) {
    00400                 /*
    00401                  * No need to DeferWinEventNotify(): a call to
    00402                  * xxxReceiveMessages is made just below
    00403                  */
    00404                 zzzReattachThreads(FALSE);
    00405                 PATHTAKEN(4);
    00406             }
    00407         }
    00408
    00409         /*
    00410          * Remember what change bits we're clearing. This is important to
    00411          * fix a bug in the input model: If an app receives a sent message
    00412          * from within SleepThread(), then does PostMessage() (which sets
    00413          * QS_POSTMESSAGE), then does a PeekMessage(...) for some different
    00414          * posted message (clears QS_POSTMESSAGE in fsChangeBits), then returns
    00415          * back into SleepThread(), it won't wake up to retrieve that newly
    00416          * posted message because the change bits are cleared.
    00417          *
    00418          * What we do is remember the change bits that are being cleared.
    00419          * Then, when we return to SleepThread(), we put these remembered
    00420          * bits back into the change bits that also have corresponding
    00421          * bits in the wakebits (so we don't set changebits that represent
    00422          * input that isn't there anymore). This way, the app will retrieve
    00423          * the newly posted message refered to earlier.
    00424          * - scottlu
    00425          *
    00426          * New for NT5: Since QS_SENDMESSAGE was never set it fsWakeMask before (NT4),
    00427          *  it was never cleared from fsChangeBits. For compatibility, we won't clear
    00428          *  it now even if specified in fsWakeMask; hence we won't affect any one
    00429          *  checking for QS_SENDMESSAGE in pcti->fsChangeBits.
    00430          */
    00431         fsRemoveBits = fsWakeMask & ~QS_SENDMESSAGE;
    00432         ptiCurrent->fsChangeBitsRemoved |= ptiCurrent->pcti->fsChangeBits & fsRemoveBits;
    00433
    00434         /*
    00435          * Clear the change bits that we're looking at, in order to detect
    00436          * incoming events that may occur the last time we checked the wake
    00437          * bits.
    00438          */
    00439         ptiCurrent->pcti->fsChangeBits &= ~fsRemoveBits;
    00440
    00441         /*
    00442          * Check for sent messages. Check the the actual wake bits (i.e, from pcti)
    00443          *  so we know for real.
    00444          */
    00445         if (ptiCurrent->pcti->fsWakeBits & fsWakeMask & QS_SENDMESSAGE) {
    00446             xxxReceiveMessages(ptiCurrent);
    00447         } else if (ptiCurrent->pcti->fsWakeBits & QS_SENDMESSAGE) {
    00448             RIPMSG2(RIP_WARNING, "xxxInternalGetMessage:(1st test) sendmsgs pending. Bits:%#lx Mask:%#lx",
    00449                         ptiCurrent->pcti->fsWakeBits, fsWakeMask);
    00450             goto NoMessages;
    00451         }
    00452
    00453         /*
    00454          * Check to see if we have any input we want.
    00455          */
    00456         if ((ptiCurrent->pcti->fsWakeBits & fsWakeMask) == 0) {
    00457             PATHTAKEN(8);
    00458             goto NoMessages;
    00459         }
    00460         fsWakeBits = ptiCurrent->pcti->fsWakeBits;
    00461
    00462         /*
    00463          * If the queue lock is != NULL (ptiSysLock) and it is this thread that
    00464          * locked it, then go get the message from the system queue. This is
    00465          * to prevent messages posted after a PeekMessage/no-remove from being
    00466          * seen before the original message from the system queue. (Aldus
    00467          * Pagemaker requires this) (bobgu 8/5/87).
    00468          */
    00469         if (ptiCurrent->pq->ptiSysLock == ptiCurrent &&
    00470                 (ptiCurrent->pq->QF_flags & QF_LOCKNOREMOVE)) {
    00471             /*
    00472              * Does the caller want mouse / keyboard?
    00473              */
    00474             if (fsWakeBits & fsWakeMask & (QS_INPUT | QS_EVENT)) {
    00475
    00476                 /*
    00477                  * It should never get here during exit.
    00478                  */
    00479                 UserAssert(gbExitInProgress == FALSE);
    00480
    00481                 if (xxxScanSysQueue(ptiCurrent, lpMsg, pwndFilter,
    00482                         msgMin, msgMax, flags,
    00483                         fsWakeBits & fsWakeMask & (QS_INPUT | QS_EVENT))) {
    00484
    00485                     PATHTAKEN(0x10);
    00486                     break;
    00487                 }
    00488             } else if (fsWakeBits & QS_EVENT) {
    00489                 RIPMSG2(RIP_WARNING,
    00490                     "xxxInternalGetMessage:(1st test)events pending. Bits:%#lx Mask:%#lx",
    00491                     fsWakeBits, fsWakeMask);
    00492                 goto NoMessages;
    00493             }
    00494         }
    00495
    00496         /*
    00497          * See if there's a message in the application queue.
    00498          */
    00499         if (fsWakeBits & fsWakeMask & QS_POSTMESSAGE) {
    00500             if (xxxReadPostMessage(ptiCurrent, lpMsg, pwndFilter,
    00501                     msgMin, msgMax, fRemove)) {
    00502                 PATHTAKEN(0x20);
    00503                 break;
    00504             }
    00505         }
    00506
    00507         /*
    00508          * Time to scan the raw input queue for input. First check to see
    00509          * if the caller wants mouse / keyboard input.
    00510          */
    00511         if (fsWakeBits & fsWakeMask & (QS_INPUT | QS_EVENT)) {
    00512
    00513             /*
    00514              * It should never get here during exit.
    00515              */
    00516             UserAssert(gbExitInProgress == FALSE);
    00517
    00518             if (xxxScanSysQueue(ptiCurrent, lpMsg, pwndFilter,
    00519                     msgMin, msgMax, flags,
    00520                     fsWakeBits & fsWakeMask & (QS_INPUT | QS_EVENT))) {
    00521                 PATHTAKEN(0x40);
    00522                 break;
    00523             }
    00524         } else if (fsWakeBits & QS_EVENT) {
    00525             RIPMSG2(RIP_WARNING, "xxxInternalGetMessage:(2nd test)events pending. Bits:%#lx Mask:%#lx",
    00526                         fsWakeBits, fsWakeMask);
    00527             goto NoMessages;
    00528         }
    00529
    00530         /*
    00531          * Check for sent messages. Check the the actual wake bits (i.e, from pcti)
    00532          *  so we know for real.
    00533          */
    00534         if (ptiCurrent->pcti->fsWakeBits & fsWakeMask & QS_SENDMESSAGE) {
    00535             xxxReceiveMessages(ptiCurrent);
    00536         } else if (ptiCurrent->pcti->fsWakeBits & QS_SENDMESSAGE) {
    00537             RIPMSG2(RIP_WARNING, "xxxInternalGetMessage:(2nd test)sendmsgs pending. Bits:%#lx Mask:%#lx",
    00538                         ptiCurrent->pcti->fsWakeBits, fsWakeMask);
    00539             goto NoMessages;
    00540         }
    00541
    00542         /*
    00543          * Get new input bits.
    00544          */
    00545         if ((ptiCurrent->pcti->fsWakeBits & fsWakeMask) == 0) {
    00546             PATHTAKEN(0x80);
    00547             goto NoMessages;
    00548         }
    00549         fsWakeBits = ptiCurrent->pcti->fsWakeBits;
    00550
    00551         /*
    00552          * Does the caller want paint messages? If so, try to find a paint.
    00553          */
    00554         if (fsWakeBits & fsWakeMask & QS_PAINT) {
    00555             if (DoPaint(pwndFilter, lpMsg)) {
    00556                 PATHTAKEN(0x100);
    00557                 break;
    00558             }
    00559         }
    00560
    00561         /*
    00562          * We must yield for 16 bit apps before checking timers or an app
    00563          * that has a fast timer could chew up all the time and never let
    00564          * anyone else run.
    00565          *
    00566          * NOTE: This could cause PeekMessage() to yield TWICE, if the user
    00567          * is filtering with a window handle. If the DoTimer() call fails
    00568          * then we end up yielding again.
    00569          */
    00570         if (!(flags & PM_NOYIELD)) {
    00571             /*
    00572              * This is the point where windows would yield.  Here we wait to wake
    00573              * up any threads waiting for this thread to hit "idle state".
    00574              */
    00575             zzzWakeInputIdle(ptiCurrent);
    00576
    00577             /*
    00578              * Yield and receive pending messages.
    00579              */
    00580             xxxUserYield(ptiCurrent);
    00581
    00582             /*
    00583              * Check new input buts and receive pending messages.
    00584              */
    00585             if (ptiCurrent->pcti->fsWakeBits & fsWakeMask & QS_SENDMESSAGE) {
    00586                 xxxReceiveMessages(ptiCurrent);
    00587             } else if (ptiCurrent->pcti->fsWakeBits & QS_SENDMESSAGE) {
    00588                 RIPMSG2(RIP_WARNING, "xxxInternalGetMessage:(3rd test) sendmsgs pending. Bits:%#lx Mask:%#lx",
    00589                             ptiCurrent->pcti->fsWakeBits, fsWakeMask);
    00590                 goto NoMessages;
    00591             }
    00592
    00593             if ((ptiCurrent->pcti->fsWakeBits & fsWakeMask) == 0) {
    00594
    00595                 PATHTAKEN(0x200);
    00596                 goto NoMessages;
    00597             }
    00598             fsWakeBits = ptiCurrent->pcti->fsWakeBits;
    00599         }
    00600
    00601         /*
    00602          * Does the app want timer messages, and if there one pending?
    00603          */
    00604         if (fsWakeBits & fsWakeMask & QS_TIMER) {
    00605             if (DoTimer(pwndFilter)) {
    00606                 /*
    00607                  * DoTimer() posted the message into the app's queue,
    00608                  * so start over and we'll grab it from there.
    00609                  */
    00610                  PATHTAKEN(0x400);
    00611                  continue;
    00612             }
    00613         }
    00614
    00615 NoMessages:
    00616         /*
    00617          * Looks like we have no input. If we're being called from GetMessage()
    00618          * then go to sleep until we find something.
    00619          */
    00620         if (!fGetMessage) {
    00621             /*
    00622              * This is one last check for pending sent messages. It also
    00623              * yields. Win3.1 does this.
    00624              */
    00625             if (!(flags & PM_NOYIELD)) {
    00626                 /*
    00627                  * This is the point where windows yields. Here we wait to wake
    00628                  * up any threads waiting for this thread to hit "idle state".
    00629                  */
    00630                 zzzWakeInputIdle(ptiCurrent);
    00631
    00632                 /*
    00633                  * Yield and receive pending messages.
    00634                  */
    00635                 xxxUserYield(ptiCurrent);
    00636             }
    00637             PATHTAKEN(0x800);
    00638             goto FalseExit;
    00639         }
    00640
    00641         /*
    00642          * This is a getmessage not a peekmessage, so sleep. When we sleep,
    00643          * zzzWakeInputIdle() is called to wake up any apps waiting on this
    00644          * app to go idle.
    00645          */
    00646         if (!xxxSleepThread(fsWakeMask, 0, TRUE))
    00647             goto FalseExit;
    00648     } /* while (TRUE) */
    00649
    00650     /*
    00651      * If we're here then we have input for this queue. Call the
    00652      * GetMessage() hook with this input.
    00653      */
    00654     if (IsHooked(ptiCurrent, WHF_GETMESSAGE))
    00655         xxxCallHook(HC_ACTION, flags, (LPARAM)lpMsg, WH_GETMESSAGE);
    00656
    00657     /*
    00658      * If called from PeekMessage(), return TRUE.
    00659      */
    00660     if (!fGetMessage) {
    00661         PATHTAKEN(0x1000);
    00662         goto TrueExit;
    00663     }
    00664
    00665     /*
    00666      * Being called from GetMessage(): return FALSE if the message is WM_QUIT,
    00667      * TRUE otherwise.
    00668      */
    00669     if (lpMsg->message == WM_QUIT) {
    00670         PATHTAKEN(0x2000);
    00671         goto FalseExit;
    00672     }
    00673
    00674     /*
    00675      * Fall through to TrueExit...
    00676      */
    00677
    00678 TrueExit:
    00679     /*
    00680      * Update timeLastRead. We use this for hung app calculations.
    00681      */
    00682     SET_TIME_LAST_READ(ptiCurrent);
    00683     fExit = TRUE;
    00684     PATHTAKEN(0x4000);
    00685     goto Exit;
    00686
    00687 FalseExit:
    00688     fExit = FALSE;
    00689
    00690 Exit:
    00691     if (fLockPwndFilter)
    00692         ThreadUnlock(&tlpwndFilter);
    00693
    00694     /*
    00695      * see CheckProcessBackground() comment above
    00696      * Check to see if we need to move this process into background
    00697      * priority.
    00698      */
    00699     if (ptiCurrent->pClientInfo->cSpins >= CSPINBACKGROUND) {
    00700
    00701         ptiCurrent->pClientInfo->cSpins = 0;
    00702
    00703         if (!(ptiCurrent->TIF_flags & TIF_SPINNING)) {
    00704
    00705             ptiCurrent->TIF_flags |= TIF_SPINNING;
    00706             ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags;
    00707
    00708             if (!(ptiCurrent->ppi->W32PF_Flags & W32PF_FORCEBACKGROUNDPRIORITY)) {
    00709
    00710                 ptiCurrent->ppi->W32PF_Flags |= W32PF_FORCEBACKGROUNDPRIORITY;
    00711
    00712                 if (ptiCurrent->ppi == gppiWantForegroundPriority) {
    00713                     SetForegroundPriority(ptiCurrent, FALSE);
    00714                 }
    00715             }
    00716         }
    00717
    00718         /*
    00719          * For spinning Message loops, we need to take the 16bit-thread out
    00720          * of the scheduler temporarily so that other processes can get a chance
    00721          * to run.  This is appearent in OLE operations where a 16bit foreground
    00722          * thread starts an OLE activation on a 32bit process.  The 32bit process
    00723          * gets starved of CPU while the 16bit thread spins.
    00724          */
    00725         if (ptiCurrent->TIF_flags & TIF_16BIT) {
    00726
    00727             /*
    00728              * Take the 16bit thread out of the scheduler.  This wakes any
    00729              * other 16bit thread needing time, and takes the current thread
    00730              * out.  We will do a brief sleep so that apps can respond in time.
    00731              * When done, we will reschedule the thread.  The zzzWakeInputIdle()
    00732              * should have been called in the no-messages section, so we have
    00733              * already set the Idle-Event.
    00734              */
    00735             xxxSleepTask(FALSE, HEVENT_REMOVEME);
    00736
    00737             LeaveCrit();
    00738             ZwYieldExecution();
    00739             CheckForClientDeath();
    00740             EnterCrit();
    00741
    00742             xxxDirectedYield(DY_OLDYIELD);
    00743         }
    00744     }
    00745
    00746     PATHTAKEN(0x8000);
    00747     DUMPPATHTAKEN();
    00748     return fExit;
    00749 }
    00750 #undef PATHTAKEN
    00751 #undef DUMPPATHTAKEN
    00752
    00753 /***************************************************************************\
    00754 * xxxDispatchMessage (API)
    00755 *
    00756 * Calls the appropriate window procedure or function with pmsg.
    00757 *
    00758 * History:
    00759 * 10-25-90 DavidPe      Created.
    00760 \***************************************************************************/
    00761
    00762 LRESULT xxxDispatchMessage(
    00763     LPMSG pmsg)
    00764 {
    00765     LRESULT lRet;
    00766     PWND pwnd;
    00767     WNDPROC_PWND lpfnWndProc;
    00768     TL tlpwnd;
    00769
    00770     pwnd = NULL;
    00771     if (pmsg->hwnd != NULL) {
    00772         if ((pwnd = ValidateHwnd(pmsg->hwnd)) == NULL)
    00773             return 0;
    00774     }
    00775
    00776     /*
    00777      * If this is a synchronous-only message (takes a pointer in wParam or
    00778      * lParam), then don't allow this message to go through since those
    00779      * parameters have not been thunked, and are pointing into outer-space
    00780      * (which would case exceptions to occur).
    00781      *
    00782      * (This api is only called in the context of a message loop, and you
    00783      * don't get synchronous-only messages in a message loop).
    00784      */
    00785     if (TESTSYNCONLYMESSAGE(pmsg->message, pmsg->wParam)) {
    00786         /*
    00787          * Fail if 32 bit app is calling.
    00788          */
    00789         if (!(PtiCurrent()->TIF_flags & TIF_16BIT)) {
    00790             RIPERR1(ERROR_MESSAGE_SYNC_ONLY, RIP_WARNING, "xxxDispatchMessage: Sync only message 0x%lX",
    00791                     pmsg->message);
    00792             return 0;
    00793         }
    00794
    00795         /*
    00796          * For wow apps, allow it to go through (for compatibility). Change
    00797          * the message id so our code doesn't understand the message - wow
    00798          * will get the message and strip out this bit before dispatching
    00799          * the message to the application.
    00800          */
    00801         pmsg->message |= MSGFLAG_WOW_RESERVED;
    00802     }
    00803
    00804     ThreadLock(pwnd, &tlpwnd);
    00805
    00806     /*
    00807      * Is this a timer?  If there's a proc address, call it,
    00808      * otherwise send it to the wndproc.
    00809      */
    00810     if ((pmsg->message == WM_TIMER) || (pmsg->message == WM_SYSTIMER)) {
    00811         if (pmsg->lParam != 0) {
    00812
    00813             /*
    00814              * System timers must be executed on the server's context.
    00815              */
    00816             if (pmsg->message == WM_SYSTIMER) {
    00817
    00818                 /*
    00819                  * Verify that it's a valid timer proc. If so,
    00820                  * don't leave the critsect to call server-side procs
    00821                  * and pass a PWND, not HWND.
    00822                  */
    00823                 PTIMER ptmr;
    00824                 lRet = 0;
    00825                 for (ptmr = gptmrFirst; ptmr != NULL; ptmr = ptmr->ptmrNext) {
    00826                     if ((pmsg->lParam == (LPARAM)ptmr->pfn) && (ptmr->flags & TMRF_SYSTEM)) {
    00827                         ptmr->pfn(pwnd, WM_SYSTIMER, (UINT)pmsg->wParam,
    00828                                   NtGetTickCount());
    00829                         break;
    00830                     }
    00831                 }
    00832                 goto Exit;
    00833             } else {
    00834                 /*
    00835                  * WM_TIMER is the same for Unicode/ANSI.
    00836                  */
    00837                 PTHREADINFO ptiCurrent = PtiCurrent();
    00838
    00839                 if (ptiCurrent->TIF_flags & TIF_SYSTEMTHREAD) {
    00840                     lRet = 0;
    00841                     goto Exit;
    00842                 }
    00843
    00844                 lRet = CallClientProcA(pwnd, WM_TIMER,
    00845                        pmsg->wParam, NtGetTickCount(), pmsg->lParam);
    00846
    00847                 goto Exit;
    00848             }
    00849         }
    00850     }
    00851
    00852     /*
    00853      * Check to see if pwnd is NULL AFTER the timer check.  Apps can set
    00854      * timers with NULL hwnd's, that's totally legal.  But NULL hwnd messages
    00855      * don't get dispatched, so check here after the timer case but before
    00856      * dispatching - if it's NULL, just return 0.
    00857      */
    00858     if (pwnd == NULL) {
    00859         lRet = 0;
    00860         goto Exit;
    00861     }
    00862
    00863     /*
    00864      * If we're dispatching a WM_PAINT message, set a flag to be used to
    00865      * determine whether it was processed properly.
    00866      */
    00867     if (pmsg->message == WM_PAINT)
    00868         SetWF(pwnd, WFPAINTNOTPROCESSED);
    00869
    00870     /*
    00871      * If this window's proc is meant to be executed from the server side
    00872      * we'll just stay inside the semaphore and call it directly.  Note
    00873      * how we don't convert the pwnd into an hwnd before calling the proc.
    00874      */
    00875     if (TestWF(pwnd, WFSERVERSIDEPROC)) {
    00876         ULONG_PTR fnMessageType;
    00877
    00878         fnMessageType = pmsg->message >= WM_USER ? (ULONG_PTR)SfnDWORD :
    00879                 (ULONG_PTR)gapfnScSendMessage[MessageTable[pmsg->message].iFunction];
    00880
    00881         /*
    00882          * Convert the WM_CHAR from ANSI to UNICODE if the source was ANSI
    00883          */
    00884         if (fnMessageType == (ULONG_PTR)SfnINWPARAMCHAR && TestWF(pwnd, WFANSIPROC)) {
    00885             UserAssert(PtiCurrent() == GETPTI(pwnd)); // use receiver's codepage
    00886             RtlMBMessageWParamCharToWCS(pmsg->message, &pmsg->wParam);
    00887         }
    00888
    00889         lRet = pwnd->lpfnWndProc(pwnd, pmsg->message, pmsg->wParam,
    00890                 pmsg->lParam);
    00891         goto Exit;
    00892     }
    00893
    00894     /*
    00895      * Cool people dereference any window structure members before they
    00896      * leave the critsect.
    00897      */
    00898     lpfnWndProc = pwnd->lpfnWndProc;
    00899
    00900     {
    00901         /*
    00902          * If we're dispatching the message to an ANSI wndproc we need to
    00903          * convert the character messages from Unicode to Ansi.
    00904          */
    00905         if (TestWF(pwnd, WFANSIPROC)) {
    00906             UserAssert(PtiCurrent() == GETPTI(pwnd)); // use receiver's codepage
    00907             RtlWCSMessageWParamCharToMB(pmsg->message, &pmsg->wParam);
    00908             lRet = CallClientProcA(pwnd, pmsg->message,
    00909                     pmsg->wParam, pmsg->lParam, (ULONG_PTR)lpfnWndProc);
    00910         } else {
    00911             lRet = CallClientProcW(pwnd, pmsg->message,
    00912                     pmsg->wParam, pmsg->lParam, (ULONG_PTR)lpfnWndProc);
    00913         }
    00914     }
    00915
    00916     /*
    00917      * If we dispatched a WM_PAINT message and it wasn't properly
    00918      * processed, do the drawing here.
    00919      */
    00920     if (pmsg->message == WM_PAINT && RevalidateHwnd(pmsg->hwnd) &&
    00921             TestWF(pwnd, WFPAINTNOTPROCESSED)) {
    00922         //RIPMSG0(RIP_WARNING,
    00923         //    "Missing BeginPaint or GetUpdateRect/Rgn(fErase == TRUE) in WM_PAINT");
    00924         ClrWF(pwnd, WFWMPAINTSENT);
    00925         xxxSimpleDoSyncPaint(pwnd);
    00926     }
    00927
    00928 Exit:
    00929     ThreadUnlock(&tlpwnd);
    00930     return lRet;
    00931 }
    00932
    00933 /***************************************************************************\
    00934 * AdjustForCoalescing
    00935 *
    00936 * If message is in the coalesce message range, and it's message and hwnd
    00937 * equals the last message in the queue, then coalesce these two messages
    00938 * by simple deleting the last one.
    00939 *
    00940 * 11-12-92 ScottLu      Created.
    00941 \***************************************************************************/
    00942
    00943 void AdjustForCoalescing(
    00944     PMLIST pml,
    00945     HWND hwnd,
    00946     UINT message)
    00947 {
    00948     /*
    00949      * First see if this message is in that range.
    00950      */
    00951     if (!CheckMsgFilter(message, WM_COALESCE_FIRST, WM_COALESCE_LAST) &&
    00952             (message != WM_TIMECHANGE))
    00953         return;
    00954
    00955     if (pml->pqmsgWriteLast == NULL)
    00956         return;
    00957
    00958     if (pml->pqmsgWriteLast->msg.message != message)
    00959         return;
    00960
    00961     if (pml->pqmsgWriteLast->msg.hwnd != hwnd)
    00962         return;
    00963
    00964     /*
    00965      * The message and hwnd are the same, so delete this message and
    00966      * the new one will added later.
    00967      */
    00968     DelQEntry(pml, pml->pqmsgWriteLast);
    00969 }
    00970
    00971 /***************************************************************************\
    00972 * _PostMessage (API)
    00973 *
    00974 * Writes a message to the message queue for pwnd.  If pwnd == -1, the message
    00975 * is broadcasted to all windows.
    00976 *
    00977 * History:
    00978 * 11-06-90 DavidPe      Created.
    00979 \***************************************************************************/
    00980
    00981 BOOL _PostMessage(
    00982     PWND pwnd,
    00983     UINT message,
    00984     WPARAM wParam,
    00985     LPARAM lParam)
    00986 {
    00987     PQMSG pqmsg;
    00988     BOOL fPwndUnlock;
    00989     BOOL fRet;
    00990     DWORD dwPostCode;
    00991     TL tlpwnd;
    00992     PTHREADINFO pti;
    00993
    00994     /*
    00995      * First check to see if this message takes DWORDs only. If it does not,
    00996      * fail the post. Cannot allow an app to post a message with pointers or
    00997      * handles in it - this can cause the server to fault and cause other
    00998      * problems - such as causing apps in separate address spaces to fault.
    00999      * (or even an app in the same address space to fault!)
    01000      */
    01001     if (TESTSYNCONLYMESSAGE(message, wParam)) {
    01002         RIPERR1(ERROR_MESSAGE_SYNC_ONLY,
    01003                 RIP_WARNING,
    01004                 "Invalid parameter \"message\" (%ld) to _PostMessage",
    01005                 message);
    01006
    01007         return FALSE;
    01008     }
    01009
    01010     /*
    01011      * Is this a BroadcastMsg()?
    01012      */
    01013     if (pwnd == PWND_BROADCAST) {
    01014         xxxBroadcastMessage(NULL, message, wParam, lParam, BMSG_POSTMSG, NULL);
    01015         return TRUE;
    01016     }
    01017
    01018     pti = PtiCurrent();
    01019
    01020     /*
    01021      * Is this posting to the current thread info?
    01022      */
    01023     if (pwnd == NULL) {
    01024         return _PostThreadMessage(pti, message, wParam, lParam);
    01025     }
    01026
    01027     fPwndUnlock = FALSE;
    01028     if (message >= WM_DDE_FIRST && message <= WM_DDE_LAST) {
    01029         ThreadLockAlwaysWithPti(pti, pwnd, &tlpwnd);
    01030         dwPostCode = xxxDDETrackPostHook(&message, pwnd, wParam, &lParam, FALSE);
    01031
    01032         if (dwPostCode != DO_POST) {
    01033             ThreadUnlock(&tlpwnd);
    01034             return (BOOL)dwPostCode;
    01035         }
    01036
    01037         fPwndUnlock = TRUE;
    01038     }
    01039
    01040     pti = GETPTI(pwnd);
    01041
    01042     /*
    01043      * Check to see if this message is in the multimedia coalescing range.
    01044      * If so, see if it can be coalesced with the previous message.
    01045      */
    01046     AdjustForCoalescing(&pti->mlPost, HWq(pwnd), message);
    01047
    01048     /*
    01049      * Allocate a key state update event if needed.
    01050      */
    01051     if (message >= WM_KEYFIRST && message <= WM_KEYLAST) {
    01052         PostUpdateKeyStateEvent(pti->pq);
    01053     }
    01054
    01055     /*
    01056      * Put this message on the 'post' list.
    01057      */
    01058     fRet = FALSE;
    01059     if ((pqmsg = AllocQEntry(&pti->mlPost)) != NULL) {
    01060         /*
    01061          * Set the QS_POSTMESSAGE bit so the thread knows it has a message.
    01062          */
    01063         StoreQMessage(pqmsg, pwnd, message, wParam, lParam, 0, 0, 0);
    01064         SetWakeBit(pti, QS_POSTMESSAGE | QS_ALLPOSTMESSAGE);
    01065
    01066         /*
    01067          * If it's a hotkey, set the QS_HOTKEY bit since we have a separate
    01068          * bit for those messages.
    01069          */
    01070         if (message == WM_HOTKEY)
    01071             SetWakeBit(pti, QS_HOTKEY);
    01072
    01073         fRet = TRUE;
    01074     } else {
    01075         RIPMSG1(RIP_WARNING, "_PostMessage: Failed to alloc Q entry: target pti=0x%p",
    01076                 pti);
    01077     }
    01078
    01079     /*
    01080      * Are we posting to the thread currently reading from the input queue?
    01081      * If so, update idSysLock with this pqmsg so that the input queue will
    01082      * not be unlocked until this message is read.
    01083      */
    01084     if (pti == pti->pq->ptiSysLock)
    01085         pti->pq->idSysLock = (ULONG_PTR)pqmsg;
    01086
    01087     if (fPwndUnlock)
    01088         ThreadUnlock(&tlpwnd);
    01089
    01090     return fRet;
    01091 }
    01092 /***************************************************************************\
    01093 * _PostQuitMessage (API)
    01094 *
    01095 * Writes a message to the message queue for pwnd.  If pwnd == -1, the message
    01096 * is broadcasted to all windows.
    01097 *
    01098 * History:
    01099 * 11-06-90 DavidPe      Created.
    01100 * 05-16-91 mikeke       Changed to return BOOL
    01101 \***************************************************************************/
    01102 BOOL IPostQuitMessage(PTHREADINFO pti, int nExitCode)
    01103 {
    01104     pti->cQuit = 1;
    01105     pti->exitCode = nExitCode;
    01106     SetWakeBit(pti, QS_POSTMESSAGE | QS_ALLPOSTMESSAGE);
    01107     return TRUE;
    01108 }
    01109 BOOL _PostQuitMessage(int nExitCode)
    01110 {
    01111     return IPostQuitMessage(PtiCurrent(), nExitCode);
    01112 }
    01113 /***************************************************************************\
    01114 * _PostThreadMessage (API)
    01115 *
    01116 * Given a thread ID, the function will post the specified message to this
    01117 * thread with pmsg->hwnd == NULL..
    01118 *
    01119 * History:
    01120 * 11-21-90 DavidPe      Created.
    01121 \***************************************************************************/
    01122
    01123 BOOL _PostThreadMessage(
    01124     PTHREADINFO pti,
    01125     UINT        message,
    01126     WPARAM      wParam,
    01127     LPARAM      lParam)
    01128 {
    01129     PQMSG       pqmsg;
    01130
    01131     if ((pti == NULL)                                ||
    01132         !(pti->TIF_flags & TIF_GUITHREADINITIALIZED) ||
    01133         (pti->TIF_flags & TIF_INCLEANUP)) {
    01134
    01135         RIPERR0(ERROR_INVALID_THREAD_ID, RIP_VERBOSE, "");
    01136         return FALSE;
    01137     }
    01138
    01139     /*
    01140      * First check to see if this message takes DWORDs only. If it does not,
    01141      * fail the post. Cannot allow an app to post a message with pointers or
    01142      * handles in it - this can cause the server to fault and cause other
    01143      * problems - such as causing apps in separate address spaces to fault.
    01144      * (or even an app in the same address space to fault!)
    01145      */
    01146     if (TESTSYNCONLYMESSAGE(message, wParam)) {
    01147         RIPERR1(ERROR_MESSAGE_SYNC_ONLY,
    01148                 RIP_WARNING,
    01149                 "Invalid parameter \"message\" (%ld) to _PostThreadMessage",
    01150                 message);
    01151
    01152         return FALSE;
    01153     }
    01154
    01155     /*
    01156      * Check to see if this message is in the multimedia coalescing range.
    01157      * If so, see if it can be coalesced with the previous message.
    01158      */
    01159     AdjustForCoalescing(&pti->mlPost, NULL, message);
    01160
    01161     /*
    01162      * Put this message on the 'post' list.
    01163      */
    01164     if ((pqmsg = AllocQEntry(&pti->mlPost)) == NULL) {
    01165         RIPMSG1(RIP_WARNING, "_PostThreadMessage: Failed to alloc Q entry: Target pti=0x%p",
    01166                 pti);
    01167         return FALSE;
    01168     }
    01169
    01170     /*
    01171      * Set the QS_POSTMESSAGE bit so the thread knows it has a message.
    01172      */
    01173     StoreQMessage(pqmsg, NULL, message, wParam, lParam, 0, 0, 0);
    01174     SetWakeBit(pti, QS_POSTMESSAGE | QS_ALLPOSTMESSAGE);
    01175
    01176     /*
    01177      * If it's a hotkey, set the QS_HOTKEY bit since we have a separate
    01178      * bit for those messages.
    01179      */
    01180     if (message == WM_HOTKEY)
    01181         SetWakeBit(pti, QS_HOTKEY);
    01182
    01183     /*
    01184      * Are we posting to the thread currently reading from the input queue?
    01185      * If so, update idSysLock with this pqmsg so that the input queue will
    01186      * not be unlocked until this message is read.
    01187      */
    01188     if (pti == pti->pq->ptiSysLock)
    01189         pti->pq->idSysLock = (ULONG_PTR)pqmsg;
    01190
    01191     return TRUE;
    01192 }
    01193
    01194
    01195 /***************************************************************************\
    01196 * _GetMessagePos (API)
    01197 *
    01198 * This API returns the cursor position when the last message was read from
    01199 * the current message queue.
    01200 *
    01201 * History:
    01202 * 11-19-90 DavidPe      Created.
    01203 \***************************************************************************/
    01204
    01205 DWORD _GetMessagePos(VOID)
    01206 {
    01207     PTHREADINFO pti;
    01208
    01209     pti = PtiCurrent();
    01210
    01211     return MAKELONG((SHORT)pti->ptLast.x, (SHORT)pti->ptLast.y);
    01212 }
    01213
    01214
    01215
    01216 #ifdef SYSMODALWINDOWS
    01217 /***************************************************************************\
    01218 * _SetSysModalWindow (API)
    01219 *
    01220 * History:
    01221 * 01-25-91 DavidPe      Created stub.
    01222 \***************************************************************************/
    01223
    01224 PWND APIENTRY _SetSysModalWindow(
    01225     PWND pwnd)
    01226 {
    01227     pwnd;
    01228     return NULL;
    01229 }
    01230
    01231
    01232 /***************************************************************************\
    01233 * _GetSysModalWindow (API)
    01234 *
    01235 * History:
    01236 * 01-25-91 DavidPe      Created stub.
    01237 \***************************************************************************/
    01238
    01239 PWND APIENTRY _GetSysModalWindow(VOID)
    01240 {
    01241     return NULL;
    01242 }
    01243 #endif //LATER
    01244
    01245 /***************************************************************************\
    01246 * PostMove
    01247 *
    01248 * This routine gets called when it is detected that the QF_MOUSEMOVED bit
    01249 * is set in a particular queue.
    01250 *
    01251 * 11-03-92 ScottLu      Created.
    01252 \***************************************************************************/
    01253
    01254 VOID PostMove(
    01255     PQ pq)
    01256 {
    01257 #ifdef REDIRECTION
    01258     POINT pt;
    01259 #endif // REDIRECTION
    01260
    01261     CheckCritIn();
    01262
    01263     /*
    01264      * set gdwMouseMoveTimeStamp to 0 after posting the move so
    01265      * subsequent calls to SetFMouseMove doesn't use the same value
    01266      * of gdwMouseMoveTimeStamp. Bug 74508.
    01267      */
    01268     if (gdwMouseMoveTimeStamp == 0) {
    01269         gdwMouseMoveTimeStamp = NtGetTickCount();
    01270     }
    01271
    01272 #ifdef REDIRECTION
    01273
    01274     PopMouseMove(pq, &pt);
    01275
    01276     PostInputMessage(pq, NULL, WM_MOUSEMOVE, 0,
    01277             MAKELONG((SHORT)pt.x, (SHORT)pt.y),
    01278             gdwMouseMoveTimeStamp, gdwMouseMoveExtraInfo);
    01279 #else
    01280     PostInputMessage(pq, NULL, WM_MOUSEMOVE, 0,
    01281             MAKELONG((SHORT)gpsi->ptCursor.x, (SHORT)gpsi->ptCursor.y),
    01282             gdwMouseMoveTimeStamp, gdwMouseMoveExtraInfo);
    01283 #endif // REDIRECTION
    01284
    01285     gdwMouseMoveTimeStamp = 0;
    01286
    01287     pq->QF_flags &= ~QF_MOUSEMOVED;
    01288 }
    01289
    01290 #ifdef REDIRECTION
    01291
    01292 typedef struct tagQMOUSEMOVE {
    01293     PQ    pq;
    01294     POINT pt;
    01295 } QMOUSEMOVE;
    01296
    01297 #define MAX_QMOUSEMOVE  16
    01298
    01299 QMOUSEMOVE gqMouseMove[MAX_QMOUSEMOVE];
    01300
    01301 int gnLastMouseMove;
    01302
    01303 VOID PushMouseMove(
    01304     PQ    pq,
    01305     POINT pt)
    01306 {
    01307     int ind;
    01308
    01309     CheckCritIn();
    01310
    01311     UserAssert(gnLastMouseMove < MAX_QMOUSEMOVE - 1);
    01312
    01313     for (ind = 0; ind < gnLastMouseMove; ind++) {
    01314         if (pq == gqMouseMove[ind].pq) {
    01315
    01316             gqMouseMove[ind].pt = pt;
    01317             return;
    01318         }
    01319     }
    01320
    01321     gqMouseMove[gnLastMouseMove].pq = pq;
    01322     gqMouseMove[gnLastMouseMove].pt = pt;
    01323
    01324     gnLastMouseMove++;
    01325 }
    01326
    01327 VOID PopMouseMove(
    01328     PQ     pq,
    01329     POINT* ppt)
    01330 {
    01331     int ind;
    01332
    01333     CheckCritIn();
    01334
    01335     for (ind = 0; ind < gnLastMouseMove; ind++) {
    01336         if (pq == gqMouseMove[ind].pq) {
    01337             *ppt = gqMouseMove[ind].pt;
    01338
    01339             RtlMoveMemory(&gqMouseMove[ind],
    01340                           &gqMouseMove[ind + 1],
    01341                           (gnLastMouseMove - ind - 1) * sizeof(QMOUSEMOVE));
    01342
    01343             gnLastMouseMove--;
    01344
    01345             return;
    01346         }
    01347     }
    01348     UserAssert(0);
    01349 }
    01350 #endif // REDIRECTION
    01351
    01352 /***************************************************************************\
    01353 * zzzSetFMouseMoved
    01354 *
    01355 * Send a mouse move through the system. This usually occurs when doing
    01356 * window management to be sure that the mouse shape accurately reflects
    01357 * the part of the window it is currently over (window managment may have
    01358 * changed this).
    01359 *
    01360 * 11-02-92 ScottLu      Created.
    01361 \***************************************************************************/
    01362
    01363 VOID zzzSetFMouseMoved()
    01364 {
    01365     PWND pwnd;
    01366     PWND pwndOldCursor;
    01367     PQ   pq;
    01368
    01369 #ifdef REDIRECTION
    01370     PWND  pwndStart;
    01371     POINT ptMouse = gpsi->ptCursor;
    01372 #endif // REDIRECTION
    01373
    01374     /*
    01375      * Need to first figure out what queue this mouse event is in. Do NOT
    01376      * check for mouse capture here !! Talk to scottlu.
    01377      */
    01378     if ((pwnd = gspwndScreenCapture) == NULL) {
    01379
    01380 #ifdef REDIRECTION
    01381         /*
    01382          * Call the speed hit test hook
    01383          */
    01384         pwndStart = xxxCallSpeedHitTestHook(&ptMouse);
    01385 #endif // REDIRECTION
    01386
    01387         if ((pwnd = gspwndMouseOwner) == NULL) {
    01388             if ((pwnd = gspwndInternalCapture) == NULL) {
    01389
    01390                 UserAssert(grpdeskRitInput != NULL);
    01391
    01392 #ifdef REDIRECTION
    01393                 if (pwndStart == NULL) {
    01394                     pwndStart = grpdeskRitInput->pDeskInfo->spwnd;
    01395                 }
    01396                 pwnd = SpeedHitTest(pwndStart, ptMouse);
    01397 #else
    01398                 pwnd = SpeedHitTest(grpdeskRitInput->pDeskInfo->spwnd, gpsi->ptCursor);
    01399 #endif // REDIRECTION
    01400
    01401             }
    01402         }
    01403     }
    01404
    01405     if (pwnd == NULL)
    01406         return;
    01407
    01408     /*
    01409      * This is apparently needed by the attach/unattach code for some
    01410      * reason. I'd like to get rid of it - scottlu.
    01411      */
    01412     pwndOldCursor = Lock(&gspwndCursor, pwnd);
    01413
    01414     /*
    01415      * If we're giving a mouse move to a new queue, be sure the cursor
    01416      * image represents what this queue thinks it should be.
    01417      */
    01418     pq = GETPTI(pwnd)->pq;
    01419
    01420     /*
    01421      * Protect pq by deferring WinEvent notification
    01422      */
    01423     DeferWinEventNotify();
    01424
    01425     if (pq != gpqCursor) {
    01426         /*
    01427          * If the old queue had the mouse captured, let him know that
    01428          * the mouse moved first. Need this to fix tooltips in
    01429          * WordPerfect Office. Do the same for mouse tracking.
    01430          */
    01431         if (gpqCursor != NULL) {
    01432
    01433             if (gpqCursor->spwndCapture != NULL) {
    01434                 gpqCursor->QF_flags |= QF_MOUSEMOVED;
    01435                 SetWakeBit(GETPTI(gpqCursor->spwndCapture), QS_MOUSEMOVE);
    01436
    01437 #ifdef REDIRECTION
    01438                 PushMouseMove(gpqCursor, ptMouse);
    01439 #endif // REDIRECTION
    01440
    01441             }
    01442
    01443             if ((pwndOldCursor != NULL) && (PtoHq(pwndOldCursor) != PtoHq(pwnd))) {
    01444                 PDESKTOP pdesk = GETPDESK(pwndOldCursor);
    01445                 if (pdesk->dwDTFlags & DF_MOUSEMOVETRK) {
    01446                     PTHREADINFO pti = GETPTI(pdesk->spwndTrack);
    01447                     PostEventMessage(pti, pti->pq, QEVENT_CANCELMOUSEMOVETRK,
    01448                                      pdesk->spwndTrack, pdesk->dwDTFlags, pdesk->htEx,
    01449                                      DF_MOUSEMOVETRK);
    01450                     pdesk->dwDTFlags &= ~DF_MOUSEMOVETRK;
    01451                 }
    01452             }
    01453         }
    01454
    01455         /*
    01456          * First re-assign gpqCursor so any zzzSetCursor() calls
    01457          * will only take effect if done by the thread that
    01458          * owns the window the mouse is currently over.
    01459          */
    01460         gpqCursor = pq;
    01461
    01462         /*
    01463          * Call zzzUpdateCursorImage() so the new gpqCursor's
    01464          * notion of the current cursor is represented.
    01465          */
    01466         zzzUpdateCursorImage();
    01467
    01468     }
    01469
    01470     /*
    01471      * Set the mouse moved bit for this queue so we know later to post
    01472      * a move message to this queue.
    01473      */
    01474     pq->QF_flags |= QF_MOUSEMOVED;
    01475
    01476 #ifdef REDIRECTION
    01477     PushMouseMove(pq, ptMouse);
    01478 #endif // REDIRECTION
    01479
    01480
    01481     /*
    01482      * Reassign mouse input to this thread - this indicates which thread
    01483      * to wake up when new input comes in.
    01484      */
    01485     pq->ptiMouse = GETPTI(pwnd);
    01486
    01487     /*
    01488      * Wake some thread within this queue to process this mouse event.
    01489      */
    01490     WakeSomeone(pq, WM_MOUSEMOVE, NULL);
    01491
    01492     /*
    01493      * We're possibly generating a fake mouse move message - it has no
    01494      * extra info associated with it - so 0 it out.
    01495      */
    01496     gdwMouseMoveExtraInfo = 0;
    01497
    01498     zzzEndDeferWinEventNotify();
    01499 }
    01500
    01501 /***************************************************************************\
    01502 * CancelForegroundActivate
    01503 *
    01504 * This routine cancels the foreground activate that we allow apps starting
    01505 * up to have. This means that if you make a request to start an app,
    01506 * if this routine is called before the app becomes foreground, it won't
    01507 * become foreground. This routine gets called if the user down clicks or
    01508 * makes a keydown event, with the idea being that if the user did this,
    01509 * the user is using some other app and doesn't want the newly starting
    01510 * application to appear on top and force itself into the foreground.
    01511 *
    01512 * 09-15-92 ScottLu      Created.
    01513 \***************************************************************************/
    01514
    01515 void CancelForegroundActivate()
    01516 {
    01517     PPROCESSINFO ppiT;
    01518
    01519     if (TEST_PUDF(PUDF_ALLOWFOREGROUNDACTIVATE)) {
    01520
    01521         for (ppiT = gppiStarting; ppiT != NULL; ppiT = ppiT->ppiNext) {
    01522             /*
    01523              * Don't cancel activation if the app is being debugged - if
    01524              * the debugger stops the application before it has created and
    01525              * activated its first window, the app will come up behind all
    01526              * others - not what you want when being debugged.
    01527              */
    01528             if (!ppiT->Process->DebugPort) {
    01529                 ppiT->W32PF_Flags &= ~W32PF_ALLOWFOREGROUNDACTIVATE;
    01530                 TAGMSG1(DBGTAG_FOREGROUND, "CancelForegroundActivate clear W32PF %#p", ppiT);
    01531             }
    01532         }
    01533
    01534         CLEAR_PUDF(PUDF_ALLOWFOREGROUNDACTIVATE);
    01535         TAGMSG0(DBGTAG_FOREGROUND, "CancelForegroundActivate clear PUDF");
    01536     }
    01537 }
    01538
    01539 /***************************************************************************\
    01540 * RestoreForegroundActivate
    01541 *
    01542 * This routine re-enables an app's right to foreground activate (activate and
    01543 * come on top) if it is starting up. This is called when we minimize or when
    01544 * the last window of a thread goes away, for example.
    01545 *
    01546 * 01-26-93 ScottLu      Created.
    01547 \***************************************************************************/
    01548
    01549 void RestoreForegroundActivate()
    01550 {
    01551     PPROCESSINFO ppiT;
    01552
    01553     for (ppiT = gppiStarting; ppiT != NULL; ppiT = ppiT->ppiNext) {
    01554         if (ppiT->W32PF_Flags & W32PF_APPSTARTING) {
    01555             ppiT->W32PF_Flags |= W32PF_ALLOWFOREGROUNDACTIVATE;
    01556             TAGMSG1(DBGTAG_FOREGROUND, "RestoreForegroundActivate set W32PF %#p", ppiT);
    01557             SET_PUDF(PUDF_ALLOWFOREGROUNDACTIVATE);
    01558             TAGMSG0(DBGTAG_FOREGROUND, "RestoreForegroundActivate set PUDF");
    01559         }
    01560     }
    01561 }
    01562
    01563
    01564 /***************************************************************************\
    01565 * PostInputMessage
    01566 *
    01567 * Puts a message on the 'input' linked-list of message for the specified
    01568 * queue.
    01569 *
    01570 * History:
    01571 * 10-25-90 DavidPe      Created.
    01572 * 01-21-92 DavidPe      Rewrote to deal with OOM errors gracefully.
    01573 \***************************************************************************/
    01574
    01575 void PostInputMessage(
    01576     PQ    pq,
    01577     PWND  pwnd,
    01578     UINT  message,
    01579     WPARAM wParam,
    01580     LPARAM lParam,
    01581     DWORD time,
    01582     ULONG_PTR dwExtraInfo)
    01583 {
    01584     PQMSG pqmsgInput, pqmsgPrev;
    01585     short sWheelDelta;
    01586
    01587     /*
    01588      * Grab the last written message before we start allocing new ones,
    01589      * so we're sure to point to the correct message.
    01590      */
    01591     pqmsgPrev = pq->mlInput.pqmsgWriteLast;
    01592
    01593     /*
    01594      * Allocate a key state update event if needed.
    01595      */
    01596     if (pq->QF_flags & QF_UPDATEKEYSTATE) {
    01597         PostUpdateKeyStateEvent(pq);
    01598     }
    01599
    01600     /*
    01601      * We want to coalesce sequential WM_MOUSEMOVE and WM_MOUSEWHEEL.
    01602      * WM_MOUSEMOVEs are coalesced by just storing the most recent
    01603      * event over the last one.
    01604      * WM_MOUSEWHEELs also add up the wheel rolls.
    01605      */
    01606     if (pqmsgPrev != NULL &&
    01607         pqmsgPrev->msg.message == message &&
    01608         (message == WM_MOUSEMOVE || message == WM_MOUSEWHEEL)) {
    01609
    01610         if (message == WM_MOUSEWHEEL) {
    01611             sWheelDelta = (short)HIWORD(wParam) + (short)HIWORD(pqmsgPrev->msg.wParam);
    01612
    01613 #if 0
    01614             /*
    01615              * LATER: We can't remove a wheel message with zero delta
    01616              * unless we know it hasn't been peeked. Ideally,
    01617              * we would check idsyspeek for this, but we're too close
    01618              * to ship and idsyspeek is too fragile. Consider also
    01619              * checking to see if mouse move messages have been peeked.
    01620              */
    01621
    01622             if (sWheelDelta == 0) {
    01623                 if ((PQMSG)pq->idSysPeek == pqmsgPrev) {
    01624                     RIPMSG0(RIP_VERBOSE,
    01625                             "Coalescing of mouse wheel messages causing "
    01626                             "idSysPeek to be reset to 0");
    01627
    01628                     pq->idSysPeek = 0;
    01629                 }
    01630
    01631                 DelQEntry(&pq->mlInput, pqmsgPrev);
    01632                 return;
    01633             }
    01634 #endif
    01635
    01636             wParam = MAKEWPARAM(LOWORD(wParam), sWheelDelta);
    01637         }
    01638
    01639         StoreQMessage(pqmsgPrev, pwnd, message, wParam, lParam, time, 0, dwExtraInfo);
    01640         WakeSomeone(pq, message, pqmsgPrev);
    01641
    01642         return;
    01643     }
    01644
    01645     /*
    01646      * Fill in pqmsgInput.
    01647      */
    01648     pqmsgInput = AllocQEntry(&pq->mlInput);
    01649     if (pqmsgInput != NULL) {
    01650         StoreQMessage(pqmsgInput, pwnd, message, wParam, lParam, time, 0, dwExtraInfo);
    01651         WakeSomeone(pq, message, pqmsgInput);
    01652     }
    01653 }
    01654
    01655 /***************************************************************************\
    01656 * WakeSomeone
    01657 *
    01658 * Figures out which thread to wake up based on the queue and message.
    01659 * If the queue pointer is NULL, figures out a likely queue.
    01660 *
    01661 * 10-23-92 ScottLu      Created.
    01662 \***************************************************************************/
    01663
    01664 void WakeSomeone(
    01665     PQ pq,
    01666     UINT message,
    01667     PQMSG pqmsg)
    01668 {
    01669     BOOL fSetLastWoken = FALSE;
    01670     PTHREADINFO ptiT;
    01671
    01672     /*
    01673      * Set the appropriate wakebits for this queue.
    01674      */
    01675     ptiT = NULL;
    01676     switch (message) {
    01677
    01678     case WM_SYSKEYDOWN:
    01679     case WM_KEYDOWN:
    01680         /*
    01681          * Don't change input ownership if the user is holding down
    01682          *  a modifier key. When doing a ctrl-drag operation for example,
    01683          *  the ctrl key must be down when the user drops the object (ie, mouse up).
    01684          *  On mouse up the RIT gives input ownership to the target; but since
    01685          *  ctrl is down, on the next repeat key we used to give input ownership
    01686          *  to the focus window (usually the drag source). Hence the target
    01687          *  would lose owenerhip and couldn't take the foreground.
    01688          */
    01689         if (pqmsg != NULL) {
    01690             switch (pqmsg->msg.wParam) {
    01691                 case VK_SHIFT:
    01692                 case VK_CONTROL:
    01693                 case VK_MENU:
    01694                     if (TestKeyStateDown(pq, pqmsg->msg.wParam)) {
    01695                         break;
    01696                     }
    01697                     /* Fall through */
    01698
    01699                 default:
    01700                     fSetLastWoken = TRUE;
    01701                     break;
    01702             }
    01703         } else {
    01704             fSetLastWoken = TRUE;
    01705         }
    01706         /* fall through */
    01707
    01708     case WM_SYSCHAR:
    01709     case WM_CHAR:
    01710         /* Freelance graphics seems to pass in WM_SYSCHARs and WM_CHARs into
    01711          * the journal playback hook, so we need to set an input bit for
    01712          * this case since that is what win3.1 does. VB2 "learning" demo does
    01713          * the same, as does Excel intro.
    01714          *
    01715          * On win3.1, the WM_CHAR would by default set the QS_MOUSEBUTTON bit.
    01716          * On NT, the WM_CHAR sets the QS_KEY bit. This is because
    01717          * ScanSysQueue() calls TransferWakeBit() with the QS_KEY bit when
    01718          * a WM_CHAR message is passed in. By using the QS_KEY bit on NT,
    01719          * we're more compatible with what win3.1 wants to be.
    01720          *
    01721          * This fixes a case where the mouse was over progman, the WM_CHAR
    01722          * would come in via journal playback, wakesomeone would be called,
    01723          * and set the mouse bit in progman. Progman would then get into
    01724          * ScanSysQueue(), callback the journal playback hook, get the WM_CHAR,
    01725          * and do it again, looping. This caught VB2 in a loop.
    01726          */
    01727
    01728         CancelForegroundActivate();
    01729
    01730         /* fall through */
    01731
    01732     case WM_KEYUP:
    01733     case WM_SYSKEYUP:
    01734     case WM_MOUSEWHEEL:
    01735         /*
    01736          * Win3.1 first looks at what thread has the active status. This
    01737          * means that we don't depend on the thread owning ptiKeyboard
    01738          * to wake up and process this key in order to give it to the
    01739          * active window, which is potentially newly active. Case in
    01740          * point: excel bringing up CBT, cbt has an error, brings up
    01741          * a message box: since excel is filtering for CBT messages only,
    01742          * ptiKeyboard never gets reassigned to CBT so CBT doesn't get
    01743          * any key messages and appears hung.
    01744          */
    01745         ptiT = pq->ptiKeyboard;
    01746         if (pq->spwndActive != NULL)
    01747             ptiT = GETPTI(pq->spwndActive);
    01748
    01749         SetWakeBit(ptiT, message == WM_MOUSEWHEEL ? QS_MOUSEBUTTON : QS_KEY);
    01750         break;
    01751
    01752     case WM_MOUSEMOVE:
    01753         /*
    01754          * Make sure we wake up the thread with the capture, if there is
    01755          * one. This fixes PC Tools screen capture program, which sets
    01756          * capture and then loops trying to remove messages from the
    01757          * queue.
    01758          */
    01759         if (pq->spwndCapture != NULL)
    01760             ptiT = GETPTI(pq->spwndCapture);
    01761         else
    01762             ptiT = pq->ptiMouse;
    01763         SetWakeBit(ptiT, QS_MOUSEMOVE);
    01764         break;
    01765
    01766
    01767     case WM_LBUTTONDOWN:
    01768     case WM_LBUTTONDBLCLK:
    01769     case WM_RBUTTONDOWN:
    01770     case WM_RBUTTONDBLCLK:
    01771     case WM_MBUTTONDOWN:
    01772     case WM_MBUTTONDBLCLK:
    01773     case WM_XBUTTONDOWN:
    01774     case WM_XBUTTONDBLCLK:
    01775             fSetLastWoken = TRUE;
    01776
    01777         /* fall through */
    01778
    01779     default:
    01780         /*
    01781          * The default case in Win3.1 for this is QS_MOUSEBUTTON.
    01782          */
    01783
    01784         CancelForegroundActivate();
    01785
    01786         /* fall through */
    01787
    01788     case WM_LBUTTONUP:
    01789     case WM_RBUTTONUP:
    01790     case WM_MBUTTONUP:
    01791     case WM_XBUTTONUP:
    01792         /*
    01793          * Make sure we wake up the thread with the capture, if there is
    01794          * one. This fixes PC Tools screen capture program, which sets
    01795          * capture and then loops trying to remove messages from the
    01796          * queue.
    01797          */
    01798         if (pq->spwndCapture != NULL &&
    01799                 message >= WM_MOUSEFIRST && message <= WM_MOUSELAST)
    01800             ptiT = GETPTI(pq->spwndCapture);
    01801         else
    01802             ptiT = pq->ptiMouse;
    01803         SetWakeBit(ptiT, QS_MOUSEBUTTON);
    01804         break;
    01805     }
    01806
    01807     /*
    01808      * If a messaged was passed in, remember in it who we woke up for this
    01809      * message. We do this so each message is ownership marked. This way
    01810      * we can merge/unmerge message streams when zzzAttachThreadInput() is
    01811      * called.
    01812      */
    01813     if (ptiT != NULL) {
    01814         if (pqmsg != NULL) {
    01815
    01816             StoreQMessagePti(pqmsg, ptiT);
    01817
    01818             UserAssert(!(ptiT->TIF_flags & TIF_INCLEANUP));
    01819         }
    01820
    01821         /*
    01822          * Remember who got the last key/click down.
    01823          */
    01824         if (fSetLastWoken) {
    01825             glinp.ptiLastWoken = ptiT;
    01826         }
    01827     }
    01828
    01829 }
    01830
    01831
    01832 /***************************************************************************\
    01833 * PostUpdateKeyStateEvent
    01834 *
    01835 * This routine posts an event which updates the local thread's keystate
    01836 * table. This makes sure the thread's key state is always up-to-date.
    01837 *
    01838 * An example is: control-esc from cmd to taskman.
    01839 * Control goes to cmd, but taskman is activated. Control state is still down
    01840 * in cmd - switch back to cmd, start typing, nothing appears because it thinks
    01841 * the control state is still down.
    01842 *
    01843 * As events go into a particular queue (queue A), the async key state table is
    01844 * updated. As long as transition events are put into queue A, the key
    01845 * state at the logical "end of the queue" is up-to-date with the async key
    01846 * state. As soon as the user posts transition events (up/down msgs) into queue
    01847 * B, the queue A's end-of-queue key state is out of date with the user. If
    01848 * the user then again added messages to queue A, when those messages are read
    01849 * the thread specific key state would not be updated correctly unless we
    01850 * did some synchronization (which this routine helps to do).
    01851 *
    01852 * As soon as transition events change queues, we go mark all the other queues
    01853 * with the QF_UPDATEKEYSTATE flag. Before any input event is posted into
    01854 * a queue, this flag is checked, and if set, this routine is called. This
    01855 * routine makes a copy of the async key state, and a copy of the bits
    01856 * representing the keys that have changed since the last update (we need to
    01857 * keep track of which keys have changed so that any state set by the
    01858 * app with SetKeyboardState() doesn't get wiped out). We take this data
    01859 * and post a new event of the type QEVENT_UPDATEKEYSTATE, which points to this
    01860 * key state and transition information. When this message is read out of the
    01861 * queue, this key state copy is copied into the thread specific key state
    01862 * table for those keys that have changed, and the copy is deallocated.
    01863 *
    01864 * This ensures all queues are input-synchronized with key transitions no matter
    01865 * where they occur. The side affect of this is that an application may suddenly
    01866 * have a key be up without seeing the up message. If this causes any problems
    01867 * we may have to generate false transition messages (this could have more nasty
    01868 * side affects as well, so it needs to be considered closely before being
    01869 * implemented.)
    01870 *
    01871 * 06-07-91 ScottLu      Created.
    01872 \***************************************************************************/
    01873
    01874 void PostUpdateKeyStateEvent(
    01875     PQ pq)
    01876 {
    01877     BYTE *pb;
    01878     PQMSG pqmsg;
    01879
    01880     if (!(pq->QF_flags & QF_UPDATEKEYSTATE))
    01881         return;
    01882
    01883     /*
    01884      * Exclude the RIT - it's queue is never read, so don't waste memory
    01885      */
    01886     if (pq->ptiKeyboard == gptiRit) {
    01887         return;
    01888     }
    01889
    01890     /*
    01891      * If there's no mousebutton or keystroke input pending, process the
    01892      * UpdateKeyState Event now: thus saving memory allocation and giving
    01893      * applications the correct KeyState immediately.
    01894      * NOTE: There may be event/activation msgs in pq->mlInput that don't
    01895      * affect keystate, so I'd like to just test QS_KEY | QS_MOUSEBUTTON
    01896      * specifically, instead of the cMsgss.  However, sometimes there are
    01897      * keystroke or mousebutton msgs in the q without those bits set! - IanJa
    01898      */
    01899     if (pq->mlInput.cMsgs == 0) {
    01900         ProcessUpdateKeyStateEvent(pq, gafAsyncKeyState, pq->afKeyRecentDown);
    01901         goto SyncQueue;
    01902     }
    01903 #if DBG
    01904     else if ((!pq->ptiKeyboard || !(pq->ptiKeyboard->pcti->fsWakeBits & QS_KEY)) &&
    01905              (!pq->ptiMouse    || !(pq->ptiMouse->pcti->fsWakeBits & QS_MOUSEBUTTON))) {
    01906         /*
    01907          * See if there are any key or mousebutton messages that aren't
    01908          * indicated by QS_KEY or QS_MOUSEBUTTON bits.
    01909          */
    01910         PQMSG pqmsgT;
    01911         for (pqmsgT = pq->mlInput.pqmsgRead; pqmsgT; pqmsgT = pqmsgT->pqmsgNext) {
    01912             if (pqmsgT->msg.message >= WM_KEYFIRST && pqmsgT->msg.message <= WM_KEYLAST) {
    01913                 TAGMSG1(DBGTAG_InputWithoutQS,
    01914                         "PostUpdateKeyStateEvent() pushing in front of a keystroke: Q %#p", pq);
    01915             } else if (pqmsgT->msg.message >= WM_LBUTTONDOWN && pqmsgT->msg.message <= WM_XBUTTONDBLCLK) {
    01916                 TAGMSG1(DBGTAG_InputWithoutQS,
    01917                         "PostUpdateKeyStateEvent() pushing in front of a mousebutton: Q %#p", pq);
    01918             }
    01919         }
    01920     }
    01921 #endif
    01922
    01923     UserAssert(pq->mlInput.pqmsgWriteLast != NULL);
    01924
    01925     /*
    01926      * If the last input message is an UPDATEKEYSTATE event, coalesce with it.
    01927      * (Prevents big memory leaks on apps that don't read input messages)
    01928      */
    01929     pqmsg = pq->mlInput.pqmsgWriteLast;
    01930     if (pqmsg->dwQEvent == QEVENT_UPDATEKEYSTATE) {
    01931         int i;
    01932         DWORD *pdw;
    01933
    01934         pb = (PBYTE)(pqmsg->msg.wParam);
    01935         pdw = (DWORD *) (pb + CBKEYSTATE);
    01936
    01937         /*
    01938          * Copy in the new key state
    01939          */
    01940         RtlCopyMemory(pb, gafAsyncKeyState, CBKEYSTATE);
    01941
    01942         /*
    01943          * Or in the recent key down state (DWORD at a time)
    01944          */
    01945 #if (CBKEYSTATERECENTDOWN % 4) != 0
    01946 #error "CBKEYSTATERECENTDOWN assumed to be an integral number of DWORDs"
    01947 #endif
    01948         for (i = 0; i < CBKEYSTATERECENTDOWN / sizeof(*pdw); i++) {
    01949             *pdw++ |= ((DWORD *)(pq->afKeyRecentDown))[i];
    01950         }
    01951
    01952         /*
    01953          * Set QS_EVENTSET as in PostEventMessage, although this is
    01954          * usually, but not always already set
    01955          */
    01956         SetWakeBit(pq->ptiKeyboard, QS_EVENTSET);
    01957         goto SyncQueue;
    01958     }
    01959
    01960     /*
    01961      * Make a copy of the async key state buffer, point to it, and add an
    01962      * event to the end of the input queue.
    01963      */
    01964     if ((pb = UserAllocPool(KEYSTATESIZE, TAG_KBDSTATE)) == NULL) {
    01965         return;
    01966     }
    01967
    01968     RtlCopyMemory(pb, gafAsyncKeyState, CBKEYSTATE);
    01969     RtlCopyMemory(pb + CBKEYSTATE, pq->afKeyRecentDown, CBKEYSTATERECENTDOWN);
    01970
    01971     if (!PostEventMessage(pq->ptiKeyboard, pq, QEVENT_UPDATEKEYSTATE,
    01972                           NULL, 0 , (WPARAM)pb, 0)) {
    01973         UserFreePool(pb);
    01974         return;
    01975     }
    01976
    01977     /*
    01978      * The key state of the queue is input-synchronized with the user.  Erase
    01979      * all 'recent down' flags.
    01980      */
    01981 SyncQueue:
    01982     RtlZeroMemory(pq->afKeyRecentDown, CBKEYSTATERECENTDOWN);
    01983     pq->QF_flags &= ~QF_UPDATEKEYSTATE;
    01984 }
    01985
    01986
    01987 /***************************************************************************\
    01988 * ProcessUpdateKeyStateEvent
    01989 *
    01990 * This is part two of the above routine, called when the QEVENT_UPDATEKEYSTATE
    01991 * message is read out of the input queue.
    01992 *
    01993 * 06-07-91 ScottLu      Created.
    01994 \***************************************************************************/
    01995
    01996 void ProcessUpdateKeyStateEvent(
    01997     PQ pq,
    01998     CONST PBYTE pbKeyState,
    01999     CONST PBYTE pbRecentDown)
    02000 {
    02001     int i, j;
    02002     BYTE *pbChange;
    02003     int vk;
    02004
    02005     pbChange = pbRecentDown;
    02006     for (i = 0; i < CBKEYSTATERECENTDOWN; i++, pbChange++) {
    02007
    02008         /*
    02009          * Find some keys that have changed.
    02010          */
    02011         if (*pbChange == 0)
    02012             continue;
    02013
    02014         /*
    02015          * Some keys have changed in this byte.  find out which key it is.
    02016          */
    02017         for (j = 0; j < 8; j++) {
    02018
    02019             /*
    02020              * Convert our counts to a virtual key index and check to see
    02021              * if this key has changed.
    02022              */
    02023             vk = (i << 3) + j;
    02024             if (!TestKeyRecentDownBit(pbRecentDown, vk))
    02025                 continue;
    02026
    02027             /*
    02028              * This key has changed.  Update it's state in the thread key
    02029              * state table.
    02030              */
    02031
    02032             if (TestKeyDownBit(pbKeyState, vk)) {
    02033                 SetKeyStateDown(pq, vk);
    02034             } else {
    02035                 ClearKeyStateDown(pq, vk);
    02036             }
    02037
    02038             if (TestKeyToggleBit(pbKeyState, vk)) {
    02039                 SetKeyStateToggle(pq, vk);
    02040             } else {
    02041                 ClearKeyStateToggle(pq, vk);
    02042             }
    02043         }
    02044     }
    02045
    02046     /*
    02047      * Update the key cache index.
    02048      */
    02049     gpsi->dwKeyCache++;
    02050
    02051     /*
    02052      * All updated.  Free the key state table if it was posted as an Event Message
    02053      */
    02054     if (pbKeyState != gafAsyncKeyState) {
    02055         UserFreePool(pbKeyState);
    02056     }
    02057 }
    02058
    02059
    02060 /***************************************************************************\
    02061 * PostEventMessage
    02062 *
    02063 *
    02064 * History:
    02065 * 03-04-91 DavidPe      Created.
    02066 \***************************************************************************/
    02067
    02068 BOOL PostEventMessage(
    02069     PTHREADINFO pti,
    02070     PQ    pq,
    02071     DWORD dwQEvent,
    02072     PWND  pwnd,
    02073     UINT  message,
    02074     WPARAM wParam,
    02075     LPARAM lParam)
    02076 {
    02077     PQMSG pqmsgEvent;
    02078
    02079     CheckCritIn();
    02080
    02081     /*
    02082      * If the thread is in cleanup, then it's possible the queue has
    02083      * already been removed for this thread.  If this is the case, then
    02084      * we should fail to post the event to a dying thread.
    02085      */
    02086     if (pti && (pti->TIF_flags & TIF_INCLEANUP))
    02087         return FALSE;
    02088
    02089     if ((pqmsgEvent = AllocQEntry(&pq->mlInput)) == NULL)
    02090         return FALSE;
    02091
    02092     StoreQMessage(pqmsgEvent, pwnd, message, wParam, lParam, 0, dwQEvent, 0);
    02093
    02094     StoreQMessagePti(pqmsgEvent, pti);
    02095
    02096     /*
    02097      * Let this thread know it has an event message to process.
    02098      */
    02099     if (pti == NULL) {
    02100         UserAssert(pti);
    02101         SetWakeBit(pq->ptiMouse, QS_EVENTSET);
    02102         SetWakeBit(pq->ptiKeyboard, QS_EVENTSET);
    02103     } else {
    02104         SetWakeBit(pti, QS_EVENTSET);
    02105     }
    02106
    02107     return TRUE;
    02108 }
    02109
    02110 /***************************************************************************\
    02111 * CheckOnTop
    02112 *
    02113 * Usually windows come to the top and activate all at once. Occasionally, a
    02114 * starting app will create a window, pause for awhile, then make itself
    02115 * visible. During that pause, if the user clicks down, the window won't be
    02116 * allowed to activate (because of our foreground activation model). But this
    02117 * still leaves the new window on top of the active window. When this click
    02118 * happens, we get here: if this window is active and is not on top, bring it
    02119 * to the top.
    02120 *
    02121 * Case in point: start winquote, click down. The window
    02122 * you clicked on is active, but winquote is on top.
    02123 *
    02124 * This rarely does anything because 99.99% of the time the active
    02125 * window is already where it should be - on top. Note that
    02126 * CalcForegroundInsertAfter() takes into account owner-based zordering.
    02127 *
    02128 *
    02129 * NOTE: the following was the original function written.  However, this
    02130 *       function has been disabled for now.  in WinWord and Excel especially,
    02131 *       this tends to cause savebits to be blown-away on mouse-activation of
    02132 *       its dialog-boxes.  This could be a problem with GW_HWNDPREV and/or
    02133 *       CalcForground not being the same, which causes a SetWindowPos to be
    02134 *       called, resulting in the freeing of the SPB.  This also solves a
    02135 *       problem with the ComboBoxes in MsMoney hiding/freeing the dropdown
    02136 *       listbox on activation as well.
    02137 *
    02138 * We need this for ActiveWindowTracking support. xxxBringWindowToTop used to be a call
    02139 *  to SetWindowPos but now it's gone.
    02140 *
    02141 * It returns TRUE if the window was brought the top; if no z-order change
    02142 *  took place, it returns FALSE.
    02143 *
    02144 * 05-20-93 ScottLu      Created.
    02145 * 10-17-94 ChrisWil     Made into stub-macro.
    02146 * 05-30-96 GerardoB     Brought it back to live for AWT
    02147 \***************************************************************************/
    02148 BOOL CheckOnTop(PTHREADINFO pti, PWND pwndTop, UINT message)
    02149 {
    02150     if (pwndTop != pti->pq->spwndActive)
    02151         return FALSE;
    02152
    02153     switch (message) {
    02154         case WM_LBUTTONDOWN:
    02155         case WM_RBUTTONDOWN:
    02156         case WM_MBUTTONDOWN:
    02157         case WM_XBUTTONDOWN:
    02158             if (   TestWF(pwndTop, WEFTOPMOST)
    02159                     || (_GetWindow(pwndTop, GW_HWNDPREV) != CalcForegroundInsertAfter(pwndTop))) {
    02160
    02161                  return xxxSetWindowPos(pwndTop, PWND_TOP, 0, 0, 0, 0,
    02162                         SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
    02163              }
    02164              break;
    02165     }
    02166
    02167     return FALSE;
    02168 }
    02169
    02170
    02171 #define MA_PASSTHRU     0
    02172 #define MA_SKIP         1
    02173 #define MA_REHITTEST    2
    02174
    02175 /***************************************************************************\
    02176 * zzzActiveCursorTracking
    02177 *
    02178 * If active window tracking is enabled, activation follows
    02179 *  the mouse. If the mouse is NOT on the active window
    02180 * (i.e., it was activated by a keyboard operation),
    02181 *  activation will change as soon as the mouse moves.
    02182 * So we have to make sure the mouse is on the active window.
    02183 *
    02184 * History
    02185 * 12/07/96  GerardoB  Created
    02186 \***************************************************************************/
    02187 void zzzActiveCursorTracking (PWND pwnd)
    02188 {
    02189     BOOL fVisible;
    02190     POINT pt;
    02191
    02192     /*
    02193      * If the last input event wasn't from the keyboard, bail
    02194      * The user is probably moving the mouse.
    02195      */
    02196     if (!(glinp.dwFlags & LINP_KEYBOARD)) {
    02197         return;
    02198     }
    02199     /*
    02200      * If we're already there, bail.
    02201      */
    02202     if (PtInRect((LPRECT)&pwnd->rcWindow, gptCursorAsync)) {
    02203         return;
    02204     }
    02205     /*
    02206      * If the window the mouse is on is not "active-trackable", then
    02207      *  we can leave the mouse right where it is
    02208      */
    02209      if ((gspwndCursor != NULL) && (GetActiveTrackPwnd(gspwndCursor, NULL) == NULL)) {
    02210          return;
    02211      }
    02212      /*
    02213       * If this window doesn't have a point visible in the screen, bail
    02214       */
    02215      pt.x = pwnd->rcWindow.left + ((pwnd->rcWindow.right  - pwnd->rcWindow.left) / 2);
    02216      pt.y = pwnd->rcWindow.top  + ((pwnd->rcWindow.bottom - pwnd->rcWindow.top)  / 2);
    02217      BoundCursor(&pt);
    02218      if (!PtInRect((LPRECT)&pwnd->rcWindow, pt)) {
    02219          return;
    02220      }
    02221     /*
    02222      * We need to make sure that this window is marked as visible or someone
    02223      *  else will be waken up to update the cursor (and might
    02224      *  activate itself because of the active tracking).
    02225      *
    02226      * Later5.0 GerardoB: If the window is still not visible when
    02227      *  it wakes up, then we're out of luck.
    02228      */
    02229     fVisible = TestWF(pwnd, WFVISIBLE);
    02230     if (!fVisible) {
    02231         SetVisible(pwnd, SV_SET);
    02232     }
    02233
    02234     /*
    02235      * Move the cursor to the center of this window
    02236      */
    02237     zzzInternalSetCursorPos(pt.x, pt.y);
    02238     /*
    02239      * Restore visible bit.
    02240      */
    02241     if (!fVisible) {
    02242         SetVisible(pwnd, SV_UNSET);
    02243     }
    02244 }
    02245 /***************************************************************************\
    02246 * GetActiveTrackPwnd
    02247 *
    02248 * History
    02249 * 12/07/96  GerardoB  Extracted from xxxActiveWindowTracking.
    02250 \***************************************************************************/
    02251 PWND GetActiveTrackPwnd(PWND pwnd, Q **ppq)
    02252 {
    02253     PWND pwndActivate;
    02254     Q *pq;
    02255
    02256     CheckCritIn();
    02257     pwndActivate = pwnd;
    02258
    02259     /*
    02260      * Find the top parent
    02261      */
    02262     while (TestwndChild(pwndActivate)) {
    02263         pwndActivate = pwndActivate->spwndParent;
    02264     }
    02265
    02266     /*
    02267      * If disabled, get a enabled popup owned by it.
    02268      */
    02269     if (TestWF(pwndActivate, WFDISABLED)) {
    02270         /*
    02271          * This is what we do elsewhere when someone clicks on a
    02272          *  disabled non-active window. It might be cheaper to check
    02273          *  pwnd->spwndLastActive first (we might need to walk up
    02274          *  the owner chain though, as this is where we set spwndLastAcitve
    02275          *  when activating a new window. see xxxActivateThisWindow).
    02276          * But let's do the same here; this should be fixed/improved
    02277          *  in DWP_GetEnabledPopup anyway. There might be a reason
    02278          *  why we don't grab spwndLastActive if OK.... perhaps it has
    02279          *  something to do with nested owner windows
    02280          */
    02281          pwndActivate = DWP_GetEnabledPopup(pwndActivate);
    02282     }
    02283
    02284     /*
    02285      * Bail if we didn't find a visible window
    02286      */
    02287     if ((pwndActivate == NULL) || !TestWF(pwndActivate, WFVISIBLE)) {
    02288         return NULL;
    02289     }
    02290
    02291     /*
    02292      * If already active in the foreground queue, nothing to do
    02293      * Don't activate the modeless menu notification window (it would
    02294      *  dismiss the menu)
    02295      */
    02296     pq = GETPTI(pwndActivate)->pq;
    02297     if ((pq == gpqForeground)
    02298             && ((pwndActivate == pq->spwndActive)
    02299                 || IsModelessMenuNotificationWindow(pwndActivate))) {
    02300
    02301         return NULL;
    02302     }
    02303
    02304     /*
    02305      * Don't activate the shell window.
    02306      */
    02307     if (pwndActivate == pwndActivate->head.rpdesk->pDeskInfo->spwndShell) {
    02308         return NULL;
    02309     }
    02310
    02311     /*
    02312      * Return the queue if requested
    02313      */
    02314     if (ppq != NULL) {
    02315         *ppq = pq;
    02316     }
    02317
    02318     return pwndActivate;
    02319 }
    02320 /***************************************************************************\
    02321 * xxxActivateWindowTracking
    02322 *
    02323 * Activates a window without z-ordering it to the top
    02324 *
    02325 * 06/05/96  GerardoB  Created
    02326 \***************************************************************************/
    02327 int xxxActiveWindowTracking(
    02328     PWND pwnd,
    02329     UINT uMsg,
    02330     int iHitTest)
    02331 {
    02332
    02333     BOOL fSuccess;
    02334     int iRet;
    02335     PWND pwndActivate;
    02336     Q *pq;
    02337     TL tlpwndActivate;
    02338
    02339     CheckLock(pwnd);
    02340     UserAssert(TestUP(ACTIVEWINDOWTRACKING));
    02341
    02342     /*
    02343      * If the mouse hasn't been long enough on this queue, bail.
    02344      */
    02345     pq = GETPTI(pwnd)->pq;
    02346     if (!(pq->QF_flags & QF_ACTIVEWNDTRACKING)) {
    02347         return MA_PASSTHRU;
    02348     }
    02349     pq->QF_flags &= ~QF_ACTIVEWNDTRACKING;
    02350
    02351     /*
    02352      * If the foreground is locked, bail
    02353      */
    02354     if (IsForegroundLocked()) {
    02355         return MA_PASSTHRU;
    02356     }
    02357
    02358     /*
    02359      * Get the window we need to activate. If none, bail.
    02360      */
    02361     pwndActivate = GetActiveTrackPwnd(pwnd, &pq);
    02362     if (pwndActivate == NULL) {
    02363         return MA_PASSTHRU;
    02364     }
    02365
    02366     /*
    02367      * Lock if needed because we're about to callback
    02368      */
    02369     if (pwnd != pwndActivate) {
    02370         ThreadLockAlways(pwndActivate, &tlpwndActivate);
    02371     }
    02372
    02373     /*
    02374      * Let's ask if it's OK to do this
    02375      *
    02376      * This message is supposed to go to the window the mouse is on.
    02377      * This could be a child window which might return MA_NOACTIVATE*.
    02378      * For mouse clicks (which is what we want to emulate here)
    02379      *  xxxButtonEvent calls xxxSetForegroundWindow2 so their
    02380      *  pwndActivate gets brought to the foreground regardless.
    02381      * So we send the message to pwndActivate instead.
    02382      */
    02383     iRet = (int)xxxSendMessage(pwndActivate, WM_MOUSEACTIVATE,
    02384             (WPARAM)(HWq(pwndActivate)), MAKELONG((SHORT)iHitTest, uMsg));
    02385
    02386
    02387     switch (iRet) {
    02388         case MA_ACTIVATE:
    02389         case MA_ACTIVATEANDEAT:
    02390             if (pq == gpqForeground) {
    02391                 fSuccess = xxxActivateThisWindow(pwndActivate, 0,
    02392                         (TestUP(ACTIVEWNDTRKZORDER) ? 0 : ATW_NOZORDER));
    02393             } else {
    02394                 fSuccess = xxxSetForegroundWindow2(pwndActivate, NULL,
    02395                         SFW_SWITCH | (TestUP(ACTIVEWNDTRKZORDER) ? 0 : SFW_NOZORDER));
    02396             }
    02397
    02398             /*
    02399              * Eat the message if activation failed.
    02400              */
    02401             if (!fSuccess) {
    02402                 iRet = MA_SKIP;
    02403             } else if (iRet == MA_ACTIVATEANDEAT) {
    02404                iRet = MA_SKIP;
    02405             }
    02406             break;
    02407
    02408         case MA_NOACTIVATEANDEAT:
    02409             iRet = MA_SKIP;
    02410             break;
    02411
    02412
    02413         case MA_NOACTIVATE:
    02414         default:
    02415             iRet = MA_PASSTHRU;
    02416             break;
    02417     }
    02418
    02419     if (pwnd != pwndActivate) {
    02420         ThreadUnlock(&tlpwndActivate);
    02421     }
    02422
    02423     return iRet;
    02424
    02425 }
    02426 /***************************************************************************\
    02427 * xxxMouseActivate
    02428 *
    02429 * This is where activation due to mouse clicks occurs.
    02430 *
    02431 * IMPLEMENTATION:
    02432 *     The message is sent to the specified window.  In xxxDefWindowProc, the
    02433 *     message is sent to the window's parent.  The receiving window may
    02434 *            a) process the message,
    02435 *            b) skip the message totally, or
    02436 *            c) re-hit test message
    02437 *
    02438 *     A WM_SETCURSOR message is also sent through the system to set the cursor.
    02439 *
    02440 * History:
    02441 * 11-22-90 DavidPe      Ported.
    02442 \***************************************************************************/
    02443
    02444 int xxxMouseActivate(
    02445     PTHREADINFO pti,
    02446     PWND pwnd,
    02447     UINT message,
    02448     WPARAM wParam,
    02449     LPPOINT lppt,
    02450     int ht)
    02451 {
    02452     UINT x, y;
    02453     PWND pwndTop;
    02454     int result;
    02455     TL tlpwndTop;
    02456     BOOL fSend;
    02457
    02458     CheckLock(pwnd);
    02459
    02460     UserAssert(_GETPDESK(pwnd) != NULL);
    02461
    02462     /*
    02463      * No mouse activation if the mouse is captured. Must check for the capture
    02464      * ONLY here. 123W depends on it - create a graph, select Rearrange..
    02465      * flip horizontal, click outside the graph. If this code checks for
    02466      * anything beside just capture, 123w will get the below messages and
    02467      * get confused.
    02468      */
    02469     if (pti->pq->spwndCapture != NULL) {
    02470         return MA_PASSTHRU;
    02471     }
    02472
    02473     result = MA_PASSTHRU;
    02474
    02475     pwndTop = pwnd;
    02476     ThreadLockWithPti(pti, pwndTop, &tlpwndTop);
    02477
    02478         /*
    02479          * B#1404
    02480          * Don't send WM_PARENTNOTIFY messages if the child has
    02481          * WS_EX_NOPARENTNOTIFY style.
    02482          *
    02483          * Unfortunately, this breaks people who create controls in
    02484          * MDI children, like WinMail.  They don't get WM_PARENTNOTIFY
    02485          * messages, which don't get passed to DefMDIChildProc(), which
    02486          * then can't update the active MDI child.  Grrr.
    02487          */
    02488
    02489     fSend = (!TestWF(pwnd, WFWIN40COMPAT) || !TestWF(pwnd, WEFNOPARENTNOTIFY));
    02490
    02491     /*
    02492      * If it's a buttondown event, send WM_PARENTNOTIFY.
    02493      */
    02494     switch (message) {
    02495     case WM_LBUTTONDOWN:
    02496     case WM_RBUTTONDOWN:
    02497     case WM_MBUTTONDOWN:
    02498     case WM_XBUTTONDOWN:
    02499         while (TestwndChild(pwndTop)) {
    02500             pwndTop = pwndTop->spwndParent;
    02501
    02502             if (fSend) {
    02503                 ThreadUnlock(&tlpwndTop);
    02504                 ThreadLockWithPti(pti, pwndTop, &tlpwndTop);
    02505                 x = (UINT)(lppt->x - pwndTop->rcClient.left);
    02506                 y = (UINT)(lppt->y - pwndTop->rcClient.top);
    02507
    02508                 /* Get the xbutton from the hiword of wParam */
    02509                 UserAssert(message == WM_XBUTTONDOWN || HIWORD(wParam) == 0);
    02510                 UserAssert(LOWORD(wParam) == 0);
    02511                 xxxSendMessage(pwndTop, WM_PARENTNOTIFY, (WPARAM)(message | wParam), MAKELPARAM(x, y));
    02512             }
    02513         }
    02514
    02515         if (!fSend) {
    02516             ThreadUnlock(&tlpwndTop);
    02517             ThreadLockAlwaysWithPti(pti, pwndTop, &tlpwndTop);
    02518         }
    02519
    02520         /*
    02521          * NOTE: We break out of this loop with pwndTop locked.
    02522          */
    02523         break;
    02524     }
    02525
    02526     /*
    02527      * The mouse was moved onto this window: make it foreground
    02528      */
    02529     if (TestUP(ACTIVEWINDOWTRACKING) && (message == WM_MOUSEMOVE)) {
    02530         result = xxxActiveWindowTracking(pwnd, WM_MOUSEMOVE, ht);
    02531     }
    02532
    02533     /*
    02534      * Are we hitting an inactive top-level window WHICH ISN'T THE DESKTOP(!)?
    02535      *
    02536      * craigc 7-14-89 hitting either inactive top level or any child window,
    02537      * to be compatible with 2.X.  Apps apparently needs this message.
    02538      */
    02539     else if ((pti->pq->spwndActive != pwnd || pti->pq->QF_flags & QF_EVENTDEACTIVATEREMOVED) &&
    02540             (pwndTop != PWNDDESKTOP(pwndTop))) {
    02541         switch (message) {
    02542         case WM_LBUTTONDOWN:
    02543         case WM_RBUTTONDOWN:
    02544         case WM_MBUTTONDOWN:
    02545         case WM_XBUTTONDOWN:
    02546
    02547             /*
    02548              * Send the MOUSEACTIVATE message.
    02549              */
    02550             result = (int)xxxSendMessage(pwnd, WM_MOUSEACTIVATE,
    02551                     (WPARAM)(HW(pwndTop)), MAKELONG((SHORT)ht, message));
    02552
    02553             switch (result) {
    02554
    02555             case 0:
    02556             case MA_ACTIVATE:
    02557             case MA_ACTIVATEANDEAT:
    02558
    02559                 /*
    02560                  * If activation fails, swallow the message.
    02561                  */
    02562                 if ((pwndTop != pti->pq->spwndActive ||
    02563                         pti->pq->QF_flags & QF_EVENTDEACTIVATEREMOVED) &&
    02564                         !xxxActivateWindow(pwndTop,
    02565                           (UINT)((pti->pq->codeCapture == NO_CAP_CLIENT) ?
    02566                           AW_TRY2 : AW_TRY))) {
    02567                     result = MA_SKIP;
    02568                 } else if (TestWF(pwndTop, WFDISABLED)) {
    02569 #ifdef NEVER
    02570
    02571                     /*
    02572                      * Althoug this is what win3 does, it is braindead: it
    02573                      * can easily cause infinite loops.  Returning "rehittest"
    02574                      * means process this event over again - nothing causes
    02575                      * anything different to happen, and we get an infinite
    02576                      * loop.  This case never gets executed on win3 because if
    02577                      * the window is disabled, it got the HTERROR hittest
    02578                      * code.  This can only be done on Win32 where input is
    02579                      * assigned to a window BEFORE process occurs to pull
    02580                      * it out of the queue.
    02581                      */
    02582                     result = MA_REHITTEST;
    02583 #endif
    02584
    02585                     /*
    02586                      * Someone clicked on a window before it was disabled...
    02587                      * Since it is disabled now, don't send this message to
    02588                      * it: instead eat it.
    02589                      */
    02590                     result = MA_SKIP;
    02591                 } else if (result == MA_ACTIVATEANDEAT) {
    02592                     result = MA_SKIP;
    02593                 } else {
    02594                     result = MA_PASSTHRU;
    02595                     goto ItsActiveJustCheckOnTop;
    02596                 }
    02597                 break;
    02598
    02599             case MA_NOACTIVATEANDEAT:
    02600                 result = MA_SKIP;
    02601                 break;
    02602             }
    02603         }
    02604     } else {
    02605 ItsActiveJustCheckOnTop:
    02606         /*
    02607          * Make sure this active window is on top (see comment
    02608          * in CheckOnTop).
    02609          */
    02610         if (TestUP(ACTIVEWINDOWTRACKING)) {
    02611             if (CheckOnTop(pti, pwndTop, message)) {
    02612                 /*
    02613                  * The window was z-ordered to the top.
    02614                  * If it is a console window, skip the message
    02615                  *  so it won't go into "selecting" mode
    02616                  * Hard error boxes are created by csrss as well
    02617                  * If we have topmost console windows someday, this
    02618                  *  will need to change
    02619                  */
    02620                  if ((ht == HTCLIENT)
    02621                         && (GETPTI(pwndTop)->TIF_flags & TIF_CSRSSTHREAD)
    02622                         && !(TestWF(pwndTop, WEFTOPMOST))) {
    02623
    02624                      RIPMSG2(RIP_WARNING, "xxxMouseActivate: Skipping msg %#lx for pwnd %#p",
    02625                             message, pwndTop);
    02626                      result = MA_SKIP;
    02627                  }
    02628             }
    02629         } /* if (TestUP(ACTIVEWINDOWTRACKING)) */
    02630     }
    02631
    02632     /*
    02633      * Now set the cursor shape.
    02634      */
    02635     if (pti->pq->spwndCapture == NULL) {
    02636         xxxSendMessage(pwnd, WM_SETCURSOR, (WPARAM)HW(pwnd),
    02637                 MAKELONG((SHORT)ht, message));
    02638     }
    02639
    02640     ThreadUnlock(&tlpwndTop);
    02641     return result;
    02642 }
    02643
    02644 /***************************************************************************\
    02645 *  ResetMouseHover()
    02646 *
    02647 *  Resets mouse hover state information.
    02648 *
    02649 *  11/03/95    francish    created.
    02650 *  09/04/97    GerardoB    Rewritten to use per desktop tracking
    02651 \***************************************************************************/
    02652
    02653 void ResetMouseHover(PDESKTOP pdesk, POINT pt)
    02654 {
    02655     /*
    02656      * Reset the timer and hover rect
    02657      */
    02658     InternalSetTimer(pdesk->spwndTrack, IDSYS_MOUSEHOVER,
    02659                      pdesk->dwMouseHoverTime,
    02660                     xxxSystemTimerProc, TMRF_SYSTEM);
    02661
    02662     SetRect(&pdesk->rcMouseHover,
    02663             pt.x - gcxMouseHover / 2,
    02664             pt.y - gcyMouseHover / 2,
    02665             pt.x + gcxMouseHover / 2,
    02666             pt.y + gcyMouseHover / 2);
    02667
    02668 }
    02669
    02670 /***************************************************************************\
    02671 *  QueryTrackMouseEvent()
    02672 *
    02673 *  Fills in a TRACKMOUSEEVENT structure describing current tracking state.
    02674 *
    02675 *  11/03/95    francish    created.
    02676 *  09/04/97    GerardoB    Rewritten to use per desktop tracking
    02677 \***************************************************************************/
    02678
    02679 BOOL QueryTrackMouseEvent(
    02680     LPTRACKMOUSEEVENT lpTME)
    02681 {
    02682     PTHREADINFO ptiCurrent = PtiCurrent();
    02683     PDESKTOP pdesk = ptiCurrent->rpdesk;
    02684
    02685     /*
    02686      * initialize the struct
    02687      */
    02688     RtlZeroMemory(lpTME, sizeof(*lpTME));
    02689     lpTME->cbSize = sizeof(*lpTME);
    02690     /*
    02691      * Bail if not tracking any mouse event
    02692      *  or if the current thread is not in spwndTrack's queue
    02693      */
    02694     if (!(pdesk->dwDTFlags & DF_TRACKMOUSEEVENT)
    02695             || (ptiCurrent->pq != GETPTI(pdesk->spwndTrack)->pq)) {
    02696         return TRUE;
    02697     }
    02698     /*
    02699      * fill in the requested information
    02700      */
    02701     if (pdesk->htEx != HTCLIENT) {
    02702         lpTME->dwFlags |= TME_NONCLIENT;
    02703     }
    02704     if (pdesk->dwDTFlags & DF_TRACKMOUSELEAVE) {
    02705         lpTME->dwFlags |= TME_LEAVE;
    02706     }
    02707     if (pdesk->dwDTFlags & DF_TRACKMOUSEHOVER) {
    02708         lpTME->dwFlags |= TME_HOVER;
    02709         lpTME->dwHoverTime = pdesk->dwMouseHoverTime;
    02710     }
    02711
    02712     lpTME->hwndTrack = HWq(pdesk->spwndTrack);
    02713
    02714     return TRUE;
    02715 }
    02716
    02717 /***************************************************************************\
    02718 *  TrackMouseEvent()
    02719 *
    02720 *  API for requesting extended mouse notifications (hover, leave, ...)
    02721 *
    02722 *  11/03/95    francish    created.
    02723 *  09/04/97    GerardoB    Rewritten to use per desktop tracking
    02724 \***************************************************************************/
    02725 BOOL TrackMouseEvent(
    02726     LPTRACKMOUSEEVENT lpTME)
    02727 {
    02728     PDESKTOP pdesk = PtiCurrent()->rpdesk;
    02729     PWND     pwnd;
    02730
    02731     /*
    02732      * Validate hwndTrack
    02733      */
    02734     pwnd = ValidateHwnd(lpTME->hwndTrack);
    02735     if (pwnd == NULL) {
    02736         return FALSE;
    02737     }
    02738     /*
    02739      * If we're not tracking this window or not in correct hittest, bail
    02740      */
    02741     if ((pwnd != pdesk->spwndTrack)
    02742             || (!!(lpTME->dwFlags & TME_NONCLIENT) ^ (pdesk->htEx != HTCLIENT))) {
    02743
    02744         if ((lpTME->dwFlags & TME_LEAVE) && !(lpTME->dwFlags & TME_CANCEL)) {
    02745             _PostMessage(pwnd,
    02746                          ((lpTME->dwFlags & TME_NONCLIENT) ? WM_NCMOUSELEAVE : WM_MOUSELEAVE),
    02747                          0, 0);
    02748         }
    02749         return TRUE;
    02750     }
    02751
    02752     /*
    02753      * Process cancel request
    02754      */
    02755     if (lpTME->dwFlags & TME_CANCEL) {
    02756         if (lpTME->dwFlags & TME_LEAVE) {
    02757             pdesk->dwDTFlags &= ~DF_TRACKMOUSELEAVE;
    02758         }
    02759         if (lpTME->dwFlags & TME_HOVER) {
    02760             if (pdesk->dwDTFlags & DF_TRACKMOUSEHOVER) {
    02761                 _KillSystemTimer(pwnd, IDSYS_MOUSEHOVER);
    02762                 pdesk->dwDTFlags &= ~DF_TRACKMOUSEHOVER;
    02763             }
    02764         }
    02765         return TRUE;
    02766     }
    02767
    02768     /*
    02769      * Track mouse leave
    02770      */
    02771     if (lpTME->dwFlags & TME_LEAVE) {
    02772         pdesk->dwDTFlags |= DF_TRACKMOUSELEAVE;
    02773     }
    02774     /*
    02775      * Track mouse hover
    02776      */
    02777     if (lpTME->dwFlags & TME_HOVER) {
    02778         pdesk->dwDTFlags |= DF_TRACKMOUSEHOVER;
    02779
    02780         pdesk->dwMouseHoverTime = lpTME->dwHoverTime;
    02781         if ((pdesk->dwMouseHoverTime == 0) || (pdesk->dwMouseHoverTime == HOVER_DEFAULT)) {
    02782             pdesk->dwMouseHoverTime = gdtMouseHover;
    02783         }
    02784
    02785         ResetMouseHover(pdesk, GETPTI(pwnd)->ptLast);
    02786     }
    02787
    02788     return TRUE;
    02789 }
    02790
    02791 /***************************************************************************\
    02792 * xxxGetNextSysMsg
    02793 *
    02794 * Returns the queue pointer of the next system message or
    02795 *  NULL             - no more messages (may be a journal playback delay)
    02796 *  PQMSG_PLAYBACK   - got a journal playback message
    02797 * (Anything else is a real pointer)
    02798 *
    02799 * 10-23-92 ScottLu      Created.
    02800 \***************************************************************************/
    02801
    02802 PQMSG xxxGetNextSysMsg(
    02803     PTHREADINFO pti,
    02804     PQMSG pqmsgPrev,
    02805     PQMSG pqmsg)
    02806 {
    02807     DWORD dt;
    02808     PMLIST pml;
    02809     PQMSG pqmsgT;
    02810
    02811     /*
    02812      * If there is a journal playback hook, call it to get the next message.
    02813      */
    02814     if ((PhkFirstGlobalValid(pti, WH_JOURNALPLAYBACK) != NULL)
    02815             && (pti->rpdesk == grpdeskRitInput)) {
    02816         /*
    02817          * We can't search through journal messages: we only get the current
    02818          * journal message. So if the caller has already called us once
    02819          * before, then exit with no messages.
    02820          */
    02821         if (pqmsgPrev != 0)
    02822             return NULL;
    02823
    02824         /*
    02825          * Tell the journal playback hook that we're done
    02826          * with this message now.
    02827          */
    02828         dt = xxxCallJournalPlaybackHook(pqmsg);
    02829         if (dt == 0xFFFFFFFF)
    02830             return NULL;
    02831
    02832         /*
    02833          * If dt == 0, then we don't need to wait: set the right wake
    02834          * bits and return this message.
    02835          */
    02836         if (dt == 0) {
    02837             WakeSomeone(pti->pq, pqmsg->msg.message, NULL);
    02838             return PQMSG_PLAYBACK;
    02839         } else {
    02840             /*
    02841              * There is logically no more input in the "queue", so clear the
    02842              * bits so that we will sleep when GetMessage is called.
    02843              */
    02844             pti->pcti->fsWakeBits &= ~QS_INPUT;
    02845             pti->pcti->fsChangeBits &= ~QS_INPUT;
    02846
    02847             /*
    02848              * Need to wait before processing this next message. Set
    02849              * a journal timer.
    02850              */
    02851             SetJournalTimer(dt, pqmsg->msg.message);
    02852
    02853             return NULL;
    02854         }
    02855     }
    02856
    02857     /*
    02858      * No journalling going on... return next message in system queue.
    02859      */
    02860
    02861     /*
    02862      * Queue up a mouse move if the mouse has moved.
    02863      */
    02864     if (pti->pq->QF_flags & QF_MOUSEMOVED) {
    02865         PostMove(pti->pq);
    02866     }
    02867
    02868     /*
    02869      * If no messages in the input queue, return with 0.
    02870      */
    02871     pml = &pti->pq->mlInput;
    02872     if (pml->cMsgs == 0)
    02873         return NULL;
    02874
    02875     /*
    02876      * If this is the first call to xxxGetNextSysMsg(), return the
    02877      * first message.
    02878      */
    02879     if (pqmsgPrev == NULL || pti->pq->idSysPeek <= (ULONG_PTR)PQMSG_PLAYBACK) {
    02880         pqmsgT = pml->pqmsgRead;
    02881     } else {
    02882         /*
    02883          * Otherwise return the next message in the queue. Index with
    02884          * idSysPeek, because that is updated by recursive calls through
    02885          * this code.
    02886          */
    02887         pqmsgT = ((PQMSG)(pti->pq->idSysPeek))->pqmsgNext;
    02888     }
    02889
    02890     /*
    02891      * Fill in the structure passed, and return the pointer to the
    02892      * current message in the message list. This will become the new
    02893      * pq->idSysPeek.
    02894      */
    02895     if (pqmsgT != NULL)
    02896         *pqmsg = *pqmsgT;
    02897     return pqmsgT;
    02898 }
    02899
    02900 /***************************************************************************\
    02901 * UpdateKeyState
    02902 *
    02903 * Updates queue key state tables.
    02904 *
    02905 * 11-11-92 ScottLu      Created.
    02906 \***************************************************************************/
    02907
    02908 void UpdateKeyState(
    02909     PQ pq,
    02910     UINT vk,
    02911     BOOL fDown)
    02912 {
    02913     if (vk != 0) {
    02914         /*
    02915          * If we're going down, toggle only if the key isn't
    02916          * already down.
    02917          */
    02918         if (fDown && !TestKeyStateDown(pq, vk)) {
    02919             if (TestKeyStateToggle(pq, vk)) {
    02920                 ClearKeyStateToggle(pq, vk);
    02921             } else {
    02922                 SetKeyStateToggle(pq, vk);
    02923             }
    02924         }
    02925
    02926         /*
    02927          * Now set/clear the key down state.
    02928          */
    02929         if (fDown) {
    02930             SetKeyStateDown(pq, vk);
    02931         } else {
    02932             ClearKeyStateDown(pq, vk);
    02933         }
    02934
    02935         /*
    02936          * If this is one of the keys we cache, update the key cache index.
    02937          */
    02938         if (vk < CVKKEYCACHE) {
    02939             gpsi->dwKeyCache++;
    02940         }
    02941     }
    02942 }
    02943
    02944 /***************************************************************************\
    02945 * EqualMsg
    02946 *
    02947 * This routine is called in case that idSysPeek points to a message
    02948 * and we are trying to remove a different message
    02949 *
    02950 * 04-25-96 CLupu      Created.
    02951 \***************************************************************************/
    02952
    02953 BOOL EqualMsg(PQMSG pqmsg1, PQMSG pqmsg2)
    02954 {
    02955     if (pqmsg1->msg.hwnd    != pqmsg2->msg.hwnd ||
    02956         pqmsg1->msg.message != pqmsg2->msg.message)
    02957         return FALSE;
    02958
    02959     /*
    02960      * This might be a coalesced WM_MOUSEMOVE
    02961      */
    02962     if (pqmsg1->msg.message == WM_MOUSEMOVE)
    02963         return TRUE;
    02964
    02965     if (pqmsg1->pti      != pqmsg2->pti ||
    02966         pqmsg1->msg.time != pqmsg2->msg.time)
    02967         return FALSE;
    02968
    02969     return TRUE;
    02970 }
    02971
    02972 /***************************************************************************\
    02973 * xxxSkipSysMsg
    02974 *
    02975 * This routine "skips" an input message: either by calling the journal
    02976 * hooks if we're journalling or by "skipping" the message in the input
    02977 * queue. Internal keystate tables are updated as well.
    02978 *
    02979 * 10-23-92 ScottLu      Created.
    02980 \***************************************************************************/
    02981
    02982 void xxxSkipSysMsg(
    02983     PTHREADINFO pti,
    02984     PQMSG pqmsg)
    02985 {
    02986     PQMSG pqmsgT;
    02987     BOOL  fDown;
    02988     BYTE  vk;
    02989     PHOOK phook;
    02990
    02991     /*
    02992      * If idSysPeek is 0, then the pqmsg that we were looking at has been
    02993      * deleted, probably because of a callout from ScanSysQueue, and that
    02994      * callout then called PeekMessage(fRemove == TRUE), and then returned.
    02995      */
    02996     if (pti->pq->idSysPeek == 0)
    02997         return;
    02998
    02999     phook = PhkFirstGlobalValid(pti, WH_JOURNALPLAYBACK);
    03000     if (phook != NULL) {
    03001         /*
    03002          * Tell the journal playback hook that we're done
    03003          * with this message now.
    03004          */
    03005         phook->flags |= HF_NEEDHC_SKIP;
    03006     } else {
    03007         phook = PhkFirstGlobalValid(pti, WH_JOURNALRECORD);
    03008         if (phook != NULL) {
    03009             /*
    03010              * We've processed a new message: tell the journal record
    03011              * hook what the message is.
    03012              */
    03013             xxxCallJournalRecordHook(pqmsg);
    03014         }
    03015
    03016         /*
    03017          * If idSysPeek is 0 now, it means we've been recursed into yet
    03018          * again. This would confuse a journalling app, but it would confuse
    03019          * us more because we'd fault. Return if idSysPeek is 0.
    03020          */
    03021         if ((pqmsgT = (PQMSG)pti->pq->idSysPeek) == NULL)
    03022             return;
    03023
    03024         /*
    03025          * Delete this message from the input queue. Make sure pqmsgT isn't
    03026          * 1: this could happen if an app unhooked a journal record hook
    03027          * during a callback from xxxScanSysQueue.
    03028          */
    03029         if (pqmsgT != PQMSG_PLAYBACK) {
    03030             /*
    03031              * There are cases when idSysPeek points to a different message
    03032              * than the one we are trying to remove. This can happen if
    03033              * two threads enters in xxxScanSysQueue, sets the idSysPeek and
    03034              * after this their queues got redistributed. The first thread
    03035              * will have the idSysPeek preserved but the second one has to
    03036              * search the queue for its message. - ask CLupu
    03037              */
    03038             if (!EqualMsg(pqmsgT, pqmsg)) {
    03039
    03040                 PQMSG pqmsgS;
    03041
    03042 #if DEBUGTAGS
    03043                 if (IsDbgTagEnabled(DBGTAG_SysPeek)) {
    03044                     gnSysPeekSearch++;
    03045                 }
    03046 #endif
    03047
    03048                 TAGMSG0(DBGTAG_SysPeek | RIP_THERESMORE,              "Different message than idSysPeek\n");
    03049                 TAGMSG2(DBGTAG_SysPeek | RIP_NONAME | RIP_THERESMORE, "pqmsg   = %#p  idSysPeek = %#p",  pqmsg,              pqmsgT);
    03050                 TAGMSG2(DBGTAG_SysPeek | RIP_NONAME | RIP_THERESMORE, "pti     = %#p  pti       = %#p",  pqmsg->pti,         pqmsgT->pti);
    03051                 TAGMSG2(DBGTAG_SysPeek | RIP_NONAME | RIP_THERESMORE, "msg     = %08lx  msg       = %08lx",  pqmsg->msg.message, pqmsgT->msg.message);
    03052                 TAGMSG2(DBGTAG_SysPeek | RIP_NONAME | RIP_THERESMORE, "hwnd    = %#p  hwnd      = %#p",  pqmsg->msg.hwnd,    pqmsgT->msg.hwnd);
    03053                 TAGMSG2(DBGTAG_SysPeek | RIP_NONAME | RIP_THERESMORE, "wParam  = %#p  wParam    = %#p",  pqmsg->msg.wParam,  pqmsgT->msg.wParam);
    03054                 TAGMSG2(DBGTAG_SysPeek | RIP_NONAME | RIP_THERESMORE, "lParam  = %#p  lParam    = %#p",  pqmsg->msg.lParam,  pqmsgT->msg.lParam);
    03055                 TAGMSG2(DBGTAG_SysPeek | RIP_NONAME | RIP_THERESMORE, "time    = %08lx  time      = %08lx",  pqmsg->msg.time,    pqmsgT->msg.time);
    03056                 TAGMSG2(DBGTAG_SysPeek | RIP_NONAME | RIP_THERESMORE, "Extra   = %08lx  Extra     = %08lx",  pqmsg->ExtraInfo,   pqmsgT->ExtraInfo);
    03057                 TAGMSG1(DBGTAG_SysPeek | RIP_NONAME,                  "\npqmsgT  = %#p", pqmsgT);
    03058
    03059                 /*
    03060                  * Begin to search for this message
    03061                  */
    03062                 pqmsgS = pti->pq->mlInput.pqmsgRead;
    03063
    03064                 while (pqmsgS != NULL) {
    03065                     if (EqualMsg(pqmsgS, pqmsg)) {
    03066                         TAGMSG2(DBGTAG_SysPeek | RIP_THERESMORE,
    03067                                 "Deleting pqmsg %#p, pti %#p",
    03068                                 pqmsgS, pqmsgS->pti);
    03069
    03070                         TAGMSG4(DBGTAG_SysPeek | RIP_NONAME,
    03071                                 "m %04lx, w %#p, l %#p, t %lx",
    03072                                 pqmsgS->msg.message, pqmsgS->msg.hwnd,
    03073                                 pqmsgS->msg.lParam, pqmsgS->msg.time);
    03074
    03075                         pqmsgT = pqmsgS;
    03076                         break;
    03077                     }
    03078                     pqmsgS = pqmsgS->pqmsgNext;
    03079                 }
    03080                 if (pqmsgS == NULL) {
    03081                     TAGMSG0(DBGTAG_SysPeek, "Didn't find a matching message. No message removed.");
    03082                     return;
    03083                 }
    03084             }
    03085
    03086             if (pqmsgT == (PQMSG)pti->pq->idSysPeek) {
    03087                 /*
    03088                  * We'll remove this message from the input queue
    03089                  * so set idSysPeek to 0.
    03090                  */
    03091                 CheckPtiSysPeek(1, pti->pq, 0);
    03092                 pti->pq->idSysPeek = 0;
    03093             }
    03094             DelQEntry(&pti->pq->mlInput, pqmsgT);
    03095         }
    03096     }
    03097
    03098     fDown = TRUE;
    03099     vk = 0;
    03100
    03101     switch (pqmsg->msg.message) {
    03102     case WM_MOUSEMOVE:
    03103     case WM_QUEUESYNC:
    03104     default:
    03105         /*
    03106          * No state change.
    03107          */
    03108         break;
    03109
    03110     case WM_KEYUP:
    03111     case WM_SYSKEYUP:
    03112         fDown = FALSE;
    03113
    03114         /*
    03115          * Fall through.
    03116          */
    03117     case WM_KEYDOWN:
    03118     case WM_SYSKEYDOWN:
    03119         vk = LOBYTE(LOWORD(pqmsg->msg.wParam));
    03120         break;
    03121
    03122     case WM_LBUTTONUP:
    03123         fDown = FALSE;
    03124
    03125         /*
    03126          * Fall through.
    03127          */
    03128     case WM_LBUTTONDOWN:
    03129         vk = VK_LBUTTON;
    03130         break;
    03131
    03132     case WM_RBUTTONUP:
    03133         fDown = FALSE;
    03134
    03135         /*
    03136          * Fall through.
    03137          */
    03138     case WM_RBUTTONDOWN:
    03139         vk = VK_RBUTTON;
    03140         break;
    03141
    03142     case WM_MBUTTONUP:
    03143         fDown = FALSE;
    03144
    03145         /*
    03146          * Fall through.
    03147          */
    03148     case WM_MBUTTONDOWN:
    03149         vk = VK_MBUTTON;
    03150         break;
    03151
    03152     case WM_XBUTTONUP:
    03153         fDown = FALSE;
    03154
    03155         /*
    03156          * Fall through.
    03157          */
    03158     case WM_XBUTTONDOWN:
    03159         UserAssert(GET_XBUTTON_WPARAM(pqmsg->msg.wParam) == XBUTTON1 ||
    03160                    GET_XBUTTON_WPARAM(pqmsg->msg.wParam) == XBUTTON2);
    03161
    03162         switch (GET_XBUTTON_WPARAM(pqmsg->msg.wParam)) {
    03163         case XBUTTON1:
    03164             vk = VK_XBUTTON1;
    03165             break;
    03166
    03167         case XBUTTON2:
    03168             vk = VK_XBUTTON2;
    03169             break;
    03170         }
    03171
    03172         break;
    03173     }
    03174
    03175     /*
    03176      * Set toggle and down bits appropriately.
    03177      */
    03178     if ((vk == VK_SHIFT) || (vk == VK_MENU) || (vk == VK_CONTROL)) {
    03179         BYTE vkHanded, vkOtherHand;
    03180         /*
    03181          * Convert this virtual key into a differentiated (Left/Right) key
    03182          * depending on the extended key bit.
    03183          */
    03184         vkHanded = (vk - VK_SHIFT) * 2 + VK_LSHIFT +
    03185                 ((pqmsg->msg.lParam & EXTENDED_BIT) ? 1 : 0);
    03186         vkOtherHand = vkHanded ^ 1;
    03187
    03188         if (vk == VK_SHIFT) {
    03189             /*
    03190              * Clear extended bit for r.h. Shift, since it isn't really
    03191              * extended (bit was set to indicate right-handed)
    03192              */
    03193             pqmsg->msg.lParam &= ~EXTENDED_BIT;
    03194         }
    03195
    03196         /*
    03197          * Update the key state for the differentiated (Left/Right) key.
    03198          */
    03199         UpdateKeyState(pti->pq, vkHanded, fDown);
    03200
    03201         /*
    03202          * Update key state for the undifferentiated (logical) key.
    03203          */
    03204         if (fDown || !TestKeyStateDown(pti->pq, vkOtherHand)) {
    03205             UpdateKeyState(pti->pq, vk, fDown);
    03206         }
    03207     } else {
    03208         UpdateKeyState(pti->pq, vk, fDown);
    03209     }
    03210 }
    03211
    03212
    03213
    03214 #if DBG
    03215 /***************************************************************************\
    03216 * LogPlayback
    03217 *
    03218 *
    03219 * History:
    03220 * 02-13-95 JimA             Created.
    03221 \***************************************************************************/
    03222
    03223 void LogPlayback(
    03224     PWND pwnd,
    03225     PMSG lpmsg)
    03226 {
    03227     static PWND pwndM = NULL, pwndK = NULL;
    03228     LPCSTR lpszMsg;
    03229     CHAR achBuf[20];
    03230
    03231     if ((lpmsg->message >= WM_MOUSEFIRST) && (lpmsg->message <= WM_MOUSELAST)) {
    03232         lpszMsg = aszMouse[lpmsg->message - WM_MOUSEFIRST];
    03233         if (pwnd != pwndM) {
    03234             DbgPrint("*** Mouse input to window \"%ws\" of class \"%s\"\n",
    03235                     pwnd->strName.Length ? pwnd->strName.Buffer : L"",
    03236                     pwnd->pcls->lpszAnsiClassName);
    03237             pwndM = pwnd;
    03238         }
    03239     } else if ((lpmsg->message >= WM_KEYFIRST) && (lpmsg->message <= WM_KEYLAST)) {
    03240         lpszMsg = aszKey[lpmsg->message - WM_KEYFIRST];
    03241         if (pwnd != pwndK) {
    03242             DbgPrint("*** Kbd input to window \"%ws\" of class \"%s\"\n",
    03243                     pwnd->strName.Length ? pwnd->strName.Buffer : L"",
    03244                     pwnd->pcls->lpszAnsiClassName);
    03245             pwndK = pwnd;
    03246         }
    03247     } else if (lpmsg->message == WM_QUEUESYNC) {
    03248         lpszMsg = "WM_QUEUESYNC";
    03249     } else {
    03250         sprintf(achBuf, "0x%4x", lpmsg->message);
    03251         lpszMsg = achBuf;
    03252     }
    03253     DbgPrint("msg = %s, wP = %x, lP = %x\n", lpszMsg,
    03254             lpmsg->wParam, lpmsg->lParam);
    03255 }
    03256 #endif  // DBG
    03257
    03258 /***************************************************************************\
    03259 *
    03260 *  GetMouseKeyFlags()
    03261 *
    03262 *  Computes MOST of the MK_ flags given a Q.
    03263 *  Does not compute MK_MOUSEENTER.
    03264 *
    03265 \***************************************************************************/
    03266
    03267 UINT GetMouseKeyFlags(
    03268     PQ pq)
    03269 {
    03270     UINT wParam = 0;
    03271
    03272     if (TestKeyStateDown(pq, VK_LBUTTON))
    03273         wParam |= MK_LBUTTON;
    03274     if (TestKeyStateDown(pq, VK_RBUTTON))
    03275         wParam |= MK_RBUTTON;
    03276     if (TestKeyStateDown(pq, VK_MBUTTON))
    03277         wParam |= MK_MBUTTON;
    03278     if (TestKeyStateDown(pq, VK_XBUTTON1))
    03279         wParam |= MK_XBUTTON1;
    03280     if (TestKeyStateDown(pq, VK_XBUTTON2))
    03281         wParam |= MK_XBUTTON2;
    03282     if (TestKeyStateDown(pq, VK_SHIFT))
    03283         wParam |= MK_SHIFT;
    03284     if (TestKeyStateDown(pq, VK_CONTROL))
    03285         wParam |= MK_CONTROL;
    03286
    03287     return wParam;
    03288 }
    03289
    03290 /***************************************************************************\
    03291 * xxxScanSysQueue
    03292 *
    03293 * This routine looks at the hardware message, determines what
    03294 * window it will be in, determines what the input message will
    03295 * be, and then checks the destination window against hwndFilter,
    03296 * and the input message against msgMinFilter and msgMaxFilter.
    03297 *
    03298 * It also updates various input synchronized states like keystate info.
    03299 *
    03300 * This is almost verbatim from Win3.1.
    03301 *
    03302 * 10-20-92 ScottLu      Created.
    03303 \***************************************************************************/
    03304
    03305 BOOL xxxScanSysQueue(
    03306     PTHREADINFO ptiCurrent,
    03307     LPMSG lpMsg,
    03308     PWND pwndFilter,
    03309     UINT msgMinFilter,
    03310     UINT msgMaxFilter,
    03311     DWORD flags,
    03312     DWORD fsReason)
    03313 {
    03314     QMSG qmsg;
    03315     HWND hwnd;
    03316     PWND pwnd;
    03317     UINT message;
    03318     WPARAM wParam;
    03319     LPARAM lParam;
    03320     PTHREADINFO ptiKeyWake, ptiMouseWake, ptiEventWake;
    03321     POINT pt, ptScreen;
    03322     UINT codeMouseDown;
    03323     BOOL fMouseHookCalled;
    03324     BOOL fKbdHookCalled;
    03325     BOOL fOtherApp;
    03326     int part;
    03327     MOUSEHOOKSTRUCTEX mhs;
    03328     PWND pwndT;
    03329     BOOL fPrevDown;
    03330     BOOL fDown;
    03331     BOOL fAlt;
    03332     TL tlpwnd;
    03333     TL tlpwndT;
    03334     BOOL fRemove = (flags & PM_REMOVE);
    03335 #ifdef FE_IME
    03336     DWORD dwImmRet = 0;
    03337 #endif
    03338 #ifdef MARKPATH
    03339     DWORD pathTaken = 0;
    03340     DWORD pathTaken2 = 0;
    03341     DWORD pathTaken3 = 0;
    03342
    03343 #define PATHTAKEN(x)  pathTaken  |= x
    03344 #define PATHTAKEN2(x) pathTaken2 |= x
    03345 #define PATHTAKEN3(x) pathTaken3 |= x
    03346 #define DUMPPATHTAKEN() if (gfMarkPath) DbgPrint("xxxScanSysQueue path:%08x %08x %08x\n", pathTaken, pathTaken2, pathTaken3)
    03347 #define DUMPSUBPATHTAKEN(p, x) if (gfMarkPath && p & x) { DbgPrint("  %08x %08x %08x\n", pathTaken, pathTaken2, pathTaken3); pathTaken = pathTaken2 = pathTaken3 = 0; }
    03348 #else
    03349 #define PATHTAKEN(x)
    03350 #define PATHTAKEN2(x)
    03351 #define PATHTAKEN3(x)
    03352 #define DUMPPATHTAKEN()
    03353 #define DUMPSUBPATHTAKEN(p, x)
    03354 #endif
    03355
    03356     UserAssert(IsWinEventNotifyDeferredOK());
    03357     UserAssert((fsReason & ~(QS_EVENT | QS_INPUT)) == 0 &&
    03358                (fsReason & (QS_EVENT | QS_INPUT)) != 0);
    03359
    03360     /*
    03361      * If we are looking at a peeked message currently (recursion into this
    03362      * routine) and the only reason we got here was because of an event
    03363      * message (an app was filtering for a non-input message), then just
    03364      * return so we don't screw up idSysPeek. If we do enter this code
    03365      * idSysPeek will get set back to 0, and when we return back into
    03366      * the previous xxxScanSysQueue(), SkipSysMsg() will do nothing, so the
    03367      * message won't get removed. (MS Publisher 2.0 does this).
    03368      */
    03369     if (fsReason == QS_EVENT) {
    03370         if (ptiCurrent->pq->idSysPeek != 0) {
    03371             PATHTAKEN(1);
    03372             DUMPPATHTAKEN();
    03373             return FALSE;
    03374         }
    03375     }
    03376
    03377     fDown = FALSE;
    03378     fMouseHookCalled = FALSE;
    03379     fKbdHookCalled = FALSE;
    03380
    03381     /*
    03382      * Lock the queue if it's currently unlocked.
    03383      */
    03384     if (ptiCurrent->pq->ptiSysLock == NULL) {
    03385         CheckSysLock(3, ptiCurrent->pq, ptiCurrent);
    03386         ptiCurrent->pq->ptiSysLock = ptiCurrent;
    03387         ptiCurrent->pcti->CTIF_flags |= CTIF_SYSQUEUELOCKED;
    03388     }
    03389
    03390     /*
    03391      * Flag to tell if locker was removing messages. If not, then next time
    03392      * Get/PeekMessage is called, the input message list is scanned before the
    03393      * post msg list.
    03394      *
    03395      * Under Win3.1, this flag only gets modified for key and mouse messages.
    03396      * Since under NT ScanSysQueue() can be called to execute event messages,
    03397      * we make this check to be compatible.
    03398      */
    03399     if (fsReason & QS_INPUT) {
    03400         if (fRemove) {
    03401             PATHTAKEN(2);
    03402             ptiCurrent->pq->QF_flags &= ~QF_LOCKNOREMOVE;
    03403         } else {
    03404             PATHTAKEN(4);
    03405             ptiCurrent->pq->QF_flags |= QF_LOCKNOREMOVE;
    03406         }
    03407     }
    03408
    03409     /*
    03410      * Return FALSE if the current thread is not the one that lock this queue.
    03411      */
    03412     if (ptiCurrent->pq->ptiSysLock != ptiCurrent) {
    03413         PATHTAKEN(8);
    03414         DUMPPATHTAKEN();
    03415         return FALSE;
    03416     }
    03417
    03418     ptiEventWake = ptiKeyWake = ptiMouseWake = NULL;
    03419
    03420     /*
    03421      * Initialize the thread lock structure here so we can unlock/lock in
    03422      * the main loop.
    03423      */
    03424     pwnd = NULL;
    03425     ThreadLockWithPti(ptiCurrent, pwnd, &tlpwnd);
    03426
    03427 RestartScan:
    03428     CheckPtiSysPeek(2, ptiCurrent->pq, 0);
    03429     ptiCurrent->pq->idSysPeek = 0;
    03430
    03431 ContinueScan:
    03432     while (TRUE) {
    03433         ULONG_PTR idSysPeek;
    03434
    03435         DUMPSUBPATHTAKEN(pathTaken, 0xf0);
    03436         /*
    03437          * Store idSysPeek in a local which forces pq to be reloaded
    03438          * in case it changed during the xxx call (the compiler can
    03439          * evaluate the LValue at any time)
    03440          */
    03441         idSysPeek = (ULONG_PTR)xxxGetNextSysMsg(ptiCurrent,
    03442                 (PQMSG)ptiCurrent->pq->idSysPeek, &qmsg);
    03443         CheckPtiSysPeek(3, ptiCurrent->pq, idSysPeek);
    03444         ptiCurrent->pq->idSysPeek = idSysPeek;
    03445
    03446         if (ptiCurrent->pq->idSysPeek == 0) {
    03447             /*
    03448              * If we are only looking for event messages and we didn't
    03449              * find any, then clear the QS_EVENT bit
    03450              */
    03451             if (fsReason == QS_EVENT)
    03452                 ClearWakeBit(ptiCurrent, QS_EVENT, FALSE);
    03453             PATHTAKEN(0x10);
    03454             goto NoMessages;
    03455         }
    03456
    03457         /*
    03458          * pwnd should be locked for the duration of this routine.
    03459          * For most messages right out of GetNextSysMsg, this is
    03460          * NULL.
    03461          */
    03462         ThreadUnlock(&tlpwnd);
    03463         pwnd = RevalidateHwnd(qmsg.msg.hwnd);
    03464         ThreadLockWithPti(ptiCurrent, pwnd, &tlpwnd);
    03465
    03466         /*
    03467          * See if this is an event message. If so, execute it regardless
    03468          * of message and window filters, but only if it is the first element
    03469          * of the input queue.
    03470          */
    03471         if (qmsg.dwQEvent != 0) {
    03472             PTHREADINFO pti;
    03473
    03474             PATHTAKEN(0x20);
    03475             /*
    03476              * Most event messages can be executed out of order relative to
    03477              * its place in the queue. There are some examples were this is
    03478              * not allowed, and we check that here. For example, we would not
    03479              * want a keystate synchronization event to be processed before
    03480              * the keystrokes that came before it in the queue!
    03481              *
    03482              * We need to have most event messages be able to get processed
    03483              * out of order because apps can be filtering for message ranges
    03484              * that don't include input (like dde) - those scenarios still
    03485              * need to process events such as deactivate event messages even
    03486              * if there is input in the input queue.
    03487              */
    03488             switch (qmsg.dwQEvent) {
    03489             case QEVENT_UPDATEKEYSTATE:
    03490                 /*
    03491                  * If the message is not the next message in the queue, don't
    03492                  * process it.
    03493                  */
    03494                 if (ptiCurrent->pq->idSysPeek !=
    03495                         (ULONG_PTR)ptiCurrent->pq->mlInput.pqmsgRead) {
    03496                     PATHTAKEN(0x40);
    03497                     continue;
    03498                 }
    03499                 break;
    03500             }
    03501
    03502             /*
    03503              * If this event isn't for this thread, wake the thread it is
    03504              * for.  A NULL qmsg.hti means that any thread can process
    03505              * the event.
    03506              */
    03507             if (qmsg.pti != NULL && (pti = qmsg.pti) != ptiCurrent) {
    03508
    03509                 /*
    03510                  * If somehow this event message got into the wrong queue,
    03511                  * then ignore it.
    03512                  */
    03513                 UserAssert(pti->pq == ptiCurrent->pq);
    03514                 if (pti->pq != ptiCurrent->pq) {
    03515                     CleanEventMessage((PQMSG)ptiCurrent->pq->idSysPeek);
    03516                     DelQEntry(&ptiCurrent->pq->mlInput,
    03517                             (PQMSG)ptiCurrent->pq->idSysPeek);
    03518                     PATHTAKEN(0x80);
    03519                     goto RestartScan;
    03520                 }
    03521
    03522                 /*
    03523                  * If ptiEventWake is already set, it means we've already
    03524                  * found a thread to wake for event.
    03525                  */
    03526                 if (ptiEventWake == NULL)
    03527                     ptiEventWake = pti;
    03528
    03529                 /*
    03530                  * Clear idSysPeek so that the targeted thread
    03531                  * can always get it.  Look at the test at the
    03532                  * start of this routine for more info.
    03533                  */
    03534                 CheckPtiSysPeek(4, ptiCurrent->pq, 0);
    03535                 ptiCurrent->pq->idSysPeek = 0;
    03536                 PATHTAKEN(0x100);
    03537                 goto NoMessages;
    03538             }
    03539
    03540             /*
    03541              * If this is called with PM_NOYIELD from a 16-bit app, skip
    03542              * processing any event that can generate activation messages.  An
    03543              * example is printing from PageMaker 5.0.  Bug #12662.
    03544              */
    03545             if ((flags & PM_NOYIELD) && (ptiCurrent->TIF_flags & TIF_16BIT)) {
    03546                 PATHTAKEN(0x200);
    03547                 switch (qmsg.dwQEvent) {
    03548
    03549                 /*
    03550                  * The following events are safe to process if no yield
    03551                  * is to occur.
    03552                  */
    03553                 case QEVENT_UPDATEKEYSTATE:
    03554                 case QEVENT_ASYNCSENDMSG:
    03555                     break;
    03556
    03557                 /*
    03558                  * Skip all other events.
    03559                  */
    03560                 default:
    03561                     ptiCurrent->TIF_flags |= TIF_DELAYEDEVENT;
    03562                     ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags;
    03563                     PATHTAKEN(0x400);
    03564                     goto ContinueScan;
    03565                 }
    03566             }
    03567
    03568             /*
    03569              * Delete this before it gets processed so there are no
    03570              * recursion problems.
    03571              */
    03572             DelQEntry(&ptiCurrent->pq->mlInput,
    03573                     (PQMSG)ptiCurrent->pq->idSysPeek);
    03574
    03575             /*
    03576              * Clear idSysPeek before processing any events messages, because
    03577              * they may recurse and want to use idSysPeek.
    03578              */
    03579             CheckPtiSysPeek(5, ptiCurrent->pq, 0);
    03580             ptiCurrent->pq->idSysPeek = 0;
    03581             xxxProcessEventMessage(ptiCurrent, &qmsg);
    03582
    03583             /*
    03584              * Restart the scan from the start so we start with 0 in
    03585              * pq->idSysPeek (since that message is now gone!).
    03586              */
    03587             PATHTAKEN(0x800);
    03588             goto RestartScan;
    03589         }
    03590
    03591         /*
    03592          * If the reason we called was just to process event messages, don't
    03593          * enumerate any other mouse or key messages!
    03594          */
    03595         if (fsReason == QS_EVENT) {
    03596             PATHTAKEN(0x1000);
    03597             continue;
    03598         }
    03599
    03600         switch (message = qmsg.msg.message) {
    03601         case WM_QUEUESYNC:
    03602             PATHTAKEN(0x2000);
    03603             /*
    03604              * This message is for CBT. Its parameters should already be
    03605              * set up correctly.
    03606              */
    03607             wParam = 0;
    03608             lParam = qmsg.msg.lParam;
    03609
    03610             /*
    03611              * Check if this is intended for the current app. Use the mouse
    03612              * bit for WM_QUEUESYNC.
    03613              */
    03614             if (pwnd != NULL && GETPTI(pwnd) != ptiCurrent) {
    03615                 /*
    03616                  * If this other app isn't going to read from this
    03617                  * queue, then skip this message. This can happen with
    03618                  * WM_QUEUESYNC if the app passed a window handle
    03619                  * to the wrong queue. This isn't likely to happen in
    03620                  * this case because WM_QUEUESYNCs come in while journalling,
    03621                  * which has all threads sharing the same queue.
    03622                  */
    03623                 if (GETPTI(pwnd)->pq != ptiCurrent->pq) {
    03624                     PATHTAKEN(0x4000);
    03625                     goto SkipMessage;
    03626                 }
    03627
    03628                 if (ptiMouseWake == NULL)
    03629                     ptiMouseWake = GETPTI(pwnd);
    03630                 PATHTAKEN(0x8000);
    03631                 goto NoMessages;
    03632             }
    03633
    03634             if (!CheckMsgFilter(message, msgMinFilter, msgMaxFilter)) {
    03635                 PATHTAKEN(0x10000);
    03636                 goto NoMessages;
    03637             }
    03638
    03639             /*
    03640              * Eat the message.
    03641              */
    03642             if (fRemove) {
    03643                 xxxSkipSysMsg(ptiCurrent, &qmsg);
    03644             }
    03645
    03646             /*
    03647              * !!HARDWARE HOOK!! goes here.
    03648              */
    03649
    03650             /*
    03651              * Return the message.
    03652              */
    03653             PATHTAKEN(0x20000);
    03654             goto ReturnMessage;
    03655             break;
    03656
    03657         /*
    03658          * Mouse message or generic hardware messages
    03659          * Key messages are handled in case statements
    03660          * further down in this switch.
    03661          */
    03662         default:
    03663 ReprocessMsg:
    03664             DUMPSUBPATHTAKEN(pathTaken, 0x40000);
    03665             PATHTAKEN(0x40000);
    03666             /*
    03667              * !!GENERIC HARDWARE MESSAGE!! support goes here.
    03668              */
    03669
    03670             /*
    03671              * Take the mouse position out of the message.
    03672              */
    03673             pt.x = (int)(short)LOWORD(qmsg.msg.lParam);
    03674             pt.y = (int)(short)HIWORD(qmsg.msg.lParam);
    03675
    03676             /*
    03677              * Assume we have a capture.
    03678              */
    03679             part = HTCLIENT;
    03680
    03681             /*
    03682              * We have a special global we use for when we're full screen.
    03683              * All mouse input will go to this window.
    03684              */
    03685             if (gspwndScreenCapture != NULL) {
    03686                 /*
    03687                  * Change the mouse coordinates to full screen.
    03688                  */
    03689                 pwnd = gspwndScreenCapture;
    03690                 lParam = MAKELONG((WORD)qmsg.msg.pt.x,
    03691                         (WORD)qmsg.msg.pt.y);
    03692                 PATHTAKEN(0x80000);
    03693             } else if ((pwnd = ptiCurrent->pq->spwndCapture) == NULL) {
    03694
    03695                 PATHTAKEN(0x100000);
    03696                 /*
    03697                  * We don't have the capture. Figure out which window owns
    03698                  * this message.
    03699                  *
    03700                  * NOTE: Use gptiRit and not ptiCurrent to get the desktop
    03701                  * window because if ptiCurrent is the thread that created
    03702                  * the main desktop, it's associated desktop is the logon
    03703                  * desktop - don't want to hittest against the logon desktop
    03704                  * while switched into the main desktop!
    03705                  */
    03706                 pwndT = gptiRit->rpdesk->pDeskInfo->spwnd;
    03707
    03708                 ThreadLockWithPti(ptiCurrent, pwndT, &tlpwndT);
    03709
    03710                 hwnd = xxxWindowHitTest(pwndT, pt, &part, WHT_IGNOREDISABLED);
    03711                 ThreadUnlock(&tlpwndT);
    03712
    03713                 if ((pwnd = RevalidateHwnd(hwnd)) == NULL) {
    03714                     pwnd = ptiCurrent->rpdesk->pDeskInfo->spwnd;
    03715                     PATHTAKEN(0x200000);
    03716                 }
    03717
    03718                 if (part == HTCLIENT) {
    03719                     /*
    03720                      * Part of the client... normal mouse message.
    03721                      * NO_CAP_CLIENT means "not captured, in client area
    03722                      * of window".
    03723                      */
    03724                     ptiCurrent->pq->codeCapture = NO_CAP_CLIENT;
    03725                     PATHTAKEN(0x400000);
    03726                 } else {
    03727                     /*
    03728                      * Not part of the client... must be an NCMOUSEMESSAGE.
    03729                      * NO_CAP_SYS is a creative name by raor which means
    03730                      * "not captured, in system area of window."
    03731                      */
    03732                     ptiCurrent->pq->codeCapture = NO_CAP_SYS;
    03733                     PATHTAKEN(0x800000);
    03734                 }
    03735             }
    03736
    03737             /*
    03738              * We've reassigned pwnd, so lock it.
    03739              */
    03740             ThreadLockExchange(pwnd, &tlpwnd);
    03741
    03742             if (fOtherApp = (GETPTI(pwnd) != ptiCurrent)) {
    03743
    03744                 PATHTAKEN(0x1000000);
    03745                 /*
    03746                  * If this other app isn't going to read from this
    03747                  * queue, then skip this message. This can happen if
    03748                  * the RIT queues up a message thinking it goes to
    03749                  * a particular hwnd, but then by the time GetMessage()
    03750                  * is called for that thread, it doesn't go to that hwnd
    03751                  * (like in the case of mouse messages, window rearrangement
    03752                  * happens which changes which hwnd the mouse hits on).
    03753                  */
    03754                 if (GETPTI(pwnd)->pq != ptiCurrent->pq) {
    03755                     zzzSetCursor(SYSCUR(ARROW));
    03756                     PATHTAKEN(0x2000000);
    03757                     goto SkipMessage;
    03758                 }
    03759
    03760                 /*
    03761                  * If we haven't already found a message that is intended
    03762                  * for another app, remember that we have one.
    03763                  */
    03764                 if (ptiMouseWake == NULL) {
    03765                     ptiMouseWake = GETPTI(pwnd);
    03766                     PATHTAKEN(0x4000000);
    03767                 }
    03768             }
    03769
    03770             /*
    03771              * Map mouse coordinates based on hit test area code.
    03772              */
    03773             ptScreen = pt;
    03774             switch (ptiCurrent->pq->codeCapture) {
    03775             case CLIENT_CAPTURE:
    03776             case NO_CAP_CLIENT:
    03777 #ifdef USE_MIRRORING
    03778                 //Screen To Client
    03779                 if (TestWF(pwnd, WEFLAYOUTRTL)) {
    03780                     pt.x = pwnd->rcClient.right - pt.x;
    03781                 } else
    03782 #endif
    03783                 {
    03784                     pt.x -= pwnd->rcClient.left;
    03785                 }
    03786                 pt.y -= pwnd->rcClient.top;
    03787                 PATHTAKEN2(2);
    03788                 break;
    03789
    03790             case WINDOW_CAPTURE:
    03791 #ifdef USE_MIRRORING
    03792                 //Screen To Window
    03793                 if (TestWF(pwnd, WEFLAYOUTRTL)) {
    03794                     pt.x = pwnd->rcWindow.right - pt.x;
    03795                 } else
    03796 #endif
    03797                 {
    03798                     pt.x -= pwnd->rcWindow.left;
    03799                 }
    03800                 pt.y -= pwnd->rcWindow.top;
    03801                 PATHTAKEN2(4);
    03802                 break;
    03803             }
    03804
    03805             /*
    03806              * Track mouse moves when it moves to a different window or
    03807              * a different hit-test area for hot-tracking, tooltips,
    03808              * active window tracking and TrackMouseEvent.
    03809              * Mouse clicks reset tracking state too.
    03810              * Do it only if the message is for the current thread;
    03811              *  otherwise, the hit test code (part) is not valid
    03812              *  (it's always HTCLIENT; see xxxWindowHitTest2).
    03813              *  Tracking will take place when that thread wakes up
    03814              * We also don't do it if this thread is not on pqCursor;
    03815              *  that would be the case for a slow app that gets the
    03816              *  input message when the mouse has already left its queue
    03817              */
    03818              if (!fOtherApp && (ptiCurrent->pq == gpqCursor)) {
    03819                  BOOL fNewpwndTrack = (ptiCurrent->rpdesk->spwndTrack != pwnd);
    03820                  int htEx = FindNCHitEx(pwnd, part, pt);
    03821                  if ((message != WM_MOUSEMOVE)
    03822                         || fNewpwndTrack
    03823                         || (ptiCurrent->rpdesk->htEx != htEx)) {
    03824
    03825                      xxxTrackMouseMove(pwnd, htEx, message);
    03826                      ValidateThreadLocks(NULL, ptiCurrent->ptl, (ULONG_PTR)&tlpwnd, TRUE);
    03827                  }
    03828
    03829                  /*
    03830                   * Reset mouse hovering if needed.
    03831                   *
    03832                   */
    03833                  if (!fNewpwndTrack && (ptiCurrent->rpdesk->dwDTFlags & DF_TRACKMOUSEHOVER)) {
    03834                      if ((message != WM_MOUSEMOVE)
    03835                             || !PtInRect(&ptiCurrent->rpdesk->rcMouseHover, ptScreen)) {
    03836
    03837                          ResetMouseHover(ptiCurrent->rpdesk, ptScreen);
    03838                      }
    03839                  } else {
    03840                      /*
    03841                       * Hover must be canceled.
    03842                       */
    03843                      UserAssert(!(ptiCurrent->rpdesk->dwDTFlags & DF_TRACKMOUSEHOVER));
    03844                  }
    03845
    03846              } /* if (!fOtherApp.... */
    03847
    03848             /*
    03849              * Now see if it matches the window handle filter. If not,
    03850              * get the next message.
    03851              */
    03852             if (!CheckPwndFilter(pwnd, pwndFilter)) {
    03853                 PATHTAKEN(0x8000000);
    03854                 continue;
    03855             }
    03856
    03857             /*
    03858              * See if we need to map to a double click.
    03859              */
    03860             codeMouseDown = 0;
    03861             switch (message) {
    03862             case WM_LBUTTONDOWN:
    03863             case WM_RBUTTONDOWN:
    03864             case WM_MBUTTONDOWN:
    03865             case WM_XBUTTONDOWN:
    03866                 if (TestCF(pwnd, CFDBLCLKS) ||
    03867                         ptiCurrent->pq->codeCapture == NO_CAP_SYS ||
    03868                         IsMenuStarted(ptiCurrent)) {
    03869                     codeMouseDown++;
    03870                     PATHTAKEN(0x10000000);
    03871                     if (qmsg.msg.time <= ptiCurrent->pq->timeDblClk &&
    03872                             (!gbClientDoubleClickSupport) &&
    03873                             HW(pwnd) == ptiCurrent->pq->hwndDblClk &&
    03874                             message == ptiCurrent->pq->msgDblClk &&
    03875                             (message != WM_XBUTTONDOWN ||
    03876                               GET_XBUTTON_WPARAM(qmsg.msg.wParam) == ptiCurrent->pq->xbtnDblClk)) {
    03877                         RECT rcDblClk = {
    03878                             ptiCurrent->pq->ptDblClk.x - SYSMET(CXDOUBLECLK) / 2,
    03879                             ptiCurrent->pq->ptDblClk.y - SYSMET(CYDOUBLECLK) / 2,
    03880                             ptiCurrent->pq->ptDblClk.x + SYSMET(CXDOUBLECLK) / 2,
    03881                             ptiCurrent->pq->ptDblClk.y + SYSMET(CYDOUBLECLK) / 2
    03882                         };
    03883                         if (PtInRect(&rcDblClk, qmsg.msg.pt)) {
    03884                             message += (WM_LBUTTONDBLCLK - WM_LBUTTONDOWN);
    03885                             codeMouseDown++;
    03886                             PATHTAKEN(0x20000000);
    03887                         }
    03888                     }
    03889                 }
    03890
    03891             // FALL THROUGH!!!
    03892
    03893             case WM_LBUTTONUP:
    03894             case WM_RBUTTONUP:
    03895             case WM_MBUTTONUP:
    03896             case WM_XBUTTONUP:
    03897                 /*
    03898                  * Note that the mouse button went up or down if we were
    03899                  * in menu status mode of alt-key down
    03900                  */
    03901
    03902                 PATHTAKEN(0x40000000);
    03903                 if (ptiCurrent->pq->QF_flags & QF_FMENUSTATUS) {
    03904                     ptiCurrent->pq->QF_flags |= QF_FMENUSTATUSBREAK;
    03905                     PATHTAKEN(0x80000000);
    03906                 }
    03907             }
    03908
    03909             /*
    03910              * Map message number based on hit test area code.
    03911              */
    03912             if (ptiCurrent->pq->codeCapture == NO_CAP_SYS) {
    03913                 message += (UINT)(WM_NCMOUSEMOVE - WM_MOUSEMOVE);
    03914                 wParam = (UINT)part;
    03915                 PATHTAKEN2(1);
    03916             }
    03917
    03918             /*
    03919              * Message number has been mapped: see if it fits the filter.
    03920              * If not, get the next message.
    03921              */
    03922             if (!CheckMsgFilter(message, msgMinFilter, msgMaxFilter)) {
    03923                 PATHTAKEN2(8);
    03924                 continue;
    03925             }
    03926
    03927              /*
    03928              * If message is for another app but it fits our filter, then
    03929              * we should stop looking for messages: this will ensure that
    03930              * we don't keep looking and find and process a message that
    03931              * occured later than the one that should be processed by the
    03932              * other guy.
    03933              */
    03934             if (fOtherApp) {
    03935                 PATHTAKEN2(0x10);
    03936                 goto NoMessages;
    03937             }
    03938
    03939             /*
    03940              * If we're doing full drag, the mouse messages should go to
    03941              * the xxxMoveSize PeekMessage loop. So we get the next message.
    03942              * This can happen when an application does a PeekMessage in
    03943              * response to a message sent inside the movesize dragging loop.
    03944              * This causes the dragging loop to not get the WM_LBUTTONUP
    03945              * message and dragging continues after the button is up
    03946              * (fix for Micrografx Draw). -johannec
    03947              */
    03948             if (message >= WM_MOUSEFIRST && message <= WM_MOUSELAST &&
    03949                     ptiCurrent->TIF_flags & TIF_MOVESIZETRACKING) {
    03950                 PATHTAKEN2(0x20);
    03951                 continue;
    03952             }
    03953
    03954             if (FWINABLE() && (ptiCurrent->TIF_flags & TIF_MSGPOSCHANGED)) {
    03955                 ptiCurrent->TIF_flags &= ~TIF_MSGPOSCHANGED;
    03956                 xxxWindowEvent(EVENT_OBJECT_LOCATIONCHANGE, NULL,
    03957                     OBJID_CURSOR, INDEXID_CONTAINER, TRUE);
    03958                 ValidateThreadLocks(NULL, ptiCurrent->ptl, (ULONG_PTR)&tlpwnd, TRUE);
    03959             }
    03960
    03961             /*
    03962              * Let us call the mouse hook to find out if this click is
    03963              * permitted by it.
    03964              *
    03965              * We want to inform the mouse hook before we test for
    03966              * HTNOWHERE and HTERROR; Otherwise, the mouse hook won't
    03967              * get these messages (sankar 12/10/91).
    03968              */
    03969             if (IsHooked(ptiCurrent, WHF_MOUSE)) {
    03970                 fMouseHookCalled = TRUE;
    03971                 mhs.pt = qmsg.msg.pt;
    03972                 mhs.hwnd = HW(pwnd);
    03973                 mhs.wHitTestCode = (UINT)part;
    03974                 mhs.dwExtraInfo = qmsg.ExtraInfo;
    03975                 UserAssert(LOWORD(qmsg.msg.wParam) == 0);
    03976                 mhs.mouseData = (DWORD)qmsg.msg.wParam;
    03977
    03978                 if (xxxCallMouseHook(message, &mhs, fRemove)) {
    03979                     /*
    03980                      * Not allowed by mouse hook; so skip it.
    03981                      */
    03982                     PATHTAKEN2(0x40);
    03983                     goto SkipMessage;
    03984                 }
    03985                 PATHTAKEN2(0x80);
    03986                 ValidateThreadLocks(NULL, ptiCurrent->ptl, (ULONG_PTR)&tlpwnd, TRUE);
    03987             }
    03988
    03989             /*
    03990              * If a HTERROR or HTNOWHERE occured, send the window the
    03991              * WM_SETCURSOR message so it can beep or whatever. Then skip
    03992              * the message and try the next one.
    03993              */
    03994             switch (part) {
    03995             case HTERROR:
    03996             case HTNOWHERE:
    03997                 /*
    03998                  * Now set the cursor shape.
    03999                  */
    04000                 xxxSendMessage(pwnd, WM_SETCURSOR, (WPARAM)HW(pwnd),
    04001                         MAKELONG(part, qmsg.msg.message));
    04002
    04003                 /*
    04004                  * Skip the message.
    04005                  */
    04006                 PATHTAKEN2(0x100);
    04007                 goto SkipMessage;
    04008                 break;
    04009             }
    04010
    04011             if (fRemove) {
    04012                 PATHTAKEN2(0x200);
    04013                 /*
    04014                  * Since the processing of a down click may cause the next
    04015                  * message to be interpreted as a double click, we only want
    04016                  * to do the double click setup if we're actually going to
    04017                  * remove the message.  Otherwise, the next time we read the
    04018                  * same message it would be interpreted as a double click.
    04019                  */
    04020                 switch (codeMouseDown) {
    04021                 case 1:
    04022                     /*
    04023                      * Down clock: set up for later possible double click.
    04024                      */
    04025                     ptiCurrent->pq->msgDblClk = qmsg.msg.message;
    04026
    04027                     /*
    04028                      * Note that even if the following assertion were not true,
    04029                      * we could still put bogus data in ptiCurrent->pq->xbtnDblClk
    04030                      * when the message is not WM_XBUTTONDOWN, since when we check
    04031                      * for dblclick we compare the message number before the xbtnDblClk.
    04032                      */
    04033                     UserAssert(qmsg.msg.message == WM_XBUTTONDOWN || GET_XBUTTON_WPARAM(qmsg.msg.wParam) == 0);
    04034                     ptiCurrent->pq->xbtnDblClk = GET_XBUTTON_WPARAM(qmsg.msg.wParam);
    04035
    04036                     ptiCurrent->pq->timeDblClk = qmsg.msg.time + gdtDblClk;
    04037                     ptiCurrent->pq->hwndDblClk = HW(pwnd);
    04038                     ptiCurrent->pq->ptDblClk = qmsg.msg.pt;
    04039                     PATHTAKEN2(0x400);
    04040                     break;
    04041
    04042                 case 2:
    04043                     /*
    04044                      * Double click: finish processing.
    04045                      */
    04046                     ptiCurrent->pq->timeDblClk = 0L;
    04047                     PATHTAKEN2(0x800);
    04048                     break;
    04049
    04050                 default:
    04051                     PATHTAKEN2(0x1000);
    04052                     break;
    04053                 }
    04054
    04055                 /*
    04056                  * Set mouse cursor and allow app to activate window
    04057                  * only if we're removing the message.
    04058                  */
    04059                 switch (xxxMouseActivate(ptiCurrent, pwnd,
    04060                         qmsg.msg.message, qmsg.msg.wParam, &qmsg.msg.pt, part)) {
    04061 SkipMessage:
    04062                 case MA_SKIP:
    04063                     DUMPSUBPATHTAKEN(pathTaken2, 0x2000);
    04064                     PATHTAKEN2(0x2000);
    04065                     xxxSkipSysMsg(ptiCurrent, &qmsg);
    04066
    04067                     /*
    04068                      * Inform the CBT hook that we skipped a mouse click.
    04069                      */
    04070                     if (fMouseHookCalled) {
    04071                         if (IsHooked(ptiCurrent, WHF_CBT)) {
    04072                             xxxCallHook(HCBT_CLICKSKIPPED, message,
    04073                                     (LPARAM)&mhs, WH_CBT);
    04074                             PATHTAKEN2(0x4000);
    04075                         }
    04076                         fMouseHookCalled = FALSE;
    04077                     }
    04078
    04079                     /*
    04080                      * Inform the CBT hook that we skipped a key
    04081                      */
    04082                     if (fKbdHookCalled) {
    04083                         if (IsHooked(ptiCurrent, WHF_CBT)) {
    04084                             xxxCallHook(HCBT_KEYSKIPPED, wParam, lParam,
    04085                                     WH_CBT);
    04086                             PATHTAKEN2(0x8000);
    04087                         }
    04088                         fKbdHookCalled = FALSE;
    04089                     }
    04090
    04091                     /*
    04092                      * If we aren't removing messages, don't reset idSysPeek
    04093                      * otherwise we will go into an infinite loop if
    04094                      * the keyboard hook says to ignore the message.
    04095                      * (bobgu 4/7/87).
    04096                      */
    04097                     if (!fRemove) {
    04098                         PATHTAKEN2(0x10000);
    04099                         goto ContinueScan;
    04100                     } else {
    04101                         PATHTAKEN2(0x20000);
    04102                         goto RestartScan;
    04103                     }
    04104                     break;
    04105
    04106                 case MA_REHITTEST:
    04107                     /*
    04108                      * Reprocess the message.
    04109                      */
    04110                     PATHTAKEN2(0x40000);
    04111                     goto ReprocessMsg;
    04112                 }
    04113             }
    04114
    04115             /*
    04116              * Eat the message from the input queue (and set the keystate
    04117              * table).
    04118              */
    04119             PATHTAKEN2(0x80000);
    04120             if (fRemove) {
    04121                 xxxSkipSysMsg(ptiCurrent, &qmsg);
    04122             }
    04123
    04124             if (fRemove && fMouseHookCalled && IsHooked(ptiCurrent, WHF_CBT)) {
    04125                 xxxCallHook(HCBT_CLICKSKIPPED, message,
    04126                         (LPARAM)&mhs, WH_CBT);
    04127             }
    04128             fMouseHookCalled = FALSE;
    04129
    04130             lParam = MAKELONG((short)pt.x, (short)pt.y);
    04131
    04132             /*
    04133              * Calculate virtual key state bitmask for wParam.
    04134              */
    04135             if (message >= WM_MOUSEFIRST) {
    04136                 /*
    04137                  * This is a USER mouse message. Calculate the bit mask for the
    04138                  * virtual key state.
    04139                  */
    04140                 wParam = GetMouseKeyFlags(ptiCurrent->pq);
    04141                 PATHTAKEN2(0x100000);
    04142             }
    04143
    04144             if (    (WM_NCXBUTTONFIRST <= message && message <= WM_NCXBUTTONLAST) ||
    04145                     (WM_XBUTTONFIRST <= message && message <= WM_XBUTTONLAST)) {
    04146
    04147                 /*
    04148                  * The hiword of wParam is assigned the xbutton number when
    04149                  * the message is queued.
    04150                  */
    04151                 UserAssert(LOWORD(qmsg.msg.wParam) == 0);
    04152                 UserAssert(HIWORD(wParam) == 0);
    04153                 wParam |= qmsg.msg.wParam;
    04154             }
    04155
    04156             PATHTAKEN2(0x200000);
    04157
    04158             /*
    04159              * If this app has a modeles menu bar,
    04160              *  then the menu code should get the first shot at messages on the menu
    04161              * Note that this assumes that xxxHandleMenuMessages
    04162              *  doens't need any of the stuff set after ReturnMessage
    04163              */
    04164             if ((part == HTMENU)
    04165                     && fRemove
    04166                     && (ptiCurrent->pMenuState != NULL)
    04167                     && ptiCurrent->pMenuState->fModelessMenu
    04168                     && (ptiCurrent->pMenuState->pGlobalPopupMenu != NULL)
    04169                     && (ptiCurrent->pMenuState->pGlobalPopupMenu->fIsMenuBar)) {
    04170
    04171                 if (xxxCallHandleMenuMessages(ptiCurrent->pMenuState, pwnd, message, wParam, lParam)) {
    04172                     goto RestartScan;
    04173                 }
    04174             }
    04175
    04176             goto ReturnMessage;
    04177             break;
    04178
    04179         case WM_KEYDOWN:
    04180         case WM_SYSKEYDOWN:
    04181             fDown = TRUE;
    04182
    04183             /*
    04184              * If we are sending keyboard input to an app that has been
    04185              * spinning then boost it back up.  If we don't you use spinning
    04186              * apps like Write or Project and do two builds in the
    04187              * background.  Note the app will also be unboosted again shortly
    04188              * after you stop typing by the old logic. #11188
    04189              */
    04190             if (ptiCurrent->TIF_flags & TIF_SPINNING)
    04191                 CheckProcessForeground(ptiCurrent);
    04192
    04193             /*
    04194              * Apps doing journal playback sometimes put trash in the hiword
    04195              * of wParam... zero it out here.
    04196              */
    04197             wParam = qmsg.msg.wParam & 0xFF;
    04198
    04199             /*
    04200              * Clear QF_FMENUSTATUS if a key other than Alt it hit
    04201              * since this means the break of the Alt wouldn't be a
    04202              * menu key anymore.
    04203              */
    04204             if (wParam != VK_MENU)
    04205                 ptiCurrent->pq->QF_flags &= ~(QF_FMENUSTATUS|QF_FMENUSTATUSBREAK);
    04206
    04207             /*
    04208              * Check for keyboard language toggle.  Build the key state
    04209              * here for use during key up processing (where the layout
    04210              * switching takes place.  This code is skipped if layout
    04211              * switching via the keyboard is disabled.
    04212              */
    04213             if (gLangToggle[0].bVkey && (gLangToggleKeyState < KLT_NONE)) {
    04214                 DWORD i;
    04215                 BYTE scancode = LOBYTE(HIWORD(qmsg.msg.lParam));
    04216                 BYTE vkey = LOBYTE(qmsg.msg.wParam);
    04217
    04218                 for (i = 0; i < LANGTOGGLEKEYS_SIZE; i++) {
    04219                     if (gLangToggle[i].bScan) {
    04220                         if (gLangToggle[i].bScan == scancode) {
    04221                             gLangToggleKeyState |= gLangToggle[i].iBitPosition;
    04222                             break;
    04223                         }
    04224                     } else {
    04225                         if (gLangToggle[i].bVkey == vkey) {
    04226                             gLangToggleKeyState |= gLangToggle[i].iBitPosition;
    04227                             break;
    04228                         }
    04229                     }
    04230                 }
    04231
    04232                 if (i == LANGTOGGLEKEYS_SIZE) {
    04233                     gLangToggleKeyState = KLT_NONE;   // not a language toggle combination
    04234                 }
    04235             }
    04236
    04237             /*
    04238              * Check if it is the PrintScrn key.
    04239              */
    04240             fAlt = TestKeyStateDown(ptiCurrent->pq, VK_MENU);
    04241             if (wParam == VK_SNAPSHOT &&
    04242                 ((fAlt && !(ptiCurrent->fsReserveKeys & CONSOLE_ALTPRTSC)) ||
    04243                  (!fAlt && !(ptiCurrent->fsReserveKeys & CONSOLE_PRTSC)))) {
    04244
    04245                 /*
    04246                  * Remove this message from the input queue.
    04247                  */
    04248                 PATHTAKEN2(0x400000);
    04249                 xxxSkipSysMsg(ptiCurrent, &qmsg);
    04250
    04251                 /*
    04252                  * PRINTSCREEN          -> Snap the whole screen.
    04253                  * ALT-PRINTSCREEN      -> Snap the current window.
    04254                  */
    04255                 pwndT = ptiCurrent->pq->spwndActive;
    04256
    04257                 /*
    04258                  * check also the scan code to see if we got here
    04259                  * through keybd_event(VK_SNAPSHOT, ...
    04260                  * the scan code is in lParam bits 16-23
    04261                  */
    04262                 if (!fAlt && ((qmsg.msg.lParam & 0x00FF0000) != 0x00010000)) {
    04263                     pwndT = ptiCurrent->rpdesk->pDeskInfo->spwnd;
    04264                 }
    04265
    04266                 if (pwndT != NULL) {
    04267                     ThreadLockAlwaysWithPti(ptiCurrent, pwndT, &tlpwndT);
    04268                     xxxSnapWindow(pwndT);
    04269                     ThreadUnlock(&tlpwndT);
    04270                 }
    04271
    04272                 PATHTAKEN2(0x800000);
    04273                 goto RestartScan;
    04274             }
    04275
    04276             /*
    04277              * Check for hot keys being hit if any are defined.
    04278              */
    04279             if (gcHotKey != 0 && (!gfEnableHexNumpad || (gfInNumpadHexInput & NUMPAD_HEXMODE_HL) == 0)) {
    04280                 UINT key;
    04281                 key = (UINT)wParam;
    04282
    04283                 if (TestKeyStateDown(ptiCurrent->pq, VK_MENU))
    04284                     key |= 0x0400;
    04285
    04286                 if (TestKeyStateDown(ptiCurrent->pq, VK_CONTROL))
    04287                     key |= 0x0200;
    04288
    04289                 if (TestKeyStateDown(ptiCurrent->pq, VK_SHIFT))
    04290                     key |= 0x0100;
    04291
    04292                 pwndT = HotKeyToWindow(key);
    04293
    04294                 if (pwndT != NULL) {
    04295                     /*
    04296                      * VK_PACKET shouldn't be a hot key.
    04297                      */
    04298                     UserAssert((key & 0xff) != VK_PACKET);
    04299
    04300                     _PostMessage(ptiCurrent->pq->spwndActive, WM_SYSCOMMAND,
    04301                                 (WPARAM)SC_HOTKEY, (LPARAM)HWq(pwndT));
    04302
    04303                     /*
    04304                      * Remove this message from the input queue.
    04305                      */
    04306                     xxxSkipSysMsg(ptiCurrent, &qmsg);
    04307                     PATHTAKEN2(0x1000000);
    04308                     goto RestartScan;
    04309                 }
    04310
    04311                 PATHTAKEN2(0x2000000);
    04312             }
    04313
    04314 #if DBG
    04315             else if (gfInNumpadHexInput & NUMPAD_HEXMODE_HL) {
    04316                 RIPMSG0(RIP_VERBOSE, "xxxScanSysQueue: gfInNumpadHexInput is true, so we skipped hotkey.");
    04317             }
    04318 #endif
    04319
    04320             if (wParam == VK_PACKET) {
    04321                 /*
    04322                  * Save the character in thread's cache for TranslateMessage
    04323                  */
    04324                 ptiCurrent->wchInjected = HIWORD(qmsg.msg.wParam);
    04325                 qmsg.msg.wParam = wParam;
    04326                 UserAssert(qmsg.msg.wParam == VK_PACKET);
    04327             }
    04328
    04329             /*
    04330              * Fall through.
    04331              */
    04332
    04333         case WM_SYSKEYUP:
    04334         case WM_KEYUP:
    04335             wParam = qmsg.msg.wParam & 0xFF;
    04336             if (wParam == VK_PACKET) {
    04337                 qmsg.msg.wParam = wParam;
    04338             }
    04339
    04340             /*
    04341              * Special processing for thai locale toggle using grave accent key
    04342              * Remove key message irrespective of fDown otherwise it will
    04343              * generate WM_CHAR message
    04344              */
    04345             if (gbGraveKeyToggle &&
    04346                 //
    04347                 // In case of mstsc.exe, should not eat Grave Accent key message.
    04348                 // TS client must send Grave Accent key message to server side.
    04349                 //
    04350                 !(GetAppImeCompatFlags(NULL) & IMECOMPAT_HYDRACLIENT) &&
    04351                 LOBYTE(HIWORD(qmsg.msg.lParam)) == SCANCODE_THAI_LAYOUT_TOGGLE &&
    04352                 fRemove &&
    04353                 !TestKeyStateDown(ptiCurrent->pq, VK_SHIFT)   &&
    04354                 !TestKeyStateDown(ptiCurrent->pq, VK_MENU)    &&
    04355                 !TestKeyStateDown(ptiCurrent->pq, VK_CONTROL) &&
    04356                 !TestKeyStateDown(ptiCurrent->pq, VK_LWIN)    &&
    04357                 !TestKeyStateDown(ptiCurrent->pq, VK_RWIN)){
    04358
    04359                 if ((pwnd = ptiCurrent->pq->spwndFocus) == NULL){
    04360                     pwnd = ptiCurrent->pq->spwndActive;
    04361                 }
    04362
    04363                 /*
    04364                  * Post message only on WM_KEYUP
    04365                  */
    04366                 if (!fDown && pwnd){
    04367                     PTHREADINFO     ptiToggle = GETPTI(pwnd);
    04368                     PKL             pkl = ptiToggle->spklActive;
    04369
    04370                     if (pkl && (pkl = HKLtoPKL(ptiToggle, (HKL)HKL_NEXT))) {
    04371                         _PostMessage(
    04372                             pwnd,
    04373                             WM_INPUTLANGCHANGEREQUEST,
    04374                             (WPARAM)(((pkl->dwFontSigs & gSystemFS) ? INPUTLANGCHANGE_SYSCHARSET : 0) | INPUTLANGCHANGE_FORWARD),
    04375                             (LPARAM)pkl->hkl
    04376                             );
    04377                     }
    04378                 }
    04379                 /*
    04380                  * eat Accent Grave's key msgs
    04381                  */
    04382                 xxxSkipSysMsg(ptiCurrent, &qmsg);
    04383                 goto RestartScan;
    04384             }
    04385
    04386             /*
    04387              * Process keyboard toggle keys only if this is
    04388              * a break event and fRemove == TRUE.  Some apps,
    04389              * for instance Word 95, call PeekMessage with
    04390              * PM_NOREMOVE followed by a call with PM_REMOVE.
    04391              * We only want to process this once.  Skip all
    04392              * of this is layout switching via the keyboard
    04393              * is disabled.
    04394              */
    04395             if (!fDown && fRemove && gLangToggle[0].bVkey) {
    04396                 BOOL bDropToggle = FALSE;
    04397                 DWORD dwDirection = 0;
    04398                 PKL pkl;
    04399                 PTHREADINFO ptiToggle;
    04400                 // PWND pwndTop;
    04401                 BOOL bArabicSwitchPresent = FALSE;
    04402                 LCID lcid;
    04403
    04404                 ZwQueryDefaultLocale(FALSE, &lcid);
    04405
    04406                 pwnd = ptiCurrent->pq->spwndFocus;
    04407                 if (pwnd == NULL) {
    04408                     pwnd = ptiCurrent->pq->spwndActive;
    04409                     if (!pwnd) {
    04410                         goto NoLayoutSwitch;
    04411                     }
    04412                 }
    04413
    04414                 ptiToggle = GETPTI(pwnd);
    04415                 pkl = ptiToggle->spklActive;
    04416                 UserAssert(ptiToggle->spklActive != NULL);
    04417
    04418                 /*
    04419                  * Check for Arabic toggle context
    04420                  */
    04421                 if (gLangToggleKeyState < KLT_NONE && PRIMARYLANGID(lcid) == LANG_ARABIC){
    04422                     PKL pkl_next = HKLtoPKL (ptiToggle, (HKL)HKL_NEXT);
    04423
    04424                     /*
    04425                      * test if there are exactly two pkl's and at least one
    04426                      * of them is arabic
    04427                      */
    04428                     if (pkl && pkl_next &&
    04429                         pkl->hkl != pkl_next->hkl && pkl_next == HKLtoPKL(ptiToggle, (HKL)HKL_PREV) &&
    04430                         (PRIMARYLANGID(HandleToUlong(pkl->hkl)) == LANG_ARABIC || PRIMARYLANGID(HandleToUlong(pkl_next->hkl)) == LANG_ARABIC)){
    04431                         bArabicSwitchPresent = TRUE;
    04432                     }
    04433                 }
    04434
    04435                 /*
    04436                  * NT has always had Alt LShift going forward (down) the list,
    04437                  * and Alt RShift going backwards. Windows '95 is different.
    04438                  */
    04439                 switch (gLangToggleKeyState) {
    04440                 case KLT_ALTLEFTSHIFT:
    04441                    bDropToggle = TRUE;
    04442                    dwDirection = INPUTLANGCHANGE_FORWARD;
    04443                    if (!bArabicSwitchPresent || PRIMARYLANGID(HandleToUlong(pkl->hkl)) == LANG_ARABIC){
    04444                        pkl = HKLtoPKL(ptiToggle, (HKL)HKL_NEXT);
    04445                    }
    04446                    break;
    04447
    04448                 case KLT_ALTRIGHTSHIFT:
    04449                    bDropToggle = TRUE;
    04450                    dwDirection = INPUTLANGCHANGE_BACKWARD;
    04451                    if (!bArabicSwitchPresent || PRIMARYLANGID(HandleToUlong(pkl->hkl)) != LANG_ARABIC){
    04452                        pkl = HKLtoPKL(ptiToggle, (HKL)HKL_PREV);
    04453                    }
    04454                    break;
    04455
    04456                 case KLT_ALTBOTHSHIFTS:
    04457                    pkl = gspklBaseLayout;
    04458                    break;
    04459
    04460                 default:
    04461                    goto NoLayoutSwitch;
    04462                    break;
    04463                 }
    04464
    04465                 if (pkl == NULL) {
    04466                     pkl = GETPTI(pwnd)->spklActive;
    04467                 }
    04468
    04469                 /*
    04470                  * If these two are not NULL, then winlogon hasn't loaded
    04471                  * any keyboard layouts yet: but nobody should be getting
    04472                  * input yet, so Assert but check pkl anyway. #99321
    04473                  */
    04474                 UserAssert(gspklBaseLayout != NULL);
    04475                 UserAssert(pkl);
    04476                 if (pkl) {
    04477                     /*
    04478                      * Not a very satisfactory window to post to, but it's hard
    04479                      * to figure out a better window. Just do as Memphis does.
    04480                      * Note: The following went up too high, bypassing Word
    04481                      * when using wordmail - IanJa bug #64744.
    04482                      *    if ((pwndTop = GetTopLevelWindow(pwnd)) != NULL) {
    04483                      *       pwnd = pwndTop;
    04484                      *    }
    04485                      */
    04486                     _PostMessage(pwnd, WM_INPUTLANGCHANGEREQUEST,
    04487                             (DWORD)(((pkl->dwFontSigs & gSystemFS) ? INPUTLANGCHANGE_SYSCHARSET : 0) | dwDirection),
    04488                             (LPARAM)pkl->hkl);
    04489                 }
    04490
    04491 NoLayoutSwitch:
    04492
    04493                 if (bDropToggle) {
    04494                     /*
    04495                      * Clear this key from the key state so that multiple key
    04496                      * presses will work (i.e., Alt+Shft+Shft).  We don't do
    04497                      * this when both shift keys are pressed simultaneously to
    04498                      * avoid two activates.
    04499                      */
    04500                     DWORD i;
    04501                     BYTE scancode = LOBYTE(HIWORD(qmsg.msg.lParam));
    04502                     BYTE vkey = LOBYTE(qmsg.msg.wParam);
    04503
    04504                     for (i = 0; i < LANGTOGGLEKEYS_SIZE; i++) {
    04505                         if (gLangToggle[i].bScan) {
    04506                             if (gLangToggle[i].bScan == scancode) {
    04507                                 gLangToggleKeyState &= ~(gLangToggle[i].iBitPosition);
    04508                             }
    04509                         } else {
    04510                             if (gLangToggle[i].bVkey == vkey) {
    04511                                 gLangToggleKeyState &= ~(gLangToggle[i].iBitPosition);
    04512                             }
    04513                         }
    04514                     }
    04515                 } else {
    04516                     gLangToggleKeyState = 0;
    04517                 }
    04518             }
    04519
    04520             /*
    04521              * Convert F10 to syskey for new apps.
    04522              */
    04523             if (wParam == VK_F10)
    04524                 message |= (WM_SYSKEYDOWN - WM_KEYDOWN);
    04525
    04526             if (TestKeyStateDown(ptiCurrent->pq, VK_CONTROL) &&
    04527                     wParam == VK_ESCAPE) {
    04528                 message |= (WM_SYSKEYDOWN - WM_KEYDOWN);
    04529             }
    04530
    04531             /*
    04532              * Clear the 'simulated keystroke' bit for all applications except
    04533              * console so it can pass it to 16-bit vdms. VDM keyboards need to
    04534              * distinguish between AltGr (where Ctrl keystroke is simulated)
    04535              * and a real Ctrl+Alt. Check TIF_CSRSSTHREAD for the console
    04536              * input thread because it lives in the server. This is a cheap
    04537              * way to check for it.
    04538              */
    04539             if (!(ptiCurrent->TIF_flags & TIF_CSRSSTHREAD))
    04540                 qmsg.msg.lParam &= ~FAKE_KEYSTROKE;
    04541             PATHTAKEN2(0x4000000);
    04542
    04543             /*
    04544              * Fall through.
    04545              */
    04546
    04547             /*
    04548              * Some apps want to be able to feed WM_CHAR messages through
    04549              * the playback hook. Why? Because they want to be able to
    04550              * convert a string of characters info key messages
    04551              * and feed them to themselves or other apps. Unfortunately,
    04552              * there are no machine independent virtual key codes for
    04553              * some characters (for example '$'), so they need to send
    04554              * those through as WM_CHARs. (6/10/87).
    04555              */
    04556
    04557         case WM_CHAR:
    04558             wParam = qmsg.msg.wParam & 0xFF;
    04559
    04560             /*
    04561              * Assign the input to the focus window. If there is no focus
    04562              * window, assign it to the active window as a SYS message.
    04563              */
    04564             pwnd = ptiCurrent->pq->spwndFocus;
    04565             if (ptiCurrent->pq->spwndFocus == NULL) {
    04566                 if ((pwnd = ptiCurrent->pq->spwndActive) != NULL) {
    04567                     if (CheckMsgFilter(message, WM_KEYDOWN, WM_DEADCHAR)) {
    04568                         message += (WM_SYSKEYDOWN - WM_KEYDOWN);
    04569                         PATHTAKEN2(0x8000000);
    04570                     }
    04571                 } else {
    04572                     PATHTAKEN2(0x10000000);
    04573                     goto SkipMessage;
    04574                 }
    04575             }
    04576
    04577             /*
    04578              * If there is no active window or focus window, eat this
    04579              * message.
    04580              */
    04581             if (pwnd == NULL) {
    04582                 PATHTAKEN2(0x20000000);
    04583                 goto SkipMessage;
    04584             }
    04585
    04586             ThreadUnlock(&tlpwnd);
    04587             ThreadLockAlwaysWithPti(ptiCurrent, pwnd, &tlpwnd);
    04588
    04589             /*
    04590              * Check if this is intended for the current app.
    04591              */
    04592             if (fOtherApp = (GETPTI(pwnd) != ptiCurrent)) {
    04593                 PWND pwndModalLoop;
    04594
    04595                 /*
    04596                  * If this other app isn't going to read from this
    04597                  * queue, then skip this message. This can happen if
    04598                  * the RIT queues up a message thinking it goes to
    04599                  * a particular hwnd, but then by the time GetMessage()
    04600                  * is called for that thread, it doesn't go to that hwnd
    04601                  * (like in the case of mouse messages, window rearrangement
    04602                  * happens which changes which hwnd the mouse hits on).
    04603                  */
    04604                 if (GETPTI(pwnd)->pq != ptiCurrent->pq) {
    04605                     PATHTAKEN2(0x40000000);
    04606                     goto SkipMessage;
    04607                 }
    04608
    04609                 /*
    04610                  * If the current thread is in the menu or movesize loop
    04611                  *  then we need to give it the input
    04612                  */
    04613                 if (IsInsideMenuLoop(ptiCurrent)) {
    04614                     pwndModalLoop = ptiCurrent->pMenuState->pGlobalPopupMenu->spwndNotify;
    04615                 } else if (ptiCurrent->pmsd != NULL) {
    04616                     pwndModalLoop = ptiCurrent->pmsd->spwnd;
    04617                     RIPMSG0(RIP_WARNING, "xxxScanSysQueue: returning key to movesize loop");
    04618                 } else {
    04619                     pwndModalLoop = NULL;
    04620                 }
    04621
    04622                 /*
    04623                  * If we're switching windows, lock the new one
    04624                  */
    04625                 if (pwndModalLoop != NULL) {
    04626                     pwnd = pwndModalLoop;
    04627                     fOtherApp = (GETPTI(pwnd) != ptiCurrent);
    04628                     ThreadUnlock(&tlpwnd);
    04629                     ThreadLockWithPti(ptiCurrent, pwnd, &tlpwnd);
    04630                     PATHTAKEN2(0x80000000);
    04631                 }
    04632
    04633                 /*
    04634                  * If not for us, then remember who it is for.
    04635                  */
    04636                 if (ptiKeyWake == NULL) {
    04637                     PATHTAKEN3(1);
    04638                     ptiKeyWake = GETPTI(pwnd);
    04639                 }
    04640             }
    04641
    04642             /*
    04643              * See if this thing matches our filter.
    04644              */
    04645             if (!CheckMsgFilter(message, msgMinFilter, msgMaxFilter) ||
    04646                     !CheckPwndFilter(pwnd, pwndFilter)) {
    04647                 PATHTAKEN3(2);
    04648                 continue;
    04649             }
    04650
    04651             /*
    04652              * This message matches our filter. If it is not for us then
    04653              * stop searching to make sure the real owner processes this
    04654              * message first.
    04655              */
    04656             if (fOtherApp) {
    04657                 PATHTAKEN3(4);
    04658                 goto NoMessages;
    04659             }
    04660
    04661             /*
    04662              * Generate some special messages if we are removing and we are
    04663              * not inside the menu loop.
    04664              */
    04665             if (fRemove && !IsInsideMenuLoop(ptiCurrent)) {
    04666                 /*
    04667                  * Generate a WM_CONTEXTMENU for the VK_APPS key
    04668                  */
    04669                 if ((wParam == VK_APPS) && (message == WM_KEYUP)) {
    04670                     _PostMessage(pwnd, WM_CONTEXTMENU, (WPARAM)PtoH(pwnd), KEYBOARD_MENU);
    04671                 }
    04672
    04673                 /*
    04674                  * If this is a WM_KEYDOWN message for F1 key then we must generate
    04675                  * the WM_KEYF1 message.
    04676                  */
    04677                 if ((wParam == VK_F1) && (message == WM_KEYDOWN)) {
    04678                     _PostMessage(pwnd, WM_KEYF1, 0, 0);
    04679                 }
    04680             }
    04681
    04682             /*
    04683              * If one Shift key is released while the other Shift key is held
    04684              * down, this keystroke is normally skipped, presumably to prevent
    04685              * applications from thinking that the shift condition no longer
    04686              * applies.
    04687              */
    04688             if (wParam == VK_SHIFT) {
    04689                 BYTE vkHanded, vkOtherHand;
    04690
    04691                 if (qmsg.msg.lParam & EXTENDED_BIT) {
    04692                     vkHanded = VK_RSHIFT;
    04693                 } else {
    04694                     vkHanded = VK_LSHIFT;
    04695                 }
    04696                 vkOtherHand = vkHanded ^ 1;
    04697
    04698                 if (!fDown && TestKeyStateDown(ptiCurrent->pq, vkOtherHand)) {
    04699                     /*
    04700                      * Unlike normal apps, Console MUST be sent a Shift break
    04701                      * even when the other Shift key is still down, since it
    04702                      * has to be passed on to VDM, which maintains it's own
    04703                      * state. Check TIF_CSRSSTHREAD for the console input
    04704                      * thread because it lives in the server. This is a cheap
    04705                      * way to check for it.
    04706                      */
    04707                     if ((ptiCurrent->TIF_flags & TIF_CSRSSTHREAD) == 0) {
    04708                         /*
    04709                          * We ignore this key event, so we must update
    04710                          * it's key state whether fRemove is TRUE or not.
    04711                          * (ignoring an key event is same as removing it)
    04712                          */
    04713                         qmsg.msg.wParam = vkHanded;
    04714                         xxxSkipSysMsg(ptiCurrent, &qmsg);
    04715                         PATHTAKEN3(8);
    04716                         goto RestartScan;
    04717                     }
    04718                     PATHTAKEN3(0x10);
    04719                 }
    04720             }
    04721
    04722             /*
    04723              * Get the previous up/down state of the key here since
    04724              * SkipSysMsg() sets the key state table and destroys
    04725              * the previous state info.
    04726              */
    04727             fPrevDown = FALSE;
    04728             if (TestKeyStateDown(ptiCurrent->pq, wParam))
    04729                 fPrevDown = TRUE;
    04730
    04731             /*
    04732              * Eat the message from the input queue and set the keystate
    04733              * table.
    04734              */
    04735             PATHTAKEN3(0x20);
    04736             if (fRemove) {
    04737                 xxxSkipSysMsg(ptiCurrent, &qmsg);
    04738             }
    04739
    04740             /*
    04741              * This gets us the LOWORD of lParam, the repeat count,
    04742              * the bit in the hi byte indicating whether this is an extended
    04743              * key, and the scan code.  We also need to re-get the wParam in
    04744              * case xxxSkipSysMsg called a hook which modified the message.
    04745              * AfterDark's password protection does this.
    04746              */
    04747             lParam = qmsg.msg.lParam;
    04748             wParam = qmsg.msg.wParam;
    04749
    04750             /*
    04751              * Indicate if it was previously down.
    04752              */
    04753             if (fPrevDown)
    04754                 lParam |= 0x40000000;           // KF_REPEAT
    04755
    04756             /*
    04757              * Set the transition bit.
    04758              */
    04759             switch (message) {
    04760             case WM_KEYUP:
    04761             case WM_SYSKEYUP:
    04762                 lParam |= 0x80000000;           // KF_UP
    04763                 break;
    04764             }
    04765
    04766             /*
    04767              * Set the alt key down bit.
    04768              */
    04769             if (TestKeyStateDown(ptiCurrent->pq, VK_MENU)) {
    04770                 lParam |= 0x20000000;           // KF_ALTDOWN
    04771             }
    04772
    04773             /*
    04774              * Set the menu state flag.
    04775              */
    04776             if (IsMenuStarted(ptiCurrent)) {
    04777                 lParam |= 0x10000000;           // KF_MENUMODE
    04778             }
    04779
    04780             /*
    04781              * Set the dialog state flag.
    04782              */
    04783             if (ptiCurrent->pq->QF_flags & QF_DIALOGACTIVE) {
    04784                 lParam |= 0x08000000;           // KF_DLGMODE
    04785             }
    04786
    04787             /*
    04788              * 0x80000000 is set if up, clear if down
    04789              * 0x40000000 is previous up/down state of key
    04790              * 0x20000000 is whether the alt key is down
    04791              * 0x10000000 is whether currently in menumode.
    04792              * 0x08000000 is whether in dialog mode
    04793              * 0x04000000 is not used
    04794              * 0x02000000 is not used
    04795              * 0x01000000 is whether this is an extended keyboard key
    04796              *
    04797              * Low word is repeat count, low byte hiword is scan code,
    04798              * hi byte hiword is all these bits.
    04799              */
    04800
    04801             /*
    04802              * Callback the client IME before calling the keyboard hook.
    04803              * If the vkey is one of the IME hotkeys, the vkey will not
    04804              * be passed to the keyboard hook.
    04805              * If IME needs this vkey, VK_PROCESSKEY will be put into the
    04806              * application queue instead of real vkey.
    04807              */
    04808             UserAssert(ptiCurrent != NULL);
    04809             if (gpImeHotKeyListHeader != NULL &&
    04810                     fRemove &&
    04811                     !IsMenuStarted(ptiCurrent) &&
    04812                     !(ptiCurrent->TIF_flags & TIF_DISABLEIME) &&
    04813                     pwnd != NULL) {
    04814
    04815                 WPARAM wParamTemp = wParam;
    04816
    04817                 if (wParam == VK_PACKET) {
    04818                     wParamTemp = MAKEWPARAM(wParam, ptiCurrent->wchInjected);
    04819                 }
    04820
    04821                 /*
    04822                  * xxxImmProcessKey also checks the registered IME hotkeys.
    04823                  */
    04824                 dwImmRet = xxxImmProcessKey( ptiCurrent->pq,
    04825                                              pwnd,
    04826                                              message,
    04827                                              wParamTemp,
    04828                                              lParam);
    04829                 if ( dwImmRet & (IPHK_HOTKEY | IPHK_SKIPTHISKEY) ) {
    04830                     dwImmRet = 0;
    04831                     goto SkipMessage;
    04832                 }
    04833             }
    04834
    04835             /*
    04836              * If we are removing the message, call the keyboard hook
    04837              * with HC_ACTION, otherwise call the hook with HC_NOREMOVE
    04838              * to let it know that the message is not being removed.
    04839              */
    04840             if (IsHooked(ptiCurrent, WHF_KEYBOARD)) {
    04841                 fKbdHookCalled = TRUE;
    04842                 if (xxxCallHook(fRemove ? HC_ACTION : HC_NOREMOVE,
    04843                         wParam, lParam, WH_KEYBOARD)) {
    04844                     PATHTAKEN3(0x40);
    04845                     goto SkipMessage;
    04846                 }
    04847             }
    04848
    04849             if (fKbdHookCalled && fRemove && IsHooked(ptiCurrent, WHF_CBT)) {
    04850                 xxxCallHook(HCBT_KEYSKIPPED, wParam, lParam, WH_CBT);
    04851                 PATHTAKEN3(0x80);
    04852             }
    04853
    04854             fKbdHookCalled = FALSE;
    04855             PATHTAKEN3(0x100);
    04856             goto ReturnMessage;
    04857
    04858         case WM_MOUSEWHEEL:
    04859             /*
    04860              * If we are sending keyboard input to an app that has been
    04861              * spinning then boost it back up.  If we don't you use spinning
    04862              * apps like Write or Project and do two builds in the
    04863              * background.  Note the app will also be unboosted again shortly
    04864              * after you stop typing by the old logic. #11188
    04865              */
    04866             if (ptiCurrent->TIF_flags & TIF_SPINNING)
    04867                 CheckProcessForeground(ptiCurrent);
    04868
    04869             /*
    04870              * Assign the input to the focus window. If there is no focus
    04871              * window, or we are in a menu loop, eat this message.
    04872              */
    04873             pwnd = ptiCurrent->pq->spwndFocus;
    04874             if (pwnd == NULL || IsInsideMenuLoop(ptiCurrent)) {
    04875                 PATHTAKEN2(0x20000000);
    04876                 goto SkipMessage;
    04877             }
    04878
    04879             ThreadUnlock(&tlpwnd);
    04880             ThreadLockAlwaysWithPti(ptiCurrent, pwnd, &tlpwnd);
    04881
    04882             /*
    04883              * Check if this is intended for the current app.
    04884              */
    04885             if (fOtherApp = (GETPTI(pwnd) != ptiCurrent)) {
    04886
    04887                 /*
    04888                  * If this other app isn't going to read from this
    04889                  * queue, then skip this message. This can happen if
    04890                  * the RIT queues up a message thinking it goes to
    04891                  * a particular hwnd, but then by the time GetMessage()
    04892                  * is called for that thread, it doesn't go to that hwnd
    04893                  * (like in the case of mouse messages, window rearrangement
    04894                  * happens which changes which hwnd the mouse hits on).
    04895                  */
    04896                 if (GETPTI(pwnd)->pq != ptiCurrent->pq) {
    04897                     PATHTAKEN2(0x40000000);
    04898                     goto SkipMessage;
    04899                 }
    04900
    04901                 /*
    04902                  * If not for us, then remember who it is for.
    04903                  */
    04904                 if (ptiKeyWake == NULL) {
    04905                     PATHTAKEN3(1);
    04906                     ptiKeyWake = GETPTI(pwnd);
    04907                 }
    04908             }
    04909
    04910             /*
    04911              * See if this thing matches our filter.
    04912              * NOTE: We need to check whether the caller is filtering
    04913              * for all mouse messages - if so, we assume the caller
    04914              * wants mouse wheel messages too.
    04915              */
    04916             if (    !CheckMsgFilter(WM_MOUSEWHEEL, msgMinFilter, msgMaxFilter) ||
    04917                     !CheckPwndFilter(pwnd, pwndFilter)) {
    04918                 PATHTAKEN3(2);
    04919                 continue;
    04920             }
    04921
    04922             /*
    04923              * This message matches our filter. If it is not for us then
    04924              * stop searching to make sure the real owner processes this
    04925              * message first.
    04926              */
    04927             if (fOtherApp) {
    04928                 PATHTAKEN3(4);
    04929                 goto NoMessages;
    04930             }
    04931
    04932             /*
    04933              * Eat the message from the input queue and set the keystate
    04934              * table.
    04935              */
    04936             PATHTAKEN3(0x20);
    04937             if (fRemove) {
    04938                 xxxSkipSysMsg(ptiCurrent, &qmsg);
    04939             }
    04940
    04941             wParam = GetMouseKeyFlags(ptiCurrent->pq);
    04942             UserAssert(LOWORD(qmsg.msg.wParam) == 0);
    04943             UserAssert(HIWORD(wParam) == 0);
    04944             wParam |= qmsg.msg.wParam;
    04945             lParam = qmsg.msg.lParam;
    04946
    04947             /*
    04948              * If we are removing the message, call the mouse hook
    04949              * with HC_ACTION, otherwise call the hook with HC_NOREM
    04950              * to let it know that the message is not being removed.
    04951              */
    04952             if (IsHooked(ptiCurrent, WHF_MOUSE)) {
    04953                 fMouseHookCalled = TRUE;
    04954                 mhs.pt = qmsg.msg.pt;
    04955                 mhs.hwnd = HW(pwnd);
    04956                 mhs.wHitTestCode = HTNOWHERE;
    04957                 mhs.dwExtraInfo = qmsg.ExtraInfo;
    04958                 mhs.mouseData = (DWORD)qmsg.msg.wParam;
    04959                 if (xxxCallMouseHook(message, &mhs, fRemove)) {
    04960                     /*
    04961                      * Not allowed by mouse hook; so skip it.
    04962                      */
    04963                     PATHTAKEN3(0x40);
    04964                     goto SkipMessage;
    04965                 }
    04966             }
    04967
    04968             if (fMouseHookCalled && fRemove && IsHooked(ptiCurrent, WHF_CBT)) {
    04969                 /*
    04970                  * CONSIDER: Add new HCBT_ constant for the mouse wheel?
    04971                  */
    04972                 xxxCallHook(HCBT_CLICKSKIPPED, message, (LPARAM)&mhs, WH_CBT);
    04973                 PATHTAKEN3(0x80);
    04974             }
    04975
    04976             fMouseHookCalled = FALSE;
    04977             PATHTAKEN3(0x100);
    04978             goto ReturnMessage;
    04979         } /* End of switch (message = qmsg.msg.message) */
    04980     } /* End of the GetNextSysMsg() loop */
    04981
    04982 ReturnMessage:
    04983     if (!RtlEqualMemory(&ptiCurrent->ptLast, &qmsg.msg.pt, sizeof(POINT))) {
    04984         ptiCurrent->TIF_flags |= TIF_MSGPOSCHANGED;
    04985     }
    04986     ptiCurrent->ptLast = qmsg.msg.pt;
    04987     ptiCurrent->timeLast = qmsg.msg.time;
    04988     ptiCurrent->pq->ExtraInfo = qmsg.ExtraInfo;
    04989
    04990     /*
    04991      * idSysLock value of 1 indicates that the message came from the input
    04992      * queue.
    04993      */
    04994     ptiCurrent->idLast = ptiCurrent->pq->idSysLock = 1;
    04995
    04996     /*
    04997      * Now see if our input bit is set for this input. If it isn't, set ours
    04998      * and clear the guy who had it previously.
    04999      */
    05000     TransferWakeBit(ptiCurrent, message);
    05001
    05002     /*
    05003      * Clear the input bits if no messages in the input queue.
    05004      */
    05005     ClearWakeBit(ptiCurrent, QS_MOUSE | QS_KEY | QS_EVENT | QS_TRANSFER, TRUE);
    05006
    05007     /*
    05008      * Get the message and split.
    05009      */
    05010     lpMsg->hwnd = HW(pwnd);
    05011     lpMsg->message = message;
    05012
    05013     /*
    05014      * If the IME claims that it needs this vkey, replace it
    05015      * with VK_PROCESSKEY. The real vkey has been saved in
    05016      * the input context in the client side.
    05017      */
    05018     lpMsg->wParam = (dwImmRet & IPHK_PROCESSBYIME) ? VK_PROCESSKEY : wParam;
    05019
    05020     lpMsg->lParam = lParam;
    05021     lpMsg->time = qmsg.msg.time;
    05022     lpMsg->pt = qmsg.msg.pt;
    05023
    05024 #if DBG
    05025     if (gfLogPlayback && ptiCurrent->pq->idSysPeek == (LONG_PTR)PQMSG_PLAYBACK)
    05026         LogPlayback(pwnd, lpMsg);
    05027 #endif  // DBG
    05028
    05029     ThreadUnlock(&tlpwnd);
    05030
    05031     PATHTAKEN3(0x200);
    05032     DUMPPATHTAKEN();
    05033     return TRUE;
    05034
    05035 NoMessages:
    05036     /*
    05037      * The message was for another app, or none were found that fit the
    05038      * filter.
    05039      */
    05040
    05041     /*
    05042      * Unlock the system queue.
    05043      */
    05044     ptiCurrent->pq->idSysLock  = 0;
    05045     CheckSysLock(4, ptiCurrent->pq, NULL);
    05046     ptiCurrent->pq->ptiSysLock = NULL;
    05047     ptiCurrent->pcti->CTIF_flags &= ~CTIF_SYSQUEUELOCKED;
    05048
    05049     /*
    05050      * Wake up someone else if we found a message for him.  QS_TRANSFER
    05051      * signifies that the thread was woken due to input transfer
    05052      * from another thread, rather than from a real input event.
    05053      */
    05054     if (ptiKeyWake != NULL || ptiMouseWake != NULL || ptiEventWake != NULL) {
    05055         PATHTAKEN3(0x400);
    05056         if (ptiKeyWake != NULL) {
    05057             SetWakeBit(ptiKeyWake, QS_KEY | QS_TRANSFER);
    05058             ClearWakeBit(ptiCurrent, QS_KEY | QS_TRANSFER, FALSE);
    05059             PATHTAKEN3(0x800);
    05060         }
    05061
    05062         if (ptiMouseWake != NULL) {
    05063             SetWakeBit(ptiMouseWake, QS_MOUSE | QS_TRANSFER);
    05064             ClearWakeBit(ptiCurrent, QS_MOUSE | QS_TRANSFER, FALSE);
    05065             PATHTAKEN3(0x1000);
    05066         }
    05067
    05068         if (ptiEventWake != NULL) {
    05069             SetWakeBit(ptiEventWake, QS_EVENTSET);
    05070             ClearWakeBit(ptiCurrent, QS_EVENT, FALSE);
    05071             PATHTAKEN3(0x2000);
    05072         } else if (FJOURNALPLAYBACK()) {
    05073
    05074             /*
    05075              * If journal playback is occuring, clear the input bits.  This will
    05076              * help prevent a race condition between two threads that call
    05077              * WaitMessage/PeekMessage.  This can occur when embedding an OLE
    05078              * object.  An example is inserting a Word object into an Excel
    05079              * spreadsheet.
    05080              * Also clear change bits else this thread might not xxxSleepThread.
    05081              */
    05082             ptiCurrent->pcti->fsWakeBitsJournal |= (ptiCurrent->pcti->fsWakeBits &
    05083                     (QS_MOUSE | QS_KEY | QS_TRANSFER));
    05084             ClearWakeBit(ptiCurrent, QS_MOUSE | QS_KEY | QS_TRANSFER, FALSE);
    05085             ptiCurrent->pcti->fsChangeBits &= ~(QS_MOUSE | QS_KEY | QS_TRANSFER);
    05086         }
    05087     } else {
    05088         /*
    05089          * Clear the input bits if no messages in the input queue.
    05090          */
    05091         ptiCurrent->pcti->fsWakeBitsJournal = 0;
    05092         ClearWakeBit(ptiCurrent, QS_MOUSE | QS_KEY | QS_EVENT |
    05093                 QS_TRANSFER, TRUE);
    05094         PATHTAKEN3(0x4000);
    05095     }
    05096
    05097     ThreadUnlock(&tlpwnd);
    05098
    05099     PATHTAKEN3(0x8000);
    05100     DUMPPATHTAKEN();
    05101     return FALSE;
    05102 }
    05103 #undef PATHTAKEN
    05104 #undef PATHTAKEN2
    05105 #undef PATHTAKEN3
    05106 #undef DUMPPATHTAKEN
    05107 #undef DUMPSUBPATHTAKEN
    05108
    05109
    05110 /***************************************************************************\
    05111 * IdleTimerProc
    05112 *
    05113 * This will start the screen saver app
    05114 *
    05115 * History:
    05116 * 09-06-91  mikeke      Created.
    05117 * 03-26-92  DavidPe     Changed to be run from hungapp timer on RIT.
    05118 \***************************************************************************/
    05119
    05120 VOID IdleTimerProc(VOID)
    05121 {
    05122
    05123     CheckCritIn();
    05124
    05125     if (    (TestAsyncKeyStateDown(VK_LBUTTON)) ||
    05126             (TestAsyncKeyStateDown(VK_RBUTTON)) ||
    05127             (TestAsyncKeyStateDown(VK_MBUTTON)) ||
    05128             (TestAsyncKeyStateDown(VK_XBUTTON1)) ||
    05129             (TestAsyncKeyStateDown(VK_XBUTTON2))) {
    05130
    05131         return;
    05132     }
    05133
    05134
    05135     if (giScreenSaveTimeOutMs > 0) {
    05136
    05137         if (IsTimeFromLastInput((DWORD)(giScreenSaveTimeOutMs))) {
    05138
    05139             if (gppiScreenSaver != NULL) {
    05140
    05141                 if (!(gppiScreenSaver->W32PF_Flags & W32PF_IDLESCREENSAVER)) {
    05142                     /*
    05143                      * Bump the priority of the screen saver down to idle.
    05144                      */
    05145                     gppiScreenSaver->W32PF_Flags |= W32PF_IDLESCREENSAVER;
    05146                     SetForegroundPriorityProcess(gppiScreenSaver, gppiScreenSaver->ptiMainThread, TRUE);
    05147                 }
    05148             } else {
    05149                 /*
    05150                  * Tell the system that it needs to bring up a screen saver.
    05151                  *
    05152                  * Carefull with the case when the active window is hung. If this
    05153                  * is the case the screen saver won't be started by winlogon because
    05154                  * DefWindowProc won't call StartScreenSaver(FALSE).
    05155                  */
    05156                 if ((gpqForeground != NULL) &&
    05157                     (gpqForeground->spwndActive != NULL) &&
    05158                     !FHungApp(GETPTI(gpqForeground->spwndActive), CMSHUNGAPPTIMEOUT)) {
    05159
    05160                     /*
    05161                      * Tell winlogon to start the screen saver if we have a secure
    05162                      * screen saver. In case we do have a secure one, the next PostMessage
    05163                      * will be ignored in winlogon.
    05164                      */
    05165                     StartScreenSaver(TRUE);
    05166                     _PostMessage(gpqForeground->spwndActive, WM_SYSCOMMAND, SC_SCREENSAVE, 0L);
    05167                 } else {
    05168                     StartScreenSaver(FALSE);
    05169                 }
    05170             }
    05171         }
    05172     }
    05173
    05174     if ((giLowPowerTimeOutMs > 0) && ((glinp.dwFlags & LINP_LOWPOWER) == 0)) {
    05175         if (IsTimeFromLastInput((DWORD)(giLowPowerTimeOutMs))) {
    05176             if ((gpqForeground != NULL) && (gpqForeground->spwndActive != NULL)) {
    05177                 _PostMessage(gpqForeground->spwndActive, WM_SYSCOMMAND, SC_MONITORPOWER, LOWPOWER_PHASE);
    05178             }
    05179         }
    05180     }
    05181
    05182     if ((giPowerOffTimeOutMs > 0) && ((glinp.dwFlags & LINP_POWEROFF) == 0)) {
    05183         if (IsTimeFromLastInput((DWORD)(giPowerOffTimeOutMs))) {
    05184             if ((gpqForeground != NULL) && (gpqForeground->spwndActive != NULL)) {
    05185                 _PostMessage(gpqForeground->spwndActive, WM_SYSCOMMAND, SC_MONITORPOWER, POWEROFF_PHASE);
    05186             }
    05187         }
    05188     }
    05189
    05190 }
    05191
    05192 /***************************************************************************\
    05193 * zzzWakeInputIdle
    05194 *
    05195 * The calling thread is going "idle". Wake up any thread waiting for this.
    05196 *
    05197 * 09-24-91 ScottLu      Created.
    05198 \***************************************************************************/
    05199
    05200 void zzzWakeInputIdle(
    05201     PTHREADINFO pti)
    05202 {
    05203     PW32PROCESS W32Process = W32GetCurrentProcess();
    05204
    05205     /*
    05206      * clear out the TIF_FIRSTIDLE since here we are
    05207      */
    05208     pti->TIF_flags &= ~TIF_FIRSTIDLE;
    05209
    05210
    05211     /*
    05212      * Shared Wow Apps use the per thread idle event for synchronization.
    05213      * Separate Wow VDMs use the regular mechanism.
    05214      */
    05215     if (pti->TIF_flags & TIF_SHAREDWOW) {
    05216         UserAssert(pti->TIF_flags & TIF_16BIT);
    05217         if (pti->ptdb->pwti) {
    05218             SET_PSEUDO_EVENT(&pti->ptdb->pwti->pIdleEvent);
    05219         }
    05220     } else {
    05221         /*
    05222          * If the main thread is NULL, set it to this queue: it is calling
    05223          * GetMessage().
    05224          */
    05225         if (pti->ppi->ptiMainThread == NULL)
    05226             pti->ppi->ptiMainThread = pti;
    05227
    05228         /*
    05229          * Wake up anyone waiting on this event.
    05230          */
    05231         if (pti->ppi->ptiMainThread == pti) {
    05232             SET_PSEUDO_EVENT(&W32Process->InputIdleEvent);
    05233         }
    05234     }
    05235
    05236     /*
    05237      * Check to see if the startglass is on, and if so turn it off and update.
    05238      */
    05239     if (W32Process->W32PF_Flags & W32PF_STARTGLASS) {
    05240         /*
    05241          * This app is no longer in "starting" mode. Recalc when to hide
    05242          * the app starting cursor.
    05243          */
    05244         W32Process->W32PF_Flags &= ~W32PF_STARTGLASS;
    05245         zzzCalcStartCursorHide(NULL, 0);
    05246     }
    05247 }
    05248
    05249 void SleepInputIdle(
    05250     PTHREADINFO pti)
    05251 {
    05252     PW32PROCESS W32Process;
    05253
    05254     /*
    05255      * Shared Wow Apps use the per thread idle event for synchronization.
    05256      * Separate Wow VDMs use the regular mechanism.
    05257      */
    05258     if (pti->TIF_flags & TIF_SHAREDWOW) {
    05259         UserAssert(pti->TIF_flags & TIF_16BIT);
    05260         if (pti->ptdb->pwti) {
    05261             RESET_PSEUDO_EVENT(&pti->ptdb->pwti->pIdleEvent);
    05262         }
    05263     } else {
    05264         /*
    05265          * If the main thread is NULL, set it to this queue: it is calling
    05266          * GetMessage().
    05267          */
    05268         if (pti->ppi->ptiMainThread == NULL)
    05269             pti->ppi->ptiMainThread = pti;
    05270
    05271         /*
    05272          * Put to sleep up anyone waiting on this event.
    05273          */
    05274         if (pti->ppi->ptiMainThread == pti) {
    05275             W32Process = W32GetCurrentProcess();
    05276             RESET_PSEUDO_EVENT(&W32Process->InputIdleEvent);
    05277         }
    05278     }
    05279 }
    05280
    05281 /***************************************************************************\
    05282 * zzzRecalcThreadAttachment
    05283 * zzzRecalc2
    05284 * zzzAddAttachment
    05285 * CheckAttachment
    05286 *
    05287 * Runs through all the attachinfo fields for all threads and calculates
    05288 * which threads share which queues. Puts calculated result in pqAttach
    05289 * field in each threadinfo structure. This is a difficult problem
    05290 * whose only solution in iterative. The basic algorithm is:
    05291 *
    05292 * 0. Find next unattached thread and attach a queue to it. If none, stop.
    05293 * 1. Loop through all threads: If thread X assigned to this queue or any
    05294 *    of X's attach requests assigned to this queue, assign X and all X's
    05295 *    attachments to this queue. Remember if we ever attach a 16 bit thread.
    05296 * 2. If thread X is a 16 bit thread and we've already attached another
    05297 *    16 bit thread, assign X and all X's attachments to this queue.
    05298 * 3. If any change found in 1-2, goto 1
    05299 * 4. Goto 0
    05300 *
    05301 * 12-11-92 ScottLu      Created.
    05302 * 01-Oct-1993 mikeke    Fixed to work with MWOWs
    05303 \***************************************************************************/
    05304
    05305 void zzzAddAttachment(
    05306     PTHREADINFO pti,
    05307     PQ pqAttach,
    05308     LPBOOL pfChanged)
    05309 {
    05310     if (pti->pqAttach != pqAttach) {
    05311         /*
    05312          * LATER
    05313          * !!! This is totally screwed up,  The only reason that this thing
    05314          * could be non null is because two threads are going through
    05315          * zzzAttachThreadInput() at the same time.  No one can predict
    05316          * what kind of problems are going to be caused by that.
    05317          * We leave the critical section in one place where we send
    05318          * WM_CANCELMODE below.  We should figure out how to remove
    05319          * the sendmessage.
    05320          *
    05321          * If there already is a queue there, as there may be, destroy it.
    05322          * Note that zzzDestroyQueue() will only get rid of the queue if the
    05323          * thread reference count goes to 0.
    05324          */
    05325         PQ pqDestroy = pti->pqAttach;
    05326         pti->pqAttach = pqAttach;
    05327         if (pqDestroy != NULL)
    05328             zzzDestroyQueue(pqDestroy, pti);
    05329         pqAttach->cThreads++;
    05330         *pfChanged = TRUE;
    05331     }
    05332 }
    05333
    05334 void zzzRecalc2(
    05335     PQ pqAttach)
    05336 {
    05337     PATTACHINFO pai;
    05338     PTHREADINFO pti;
    05339     BOOL fChanged;
    05340     PLIST_ENTRY pHead, pEntry;
    05341
    05342     /*
    05343      * Defer Win Event notifications so we can traverse PtiList with impunity
    05344      * #bug number from shiflet
    05345      */
    05346     DeferWinEventNotify();
    05347     BEGINATOMICCHECK();
    05348
    05349     /*
    05350      * Keep adding attachments until everything that should be attached to this
    05351      * queue is attached
    05352      */
    05353     do {
    05354         fChanged = FALSE;
    05355
    05356         /*
    05357          * If a thread is attached to this Q attach all of it's attachments
    05358          * and MWOW buddies if they aren't already attached.
    05359          */
    05360         pHead = &PtiCurrent()->rpdesk->PtiList;
    05361         for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink) {
    05362             pti = CONTAINING_RECORD(pEntry, THREADINFO, PtiLink);
    05363
    05364             if (pti->pqAttach == pqAttach) {
    05365                 /*
    05366                  * check each of the attachments to see if this thread is attached
    05367                  * to any other threads
    05368                  */
    05369                 for (pai = gpai; pai != NULL; pai = pai->paiNext) {
    05370                     /*
    05371                      * if they weren't attached already, attach them
    05372                      */
    05373                     if (pai->pti1 == pti || pai->pti2 == pti) {
    05374                         zzzAddAttachment((pai->pti1 == pti) ? pai->pti2 : pai->pti1,
    05375                                 pqAttach, &fChanged);
    05376                     }
    05377                 }
    05378
    05379                 /*
    05380                  * If this is a 16bit thread attach to all other threads in
    05381                  * it's MWOW
    05382                  */
    05383                 if (pti->TIF_flags & TIF_16BIT) {
    05384                     PTHREADINFO ptiAttach;
    05385                     PLIST_ENTRY pHeadAttach, pEntryAttach;
    05386
    05387                     pHeadAttach = &pti->rpdesk->PtiList;
    05388                     for (pEntryAttach = pHeadAttach->Flink;
    05389                             pEntryAttach != pHeadAttach;
    05390                             pEntryAttach = pEntryAttach->Flink) {
    05391                         ptiAttach = CONTAINING_RECORD(pEntryAttach, THREADINFO, PtiLink);
    05392
    05393                         if (ptiAttach->TIF_flags & TIF_16BIT &&
    05394                             ptiAttach->ppi == pti->ppi) {
    05395                             zzzAddAttachment(ptiAttach, pqAttach, &fChanged);
    05396                         }
    05397                     }
    05398                 }
    05399             }
    05400         }
    05401     } while (fChanged);
    05402     ENDATOMICCHECK();
    05403     zzzEndDeferWinEventNotify();
    05404 }
    05405
    05406
    05407 void zzzRecalcThreadAttachment()
    05408 {
    05409     PTHREADINFO pti;
    05410     PLIST_ENTRY pHead, pEntry;
    05411
    05412     /*
    05413      * Win Event notifications must be defered so we can traverse PtiList with impunity
    05414      */
    05415     UserAssert(IsWinEventNotifyDeferred());
    05416
    05417     /*
    05418      * For all threads, start an attach queue if a thread hasn't been
    05419      * attached yet.
    05420      */
    05421     pHead = &PtiCurrent()->rpdesk->PtiList;
    05422     for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink) {
    05423         pti = CONTAINING_RECORD(pEntry, THREADINFO, PtiLink);
    05424
    05425         /*
    05426          * Assert: We should not leave the critsect from xxxCreateThreadInfo
    05427          * with the new thread in the rpdesk->PtiList but not yet with a queue.
    05428          */
    05429         UserAssert(pti->pq != NULL);
    05430
    05431         if (pti->pqAttach == NULL) {
    05432
    05433             /*
    05434              * Allocate a new queue for this thread if more than
    05435              * one thread references it.
    05436              */
    05437             if (pti->pq->cThreads > 1) {
    05438                 pti->pqAttach = AllocQueue(NULL, NULL);
    05439
    05440                 if (pti->pqAttach == NULL) {
    05441                     RIPMSG0(RIP_WARNING, "zzzRecalcThreadAttachment: AllocQueue failed");
    05442                     break;
    05443                 }
    05444
    05445                 pti->pqAttach->cThreads++;
    05446             } else {
    05447                 pti->pqAttach = pti->pq;
    05448             }
    05449
    05450             /*
    05451              * Attach every thread that is directly or indirectly attached
    05452              * to this thread.
    05453              */
    05454             zzzRecalc2(pti->pqAttach);
    05455         }
    05456     }
    05457 }
    05458
    05459
    05460 /***************************************************************************\
    05461 * RedistributeInput
    05462 *
    05463 * This routine takes a input stream from the queue being left, and
    05464 * redistributes it. This effectively filters out the messages destined
    05465 * to the thread that left the queue.
    05466 *
    05467 * 12-10-92 ScottLu      Created.
    05468 \***************************************************************************/
    05469
    05470 void RedistributeInput(
    05471     PQMSG pqmsgS,
    05472     PQ    pqRedist)
    05473 {
    05474     PTHREADINFO ptiSave;
    05475     PTHREADINFO ptiT;
    05476     PQMSG *ppqmsgD;
    05477     PQMSG pqmsgT;
    05478     PMLIST pmlInput;
    05479
    05480     /*
    05481      * Since the thread attaching or unattaching may have left a queue
    05482      * shared by other threads, the messages we are going to requeue
    05483      * may have multiple destinations. On top of this, once we find
    05484      * a home queue for a message, it needs to be inserted in the
    05485      * list ordered by its time stamp (older messages go at the end).
    05486      */
    05487
    05488     /*
    05489      * Loop through a given dest's messages to find where to insert
    05490      * the source messages, based on message time stamp. Be sure
    05491      * to deal with empty message lists (meaning, check for NULL).
    05492      */
    05493
    05494     ptiT = NULL;
    05495     ppqmsgD = NULL;
    05496     pmlInput = NULL;
    05497
    05498     while (pqmsgS != NULL) {
    05499
    05500         /*
    05501          * Find out where this message should go.
    05502          */
    05503         ptiSave = ptiT;
    05504         ptiT = pqmsgS->pti;
    05505
    05506         /*
    05507          * Get rid of some event messages.
    05508          *
    05509          * QEVENT_UPDATEKEYSTATE: key state already up to date
    05510          */
    05511         if (pqmsgS->dwQEvent == QEVENT_UPDATEKEYSTATE) {
    05512             ptiT = NULL;
    05513         }
    05514
    05515         if (ptiT == NULL) {
    05516             /*
    05517              * Unlink it. pqmsgS should be the first in the list
    05518              */
    05519
    05520             UserAssert(!pqmsgS->pqmsgPrev);
    05521             if (pqmsgS->pqmsgNext != NULL) {
    05522                 pqmsgS->pqmsgNext->pqmsgPrev = NULL;
    05523             }
    05524
    05525             pqmsgT = pqmsgS;
    05526             pqmsgS = pqmsgS->pqmsgNext;
    05527
    05528             /*
    05529              * Clean it / free it.
    05530              */
    05531             CleanEventMessage(pqmsgT);
    05532             FreeQEntry(pqmsgT);
    05533
    05534             ptiT = ptiSave;
    05535             continue;
    05536         }
    05537
    05538         /*
    05539          * Point to the pointer that points to the first message
    05540          * that this message should go to, so that pointer is easy to
    05541          * update, no matter where it is.
    05542          */
    05543         if (ppqmsgD == NULL || ptiSave != ptiT) {
    05544
    05545             /*
    05546              * If the source is younger than the last message in the
    05547              * destination, go to the end.  Otherwise, start at the
    05548              * head of the desination list and find a place to insert
    05549              * the message.
    05550              */
    05551             if (ptiT->pq->mlInput.pqmsgWriteLast != NULL &&
    05552                     pqmsgS->msg.time >= ptiT->pq->mlInput.pqmsgWriteLast->msg.time) {
    05553                 ppqmsgD = &ptiT->pq->mlInput.pqmsgWriteLast->pqmsgNext;
    05554             } else {
    05555                 ppqmsgD = &ptiT->pq->mlInput.pqmsgRead;
    05556             }
    05557
    05558             pmlInput = &ptiT->pq->mlInput;
    05559         }
    05560
    05561         /*
    05562          * If we're not at the end of the destination AND the destination
    05563          * message time is younger than the source time, go on to
    05564          * the next message.
    05565          */
    05566         while (*ppqmsgD != NULL && ((*ppqmsgD)->msg.time <= pqmsgS->msg.time)) {
    05567             ppqmsgD = &((*ppqmsgD)->pqmsgNext);
    05568         }
    05569
    05570         /*
    05571          * Link in the source before the dest message. Update
    05572          * it's next and prev pointers. Update the dest prev
    05573          * pointer.
    05574          */
    05575         pqmsgT = pqmsgS;
    05576         pqmsgS = pqmsgS->pqmsgNext;
    05577         pqmsgT->pqmsgNext = *ppqmsgD;
    05578
    05579         if (*ppqmsgD != NULL) {
    05580             pqmsgT->pqmsgPrev = (*ppqmsgD)->pqmsgPrev;
    05581             (*ppqmsgD)->pqmsgPrev = pqmsgT;
    05582         } else {
    05583             pqmsgT->pqmsgPrev = pmlInput->pqmsgWriteLast;
    05584             pmlInput->pqmsgWriteLast = pqmsgT;
    05585         }
    05586         *ppqmsgD = pqmsgT;
    05587         ppqmsgD = &pqmsgT->pqmsgNext;
    05588         pmlInput->cMsgs++;
    05589
    05590         /*
    05591          * If the thread has an event message, make sure it's going to wake
    05592          * up to process it. The QS_EVENT flag might not be set if the thread
    05593          * previously found an event message for another thread and passed
    05594          * control over to him.
    05595          */
    05596         if (pqmsgT->dwQEvent != 0 && !(ptiT->pcti->fsWakeBits & QS_EVENT)) {
    05597             SetWakeBit(ptiT, QS_EVENTSET);
    05598         }
    05599
    05600         /*
    05601          * Preserve the 'idSysPeek' from the old queue.
    05602          * Carefull if the redistributed queue is the same as ptiT->pq.
    05603          */
    05604         if (pqmsgT == (PQMSG)(pqRedist->idSysPeek) && (pqRedist != ptiT->pq)) {
    05605
    05606             if (ptiT->pq->idSysPeek == 0) {
    05607                 CheckPtiSysPeek(6, ptiT->pq, pqRedist->idSysPeek);
    05608                 ptiT->pq->idSysPeek = pqRedist->idSysPeek;
    05609             }
    05610
    05611 #if DEBUGTAGS
    05612             else {
    05613                 TAGMSG2(DBGTAG_SysPeek,
    05614                         "idSysPeek %#p already set in pq %#p",
    05615                         ptiT->pq->idSysPeek, ptiT->pq);
    05616
    05617             }
    05618 #endif
    05619             /*
    05620              * Set the 'idSysPeek' of this queue to 0 since
    05621              * we moved the idSysPeek to other queue
    05622              */
    05623             CheckPtiSysPeek(7, pqRedist, 0);
    05624             pqRedist->idSysPeek = 0;
    05625
    05626             /*
    05627              * Preserve also 'ptiSysLock'.
    05628              * Set ptiSysLock to the ptiT->pq only if it points to
    05629              * that queue.
    05630              */
    05631             if (ptiT->pq->ptiSysLock == NULL &&
    05632                 pqRedist->ptiSysLock != NULL &&
    05633                 pqRedist->ptiSysLock->pq == ptiT->pq) {
    05634
    05635                 CheckSysLock(4, ptiT->pq, pqRedist->ptiSysLock);
    05636                 ptiT->pq->ptiSysLock = pqRedist->ptiSysLock;
    05637
    05638                 CheckSysLock(5, pqRedist, NULL);
    05639                 pqRedist->ptiSysLock = NULL;
    05640             }
    05641 #if DEBUGTAGS
    05642             else {
    05643                 TAGMSG2(DBGTAG_SysPeek,
    05644                         "ptiSysLock %#p already set in pq %#p\n",
    05645                         ptiT->pq->ptiSysLock, ptiT->pq);
    05646             }
    05647 #endif
    05648         }
    05649
    05650         /*
    05651          * Don't want the prev pointer on our message list to point
    05652          * to this message which is on a different list (doesn't
    05653          * really matter because we're about to link it anyway,
    05654          * but completeness shouldn't hurt).
    05655          */
    05656         if (pqmsgS != NULL) {
    05657             pqmsgS->pqmsgPrev = NULL;
    05658         }
    05659     }
    05660 }
    05661
    05662 /***************************************************************************\
    05663 * CancelInputState
    05664 *
    05665 * This routine takes a queue and "cancels" input state in it - i.e., if the
    05666 * app thinks it is active, make it think it is not active, etc.
    05667 *
    05668 * 12-10-92 ScottLu      Created.
    05669 \***************************************************************************/
    05670
    05671 VOID CancelInputState(
    05672     PTHREADINFO pti,
    05673     DWORD cmd)
    05674 {
    05675     PTHREADINFO ptiCurrent = PtiCurrent();
    05676     PWND pwndT;
    05677     TL tlpwndT;
    05678     TL tlpwndChild;
    05679     AAS aas;
    05680
    05681     /*
    05682      * In all cases, do not leave do any send messages or any callbacks!
    05683      * This is because this code is called from
    05684      * SetWindowsHook(WH_JOURNALPLAYBACK | WH_JOURNALRECORD). No app currently
    05685      * calling this routine expects to be called before this routine returns.
    05686      * (If you do callback before it returns, you'll break at least Access
    05687      * for Windows). - scottlu
    05688      */
    05689     switch (cmd) {
    05690     case CANCEL_ACTIVESTATE:
    05691         /*
    05692          * Active state.
    05693          */
    05694         pwndT = pti->pq->spwndActive;
    05695         ThreadLockWithPti(ptiCurrent, pwndT, &tlpwndT);
    05696
    05697         QueueNotifyMessage(pwndT, WM_NCACTIVATE, FALSE, 0);
    05698         QueueNotifyMessage(pwndT, WM_ACTIVATE,
    05699                 MAKELONG(WA_INACTIVE, TestWF(pwndT, WFMINIMIZED)),
    05700                 0);
    05701
    05702         if (pwndT == pti->pq->spwndActive)
    05703             Unlock(&pti->pq->spwndActive);
    05704
    05705         aas.ptiNotify = GETPTI(pwndT);
    05706         aas.tidActDeact = TIDq(GETPTI(pwndT));
    05707         aas.fActivating = FALSE;
    05708         aas.fQueueNotify = TRUE;
    05709
    05710         /*
    05711          * Even though this in an xxx call, it does NOT leave any critical
    05712          * sections (because fQueueNotify is TRUE).
    05713          */
    05714         ThreadLockWithPti(ptiCurrent, GETPTI(pwndT)->rpdesk->pDeskInfo->spwnd->spwndChild, &tlpwndChild);
    05715         xxxInternalEnumWindow(GETPTI(pwndT)->rpdesk->pDeskInfo->spwnd->spwndChild,
    05716                 (WNDENUMPROC_PWND)xxxActivateApp, (LPARAM)&aas, BWL_ENUMLIST);
    05717         ThreadUnlock(&tlpwndChild);
    05718
    05719         ThreadUnlock(&tlpwndT);
    05720         break;
    05721
    05722     case CANCEL_FOCUSSTATE:
    05723         /*
    05724          * Focus state.
    05725          */
    05726         pwndT = pti->pq->spwndFocus;
    05727         ThreadLockWithPti(ptiCurrent, pwndT, &tlpwndT);
    05728
    05729         QueueNotifyMessage(pwndT, WM_KILLFOCUS, 0, 0);
    05730 #ifdef FE_IME
    05731         if (IS_IME_ENABLED()) {
    05732             /*
    05733              * Even though this in an xxx call, it does NOT leave any
    05734              * critical section (because fQueueMsg is TRUE).
    05735              */
    05736             xxxFocusSetInputContext(pwndT, FALSE, TRUE);
    05737         }
    05738 #endif
    05739         if (pwndT == pti->pq->spwndFocus)
    05740             Unlock(&pti->pq->spwndFocus);
    05741
    05742         ThreadUnlock(&tlpwndT);
    05743         break;
    05744
    05745     case CANCEL_CAPTURESTATE:
    05746         /*
    05747          * Capture state.
    05748          */
    05749
    05750         /*
    05751          * We shouldn't be nuking the capture of a modal menu mode.
    05752          */
    05753         UserAssert((pti->pMenuState == NULL)
    05754                     || pti->pMenuState->fModelessMenu
    05755                     || pti->pMenuState->fInDoDragDrop);
    05756
    05757         pti->pq->QF_flags &= ~QF_CAPTURELOCKED;
    05758         pwndT = pti->pq->spwndCapture;
    05759         ThreadLockWithPti(ptiCurrent, pwndT, &tlpwndT);
    05760
    05761         QueueNotifyMessage(pwndT, WM_CANCELMODE, 0, 0);
    05762         if (pwndT == pti->pq->spwndCapture)
    05763             UnlockCaptureWindow(pti->pq);
    05764
    05765         ThreadUnlock(&tlpwndT);
    05766         break;
    05767     }
    05768
    05769     return;
    05770 }
    05771
    05772 /***************************************************************************\
    05773 * DBGValidateQueueStates
    05774 *
    05775 * Verifies that all queues point to stuff owned by a thread attached to
    05776 *  the queue.
    05777 *
    05778 * 07/29/97 GerardoB     Created
    05779 \***************************************************************************/
    05780 #if DBG
    05781 #define VALIDATEQSPWND(spwnd) \
    05782         if (pq-> ## spwnd != NULL) { \
    05783             ptiwnd = GETPTI(pq-> ## spwnd); \
    05784             fDestroyedOK = (TestWF(pq-> ## spwnd, WFDESTROYED) && (ptiwnd == gptiRit)); \
    05785             UserAssert((pti->rpdesk == ptiwnd->rpdesk) || fDestroyedOK); \
    05786             UserAssert((pti == ptiwnd) \
    05787                         || (fAttached && (pq == ptiwnd->pq)) \
    05788                         || fDestroyedOK); \
    05789         }
    05790
    05791
    05792 void DBGValidateQueueStates (PDESKTOP pdesk)
    05793 {
    05794     BOOL fAttached, fDestroyedOK;
    05795     PQ pq;
    05796     PLIST_ENTRY pHead, pEntry;
    05797     PTHREADINFO pti, ptiwnd;
    05798     DWORD dwInForeground = 0;
    05799
    05800     UserAssert((gpqForeground == NULL)
    05801                 || ((gpqForeground->ptiMouse->rpdesk == grpdeskRitInput)
    05802                     && (gpqForeground->cThreads != 0)));
    05803
    05804     if (pdesk == NULL) {
    05805         RIPMSG0(RIP_WARNING, "DBGValidateQueueStates: Null pdesk parameter");
    05806         return;
    05807     }
    05808     pHead = &pdesk->PtiList;
    05809     for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink) {
    05810
    05811         pti = CONTAINING_RECORD(pEntry, THREADINFO, PtiLink);
    05812         pq = pti->pq;
    05813         if (pq == NULL) {
    05814             RIPMSG2(RIP_WARNING, "DBGValidateQueueStates: Null pq. pti:%#p. pdesk:%#p", pti, pdesk);
    05815             continue;
    05816         }
    05817         /*
    05818          * The queue should have a non-null cThreads excepting when it is
    05819          * QF_INDESTROY
    05820          */
    05821         if (!(pq->QF_flags & QF_INDESTROY)) {
    05822             UserAssert(pq->cThreads != 0);
    05823         }
    05824         fAttached = (pq->cThreads > 1);
    05825         if (pti->pq == gpqForeground) {
    05826             dwInForeground++;
    05827         }
    05828         /*
    05829          * pti's
    05830          */
    05831         UserAssert((pti == pq->ptiMouse)
    05832                     || (fAttached && (pq == pq->ptiMouse->pq)));
    05833         UserAssert(pti->rpdesk == pq->ptiMouse->rpdesk);
    05834         UserAssert((pti == pq->ptiKeyboard)
    05835                     || (fAttached && (pq == pq->ptiKeyboard->pq)));
    05836         UserAssert(pti->rpdesk == pq->ptiKeyboard->rpdesk);
    05837         if (pq->ptiSysLock != NULL) {
    05838             UserAssert((pti == pq->ptiSysLock)
    05839                         || (fAttached && (pq == pq->ptiSysLock->pq)));
    05840         }
    05841         /*
    05842          * pwnd's.
    05843          */
    05844         VALIDATEQSPWND(spwndActive);
    05845         VALIDATEQSPWND(spwndFocus);
    05846         VALIDATEQSPWND(spwndCapture);
    05847         VALIDATEQSPWND(spwndActivePrev);
    05848     }
    05849
    05850     UserAssert((gpqForeground == NULL)
    05851                 || (dwInForeground == 0)
    05852                 || (gpqForeground->cThreads == dwInForeground));
    05853 }
    05854
    05855 /***************************************************************************\
    05856 * DBGValidateQueue
    05857 *
    05858 * Verifies that queue is readable and fields are valid.
    05859 *
    05860 * 02-Sep-1999 JerrySh   Created.
    05861 \***************************************************************************/
    05862 void DBGValidateQueue(PQ pq)
    05863 {
    05864     if (pq != NULL) {
    05865         Q q = *pq;
    05866         UserAssert(q.spwndActive == HtoP(PtoH(q.spwndActive)));
    05867         UserAssert(q.spwndFocus == HtoP(PtoH(q.spwndFocus)));
    05868         UserAssert(q.spwndCapture == HtoP(PtoH(q.spwndCapture)));
    05869         UserAssert(q.spwndActivePrev == HtoP(PtoH(q.spwndActivePrev)));
    05870         UserAssert(q.spcurCurrent == HtoP(PtoH(q.spcurCurrent)));
    05871     }
    05872 }
    05873 #endif /* DBG */
    05874 /***************************************************************************\
    05875 * zzzAttachThreadInput (API)
    05876 * zzzReattachThreads
    05877 * zzzAttachToQueue
    05878 * CheckTransferState
    05879 *
    05880 * Attaches a given thread to another input queue, either by attaching to
    05881 * a queue (referenced by another thread id), or detaching from one.
    05882 *
    05883 * 12-09-92  ScottLu     Created.
    05884 \***************************************************************************/
    05885
    05886 #define CTS_DONOTHING 0
    05887 #define CTS_CANCELOLD 1
    05888 #define CTS_TRANSFER  2
    05889
    05890 DWORD CheckTransferState(
    05891     PTHREADINFO pti,
    05892     PQ pqAttach,
    05893     LONG offset,
    05894     BOOL fJoiningForeground)
    05895 {
    05896     PWND pwndOld, pwndNew, pwndForegroundState;
    05897
    05898     /*
    05899      * return 0: do nothing.
    05900      * return 1: cancel the old state.
    05901      * return 2: transfer the old state to the new state
    05902      */
    05903     pwndOld = *(PWND *)(((BYTE *)pti->pq) + offset);
    05904     pwndNew = *(PWND *)(((BYTE *)pqAttach) + offset);
    05905
    05906     /*
    05907      * Make sure the old state even exists, and that the old state is
    05908      * owned by this thread. If not, nothing happens.
    05909      */
    05910     if (pwndOld == NULL || GETPTI(pwndOld) != pti)
    05911         return CTS_DONOTHING;
    05912
    05913     /*
    05914      * If the new state already exists, cancel the old state.
    05915      */
    05916     if (pwndNew != NULL)
    05917         return CTS_CANCELOLD;
    05918
    05919     /*
    05920      * Transfer this old state if this thread is not joining the foreground.
    05921      */
    05922     if (gpqForeground == NULL || !fJoiningForeground)
    05923         return CTS_TRANSFER;
    05924
    05925     /*
    05926      * We're joining the foreground - only transfer the old state if we own
    05927      * that foreground state or if there is no foreground state.
    05928      */
    05929     pwndForegroundState = *(PWND *)(((BYTE *)gpqForeground) + offset);
    05930     if (pwndForegroundState == NULL || pwndOld == pwndForegroundState)
    05931         return CTS_TRANSFER;
    05932
    05933     /*
    05934      * We're joining the foreground but we didn't set that foreground state.
    05935      * Don't allow the transfer of that state.
    05936      */
    05937     return CTS_CANCELOLD;
    05938 }
    05939
    05940 void zzzAttachToQueue(
    05941     PTHREADINFO pti,
    05942     PQ   pqAttach,
    05943     PQ   pqJournal,
    05944     BOOL fJoiningForeground)
    05945 {
    05946     PQMSG pqmsgT;
    05947     PQ pqDestroy;
    05948
    05949     /*
    05950      * Check active state.
    05951      */
    05952     switch (CheckTransferState(pti, pqAttach,
    05953             FIELD_OFFSET(Q, spwndActive), fJoiningForeground)) {
    05954     case CTS_CANCELOLD:
    05955         CancelInputState(pti, CANCEL_ACTIVESTATE);
    05956         break;
    05957
    05958     case CTS_TRANSFER:
    05959         Lock(&pqAttach->spwndActive, pti->pq->spwndActive);
    05960         Unlock(&pti->pq->spwndActive);
    05961
    05962         /*
    05963          * The caret usually follows the focus window, which follows
    05964          * the active window...
    05965          */
    05966         if (pti->pq->caret.spwnd != NULL) {
    05967
    05968             if (GETPTI(pti->pq->caret.spwnd) == pti) {
    05969                 /*
    05970                  * Just copy the entire caret structure... that way we
    05971                  * don't need to deal with locking/unlocking the spwnd.
    05972                  */
    05973                 if (pqAttach->caret.spwnd == NULL) {
    05974                     pqAttach->caret = pti->pq->caret;
    05975                     pti->pq->caret.spwnd = NULL;
    05976                 }
    05977             }
    05978         }
    05979         break;
    05980     }
    05981
    05982     /*
    05983      * Check focus state.
    05984      */
    05985     switch (CheckTransferState(pti, pqAttach,
    05986             FIELD_OFFSET(Q, spwndFocus), fJoiningForeground)) {
    05987     case CTS_CANCELOLD:
    05988         CancelInputState(pti, CANCEL_FOCUSSTATE);
    05989         break;
    05990
    05991     case CTS_TRANSFER:
    05992         Lock(&pqAttach->spwndFocus, pti->pq->spwndFocus);
    05993         Unlock(&pti->pq->spwndFocus);
    05994         break;
    05995     }
    05996
    05997     /*
    05998      * Check capture state.
    05999      */
    06000     switch (CheckTransferState(pti, pqAttach,
    06001             FIELD_OFFSET(Q, spwndCapture), fJoiningForeground)) {
    06002     case CTS_CANCELOLD:
    06003         CancelInputState(pti, CANCEL_CAPTURESTATE);
    06004         break;
    06005
    06006     case CTS_TRANSFER:
    06007         LockCaptureWindow(pqAttach, pti->pq->spwndCapture);
    06008         UnlockCaptureWindow(pti->pq);
    06009         pqAttach->codeCapture = pti->pq->codeCapture;
    06010         pqAttach->QF_flags ^= ((pqAttach->QF_flags ^ pti->pq->QF_flags) & QF_CAPTURELOCKED);
    06011         break;
    06012
    06013 #if DBG
    06014     case CTS_DONOTHING:
    06015         /*
    06016          * We should always transfer the capture state of a thread
    06017          *  in modal menu mode
    06018          */
    06019         UserAssert((pti->pMenuState == NULL)
    06020                     || ExitMenuLoop(pti->pMenuState, pti->pMenuState->pGlobalPopupMenu)
    06021                     || pti->pMenuState->fModelessMenu
    06022                     || pti->pMenuState->fInDoDragDrop);
    06023
    06024         break;
    06025 #endif
    06026     }
    06027
    06028     /*
    06029      * Check spwndActivePrev state.  This check has some considerations.
    06030      * If the CTS_TRANSFER is returned, it usually means there was no
    06031      * prev-active in the attach-queue, and it will use the first
    06032      * window it encounters.  Since we walk the thread-list, a out-of-zorder
    06033      * window could be chosen.  So, to counter this, we'll check the
    06034      * attach-queue-next-prev against the thread-previous window to see
    06035      * if it is truly the next-zorder window.
    06036      */
    06037     switch (CheckTransferState(pti, pqAttach,
    06038             FIELD_OFFSET(Q, spwndActivePrev), fJoiningForeground)) {
    06039     case CTS_TRANSFER:
    06040         Lock(&pqAttach->spwndActivePrev, pti->pq->spwndActivePrev);
    06041         Unlock(&pti->pq->spwndActivePrev);
    06042         break;
    06043
    06044     case CTS_CANCELOLD:
    06045
    06046         /*
    06047          * Check to see if the previous window is what we would expect it
    06048          * to be.
    06049          */
    06050         if (pqAttach->spwndActive &&
    06051             (pqAttach->spwndActivePrev && pti->pq->spwndActivePrev) &&
    06052             (pqAttach->spwndActive->spwndNext == pti->pq->spwndActivePrev)) {
    06053
    06054             Lock(&pqAttach->spwndActivePrev, pti->pq->spwndActivePrev);
    06055             Unlock(&pti->pq->spwndActivePrev);
    06056         }
    06057         break;
    06058     }
    06059
    06060     if (pti == pti->pq->ptiSysLock) {
    06061         /*
    06062          * Preserve any of the flags we might have already been set on pqAttach.
    06063          * Note that these flags might have been set on a previous call
    06064         *   to this function that received the same pqAttach
    06065          */
    06066         pqAttach->QF_flags ^= ((pqAttach->QF_flags ^ pti->pq->QF_flags)
    06067                                 & ~(QF_CAPTURELOCKED));
    06068
    06069         /*
    06070          *  Fix for 29967 "Start menu disappears when clicked  and Office
    06071          *  taskbar has focus!". Win95 uses a global counter instead of this
    06072          *  flag. In NT when we click on Office taskbar and then on the Start
    06073          *  Menu, MSoffice calls zzzAttachThreadInput() which changes the Start
    06074          *  Menu's queue and the new queue has the QF_ACTIVATIONCHANGE flag on.
    06075          *  Inside the xxxMNLoop we test if this flag is on and if it is we
    06076          *  exit the menu.
    06077          */
    06078         if (!IsInsideMenuLoop(pti)) {
    06079             pqAttach->QF_flags &= ~QF_ACTIVATIONCHANGE;
    06080         }
    06081
    06082         /*
    06083          * Unlock the queue since pti is moving to anotther queue.
    06084          */
    06085         /* CheckSysLock(6, pq, NULL); what number? */
    06086         pti->pq->ptiSysLock = NULL;
    06087     }
    06088
    06089     if (gspwndCursor != NULL && pti == GETPTI(gspwndCursor)) {
    06090         LockQCursor(pqAttach, pti->pq->spcurCurrent);
    06091     }
    06092
    06093     /*
    06094      * Each thread has its own cursor level, which is a count of the number
    06095      * of times that app has called show/hide cursor. This gets added into
    06096      * the queue's count for a completely accurate count every time this
    06097      * queue recalculation is done.
    06098      */
    06099     /*
    06100      * LATER
    06101      * We need to adjust the iCursorLevel of the old queue to reflect
    06102      * the fact that a thread is departing.
    06103      * FritzS
    06104      */
    06105     pqAttach->iCursorLevel += pti->iCursorLevel;
    06106
    06107     /*
    06108      * Pump up the new queue with the right input variables.
    06109      */
    06110     pqAttach->ptiMouse    = pti;
    06111     pqAttach->ptiKeyboard = pti;
    06112
    06113     pqDestroy = pti->pq;
    06114
    06115     /*
    06116      * Don't increment the thread count here because we already incremented
    06117      * it when we put it in pti->pqAttach. Since we're moving it from pqAttach
    06118      * to pq, we don't mess with the reference count.
    06119      */
    06120     pti->pq = pqAttach;
    06121
    06122     /*
    06123      * If the thread is using the journal queue, leave the message list
    06124      * alone.  Otherwise, redistribute the messages.
    06125      */
    06126     if (pqDestroy != pqJournal) {
    06127
    06128         /*
    06129          * Remember the current message list so it can get redistributed taking
    06130          * into account ptiAttach's new queue.
    06131          */
    06132         pqmsgT = pqDestroy->mlInput.pqmsgRead;
    06133         pqDestroy->mlInput.pqmsgRead      = NULL;
    06134         pqDestroy->mlInput.pqmsgWriteLast = NULL;
    06135         pqDestroy->mlInput.cMsgs          = 0;
    06136
    06137         /*
    06138          * Now redistribute the input messages from the old queue they go into the
    06139          * right queues.
    06140          *
    06141          * Preserve the 'idSysPeek' when redistributing the queue
    06142          */
    06143         RedistributeInput(pqmsgT, pqDestroy);
    06144
    06145         /*
    06146          * Officially attach the new queue to this thread. Note that zzzDestroyQueue()
    06147          * doesn't actually destroy anything until the thread reference count goes
    06148          * to 0.
    06149          */
    06150         zzzDestroyQueue(pqDestroy, pti);
    06151
    06152     } else {
    06153         UserAssert(pqDestroy->cThreads);
    06154         pqDestroy->cThreads--;
    06155     }
    06156 }
    06157
    06158 BOOL zzzReattachThreads(
    06159     BOOL fJournalAttach)
    06160 {
    06161     PTHREADINFO ptiCurrent = PtiCurrent();
    06162     PTHREADINFO pti;
    06163     PQ          pqForegroundPrevNew;
    06164     PQ          pqForegroundNew;
    06165     PQ          pqAttach;
    06166     PQ          pqJournal;
    06167     PLIST_ENTRY pHead, pEntry;
    06168     BOOL        bHadAnActiveForegroundWindow;
    06169
    06170     /*
    06171      * In all cases, do not leave do any send messages or any callbacks!
    06172      * This is because this code is called from
    06173      * SetWindowsHook(WH_JOURNALPLAYBACK | WH_JOURNALRECORD). No app currently
    06174      * calling this routine expects to be called before this routine returns.
    06175      * (If you do callback before it returns, you'll break at least Access
    06176      * for Windows). - scottlu
    06177      */
    06178
    06179 #if DBG
    06180     DBGValidateQueueStates(ptiCurrent->rpdesk);
    06181 #endif
    06182
    06183     /*
    06184      * Defer Win Event notifications so we can traverse PtiList with impunity
    06185      * Also, we don't want to callback while we're half way attached
    06186      */
    06187     DeferWinEventNotify();
    06188     BEGINATOMICCHECK();
    06189
    06190     /*
    06191      * Don't recalc attach info if this is a journal attach, because
    06192      * the journal attach code has already done this for us.
    06193      */
    06194     if (!fJournalAttach) {
    06195
    06196         /*
    06197          * Now recalculate all the different queue groups, based on the
    06198          * attach requests. This fills in the pqAttach of each thread info
    06199          * with the new queue this thread belongs to. Always takes into
    06200          * account all attachment requests.
    06201          */
    06202         zzzRecalcThreadAttachment();
    06203
    06204         /*
    06205          * Make a guess about which queue is the journal queue.
    06206          */
    06207         pqJournal = gpqForeground;
    06208         if (pqJournal == NULL)
    06209             pqJournal = ptiCurrent->pq;
    06210
    06211         /*
    06212          * If the queue is only used by one thread, perform normal processing.
    06213          */
    06214         if (pqJournal->cThreads == 1) {
    06215             pqJournal = NULL;
    06216         } else {
    06217
    06218             /*
    06219              * Lock the queue to ensure that it stays valid
    06220              * until we have redistributed the input.
    06221              */
    06222             (pqJournal->cLockCount)++;
    06223         }
    06224     } else {
    06225         pqJournal = NULL;
    06226     }
    06227
    06228     /*
    06229      * What will be the new foreground queue?
    06230      */
    06231     pqForegroundNew = NULL;
    06232
    06233     /*
    06234      * Remember if there is a foreground window so we don't force one
    06235      * at the end if there wasn't one before the attach
    06236      */
    06237     if (gpqForeground != NULL && gpqForeground->spwndActive != NULL) {
    06238         bHadAnActiveForegroundWindow = TRUE;
    06239         pqForegroundNew = GETPTI(gpqForeground->spwndActive)->pqAttach;
    06240     } else {
    06241         bHadAnActiveForegroundWindow = FALSE;
    06242     }
    06243
    06244     pqForegroundPrevNew = NULL;
    06245     if (gpqForegroundPrev != NULL && gpqForegroundPrev->spwndActivePrev != NULL) {
    06246         pqForegroundPrevNew = GETPTI(gpqForegroundPrev->spwndActivePrev)->pqAttach;
    06247     }
    06248
    06249
    06250     pHead = &ptiCurrent->rpdesk->PtiList;
    06251     for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink) {
    06252         pti = CONTAINING_RECORD(pEntry, THREADINFO, PtiLink);
    06253
    06254         if (pti->pqAttach == pti->pq) {
    06255             pti->pqAttach = NULL;
    06256         } else if(pti->pqAttach != NULL) {
    06257             /*
    06258              * It is crucial that we NULL out pqAttach for this queue once
    06259              * we have it in a local variable because the NULL-ness of this
    06260              * field is checked in attach operations.
    06261              */
    06262             pqAttach = pti->pqAttach;
    06263             pti->pqAttach = NULL;
    06264
    06265             zzzAttachToQueue(pti, pqAttach, pqJournal, pqForegroundNew == pqAttach);
    06266         }
    06267
    06268     }
    06269
    06270     /*
    06271      * If we are doing a journal detach, redistribute the input messages
    06272      * from the old queue.
    06273      */
    06274     if (pqJournal != NULL) {
    06275         PQMSG pqmsgRedist;
    06276
    06277         UserAssert(pqJournal->cLockCount);
    06278         (pqJournal->cLockCount)--;
    06279         pqmsgRedist = pqJournal->mlInput.pqmsgRead;
    06280
    06281         pqJournal->mlInput.pqmsgRead      = NULL;
    06282         pqJournal->mlInput.pqmsgWriteLast = NULL;
    06283         pqJournal->mlInput.cMsgs          = 0;
    06284         RedistributeInput(pqmsgRedist, pqJournal);
    06285
    06286         /*
    06287          * Only destroy the queue if it is no longer is use.
    06288          */
    06289         if (pqJournal->cThreads == 0) {
    06290             pqJournal->cThreads = 1;    // prevent underflow
    06291             zzzDestroyQueue(pqJournal, pti); // DeferWinEventNotify() ?? IANJA ??
    06292         } else {
    06293             /*
    06294              * Make sure that this queue doesn't point to a pti
    06295              *  no longer attached to it.
    06296              * Hopefully we'll go to zzzDestroyQueue only once
    06297              * Increment cThreads so the queue won't be destroyed
    06298              *  but we'll simply reassign the pti fields.
    06299              */
    06300             if ((pqJournal->ptiMouse != NULL)
    06301                     && (pqJournal != pqJournal->ptiMouse->pq)) {
    06302                 pqJournal->cThreads++;
    06303                 zzzDestroyQueue(pqJournal, pqJournal->ptiMouse);
    06304             }
    06305             if ((pqJournal->ptiKeyboard != NULL)
    06306                     && (pqJournal != pqJournal->ptiKeyboard->pq)) {
    06307                 pqJournal->cThreads++;
    06308                 zzzDestroyQueue(pqJournal, pqJournal->ptiKeyboard);
    06309             }
    06310         }
    06311     }
    06312
    06313 #if DBG
    06314     DBGValidateQueueStates(ptiCurrent->rpdesk);
    06315 #endif
    06316
    06317     /*
    06318      * If the current thread is not on the active desktop, do not
    06319      * change the global foreground state.
    06320      */
    06321     if (PtiCurrent()->rpdesk != grpdeskRitInput) {
    06322         EXITATOMICCHECK();
    06323         zzzEndDeferWinEventNotify();
    06324         return TRUE;
    06325     }
    06326
    06327     /*
    06328      * We're done attaching. gptiForeground hasn't changed... but
    06329      * gpqForeground has!  Try not to leave NULL as the foreground.
    06330      */
    06331 #if DBG
    06332     DBGValidateQueue(pqForegroundNew);
    06333 #endif
    06334     gpqForeground = pqForegroundNew;
    06335     // So we can Alt-Esc xxxNextWindow without an AV
    06336     // If we have a non-NULL gpqForeground, its kbd input thread better have an rpdesk!
    06337     UserAssert(!gpqForeground || (gpqForeground->ptiKeyboard && gpqForeground->ptiKeyboard->rpdesk));
    06338     gpqForegroundPrev = pqForegroundPrevNew;
    06339
    06340     ENDATOMICCHECK();
    06341     zzzEndDeferWinEventNotify();
    06342
    06343     if ((gpqForeground == NULL) && (bHadAnActiveForegroundWindow))  {
    06344         PWND pwndNewForeground;
    06345         PTHREADINFO pti = PtiCurrent();
    06346
    06347         pwndNewForeground = _GetNextQueueWindow(pti->rpdesk->pDeskInfo->spwnd->spwndChild, 0, FALSE);
    06348
    06349         /*
    06350          * Don't use xxxSetForegroundWindow2 because we must not leave
    06351          * the critical section.  There is no currently active foreground
    06352          * so all that is needed is to post an activate event to the
    06353          * new foreground queue.
    06354          */
    06355         if (pwndNewForeground != NULL) {
    06356             PostEventMessage(GETPTI(pwndNewForeground),
    06357                     GETPTI(pwndNewForeground)->pq, QEVENT_ACTIVATE, NULL, 0,
    06358                     0, (LPARAM)HWq(pwndNewForeground));
    06359         }
    06360     }
    06361     zzzSetFMouseMoved();
    06362     UserAssert((gpqForeground == NULL) || (gpqForeground->ptiMouse->rpdesk == grpdeskRitInput));
    06363
    06364     return TRUE;
    06365 }
    06366
    06367 BOOL zzzAttachThreadInput(
    06368     PTHREADINFO ptiAttach,
    06369     PTHREADINFO ptiAttachTo,
    06370     BOOL fAttach)
    06371 {
    06372     CheckCritIn();
    06373
    06374     /*
    06375      * Attaching to yourself doesn't make any sense.
    06376      */
    06377     if (ptiAttach == ptiAttachTo)
    06378         return FALSE;
    06379
    06380 #if defined(FE_IME)
    06381     /*
    06382      * For console IME issue
    06383      *
    06384      * Console IME do attach to console input thread message queue.
    06385      * So needs share message queue for synchronize a key state.
    06386      */
    06387     if (IS_IME_ENABLED()) {
    06388         PTHREADINFO ptiConsoleIME;
    06389         PTHREADINFO ptiConsoleInput;
    06390
    06391         if ( ((ptiConsoleIME   = PtiFromThreadId(ptiAttach->rpdesk->dwConsoleIMEThreadId)) != NULL) &&
    06392              ((ptiConsoleInput = PtiFromThreadId(ptiAttach->rpdesk->dwConsoleThreadId)) != NULL)    &&
    06393              (ptiAttach == ptiConsoleIME)     &&
    06394              (ptiAttachTo == ptiConsoleInput) &&
    06395              (ptiConsoleIME->TIF_flags & TIF_DONTATTACHQUEUE)
    06396            )
    06397         {
    06398             goto SkipCheck;
    06399         }
    06400     }
    06401 #endif
    06402     /*
    06403      * Will this thread allow attaching? Shell threads and system threads
    06404      * won't allow attaching.
    06405      */
    06406     if (ptiAttachTo->TIF_flags & TIF_DONTATTACHQUEUE)
    06407         return FALSE;
    06408     if (ptiAttach->TIF_flags & TIF_DONTATTACHQUEUE)
    06409         return FALSE;
    06410
    06411 #if defined(FE_IME)
    06412 SkipCheck:
    06413 #endif
    06414     /*
    06415      * Don't allow attaching across desktops, either.
    06416      */
    06417     if (ptiAttachTo->rpdesk != ptiAttach->rpdesk)
    06418         return FALSE;
    06419
    06420     /*
    06421      * If attaching, make a new attachinfo structure for this thread.
    06422      * If not attaching, remove an existing attach reference.
    06423      */
    06424     if (fAttach) {
    06425         PATTACHINFO pai;
    06426
    06427          /*
    06428          * Alloc a new attachinfo struct, fill it in, link it in.
    06429          */
    06430         if ((pai = (PATTACHINFO)UserAllocPool(sizeof(ATTACHINFO), TAG_ATTACHINFO)) == NULL)
    06431             return FALSE;
    06432
    06433         pai->pti1 = ptiAttach;
    06434         pai->pti2 = ptiAttachTo;;
    06435         pai->paiNext = gpai;
    06436         gpai = pai;
    06437     } else {
    06438         PATTACHINFO *ppai;
    06439         BOOL fFound = FALSE;
    06440
    06441         /*
    06442          * Search for this attachinfo struct. If we can't find it, fail.
    06443          * If we do find it, unlink it and free it.
    06444          */
    06445         for (ppai = &gpai; (*ppai) != NULL; ppai = &(*ppai)->paiNext) {
    06446             if (((*ppai)->pti2 == ptiAttachTo) && ((*ppai)->pti1 == ptiAttach)) {
    06447                 PATTACHINFO paiKill = *ppai;
    06448                 fFound = TRUE;
    06449                 *ppai = (*ppai)->paiNext;
    06450                 UserFreePool((HLOCAL)paiKill);
    06451                 break;
    06452             }
    06453         }
    06454
    06455         /*
    06456          * If we couldn't find this reference, then fail.
    06457          */
    06458         if (!fFound) {
    06459             return FALSE;
    06460         }
    06461     }
    06462
    06463     /*
    06464      * Now do the actual reattachment work for all threads - unless we're
    06465      * journalling. If we did by mistake do attachment while journalling
    06466      * was occuring, journalling would be hosed because journalling requires
    06467      * all threads to be attached - but it is also treated as a special
    06468      * case so it doesn't affect the ATTACHINFO structures. Therefore
    06469      * recalcing attach info based on ATTACHINFO structures would break
    06470      * the attachment required for journalling.
    06471      */
    06472     if (!FJOURNALRECORD() && !FJOURNALPLAYBACK())
    06473         return zzzReattachThreads(FALSE);
    06474
    06475     return TRUE;
    06476 }
    06477
    06478 /***************************************************************************\
    06479 * _SetMessageExtraInfo (API)
    06480 *
    06481 * History:
    06482 * 1-May-1995 FritzS
    06483 \***************************************************************************/
    06484
    06485 LONG_PTR _SetMessageExtraInfo(LONG_PTR lData)
    06486 {
    06487     LONG_PTR lRet;
    06488     PTHREADINFO pti = PtiCurrent();
    06489
    06490     lRet = pti->pq->ExtraInfo;
    06491     pti->pq->ExtraInfo = lData;
    06492     return lRet;
    06493 }