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 }

Generated on Sat May 15 19:40:24 2004 for test by doxygen 1.3.7