Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

7083 lines
237 KiB

  1. /****************************** Module Header ******************************\
  2. * Module Name: input.c
  3. *
  4. * Copyright (c) 1985 - 1999, Microsoft Corporation
  5. *
  6. * This module contains the core functions of the input sub-system
  7. *
  8. * History:
  9. * 10-18-90 DavidPe Created.
  10. * 02-14-91 mikeke Added Revalidation code
  11. \***************************************************************************/
  12. #include "precomp.h"
  13. #pragma hdrstop
  14. //#define MARKPATH
  15. #ifdef MARKPATH
  16. BOOL gfMarkPath;
  17. #endif
  18. #define IsOnInputDesktop(pti) (pti->rpdesk == grpdeskRitInput)
  19. #if DBG
  20. int gnSysPeekSearch;
  21. VOID CheckPtiSysPeek(
  22. int where, PQ pq,
  23. ULONG_PTR newIdSysPeek)
  24. {
  25. PTHREADINFO ptiCurrent = PtiCurrent();
  26. DWORD dwRip;
  27. dwRip = (newIdSysPeek > 1) ? RIP_THERESMORE : 0;
  28. TAGMSG5(DBGTAG_SysPeek | dwRip,
  29. "%d pti %#p sets id %#p to pq %#p ; old id %#p",
  30. where, ptiCurrent, newIdSysPeek, pq, pq->idSysPeek);
  31. if (newIdSysPeek > 1) {
  32. PQMSG pqmsg = (PQMSG)newIdSysPeek;
  33. TAGMSG5(DBGTAG_SysPeek | RIP_NONAME,
  34. "-> msg %lx hwnd %#p w %#p l %#p pti %#p",
  35. pqmsg->msg.message, pqmsg->msg.hwnd, pqmsg->msg.wParam,
  36. pqmsg->msg.lParam, pqmsg->pti);
  37. }
  38. }
  39. VOID CheckSysLock(
  40. int where,
  41. PQ pq,
  42. PTHREADINFO ptiSysLock)
  43. {
  44. PTHREADINFO ptiCurrent = PtiCurrent();
  45. TAGMSG5(DBGTAG_SysPeek,
  46. "%d pti 0x%p sets ptiSL 0x%p to pq 0x%p ; old ptiSL 0x%p",
  47. where,
  48. ptiCurrent,
  49. ptiSysLock,
  50. pq,
  51. pq->ptiSysLock);
  52. }
  53. #endif
  54. #if DBG
  55. BOOL gfLogPlayback;
  56. LPCSTR aszMouse[] = {
  57. "WM_MOUSEMOVE",
  58. "WM_LBUTTONDOWN",
  59. "WM_LBUTTONUP",
  60. "WM_LBUTTONDBLCLK",
  61. "WM_RBUTTONDOWN",
  62. "WM_RBUTTONUP",
  63. "WM_RBUTTONDBLCLK",
  64. "WM_MBUTTONDOWN",
  65. "WM_MBUTTONUP",
  66. "WM_MBUTTONDBLCLK"
  67. "WM_MOUSEWHEEL",
  68. "WM_XBUTTONDOWN",
  69. "WM_XBUTTONUP",
  70. "WM_XBUTTONDBLCLK",
  71. };
  72. LPCSTR aszKey[] = {
  73. "WM_KEYDOWN",
  74. "WM_KEYUP",
  75. "WM_CHAR",
  76. "WM_DEADCHAR",
  77. "WM_SYSKEYDOWN",
  78. "WM_SYSKEYUP",
  79. "WM_SYSCHAR",
  80. "WM_SYSDEADCHAR",
  81. "WM_CONVERTREQUESTEX",
  82. "WM_YOMICHAR",
  83. "WM_UNICHAR"
  84. };
  85. #endif // DBG
  86. #define CANCEL_ACTIVESTATE 0
  87. #define CANCEL_FOCUSSTATE 1
  88. #define CANCEL_CAPTURESTATE 2
  89. #define KEYSTATESIZE (CBKEYSTATE + CBKEYSTATERECENTDOWN)
  90. /*
  91. * xxxGetNextSysMsg return values
  92. */
  93. #define PQMSG_PLAYBACK ((PQMSG)1)
  94. BOOL xxxScanSysQueue(PTHREADINFO ptiCurrent, LPMSG lpMsg, PWND pwndFilter,
  95. UINT msgMinFilter, UINT msgMaxFilter, DWORD flags, DWORD fsReason);
  96. BOOL xxxReadPostMessage(PTHREADINFO pti, LPMSG lpMsg, PWND pwndFilter,
  97. UINT msgMin, UINT msgMax, BOOL fRemoveMsg);
  98. void CleanEventMessage(PQMSG pqmsg);
  99. #ifdef MESSAGE_PUMP_HOOK
  100. /***************************************************************************\
  101. * xxxWaitMessageEx (API)
  102. *
  103. * This API will block until an input message is received on
  104. * the current queue.
  105. *
  106. * History:
  107. * 10-25-1990 DavidPe Created.
  108. * 06-12-2000 JStall Changed to "Ex"
  109. \***************************************************************************/
  110. BOOL xxxWaitMessageEx(
  111. UINT fsWakeMask,
  112. DWORD Timeout)
  113. {
  114. PCLIENTTHREADINFO pcti = gptiCurrent->pcti;
  115. if (IsInsideMPH()) {
  116. /*
  117. * This thread is has MPH's installed, so we need to callback into User
  118. * mode to allow the application to provide an implementation
  119. */
  120. return ClientWaitMessageExMPH(fsWakeMask, Timeout);
  121. } else {
  122. /*
  123. * This thread does not have any MPH's installed, so we can just
  124. * directly process.
  125. */
  126. return xxxRealWaitMessageEx(fsWakeMask, Timeout);
  127. }
  128. }
  129. BOOL xxxRealWaitMessageEx(
  130. UINT fsWakeMask,
  131. DWORD Timeout)
  132. {
  133. return xxxSleepThread(fsWakeMask, Timeout, TRUE);
  134. }
  135. #else // MESSAGE_PUMP_HOOK
  136. /***************************************************************************\
  137. * xxxWaitMessage (API)
  138. *
  139. * This API will block until an input message is received on
  140. * the current queue.
  141. *
  142. * History:
  143. * 10-25-90 DavidPe Created.
  144. \***************************************************************************/
  145. BOOL xxxWaitMessage(
  146. VOID)
  147. {
  148. return xxxSleepThread(QS_ALLINPUT | QS_EVENT, 0, TRUE);
  149. }
  150. #endif // MESSAGE_PUMP_HOOK
  151. /***************************************************************************\
  152. * CheckProcessBackground/Foreground
  153. *
  154. * This checks to see if the process is at the right priority. If CSPINS is
  155. * greater than CSPINBACKGROUND and the process isn't at the background
  156. * priority, put it there. If it is less than this and should be foreground
  157. * and isn't put it there.
  158. *
  159. * Need to put foreground spinning apps in the background (make it the same
  160. * priority as all other background apps) so that bad apps can still communicate
  161. * with apps in the background via dde, for example. There are other cases
  162. * where spinning foreground apps affect the server, the printer spooler, and
  163. * mstest scenarios. On Win3.1, calling PeekMessage() involves making a trip
  164. * through the scheduler, forcing other apps to run. Processes run with
  165. * priority on NT, where the foreground process gets foreground priority for
  166. * greater responsiveness.
  167. *
  168. * If an app calls peek/getmessage without idling, count how many times this
  169. * happens - if it happens CSPINBACKGROUND or more times, make the process
  170. * background. This handles most of the win3.1 app compatibility spinning
  171. * cases. If there is no priority contention, the app continues to run at
  172. * full speed (no performance scenarios should be adversely affected by this).
  173. *
  174. * This solves these cases:
  175. *
  176. * - high speed timer not allowing app to go idle
  177. * - post/peek loop (receiving a WM_ENTERIDLE, and posting a msg, for example)
  178. * - peek no remove loop (winword "idle" state, most dde loops, as examples)
  179. *
  180. * But doesn't protect against these sort of cases:
  181. *
  182. * - app calls getmessage, then goes into a tight loop
  183. * - non-gui threads in tight cpu loops
  184. *
  185. * 02-08-93 ScottLu Created.
  186. \***************************************************************************/
  187. NTSTATUS CheckProcessForeground(
  188. PTHREADINFO pti)
  189. {
  190. PTHREADINFO ptiT;
  191. /*
  192. * Check to see if we need to move this process into foreground
  193. * priority.
  194. */
  195. try {
  196. pti->pClientInfo->cSpins = 0;
  197. pti->pClientInfo->dwTIFlags = pti->TIF_flags & ~TIF_SPINNING;
  198. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  199. return GetExceptionCode();
  200. }
  201. pti->TIF_flags &= ~TIF_SPINNING;
  202. if (pti->ppi->W32PF_Flags & W32PF_FORCEBACKGROUNDPRIORITY) {
  203. /*
  204. * See if any thread of this process is spinning. If none
  205. * are, we can remove the force to background.
  206. */
  207. for (ptiT = pti->ppi->ptiList; ptiT != NULL; ptiT = ptiT->ptiSibling) {
  208. if (ptiT->TIF_flags & TIF_SPINNING)
  209. return STATUS_SUCCESS;
  210. }
  211. pti->ppi->W32PF_Flags &= ~W32PF_FORCEBACKGROUNDPRIORITY;
  212. if (pti->ppi == gppiWantForegroundPriority) {
  213. SetForegroundPriority(pti, TRUE);
  214. }
  215. }
  216. return STATUS_SUCCESS;
  217. }
  218. /***************************************************************************\
  219. * xxxInternalGetMessage
  220. *
  221. * This routine is the worker for both xxxGetMessage() and xxxPeekMessage()
  222. * and is modelled after its win3.1 counterpart. From Win3.1:
  223. *
  224. * Get msg from the app queue or sys queue if there is one that matches
  225. * hwndFilter and matches msgMin/msgMax. If no messages in either queue, check
  226. * the QS_PAINT and QS_TIMER bits, call DoPaint or DoTimer to Post the
  227. * appropriate message to the application queue, and then Read that message.
  228. * Otherwise, if in GetMessage, Sleep until a wake bit is set indicating there
  229. * is something we need to do. If in PeekMessage, return to caller. Before
  230. * reading messages from the queues, check to see if the QS_SENDMESSAGE bit
  231. * is set, and if so, call ReceiveMessage().
  232. *
  233. * New for NT5: HIWORD(flags) contains a wake mask provided by the caller.
  234. * This mask is passed to CalcWakeMask to be combined with the mask generated
  235. * from msgMin and MsgMax. The default mask includes QS_SENDMESSAGE
  236. * now; we won't call xxxReceiveMessages (directly) unless this bit is set;
  237. * however, to avoid potentail deadlocks and maintain NT4 compatibility as
  238. * much as possible, we fail the call if QS_SENDMESSAGE is set in fsWakeBits
  239. * but not requested by the caller. The same applies to QS_EVENT which we would
  240. * always process in NT4.
  241. *
  242. *
  243. * 10-19-92 ScottLu Created.
  244. \***************************************************************************/
  245. #ifdef MARKPATH
  246. #define PATHTAKEN(x) pathTaken |= x
  247. #define DUMPPATHTAKEN() if (gfMarkPath) DbgPrint("xxxInternalGetMessage path:%08x\n", pathTaken)
  248. #else
  249. #define PATHTAKEN(x)
  250. #define DUMPPATHTAKEN()
  251. #endif
  252. BOOL xxxInternalGetMessage(
  253. LPMSG lpMsg,
  254. HWND hwndFilter,
  255. UINT msgMin,
  256. UINT msgMax,
  257. UINT flags,
  258. BOOL fGetMessage)
  259. {
  260. #ifdef MESSAGE_PUMP_HOOK
  261. PCLIENTTHREADINFO pcti = gptiCurrent->pcti;
  262. if (IsInsideMPH()) {
  263. /*
  264. * This thread has MPH's installed, so we need to callback into User
  265. * mode to allow the application to provide an implementation
  266. */
  267. return ClientGetMessageMPH(lpMsg, hwndFilter, msgMin, msgMax, flags, fGetMessage);
  268. } else {
  269. /*
  270. * This thread does not have any MPH's installed, so we can just
  271. * directly process.
  272. */
  273. return xxxRealInternalGetMessage(lpMsg, hwndFilter, msgMin, msgMax, flags, fGetMessage);
  274. }
  275. }
  276. BOOL xxxRealInternalGetMessage(
  277. LPMSG lpMsg,
  278. HWND hwndFilter,
  279. UINT msgMin,
  280. UINT msgMax,
  281. UINT flags,
  282. BOOL fGetMessage)
  283. {
  284. #endif MESSAGE_PUMP_HOOK
  285. UINT fsWakeBits;
  286. UINT fsWakeMask;
  287. UINT fsRemoveBits;
  288. PTHREADINFO ptiCurrent;
  289. PW32PROCESS W32Process;
  290. PWND pwndFilter;
  291. BOOL fLockPwndFilter;
  292. TL tlpwndFilter;
  293. BOOL fRemove;
  294. BOOL fExit;
  295. PQ pq;
  296. #ifdef MARKPATH
  297. DWORD pathTaken = 0;
  298. #endif
  299. BOOL bBackground;
  300. CheckCritIn();
  301. UserAssert(IsWinEventNotifyDeferredOK());
  302. ptiCurrent = PtiCurrent();
  303. /*
  304. * PeekMessage accepts NULL, 0x0000FFFF, and -1 as valid HWNDs.
  305. * If hwndFilter is invalid we can't just return FALSE because that will
  306. * hose existing badly behaved apps who might attempt to dispatch
  307. * the random contents of pmsg.
  308. */
  309. if ((hwndFilter == (HWND)-1) || (hwndFilter == (HWND)0x0000FFFF)) {
  310. hwndFilter = (HWND)1;
  311. }
  312. if ((hwndFilter != NULL) && (hwndFilter != (HWND)1)) {
  313. if ((pwndFilter = ValidateHwnd(hwndFilter)) == NULL) {
  314. lpMsg->hwnd = NULL;
  315. lpMsg->message = WM_NULL;
  316. PATHTAKEN(1);
  317. DUMPPATHTAKEN();
  318. if (fGetMessage)
  319. return -1;
  320. else
  321. return 0;
  322. }
  323. ThreadLockAlwaysWithPti(ptiCurrent, pwndFilter, &tlpwndFilter);
  324. fLockPwndFilter = TRUE;
  325. } else {
  326. pwndFilter = (PWND)hwndFilter;
  327. fLockPwndFilter = FALSE;
  328. }
  329. /*
  330. * Add one to our spin count. At this end of this routine we'll check
  331. * to see if the spin count gets >= CSPINBACKGROUND. If so we'll put this
  332. * process into the background.
  333. */
  334. try {
  335. ptiCurrent->pClientInfo->cSpins++;
  336. } except (W32ExceptionHandler(TRUE, RIP_WARNING)) {
  337. return -1;
  338. }
  339. /*
  340. * Check to see if the startglass is on, and if so turn it off and update.
  341. */
  342. W32Process = W32GetCurrentProcess();
  343. if (W32Process->W32PF_Flags & W32PF_STARTGLASS) {
  344. /*
  345. * This app is no longer in "starting" mode. Recalc when to hide
  346. * the app starting cursor.
  347. */
  348. W32Process->W32PF_Flags &= ~W32PF_STARTGLASS;
  349. /*
  350. * Don't need DeferWinEventNotify() - xxxDoSysExpunge below doesn't
  351. */
  352. zzzCalcStartCursorHide(NULL, 0);
  353. }
  354. /*
  355. * Next check to see if any .dlls need freeing in
  356. * the context of this client (used for windows hooks).
  357. */
  358. if (ptiCurrent->ppi->cSysExpunge != gcSysExpunge) {
  359. ptiCurrent->ppi->cSysExpunge = gcSysExpunge;
  360. if (ptiCurrent->ppi->dwhmodLibLoadedMask & gdwSysExpungeMask)
  361. xxxDoSysExpunge(ptiCurrent);
  362. }
  363. /*
  364. * Set up BOOL fRemove local variable from for ReadMessage()
  365. */
  366. fRemove = flags & PM_REMOVE;
  367. /*
  368. * Unlock the system queue if it's owned by us.
  369. */
  370. /*
  371. * If we're currently processing a message, unlock the input queue
  372. * because the sender, who is blocked, might be the owner, and in order
  373. * to reply, the receiver may need to read keyboard / mouse input.
  374. */
  375. /*
  376. * If this thread has the input queue locked and the last message removed
  377. * is the last message we looked at, then unlock - we're ready for anyone
  378. * to get the next message.
  379. */
  380. pq = ptiCurrent->pq;
  381. if ( (ptiCurrent->psmsCurrent != NULL)
  382. || (pq->ptiSysLock == ptiCurrent && pq->idSysLock == ptiCurrent->idLast)
  383. ) {
  384. CheckSysLock(1, pq, NULL);
  385. pq->ptiSysLock = NULL;
  386. PATHTAKEN(2);
  387. } else if (pq->ptiSysLock
  388. && (pq->ptiSysLock->cVisWindows == 0)
  389. && (PhkFirstGlobalValid(ptiCurrent, WH_JOURNALPLAYBACK) != NULL)) {
  390. /*
  391. * If the thread that has the system queue lock has no windows visible
  392. * (can happen if it just hid its last window), don't expect it to call
  393. * GetMessage() again! - unlock the system queue. --- ScottLu
  394. * This condition creates a hole by which a second thread attached to
  395. * the same queue as thread 1 can alter pq->idSysPeek during a callback
  396. * made by thread 1 so that thread 1 will delete the wrong message
  397. * (losing keystrokes - causing Shift to appear be stuck down editing a
  398. * Graph5 caption embedded in Word32 document #5032. However, MSTEST
  399. * requires this hole, so allow it if Journal Playback is occurring
  400. * #8850 (yes, a hack) Chicago also has this behavior. --- IanJa
  401. */
  402. CheckSysLock(2, pq, NULL);
  403. pq->ptiSysLock = NULL;
  404. PATHTAKEN(3);
  405. }
  406. if (pq->ptiSysLock != ptiCurrent) {
  407. ptiCurrent->pcti->CTIF_flags &= ~CTIF_SYSQUEUELOCKED;
  408. }
  409. /*
  410. * If msgMax == 0 then msgMax = -1: that makes our range checking only
  411. * have to deal with msgMin < msgMax.
  412. */
  413. if (msgMax == 0)
  414. msgMax--;
  415. /*
  416. * Compute the QS* mask that corresponds to the message range
  417. * and the wake mask filter (HIWORD(flags))
  418. */
  419. fsWakeMask = CalcWakeMask(msgMin, msgMax, HIWORD(flags));
  420. ptiCurrent->fsChangeBitsRemoved = 0;
  421. /*
  422. * If we can yield and one or more events were skipped,
  423. * set the wakebits for event
  424. */
  425. if (!(flags & PM_NOYIELD) && ptiCurrent->TIF_flags & TIF_DELAYEDEVENT) {
  426. try {
  427. ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags & ~TIF_DELAYEDEVENT;
  428. } except (W32ExceptionHandler(TRUE, RIP_WARNING)) {
  429. return -1;
  430. }
  431. ptiCurrent->pcti->fsWakeBits |= QS_EVENT;
  432. ptiCurrent->pcti->fsChangeBits |= QS_EVENT;
  433. ptiCurrent->TIF_flags &= ~TIF_DELAYEDEVENT;
  434. }
  435. while (TRUE) {
  436. /*
  437. * Restore any wake bits saved while journalling
  438. */
  439. ptiCurrent->pcti->fsWakeBits |= ptiCurrent->pcti->fsWakeBitsJournal;
  440. /*
  441. * If we need to recalc queue attachments, do it here. Do it on the
  442. * right desktop or else the queues will get created in the wrong
  443. * heap.
  444. */
  445. if (ptiCurrent->rpdesk == gpdeskRecalcQueueAttach) {
  446. gpdeskRecalcQueueAttach = NULL;
  447. if (ptiCurrent->rpdesk != NULL && !FJOURNALRECORD() && !FJOURNALPLAYBACK()) {
  448. /*
  449. * No need to DeferWinEventNotify(): a call to
  450. * xxxReceiveMessages is made just below
  451. */
  452. zzzReattachThreads(FALSE);
  453. PATHTAKEN(4);
  454. }
  455. }
  456. /*
  457. * Remember what change bits we're clearing. This is important to
  458. * fix a bug in the input model: If an app receives a sent message
  459. * from within SleepThread(), then does PostMessage() (which sets
  460. * QS_POSTMESSAGE), then does a PeekMessage(...) for some different
  461. * posted message (clears QS_POSTMESSAGE in fsChangeBits), then returns
  462. * back into SleepThread(), it won't wake up to retrieve that newly
  463. * posted message because the change bits are cleared.
  464. *
  465. * What we do is remember the change bits that are being cleared.
  466. * Then, when we return to SleepThread(), we put these remembered
  467. * bits back into the change bits that also have corresponding
  468. * bits in the wakebits (so we don't set changebits that represent
  469. * input that isn't there anymore). This way, the app will retrieve
  470. * the newly posted message refered to earlier.
  471. * - scottlu
  472. *
  473. * New for NT5: Since QS_SENDMESSAGE was never set it fsWakeMask before (NT4),
  474. * it was never cleared from fsChangeBits. For compatibility, we won't clear
  475. * it now even if specified in fsWakeMask; hence we won't affect any one
  476. * checking for QS_SENDMESSAGE in pcti->fsChangeBits.
  477. */
  478. fsRemoveBits = fsWakeMask & ~QS_SENDMESSAGE;
  479. ptiCurrent->fsChangeBitsRemoved |= ptiCurrent->pcti->fsChangeBits & fsRemoveBits;
  480. /*
  481. * Clear the change bits that we're looking at, in order to detect
  482. * incoming events that may occur the last time we checked the wake
  483. * bits.
  484. */
  485. ptiCurrent->pcti->fsChangeBits &= ~fsRemoveBits;
  486. /*
  487. * Check for sent messages. Check the the actual wake bits (i.e, from pcti)
  488. * so we know for real.
  489. */
  490. if (ptiCurrent->pcti->fsWakeBits & fsWakeMask & QS_SENDMESSAGE) {
  491. xxxReceiveMessages(ptiCurrent);
  492. } else if (ptiCurrent->pcti->fsWakeBits & QS_SENDMESSAGE) {
  493. RIPMSG2(RIP_WARNING, "xxxInternalGetMessage:(1st test) sendmsgs pending. Bits:%#lx Mask:%#lx",
  494. ptiCurrent->pcti->fsWakeBits, fsWakeMask);
  495. goto NoMessages;
  496. }
  497. /*
  498. * Check to see if we have any input we want.
  499. */
  500. if ((ptiCurrent->pcti->fsWakeBits & fsWakeMask) == 0) {
  501. PATHTAKEN(8);
  502. goto NoMessages;
  503. }
  504. fsWakeBits = ptiCurrent->pcti->fsWakeBits;
  505. /*
  506. * If the queue lock is != NULL (ptiSysLock) and it is this thread that
  507. * locked it, then go get the message from the system queue. This is
  508. * to prevent messages posted after a PeekMessage/no-remove from being
  509. * seen before the original message from the system queue. (Aldus
  510. * Pagemaker requires this) (bobgu 8/5/87).
  511. */
  512. if (ptiCurrent->pq->ptiSysLock == ptiCurrent &&
  513. (ptiCurrent->pq->QF_flags & QF_LOCKNOREMOVE)) {
  514. /*
  515. * Does the caller want mouse / keyboard?
  516. */
  517. if (fsWakeBits & fsWakeMask & (QS_INPUT | QS_EVENT)) {
  518. /*
  519. * It should never get here during exit.
  520. */
  521. UserAssert(gbExitInProgress == FALSE);
  522. if (xxxScanSysQueue(ptiCurrent, lpMsg, pwndFilter,
  523. msgMin, msgMax, flags,
  524. fsWakeBits & fsWakeMask & (QS_INPUT | QS_EVENT))) {
  525. PATHTAKEN(0x10);
  526. break;
  527. }
  528. } else if (fsWakeBits & QS_EVENT) {
  529. RIPMSG2(RIP_WARNING,
  530. "xxxInternalGetMessage:(1st test)events pending. Bits:%#lx Mask:%#lx",
  531. fsWakeBits, fsWakeMask);
  532. goto NoMessages;
  533. }
  534. }
  535. /*
  536. * See if there's a message in the application queue.
  537. */
  538. if (fsWakeBits & fsWakeMask & QS_POSTMESSAGE) {
  539. if (xxxReadPostMessage(ptiCurrent, lpMsg, pwndFilter,
  540. msgMin, msgMax, fRemove)) {
  541. PATHTAKEN(0x20);
  542. break;
  543. }
  544. }
  545. /*
  546. * If pwndFilter == 1, this app was only interested in messages
  547. * that were posted via PostThreadMessage. Since we checked the
  548. * posted message queue above, let's skip doing needless work.
  549. */
  550. if (pwndFilter == (PWND)1) {
  551. goto NoMessages;
  552. }
  553. /*
  554. * Time to scan the raw input queue for input. First check to see
  555. * if the caller wants mouse / keyboard input.
  556. */
  557. if (fsWakeBits & fsWakeMask & (QS_INPUT | QS_EVENT)) {
  558. /*
  559. * It should never get here during exit.
  560. */
  561. UserAssert(gbExitInProgress == FALSE);
  562. if (xxxScanSysQueue(ptiCurrent, lpMsg, pwndFilter,
  563. msgMin, msgMax, flags,
  564. fsWakeBits & fsWakeMask & (QS_INPUT | QS_EVENT))) {
  565. PATHTAKEN(0x40);
  566. break;
  567. }
  568. } else if (fsWakeBits & QS_EVENT) {
  569. RIPMSG2(RIP_WARNING, "xxxInternalGetMessage:(2nd test)events pending. Bits:%#lx Mask:%#lx",
  570. fsWakeBits, fsWakeMask);
  571. goto NoMessages;
  572. }
  573. /*
  574. * Check for sent messages. Check the the actual wake bits (i.e, from pcti)
  575. * so we know for real.
  576. */
  577. if (ptiCurrent->pcti->fsWakeBits & fsWakeMask & QS_SENDMESSAGE) {
  578. xxxReceiveMessages(ptiCurrent);
  579. } else if (ptiCurrent->pcti->fsWakeBits & QS_SENDMESSAGE) {
  580. RIPMSG2(RIP_WARNING, "xxxInternalGetMessage:(2nd test)sendmsgs pending. Bits:%#lx Mask:%#lx",
  581. ptiCurrent->pcti->fsWakeBits, fsWakeMask);
  582. goto NoMessages;
  583. }
  584. /*
  585. * Get new input bits.
  586. */
  587. if ((ptiCurrent->pcti->fsWakeBits & fsWakeMask) == 0) {
  588. PATHTAKEN(0x80);
  589. goto NoMessages;
  590. }
  591. fsWakeBits = ptiCurrent->pcti->fsWakeBits;
  592. /*
  593. * Does the caller want paint messages? If so, try to find a paint.
  594. */
  595. if (fsWakeBits & fsWakeMask & QS_PAINT) {
  596. if (xxxDoPaint(pwndFilter, lpMsg)) {
  597. PATHTAKEN(0x100);
  598. break;
  599. }
  600. }
  601. /*
  602. * We must yield for 16 bit apps before checking timers or an app
  603. * that has a fast timer could chew up all the time and never let
  604. * anyone else run.
  605. *
  606. * NOTE: This could cause PeekMessage() to yield TWICE, if the user
  607. * is filtering with a window handle. If the DoTimer() call fails
  608. * then we end up yielding again.
  609. */
  610. if (!(flags & PM_NOYIELD)) {
  611. /*
  612. * This is the point where windows would yield. Here we wait to wake
  613. * up any threads waiting for this thread to hit "idle state".
  614. */
  615. zzzWakeInputIdle(ptiCurrent);
  616. /*
  617. * Yield and receive pending messages.
  618. */
  619. xxxUserYield(ptiCurrent);
  620. /*
  621. * Check new input buts and receive pending messages.
  622. */
  623. if (ptiCurrent->pcti->fsWakeBits & fsWakeMask & QS_SENDMESSAGE) {
  624. xxxReceiveMessages(ptiCurrent);
  625. } else if (ptiCurrent->pcti->fsWakeBits & QS_SENDMESSAGE) {
  626. RIPMSG2(RIP_WARNING, "xxxInternalGetMessage:(3rd test) sendmsgs pending. Bits:%#lx Mask:%#lx",
  627. ptiCurrent->pcti->fsWakeBits, fsWakeMask);
  628. goto NoMessages;
  629. }
  630. if ((ptiCurrent->pcti->fsWakeBits & fsWakeMask) == 0) {
  631. PATHTAKEN(0x200);
  632. goto NoMessages;
  633. }
  634. fsWakeBits = ptiCurrent->pcti->fsWakeBits;
  635. }
  636. /*
  637. * Does the app want timer messages, and if there one pending?
  638. */
  639. if (fsWakeBits & fsWakeMask & QS_TIMER) {
  640. if (DoTimer(pwndFilter)) {
  641. /*
  642. * DoTimer() posted the message into the app's queue,
  643. * so start over and we'll grab it from there.
  644. */
  645. PATHTAKEN(0x400);
  646. continue;
  647. }
  648. }
  649. NoMessages:
  650. /*
  651. * Looks like we have no input. If we're being called from GetMessage()
  652. * then go to sleep until we find something.
  653. */
  654. if (!fGetMessage) {
  655. /*
  656. * This is one last check for pending sent messages. It also
  657. * yields. Win3.1 does this.
  658. */
  659. if (!(flags & PM_NOYIELD)) {
  660. /*
  661. * This is the point where windows yields. Here we wait to wake
  662. * up any threads waiting for this thread to hit "idle state".
  663. */
  664. zzzWakeInputIdle(ptiCurrent);
  665. /*
  666. * Yield and receive pending messages.
  667. */
  668. xxxUserYield(ptiCurrent);
  669. }
  670. PATHTAKEN(0x800);
  671. goto FalseExit;
  672. }
  673. /*
  674. * This is a getmessage not a peekmessage, so sleep. When we sleep,
  675. * zzzWakeInputIdle() is called to wake up any apps waiting on this
  676. * app to go idle.
  677. */
  678. if (!xxxSleepThread(fsWakeMask, 0, TRUE))
  679. goto FalseExit;
  680. } /* while (TRUE) */
  681. /*
  682. * If we're here then we have input for this queue. Call the
  683. * GetMessage() hook with this input.
  684. */
  685. if (IsHooked(ptiCurrent, WHF_GETMESSAGE))
  686. xxxCallHook(HC_ACTION, flags, (LPARAM)lpMsg, WH_GETMESSAGE);
  687. /*
  688. * If called from PeekMessage(), return TRUE.
  689. */
  690. if (!fGetMessage) {
  691. PATHTAKEN(0x1000);
  692. goto TrueExit;
  693. }
  694. /*
  695. * Being called from GetMessage(): return FALSE if the message is WM_QUIT,
  696. * TRUE otherwise.
  697. */
  698. if (lpMsg->message == WM_QUIT) {
  699. PATHTAKEN(0x2000);
  700. goto FalseExit;
  701. }
  702. /*
  703. * Fall through to TrueExit...
  704. */
  705. TrueExit:
  706. /*
  707. * Update timeLastRead. We use this for hung app calculations.
  708. */
  709. SET_TIME_LAST_READ(ptiCurrent);
  710. fExit = TRUE;
  711. #ifdef GENERIC_INPUT
  712. if (fRemove) {
  713. /*
  714. * This version simply frees the previous HIDDATA.
  715. */
  716. if (ptiCurrent->hPrevHidData) {
  717. PHIDDATA pPrevHidData = HMValidateHandleNoRip(ptiCurrent->hPrevHidData, TYPE_HIDDATA);
  718. TAGMSG1(DBGTAG_PNP, "xxxInternalGetMessage: WM_INPUT prev=%p", ptiCurrent->hPrevHidData);
  719. if (pPrevHidData) {
  720. FreeHidData(pPrevHidData);
  721. } else {
  722. RIPMSG1(RIP_WARNING, "xxxInternalGetMessage: WM_INPUT bogus hPrev=%p",
  723. ptiCurrent->hPrevHidData);
  724. }
  725. ptiCurrent->hPrevHidData = NULL;
  726. }
  727. if (lpMsg->message == WM_INPUT) {
  728. if (lpMsg->wParam == RIM_INPUT
  729. #ifdef GI_SINK
  730. || lpMsg->wParam == RIM_INPUTSINK
  731. #endif
  732. ) {
  733. ptiCurrent->hPrevHidData = (HANDLE)lpMsg->lParam;
  734. #if DBG
  735. {
  736. PHIDDATA pHidData = HMValidateHandle((HANDLE)lpMsg->lParam, TYPE_HIDDATA);
  737. TAGMSG1(DBGTAG_PNP, "xxxInternalGetMessage: WM_INPUT new=%p", PtoH(pHidData));
  738. if (pHidData == NULL) {
  739. RIPMSG2(RIP_WARNING, "xxxInternalGetMessage: WM_INPUT bogus parameter wp=%x, lp=%x",
  740. lpMsg->wParam, lpMsg->lParam);
  741. }
  742. }
  743. #endif
  744. } else {
  745. RIPMSG1(RIP_WARNING, "xxxInternalGetMessage: WM_INPUT bogus wParam %x",
  746. lpMsg->wParam);
  747. }
  748. }
  749. }
  750. #endif
  751. PATHTAKEN(0x4000);
  752. goto Exit;
  753. FalseExit:
  754. fExit = FALSE;
  755. Exit:
  756. if (fLockPwndFilter)
  757. ThreadUnlock(&tlpwndFilter);
  758. /*
  759. * see CheckProcessBackground() comment above
  760. * Check to see if we need to move this process into background
  761. * priority.
  762. */
  763. try {
  764. bBackground = ((ptiCurrent->pClientInfo->cSpins >= CSPINBACKGROUND) != 0);
  765. if (bBackground) {
  766. ptiCurrent->pClientInfo->cSpins = 0;
  767. if (!(ptiCurrent->TIF_flags & TIF_SPINNING)) {
  768. ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags | TIF_SPINNING;
  769. }
  770. }
  771. } except (W32ExceptionHandler(TRUE, RIP_WARNING)) {
  772. fExit = FALSE;
  773. goto Error;
  774. }
  775. if (bBackground) {
  776. if (!(ptiCurrent->TIF_flags & TIF_SPINNING)) {
  777. ptiCurrent->TIF_flags |= TIF_SPINNING;
  778. if (!(ptiCurrent->ppi->W32PF_Flags & W32PF_FORCEBACKGROUNDPRIORITY)) {
  779. ptiCurrent->ppi->W32PF_Flags |= W32PF_FORCEBACKGROUNDPRIORITY;
  780. if (ptiCurrent->ppi == gppiWantForegroundPriority) {
  781. SetForegroundPriority(ptiCurrent, FALSE);
  782. }
  783. }
  784. }
  785. /*
  786. * For spinning Message loops, we need to take the 16bit-thread out
  787. * of the scheduler temporarily so that other processes can get a chance
  788. * to run. This is appearent in OLE operations where a 16bit foreground
  789. * thread starts an OLE activation on a 32bit process. The 32bit process
  790. * gets starved of CPU while the 16bit thread spins.
  791. */
  792. if (ptiCurrent->TIF_flags & TIF_16BIT) {
  793. /*
  794. * Take the 16bit thread out of the scheduler. This wakes any
  795. * other 16bit thread needing time, and takes the current thread
  796. * out. We will do a brief sleep so that apps can respond in time.
  797. * When done, we will reschedule the thread. The zzzWakeInputIdle()
  798. * should have been called in the no-messages section, so we have
  799. * already set the Idle-Event.
  800. */
  801. xxxSleepTask(FALSE, HEVENT_REMOVEME);
  802. LeaveCrit();
  803. ZwYieldExecution();
  804. EnterCrit();
  805. xxxDirectedYield(DY_OLDYIELD);
  806. }
  807. }
  808. Error:
  809. PATHTAKEN(0x8000);
  810. DUMPPATHTAKEN();
  811. return fExit;
  812. }
  813. #undef PATHTAKEN
  814. #undef DUMPPATHTAKEN
  815. __inline PTIMER FindSystemTimer(
  816. PMSG pmsg)
  817. {
  818. PTIMER ptmr;
  819. const BOOL fWow64 =
  820. #ifdef _WIN64
  821. PtiCurrent()->TIF_flags & TIF_WOW64;
  822. #else
  823. FALSE;
  824. #endif
  825. for (ptmr = gptmrFirst; ptmr; ptmr = ptmr->ptmrNext) {
  826. if (ptmr->flags & TMRF_SYSTEM) {
  827. if (pmsg->lParam == (LPARAM)ptmr->pfn) {
  828. return ptmr;
  829. }
  830. /*
  831. * 64bit only: lParam might be truncated, if the application is 32bit.
  832. * We do our best to pick up the right guy, by comparing the
  833. * lower 32bit in the pointer and the timer id.
  834. */
  835. if (fWow64 && (ULONG)pmsg->lParam == PtrToUlong(ptmr->pfn) && pmsg->wParam == ptmr->nID) {
  836. return ptmr;
  837. }
  838. }
  839. }
  840. return NULL;
  841. }
  842. /***************************************************************************\
  843. * ValidateTimerCallback
  844. *
  845. * Checks if the timer callback (with lParam != 0) is legitimate,
  846. * in order to avoid mulicious applications to break the other applications.
  847. *
  848. * History:
  849. * 08-10-2002 Hiro Created.
  850. \***************************************************************************/
  851. BOOL ValidateTimerCallback(
  852. PTHREADINFO pti,
  853. LPARAM pfnCallback)
  854. {
  855. PTIMER pTimer;
  856. UserAssert(pti);
  857. /*
  858. * AppCompat: if the flag is set, skip the checking and allow
  859. * lParam based callbacks.
  860. */
  861. if (GetAppCompatFlags2ForPti(pti, VER51) & GACF2_NOTIMERCBPROTECTION) {
  862. /*
  863. * But we always protect CSRSS and WinLogon.
  864. */
  865. if ((pti->TIF_flags & (TIF_CSRSSTHREAD | TIF_SYSTEMTHREAD)) == 0 &&
  866. PsGetProcessId(pti->ppi->Process) != gpidLogon) {
  867. return TRUE;
  868. }
  869. }
  870. for (pTimer = gptmrFirst; pTimer; pTimer = pTimer->ptmrNext) {
  871. /*
  872. * Is this the timer we're looking for?
  873. */
  874. if (pTimer->pti->ppi == pti->ppi &&
  875. (pTimer->flags & (TMRF_SYSTEM | TMRF_RIT)) == 0 &&
  876. pTimer->pfn == (TIMERPROC_PWND)pfnCallback) {
  877. /*
  878. * Found the timer, tell the caller so.
  879. */
  880. return TRUE;
  881. }
  882. }
  883. /*
  884. * No, we didn't find a matching timer.
  885. */
  886. return FALSE;
  887. }
  888. /***************************************************************************\
  889. * xxxDispatchMessage (API)
  890. *
  891. * Calls the appropriate window procedure or function with pmsg.
  892. *
  893. * History:
  894. * 10-25-90 DavidPe Created.
  895. \***************************************************************************/
  896. LRESULT xxxDispatchMessage(
  897. LPMSG pmsg)
  898. {
  899. LRESULT lRet;
  900. PWND pwnd;
  901. WNDPROC_PWND lpfnWndProc;
  902. TL tlpwnd;
  903. pwnd = NULL;
  904. if (pmsg->hwnd != NULL) {
  905. if ((pwnd = ValidateHwnd(pmsg->hwnd)) == NULL)
  906. return 0;
  907. }
  908. /*
  909. * If this is a synchronous-only message (takes a pointer in wParam or
  910. * lParam), then don't allow this message to go through since those
  911. * parameters have not been thunked, and are pointing into outer-space
  912. * (which would case exceptions to occur).
  913. *
  914. * (This api is only called in the context of a message loop, and you
  915. * don't get synchronous-only messages in a message loop).
  916. */
  917. if (TESTSYNCONLYMESSAGE(pmsg->message, pmsg->wParam)) {
  918. /*
  919. * Fail if 32 bit app is calling.
  920. */
  921. if (!(PtiCurrent()->TIF_flags & TIF_16BIT)) {
  922. RIPERR1(ERROR_MESSAGE_SYNC_ONLY, RIP_WARNING, "xxxDispatchMessage: Sync only message 0x%lX",
  923. pmsg->message);
  924. return 0;
  925. }
  926. /*
  927. * For wow apps, allow it to go through (for compatibility). Change
  928. * the message id so our code doesn't understand the message - wow
  929. * will get the message and strip out this bit before dispatching
  930. * the message to the application.
  931. */
  932. pmsg->message |= MSGFLAG_WOW_RESERVED;
  933. }
  934. ThreadLock(pwnd, &tlpwnd);
  935. /*
  936. * Is this a timer? If there's a proc address, call it,
  937. * otherwise send it to the wndproc.
  938. */
  939. if ((pmsg->message == WM_TIMER) || (pmsg->message == WM_SYSTIMER)) {
  940. if (pmsg->lParam != 0) {
  941. /*
  942. * System timers must be executed on the server's context.
  943. */
  944. if (pmsg->message == WM_SYSTIMER) {
  945. /*
  946. * Verify that it's a valid timer proc. If so,
  947. * don't leave the critsect to call server-side procs
  948. * and pass a PWND, not HWND.
  949. */
  950. PTIMER ptmr;
  951. lRet = 0;
  952. ptmr = FindSystemTimer(pmsg);
  953. if (ptmr) {
  954. ptmr->pfn(pwnd, WM_SYSTIMER, (UINT)pmsg->wParam,
  955. NtGetTickCount());
  956. }
  957. goto Exit;
  958. } else {
  959. /*
  960. * WM_TIMER is the same for Unicode/ANSI.
  961. */
  962. PTHREADINFO ptiCurrent = PtiCurrent();
  963. if (ptiCurrent->TIF_flags & TIF_SYSTEMTHREAD) {
  964. lRet = 0;
  965. goto Exit;
  966. }
  967. /*
  968. * Check the legitimacy of this WM_TIMER callback,
  969. * and bail out if it's not valid.
  970. */
  971. if (!ValidateTimerCallback(ptiCurrent, pmsg->lParam)) {
  972. RIPMSGF2(RIP_WARNING, "Bogus WM_TIMER callback: nID=%p, pfn=%p", pmsg->wParam, pmsg->lParam);
  973. lRet = 0;
  974. goto Exit;
  975. }
  976. lRet = CallClientProcA(pwnd, WM_TIMER,
  977. pmsg->wParam, NtGetTickCount(), pmsg->lParam);
  978. goto Exit;
  979. }
  980. }
  981. }
  982. /*
  983. * Check to see if pwnd is NULL AFTER the timer check. Apps can set
  984. * timers with NULL hwnd's, that's totally legal. But NULL hwnd messages
  985. * don't get dispatched, so check here after the timer case but before
  986. * dispatching - if it's NULL, just return 0.
  987. */
  988. if (pwnd == NULL) {
  989. lRet = 0;
  990. goto Exit;
  991. }
  992. /*
  993. * If we're dispatching a WM_PAINT message, set a flag to be used to
  994. * determine whether it was processed properly.
  995. */
  996. if (pmsg->message == WM_PAINT)
  997. SetWF(pwnd, WFPAINTNOTPROCESSED);
  998. /*
  999. * If this window's proc is meant to be executed from the server side
  1000. * we'll just stay inside the semaphore and call it directly. Note
  1001. * how we don't convert the pwnd into an hwnd before calling the proc.
  1002. */
  1003. if (TestWF(pwnd, WFSERVERSIDEPROC)) {
  1004. ULONG_PTR fnMessageType;
  1005. fnMessageType = pmsg->message >= WM_USER ? (ULONG_PTR)SfnDWORD :
  1006. (ULONG_PTR)gapfnScSendMessage[MessageTable[pmsg->message].iFunction];
  1007. /*
  1008. * Convert the WM_CHAR from ANSI to UNICODE if the source was ANSI
  1009. */
  1010. if (fnMessageType == (ULONG_PTR)SfnINWPARAMCHAR && TestWF(pwnd, WFANSIPROC)) {
  1011. UserAssert(PtiCurrent() == GETPTI(pwnd)); // use receiver's codepage
  1012. RtlMBMessageWParamCharToWCS(pmsg->message, &pmsg->wParam);
  1013. }
  1014. lRet = pwnd->lpfnWndProc(pwnd, pmsg->message, pmsg->wParam,
  1015. pmsg->lParam);
  1016. goto Exit;
  1017. }
  1018. /*
  1019. * Cool people dereference any window structure members before they
  1020. * leave the critsect.
  1021. */
  1022. lpfnWndProc = pwnd->lpfnWndProc;
  1023. {
  1024. /*
  1025. * If we're dispatching the message to an ANSI wndproc we need to
  1026. * convert the character messages from Unicode to Ansi.
  1027. */
  1028. if (TestWF(pwnd, WFANSIPROC)) {
  1029. UserAssert(PtiCurrent() == GETPTI(pwnd)); // use receiver's codepage
  1030. RtlWCSMessageWParamCharToMB(pmsg->message, &pmsg->wParam);
  1031. lRet = CallClientProcA(pwnd, pmsg->message,
  1032. pmsg->wParam, pmsg->lParam, (ULONG_PTR)lpfnWndProc);
  1033. } else {
  1034. lRet = CallClientProcW(pwnd, pmsg->message,
  1035. pmsg->wParam, pmsg->lParam, (ULONG_PTR)lpfnWndProc);
  1036. }
  1037. }
  1038. /*
  1039. * If we dispatched a WM_PAINT message and it wasn't properly
  1040. * processed, do the drawing here.
  1041. */
  1042. if (pmsg->message == WM_PAINT && RevalidateHwnd(pmsg->hwnd) &&
  1043. TestWF(pwnd, WFPAINTNOTPROCESSED)) {
  1044. //RIPMSG0(RIP_WARNING,
  1045. // "Missing BeginPaint or GetUpdateRect/Rgn(fErase == TRUE) in WM_PAINT");
  1046. ClrWF(pwnd, WFWMPAINTSENT);
  1047. xxxSimpleDoSyncPaint(pwnd);
  1048. }
  1049. Exit:
  1050. ThreadUnlock(&tlpwnd);
  1051. return lRet;
  1052. }
  1053. /***************************************************************************\
  1054. * AdjustForCoalescing
  1055. *
  1056. * If message is in the coalesce message range, and it's message and hwnd
  1057. * equals the last message in the queue, then coalesce these two messages
  1058. * by simple deleting the last one.
  1059. *
  1060. * 11-12-92 ScottLu Created.
  1061. \***************************************************************************/
  1062. void AdjustForCoalescing(
  1063. PMLIST pml,
  1064. HWND hwnd,
  1065. UINT message)
  1066. {
  1067. /*
  1068. * First see if this message is in that range.
  1069. */
  1070. if (!CheckMsgFilter(message, WM_COALESCE_FIRST, WM_COALESCE_LAST) &&
  1071. (message != WM_TIMECHANGE))
  1072. return;
  1073. if (pml->pqmsgWriteLast == NULL)
  1074. return;
  1075. if (pml->pqmsgWriteLast->msg.message != message)
  1076. return;
  1077. if (pml->pqmsgWriteLast->msg.hwnd != hwnd)
  1078. return;
  1079. /*
  1080. * The message and hwnd are the same, so delete this message and
  1081. * the new one will added later.
  1082. */
  1083. DelQEntry(pml, pml->pqmsgWriteLast);
  1084. }
  1085. /***************************************************************************\
  1086. * _PostMessage (API)
  1087. *
  1088. * Writes a message to the message queue for pwnd. If pwnd == -1, the message
  1089. * is broadcasted to all windows.
  1090. *
  1091. * History:
  1092. * 11-06-90 DavidPe Created.
  1093. \***************************************************************************/
  1094. BOOL _PostMessage(
  1095. PWND pwnd,
  1096. UINT message,
  1097. WPARAM wParam,
  1098. LPARAM lParam)
  1099. {
  1100. PQMSG pqmsg;
  1101. BOOL fPwndUnlock;
  1102. BOOL fRet;
  1103. DWORD dwPostCode;
  1104. TL tlpwnd;
  1105. PTHREADINFO pti;
  1106. /*
  1107. * First check to see if this message takes DWORDs only. If it does not,
  1108. * fail the post. Cannot allow an app to post a message with pointers or
  1109. * handles in it - this can cause the server to fault and cause other
  1110. * problems - such as causing apps in separate address spaces to fault.
  1111. * (or even an app in the same address space to fault!)
  1112. *
  1113. * Block certain messages cross LUIDs to avoid security threats.
  1114. */
  1115. if (TESTSYNCONLYMESSAGE(message, wParam) ||
  1116. BLOCKMESSAGECROSSLUID(message,
  1117. PpiCurrent(),
  1118. GETPTI(pwnd)->ppi)) {
  1119. RIPERR1(ERROR_MESSAGE_SYNC_ONLY,
  1120. RIP_WARNING,
  1121. "Invalid parameter \"message\" (%ld) to _PostMessage",
  1122. message);
  1123. return FALSE;
  1124. }
  1125. /*
  1126. * Is this a BroadcastMsg()?
  1127. */
  1128. if (pwnd == PWND_BROADCAST) {
  1129. xxxBroadcastMessage(NULL, message, wParam, lParam, BMSG_POSTMSG, NULL);
  1130. return TRUE;
  1131. }
  1132. pti = PtiCurrent();
  1133. /*
  1134. * Is this posting to the current thread info?
  1135. */
  1136. if (pwnd == NULL) {
  1137. return _PostThreadMessage(pti, message, wParam, lParam);
  1138. }
  1139. fPwndUnlock = FALSE;
  1140. if (message >= WM_DDE_FIRST && message <= WM_DDE_LAST) {
  1141. ThreadLockAlwaysWithPti(pti, pwnd, &tlpwnd);
  1142. dwPostCode = xxxDDETrackPostHook(&message, pwnd, wParam, &lParam, FALSE);
  1143. if (dwPostCode != DO_POST) {
  1144. ThreadUnlock(&tlpwnd);
  1145. return (BOOL)dwPostCode;
  1146. }
  1147. fPwndUnlock = TRUE;
  1148. }
  1149. pti = GETPTI(pwnd);
  1150. /*
  1151. * Check to see if this message is in the multimedia coalescing range.
  1152. * If so, see if it can be coalesced with the previous message.
  1153. */
  1154. AdjustForCoalescing(&pti->mlPost, HWq(pwnd), message);
  1155. #ifdef GENERIC_INPUT
  1156. #if LOCK_HIDDATA
  1157. /*
  1158. * If someone is posting this message, we need to bump up the reference
  1159. * count of the HID data so it doesn't get freed too early.
  1160. */
  1161. if (message == WM_INPUT) {
  1162. // lParam is an HRAWINPUT
  1163. PHIDDATA pHidData = HMValidateHandle((HANDLE)lParam, TYPE_HIDDATA);
  1164. TAGMSG1(DBGTAG_PNP, "_PostMessage: Got WM_INPUT pHidData=%p", pHidData);
  1165. if (pHidData != NULL) {
  1166. HMLockObject(pHidData);
  1167. } else {
  1168. RIPMSG1(RIP_WARNING, "_PostMessage: invalid handle %p for WM_INPUT", lParam);
  1169. return FALSE;
  1170. }
  1171. } else
  1172. #endif
  1173. #endif // GENERIC_INPUT
  1174. /*
  1175. * Allocate a key state update event if needed.
  1176. */
  1177. if (message >= WM_KEYFIRST && message <= WM_KEYLAST) {
  1178. PostUpdateKeyStateEvent(pti->pq);
  1179. }
  1180. /*
  1181. * Put this message on the 'post' list.
  1182. */
  1183. fRet = FALSE;
  1184. if ((pqmsg = AllocQEntry(&pti->mlPost)) != NULL) {
  1185. /*
  1186. * Set the QS_POSTMESSAGE bit so the thread knows it has a message.
  1187. */
  1188. StoreQMessage(pqmsg, pwnd, message, wParam, lParam, 0, 0, 0);
  1189. SetWakeBit(pti, QS_POSTMESSAGE | QS_ALLPOSTMESSAGE);
  1190. /*
  1191. * If it's a hotkey, set the QS_HOTKEY bit since we have a separate
  1192. * bit for those messages.
  1193. */
  1194. if (message == WM_HOTKEY)
  1195. SetWakeBit(pti, QS_HOTKEY);
  1196. fRet = TRUE;
  1197. }
  1198. /*
  1199. * Are we posting to the thread currently reading from the input queue?
  1200. * If so, update idSysLock with this pqmsg so that the input queue will
  1201. * not be unlocked until this message is read.
  1202. */
  1203. if (pti == pti->pq->ptiSysLock)
  1204. pti->pq->idSysLock = (ULONG_PTR)pqmsg;
  1205. if (fPwndUnlock)
  1206. ThreadUnlock(&tlpwnd);
  1207. return fRet;
  1208. }
  1209. /***************************************************************************\
  1210. * _PostQuitMessage (API)
  1211. *
  1212. * Writes a message to the message queue for pwnd. If pwnd == -1, the message
  1213. * is broadcasted to all windows.
  1214. *
  1215. * History:
  1216. * 11-06-90 DavidPe Created.
  1217. * 05-16-91 mikeke Changed to return BOOL
  1218. \***************************************************************************/
  1219. BOOL IPostQuitMessage(PTHREADINFO pti, int nExitCode)
  1220. {
  1221. pti->TIF_flags |= TIF_QUITPOSTED;
  1222. pti->exitCode = nExitCode;
  1223. SetWakeBit(pti, QS_POSTMESSAGE | QS_ALLPOSTMESSAGE);
  1224. return TRUE;
  1225. }
  1226. BOOL _PostQuitMessage(int nExitCode)
  1227. {
  1228. return IPostQuitMessage(PtiCurrent(), nExitCode);
  1229. }
  1230. /***************************************************************************\
  1231. * _PostThreadMessage (API)
  1232. *
  1233. * Given a thread ID, the function will post the specified message to this
  1234. * thread with pmsg->hwnd == NULL..
  1235. *
  1236. * History:
  1237. * 11-21-90 DavidPe Created.
  1238. \***************************************************************************/
  1239. BOOL _PostThreadMessage(
  1240. PTHREADINFO pti,
  1241. UINT message,
  1242. WPARAM wParam,
  1243. LPARAM lParam)
  1244. {
  1245. PQMSG pqmsg;
  1246. if ((pti == NULL) ||
  1247. !(pti->TIF_flags & TIF_GUITHREADINITIALIZED) ||
  1248. (pti->TIF_flags & TIF_INCLEANUP)) {
  1249. RIPERR0(ERROR_INVALID_THREAD_ID, RIP_VERBOSE, "");
  1250. return FALSE;
  1251. }
  1252. /*
  1253. * First check to see if this message takes DWORDs only. If it does not,
  1254. * fail the post. Cannot allow an app to post a message with pointers or
  1255. * handles in it - this can cause the server to fault and cause other
  1256. * problems - such as causing apps in separate address spaces to fault.
  1257. * (or even an app in the same address space to fault!)
  1258. *
  1259. * Block certain messages cross LUIDs to avoid security threats.
  1260. */
  1261. if (TESTSYNCONLYMESSAGE(message, wParam) ||
  1262. BLOCKMESSAGECROSSLUID(message,
  1263. PpiCurrent(),
  1264. pti->ppi)) {
  1265. RIPERR1(ERROR_MESSAGE_SYNC_ONLY,
  1266. RIP_WARNING,
  1267. "Invalid parameter \"message\" (%ld) to _PostThreadMessage",
  1268. message);
  1269. return FALSE;
  1270. }
  1271. /*
  1272. * Check to see if this message is in the multimedia coalescing range.
  1273. * If so, see if it can be coalesced with the previous message.
  1274. */
  1275. AdjustForCoalescing(&pti->mlPost, NULL, message);
  1276. /*
  1277. * Put this message on the 'post' list.
  1278. */
  1279. if ((pqmsg = AllocQEntry(&pti->mlPost)) == NULL) {
  1280. RIPMSG1(RIP_WARNING, "_PostThreadMessage: Failed to alloc Q entry: Target pti=0x%p",
  1281. pti);
  1282. return FALSE;
  1283. }
  1284. /*
  1285. * Set the QS_POSTMESSAGE bit so the thread knows it has a message.
  1286. */
  1287. StoreQMessage(pqmsg, NULL, message, wParam, lParam, 0, 0, 0);
  1288. SetWakeBit(pti, QS_POSTMESSAGE | QS_ALLPOSTMESSAGE);
  1289. /*
  1290. * If it's a hotkey, set the QS_HOTKEY bit since we have a separate
  1291. * bit for those messages.
  1292. */
  1293. if (message == WM_HOTKEY)
  1294. SetWakeBit(pti, QS_HOTKEY);
  1295. /*
  1296. * Are we posting to the thread currently reading from the input queue?
  1297. * If so, update idSysLock with this pqmsg so that the input queue will
  1298. * not be unlocked until this message is read.
  1299. */
  1300. if (pti == pti->pq->ptiSysLock)
  1301. pti->pq->idSysLock = (ULONG_PTR)pqmsg;
  1302. return TRUE;
  1303. }
  1304. /***************************************************************************\
  1305. * _GetMessagePos (API)
  1306. *
  1307. * This API returns the cursor position when the last message was read from
  1308. * the current message queue.
  1309. *
  1310. * History:
  1311. * 11-19-90 DavidPe Created.
  1312. \***************************************************************************/
  1313. DWORD _GetMessagePos(VOID)
  1314. {
  1315. PTHREADINFO pti;
  1316. pti = PtiCurrent();
  1317. return MAKELONG((SHORT)pti->ptLast.x, (SHORT)pti->ptLast.y);
  1318. }
  1319. #ifdef SYSMODALWINDOWS
  1320. /***************************************************************************\
  1321. * _SetSysModalWindow (API)
  1322. *
  1323. * History:
  1324. * 01-25-91 DavidPe Created stub.
  1325. \***************************************************************************/
  1326. PWND APIENTRY _SetSysModalWindow(
  1327. PWND pwnd)
  1328. {
  1329. pwnd;
  1330. return NULL;
  1331. }
  1332. /***************************************************************************\
  1333. * _GetSysModalWindow (API)
  1334. *
  1335. * History:
  1336. * 01-25-91 DavidPe Created stub.
  1337. \***************************************************************************/
  1338. PWND APIENTRY _GetSysModalWindow(VOID)
  1339. {
  1340. return NULL;
  1341. }
  1342. #endif //LATER
  1343. /***************************************************************************\
  1344. * PostMove
  1345. *
  1346. * This routine gets called when it is detected that the QF_MOUSEMOVED bit
  1347. * is set in a particular queue.
  1348. *
  1349. * 11-03-92 ScottLu Created.
  1350. \***************************************************************************/
  1351. VOID PostMove(
  1352. PQ pq)
  1353. {
  1354. #ifdef REDIRECTION
  1355. POINT pt;
  1356. #endif // REDIRECTION
  1357. CheckCritIn();
  1358. /*
  1359. * set gdwMouseMoveTimeStamp to 0 after posting the move so
  1360. * subsequent calls to SetFMouseMove doesn't use the same value
  1361. * of gdwMouseMoveTimeStamp. Bug 74508.
  1362. */
  1363. if (gdwMouseMoveTimeStamp == 0) {
  1364. gdwMouseMoveTimeStamp = NtGetTickCount();
  1365. }
  1366. #ifdef GENERIC_INPUT
  1367. if (TestRawInputMode(pq->ptiMouse, NoLegacyMouse)) {
  1368. goto nopost;
  1369. }
  1370. #endif
  1371. #ifdef REDIRECTION
  1372. PopMouseMove(pq, &pt);
  1373. PostInputMessage(pq, NULL, WM_MOUSEMOVE, 0,
  1374. MAKELONG((SHORT)pt.x, (SHORT)pt.y),
  1375. gdwMouseMoveTimeStamp, gdwMouseMoveExtraInfo);
  1376. #else
  1377. PostInputMessage(pq, NULL, WM_MOUSEMOVE, 0,
  1378. MAKELONG((SHORT)gpsi->ptCursor.x, (SHORT)gpsi->ptCursor.y),
  1379. gdwMouseMoveTimeStamp, gdwMouseMoveExtraInfo);
  1380. #endif // REDIRECTION
  1381. #ifdef GENERIC_INPUT
  1382. nopost:
  1383. #endif
  1384. gdwMouseMoveTimeStamp = 0;
  1385. pq->QF_flags &= ~QF_MOUSEMOVED;
  1386. }
  1387. #ifdef REDIRECTION
  1388. typedef struct tagQMOUSEMOVE {
  1389. PQ pq;
  1390. POINT pt;
  1391. } QMOUSEMOVE;
  1392. #define MAX_QMOUSEMOVE 16
  1393. QMOUSEMOVE gqMouseMove[MAX_QMOUSEMOVE];
  1394. int gnLastMouseMove;
  1395. VOID PushMouseMove(
  1396. PQ pq,
  1397. POINT pt)
  1398. {
  1399. int ind;
  1400. CheckCritIn();
  1401. UserAssert(gnLastMouseMove < MAX_QMOUSEMOVE - 1);
  1402. for (ind = 0; ind < gnLastMouseMove; ind++) {
  1403. if (pq == gqMouseMove[ind].pq) {
  1404. gqMouseMove[ind].pt = pt;
  1405. return;
  1406. }
  1407. }
  1408. gqMouseMove[gnLastMouseMove].pq = pq;
  1409. gqMouseMove[gnLastMouseMove].pt = pt;
  1410. gnLastMouseMove++;
  1411. }
  1412. VOID PopMouseMove(
  1413. PQ pq,
  1414. POINT* ppt)
  1415. {
  1416. int ind;
  1417. CheckCritIn();
  1418. for (ind = 0; ind < gnLastMouseMove; ind++) {
  1419. if (pq == gqMouseMove[ind].pq) {
  1420. *ppt = gqMouseMove[ind].pt;
  1421. RtlMoveMemory(&gqMouseMove[ind],
  1422. &gqMouseMove[ind + 1],
  1423. (gnLastMouseMove - ind - 1) * sizeof(QMOUSEMOVE));
  1424. gnLastMouseMove--;
  1425. return;
  1426. }
  1427. }
  1428. UserAssert(0);
  1429. }
  1430. #endif // REDIRECTION
  1431. /***************************************************************************\
  1432. * zzzSetFMouseMoved
  1433. *
  1434. * Send a mouse move through the system. This usually occurs when doing
  1435. * window management to be sure that the mouse shape accurately reflects
  1436. * the part of the window it is currently over (window managment may have
  1437. * changed this).
  1438. *
  1439. * 11-02-92 ScottLu Created.
  1440. \***************************************************************************/
  1441. VOID zzzSetFMouseMoved(
  1442. VOID)
  1443. {
  1444. PWND pwnd;
  1445. PWND pwndOldCursor;
  1446. PQ pq;
  1447. #ifdef REDIRECTION
  1448. PWND pwndStart;
  1449. POINT ptMouse = gpsi->ptCursor;
  1450. #endif // REDIRECTION
  1451. /*
  1452. * Need to first figure out what queue this mouse event is in. Do NOT
  1453. * check for mouse capture here !! Talk to scottlu.
  1454. */
  1455. if ((pwnd = gspwndScreenCapture) == NULL) {
  1456. #ifdef REDIRECTION
  1457. /*
  1458. * Call the speed hit test hook
  1459. */
  1460. pwndStart = xxxCallSpeedHitTestHook(&ptMouse);
  1461. #endif // REDIRECTION
  1462. if ((pwnd = gspwndMouseOwner) == NULL) {
  1463. if ((pwnd = gspwndInternalCapture) == NULL) {
  1464. UserAssert(grpdeskRitInput != NULL);
  1465. #ifdef REDIRECTION
  1466. if (pwndStart == NULL) {
  1467. pwndStart = grpdeskRitInput->pDeskInfo->spwnd;
  1468. }
  1469. pwnd = SpeedHitTest(pwndStart, ptMouse);
  1470. #else
  1471. pwnd = SpeedHitTest(grpdeskRitInput->pDeskInfo->spwnd, gpsi->ptCursor);
  1472. #endif // REDIRECTION
  1473. }
  1474. }
  1475. }
  1476. if (pwnd == NULL)
  1477. return;
  1478. /*
  1479. * This is apparently needed by the attach/unattach code for some
  1480. * reason. I'd like to get rid of it - scottlu.
  1481. */
  1482. pwndOldCursor = Lock(&gspwndCursor, pwnd);
  1483. /*
  1484. * If we're giving a mouse move to a new queue, be sure the cursor
  1485. * image represents what this queue thinks it should be.
  1486. */
  1487. pq = GETPTI(pwnd)->pq;
  1488. /*
  1489. * Protect pq by deferring WinEvent notification
  1490. */
  1491. DeferWinEventNotify();
  1492. if (pq != gpqCursor) {
  1493. /*
  1494. * If the old queue had the mouse captured, let him know that
  1495. * the mouse moved first. Need this to fix tooltips in
  1496. * WordPerfect Office. Do the same for mouse tracking.
  1497. */
  1498. if (gpqCursor != NULL) {
  1499. if (gpqCursor->spwndCapture != NULL) {
  1500. gpqCursor->QF_flags |= QF_MOUSEMOVED;
  1501. SetWakeBit(GETPTI(gpqCursor->spwndCapture), QS_MOUSEMOVE);
  1502. #ifdef REDIRECTION
  1503. PushMouseMove(gpqCursor, ptMouse);
  1504. #endif // REDIRECTION
  1505. }
  1506. if ((pwndOldCursor != NULL) && (PtoHq(pwndOldCursor) != PtoHq(pwnd))) {
  1507. PDESKTOP pdesk = GETPDESK(pwndOldCursor);
  1508. if (pdesk->dwDTFlags & DF_MOUSEMOVETRK) {
  1509. PTHREADINFO pti = GETPTI(pdesk->spwndTrack);
  1510. PostEventMessage(pti, pti->pq, QEVENT_CANCELMOUSEMOVETRK,
  1511. pdesk->spwndTrack, pdesk->dwDTFlags, pdesk->htEx,
  1512. DF_MOUSEMOVETRK);
  1513. pdesk->dwDTFlags &= ~DF_MOUSEMOVETRK;
  1514. }
  1515. }
  1516. }
  1517. /*
  1518. * First re-assign gpqCursor so any zzzSetCursor() calls
  1519. * will only take effect if done by the thread that
  1520. * owns the window the mouse is currently over.
  1521. */
  1522. gpqCursor = pq;
  1523. /*
  1524. * Call zzzUpdateCursorImage() so the new gpqCursor's
  1525. * notion of the current cursor is represented.
  1526. */
  1527. zzzUpdateCursorImage();
  1528. }
  1529. /*
  1530. * Set the mouse moved bit for this queue so we know later to post
  1531. * a move message to this queue.
  1532. */
  1533. pq->QF_flags |= QF_MOUSEMOVED;
  1534. #ifdef REDIRECTION
  1535. PushMouseMove(pq, ptMouse);
  1536. #endif // REDIRECTION
  1537. /*
  1538. * Reassign mouse input to this thread - this indicates which thread
  1539. * to wake up when new input comes in.
  1540. */
  1541. pq->ptiMouse = GETPTI(pwnd);
  1542. /*
  1543. * Wake some thread within this queue to process this mouse event.
  1544. */
  1545. WakeSomeone(pq, WM_MOUSEMOVE, NULL);
  1546. /*
  1547. * We're possibly generating a fake mouse move message - it has no
  1548. * extra info associated with it - so 0 it out.
  1549. */
  1550. gdwMouseMoveExtraInfo = 0;
  1551. zzzEndDeferWinEventNotify();
  1552. }
  1553. /***************************************************************************\
  1554. * CancelForegroundActivate
  1555. *
  1556. * This routine cancels the foreground activate that we allow apps starting
  1557. * up to have. This means that if you make a request to start an app,
  1558. * if this routine is called before the app becomes foreground, it won't
  1559. * become foreground. This routine gets called if the user down clicks or
  1560. * makes a keydown event, with the idea being that if the user did this,
  1561. * the user is using some other app and doesn't want the newly starting
  1562. * application to appear on top and force itself into the foreground.
  1563. *
  1564. * 09-15-92 ScottLu Created.
  1565. \***************************************************************************/
  1566. void CancelForegroundActivate()
  1567. {
  1568. PPROCESSINFO ppiT;
  1569. if (TEST_PUDF(PUDF_ALLOWFOREGROUNDACTIVATE)) {
  1570. for (ppiT = gppiStarting; ppiT != NULL; ppiT = ppiT->ppiNext) {
  1571. /*
  1572. * Don't cancel activation if the app is being debugged - if
  1573. * the debugger stops the application before it has created and
  1574. * activated its first window, the app will come up behind all
  1575. * others - not what you want when being debugged.
  1576. */
  1577. if (!PsGetProcessDebugPort(ppiT->Process)) {
  1578. ppiT->W32PF_Flags &= ~W32PF_ALLOWFOREGROUNDACTIVATE;
  1579. TAGMSG1(DBGTAG_FOREGROUND, "CancelForegroundActivate clear W32PF %#p", ppiT);
  1580. }
  1581. }
  1582. CLEAR_PUDF(PUDF_ALLOWFOREGROUNDACTIVATE);
  1583. TAGMSG0(DBGTAG_FOREGROUND, "CancelForegroundActivate clear PUDF");
  1584. }
  1585. }
  1586. /***************************************************************************\
  1587. * RestoreForegroundActivate
  1588. *
  1589. * This routine re-enables an app's right to foreground activate (activate and
  1590. * come on top) if it is starting up. This is called when we minimize or when
  1591. * the last window of a thread goes away, for example.
  1592. *
  1593. * 01-26-93 ScottLu Created.
  1594. \***************************************************************************/
  1595. void RestoreForegroundActivate()
  1596. {
  1597. PPROCESSINFO ppiT;
  1598. for (ppiT = gppiStarting; ppiT != NULL; ppiT = ppiT->ppiNext) {
  1599. if (ppiT->W32PF_Flags & W32PF_APPSTARTING) {
  1600. ppiT->W32PF_Flags |= W32PF_ALLOWFOREGROUNDACTIVATE;
  1601. TAGMSG1(DBGTAG_FOREGROUND, "RestoreForegroundActivate set W32PF %#p", ppiT);
  1602. SET_PUDF(PUDF_ALLOWFOREGROUNDACTIVATE);
  1603. TAGMSG0(DBGTAG_FOREGROUND, "RestoreForegroundActivate set PUDF");
  1604. }
  1605. }
  1606. }
  1607. /***************************************************************************\
  1608. * PostInputMessage
  1609. *
  1610. * Puts a message on the 'input' linked-list of message for the specified
  1611. * queue.
  1612. *
  1613. * History:
  1614. * 10-25-90 DavidPe Created.
  1615. * 01-21-92 DavidPe Rewrote to deal with OOM errors gracefully.
  1616. \***************************************************************************/
  1617. BOOL PostInputMessage(
  1618. PQ pq,
  1619. PWND pwnd,
  1620. UINT message,
  1621. WPARAM wParam,
  1622. LPARAM lParam,
  1623. DWORD time,
  1624. ULONG_PTR dwExtraInfo)
  1625. {
  1626. PQMSG pqmsgInput, pqmsgPrev;
  1627. short sWheelDelta;
  1628. #ifdef GENERIC_INPUT
  1629. #if DBG
  1630. /*
  1631. * Verify that the wParam that'll be sent with the WM_INPUT matches
  1632. * what's stored in the RAWINPUTHEADER.
  1633. */
  1634. if (message == WM_INPUT) {
  1635. PHIDDATA pHidData = HtoP(lParam);
  1636. UserAssert(pHidData->rid.header.wParam == wParam);
  1637. }
  1638. #endif // DBG
  1639. #endif // GENERIC_INPUT
  1640. /*
  1641. * Grab the last written message before we start allocing new ones,
  1642. * so we're sure to point to the correct message.
  1643. */
  1644. pqmsgPrev = pq->mlInput.pqmsgWriteLast;
  1645. /*
  1646. * Allocate a key state update event if needed.
  1647. */
  1648. if (pq->QF_flags & QF_UPDATEKEYSTATE) {
  1649. PostUpdateKeyStateEvent(pq);
  1650. }
  1651. #ifdef GENERIC_INPUT
  1652. /*
  1653. * We don't want WM_INPUT messages inhibiting the coalescing of
  1654. * WM_MOUSEMOVE and WM_MOUSEWHEEL, so if the message being posted
  1655. * is one of those we check to see if there are any previous ones
  1656. * that would be "hidden" by a WM_INPUT.
  1657. */
  1658. if (message == WM_MOUSEMOVE || message == WM_MOUSEWHEEL) {
  1659. while (pqmsgPrev && pqmsgPrev->msg.message == WM_INPUT) {
  1660. pqmsgPrev = pqmsgPrev->pqmsgPrev;
  1661. }
  1662. }
  1663. #endif // GENERIC_INPUT
  1664. /*
  1665. * We want to coalesce sequential WM_MOUSEMOVE and WM_MOUSEWHEEL.
  1666. * WM_MOUSEMOVEs are coalesced by just storing the most recent
  1667. * event over the last one.
  1668. * WM_MOUSEWHEELs also add up the wheel rolls.
  1669. */
  1670. if (pqmsgPrev != NULL &&
  1671. pqmsgPrev->msg.message == message &&
  1672. (message == WM_MOUSEMOVE || message == WM_MOUSEWHEEL)) {
  1673. if (message == WM_MOUSEWHEEL) {
  1674. sWheelDelta = (short)HIWORD(wParam) + (short)HIWORD(pqmsgPrev->msg.wParam);
  1675. #if 0
  1676. /*
  1677. * LATER: We can't remove a wheel message with zero delta
  1678. * unless we know it hasn't been peeked. Ideally,
  1679. * we would check idsyspeek for this, but we're too close
  1680. * to ship and idsyspeek is too fragile. Consider also
  1681. * checking to see if mouse move messages have been peeked.
  1682. */
  1683. if (sWheelDelta == 0) {
  1684. if ((PQMSG)pq->idSysPeek == pqmsgPrev) {
  1685. RIPMSG0(RIP_VERBOSE,
  1686. "Coalescing of mouse wheel messages causing "
  1687. "idSysPeek to be reset to 0");
  1688. pq->idSysPeek = 0;
  1689. }
  1690. DelQEntry(&pq->mlInput, pqmsgPrev);
  1691. return;
  1692. }
  1693. #endif
  1694. wParam = MAKEWPARAM(LOWORD(wParam), sWheelDelta);
  1695. }
  1696. StoreQMessage(pqmsgPrev, pwnd, message, wParam, lParam, time, 0, dwExtraInfo);
  1697. WakeSomeone(pq, message, pqmsgPrev);
  1698. return TRUE;
  1699. }
  1700. /*
  1701. * Fill in pqmsgInput.
  1702. */
  1703. pqmsgInput = AllocQEntry(&pq->mlInput);
  1704. if (pqmsgInput == NULL) {
  1705. return FALSE;
  1706. }
  1707. StoreQMessage(pqmsgInput, pwnd, message, wParam, lParam, time, 0, dwExtraInfo);
  1708. WakeSomeone(pq, message, pqmsgInput);
  1709. return TRUE;
  1710. }
  1711. /***************************************************************************\
  1712. * WakeSomeone
  1713. *
  1714. * Figures out which thread to wake up based on the queue and message.
  1715. * If the queue pointer is NULL, figures out a likely queue.
  1716. *
  1717. * 10-23-92 ScottLu Created.
  1718. \***************************************************************************/
  1719. void WakeSomeone(
  1720. PQ pq,
  1721. UINT message,
  1722. PQMSG pqmsg)
  1723. {
  1724. BOOL fSetLastWoken = FALSE;
  1725. PTHREADINFO ptiT;
  1726. /*
  1727. * Set the appropriate wakebits for this queue.
  1728. */
  1729. ptiT = NULL;
  1730. switch (message) {
  1731. case WM_SYSKEYDOWN:
  1732. case WM_KEYDOWN:
  1733. /*
  1734. * Don't change input ownership if the user is holding down
  1735. * a modifier key. When doing a ctrl-drag operation for example,
  1736. * the ctrl key must be down when the user drops the object (ie, mouse up).
  1737. * On mouse up the RIT gives input ownership to the target; but since
  1738. * ctrl is down, on the next repeat key we used to give input ownership
  1739. * to the focus window (usually the drag source). Hence the target
  1740. * would lose owenerhip and couldn't take the foreground.
  1741. */
  1742. if (pqmsg != NULL) {
  1743. switch (pqmsg->msg.wParam) {
  1744. case VK_SHIFT:
  1745. case VK_CONTROL:
  1746. case VK_MENU:
  1747. if (TestKeyStateDown(pq, pqmsg->msg.wParam)) {
  1748. break;
  1749. }
  1750. /* Fall through */
  1751. default:
  1752. fSetLastWoken = TRUE;
  1753. break;
  1754. }
  1755. } else {
  1756. fSetLastWoken = TRUE;
  1757. }
  1758. /* fall through */
  1759. case WM_SYSCHAR:
  1760. case WM_CHAR:
  1761. /* Freelance graphics seems to pass in WM_SYSCHARs and WM_CHARs into
  1762. * the journal playback hook, so we need to set an input bit for
  1763. * this case since that is what win3.1 does. VB2 "learning" demo does
  1764. * the same, as does Excel intro.
  1765. *
  1766. * On win3.1, the WM_CHAR would by default set the QS_MOUSEBUTTON bit.
  1767. * On NT, the WM_CHAR sets the QS_KEY bit. This is because
  1768. * ScanSysQueue() calls TransferWakeBit() with the QS_KEY bit when
  1769. * a WM_CHAR message is passed in. By using the QS_KEY bit on NT,
  1770. * we're more compatible with what win3.1 wants to be.
  1771. *
  1772. * This fixes a case where the mouse was over progman, the WM_CHAR
  1773. * would come in via journal playback, wakesomeone would be called,
  1774. * and set the mouse bit in progman. Progman would then get into
  1775. * ScanSysQueue(), callback the journal playback hook, get the WM_CHAR,
  1776. * and do it again, looping. This caught VB2 in a loop.
  1777. */
  1778. CancelForegroundActivate();
  1779. /* fall through */
  1780. case WM_KEYUP:
  1781. case WM_SYSKEYUP:
  1782. case WM_MOUSEWHEEL:
  1783. /*
  1784. * Win3.1 first looks at what thread has the active status. This
  1785. * means that we don't depend on the thread owning ptiKeyboard
  1786. * to wake up and process this key in order to give it to the
  1787. * active window, which is potentially newly active. Case in
  1788. * point: excel bringing up CBT, cbt has an error, brings up
  1789. * a message box: since excel is filtering for CBT messages only,
  1790. * ptiKeyboard never gets reassigned to CBT so CBT doesn't get
  1791. * any key messages and appears hung.
  1792. */
  1793. ptiT = pq->ptiKeyboard;
  1794. if (pq->spwndActive != NULL)
  1795. ptiT = GETPTI(pq->spwndActive);
  1796. #ifdef GENERIC_INPUT
  1797. UserAssert(ptiT == PtiKbdFromQ(pq));
  1798. #endif
  1799. SetWakeBit(ptiT, message == WM_MOUSEWHEEL ? QS_MOUSEBUTTON : QS_KEY);
  1800. break;
  1801. case WM_MOUSEMOVE:
  1802. /*
  1803. * Make sure we wake up the thread with the capture, if there is
  1804. * one. This fixes PC Tools screen capture program, which sets
  1805. * capture and then loops trying to remove messages from the
  1806. * queue.
  1807. */
  1808. if (pq->spwndCapture != NULL)
  1809. ptiT = GETPTI(pq->spwndCapture);
  1810. else
  1811. ptiT = pq->ptiMouse;
  1812. #ifdef GENERIC_INPUT
  1813. UserAssert(ptiT == PtiMouseFromQ(pq));
  1814. #endif
  1815. SetWakeBit(ptiT, QS_MOUSEMOVE);
  1816. break;
  1817. case WM_LBUTTONDOWN:
  1818. case WM_LBUTTONDBLCLK:
  1819. case WM_RBUTTONDOWN:
  1820. case WM_RBUTTONDBLCLK:
  1821. case WM_MBUTTONDOWN:
  1822. case WM_MBUTTONDBLCLK:
  1823. case WM_XBUTTONDOWN:
  1824. case WM_XBUTTONDBLCLK:
  1825. fSetLastWoken = TRUE;
  1826. /* fall through */
  1827. default:
  1828. /*
  1829. * The default case in Win3.1 for this is QS_MOUSEBUTTON.
  1830. */
  1831. CancelForegroundActivate();
  1832. /* fall through */
  1833. case WM_LBUTTONUP:
  1834. case WM_RBUTTONUP:
  1835. case WM_MBUTTONUP:
  1836. case WM_XBUTTONUP:
  1837. /*
  1838. * Make sure we wake up the thread with the capture, if there is
  1839. * one. This fixes PC Tools screen capture program, which sets
  1840. * capture and then loops trying to remove messages from the
  1841. * queue.
  1842. */
  1843. if (pq->spwndCapture != NULL &&
  1844. message >= WM_MOUSEFIRST && message <= WM_MOUSELAST)
  1845. ptiT = GETPTI(pq->spwndCapture);
  1846. else
  1847. ptiT = pq->ptiMouse;
  1848. SetWakeBit(ptiT, QS_MOUSEBUTTON);
  1849. break;
  1850. #ifdef GENERIC_INPUT
  1851. case WM_INPUT:
  1852. if (pqmsg->msg.hwnd) {
  1853. PWND pwnd = ValidateHwnd(pqmsg->msg.hwnd);
  1854. if (pwnd) {
  1855. ptiT = GETPTI(pwnd);
  1856. TAGMSG2(DBGTAG_PNP, "WakeSomeone: adjusted receiver pti %p for pwndTarget %p", ptiT, pwnd);
  1857. }
  1858. }
  1859. if (ptiT == NULL) {
  1860. ptiT = PtiKbdFromQ(pq);
  1861. }
  1862. SetWakeBit(ptiT, QS_RAWINPUT);
  1863. break;
  1864. #endif
  1865. }
  1866. /*
  1867. * If a messaged was passed in, remember in it who we woke up for this
  1868. * message. We do this so each message is ownership marked. This way
  1869. * we can merge/unmerge message streams when zzzAttachThreadInput() is
  1870. * called.
  1871. */
  1872. if (ptiT != NULL) {
  1873. if (pqmsg != NULL) {
  1874. StoreQMessagePti(pqmsg, ptiT);
  1875. UserAssert(!(ptiT->TIF_flags & TIF_INCLEANUP));
  1876. }
  1877. /*
  1878. * Remember who got the last key/click down.
  1879. */
  1880. if (fSetLastWoken) {
  1881. glinp.ptiLastWoken = ptiT;
  1882. }
  1883. }
  1884. }
  1885. /***************************************************************************\
  1886. * PostUpdateKeyStateEvent
  1887. *
  1888. * This routine posts an event which updates the local thread's keystate
  1889. * table. This makes sure the thread's key state is always up-to-date.
  1890. *
  1891. * An example is: control-esc from cmd to taskman.
  1892. * Control goes to cmd, but taskman is activated. Control state is still down
  1893. * in cmd - switch back to cmd, start typing, nothing appears because it thinks
  1894. * the control state is still down.
  1895. *
  1896. * As events go into a particular queue (queue A), the async key state table is
  1897. * updated. As long as transition events are put into queue A, the key
  1898. * state at the logical "end of the queue" is up-to-date with the async key
  1899. * state. As soon as the user posts transition events (up/down msgs) into queue
  1900. * B, the queue A's end-of-queue key state is out of date with the user. If
  1901. * the user then again added messages to queue A, when those messages are read
  1902. * the thread specific key state would not be updated correctly unless we
  1903. * did some synchronization (which this routine helps to do).
  1904. *
  1905. * As soon as transition events change queues, we go mark all the other queues
  1906. * with the QF_UPDATEKEYSTATE flag. Before any input event is posted into
  1907. * a queue, this flag is checked, and if set, this routine is called. This
  1908. * routine makes a copy of the async key state, and a copy of the bits
  1909. * representing the keys that have changed since the last update (we need to
  1910. * keep track of which keys have changed so that any state set by the
  1911. * app with SetKeyboardState() doesn't get wiped out). We take this data
  1912. * and post a new event of the type QEVENT_UPDATEKEYSTATE, which points to this
  1913. * key state and transition information. When this message is read out of the
  1914. * queue, this key state copy is copied into the thread specific key state
  1915. * table for those keys that have changed, and the copy is deallocated.
  1916. *
  1917. * This ensures all queues are input-synchronized with key transitions no matter
  1918. * where they occur. The side affect of this is that an application may suddenly
  1919. * have a key be up without seeing the up message. If this causes any problems
  1920. * we may have to generate false transition messages (this could have more nasty
  1921. * side affects as well, so it needs to be considered closely before being
  1922. * implemented.)
  1923. *
  1924. * 06-07-91 ScottLu Created.
  1925. \***************************************************************************/
  1926. void PostUpdateKeyStateEvent(
  1927. PQ pq)
  1928. {
  1929. BYTE *pb;
  1930. PQMSG pqmsg;
  1931. if (!(pq->QF_flags & QF_UPDATEKEYSTATE))
  1932. return;
  1933. /*
  1934. * Exclude the RIT - it's queue is never read, so don't waste memory
  1935. */
  1936. if (pq->ptiKeyboard == gptiRit) {
  1937. return;
  1938. }
  1939. /*
  1940. * If there's no mousebutton or keystroke input pending, process the
  1941. * UpdateKeyState Event now: thus saving memory allocation and giving
  1942. * applications the correct KeyState immediately.
  1943. * NOTE: There may be event/activation msgs in pq->mlInput that don't
  1944. * affect keystate, so I'd like to just test QS_KEY | QS_MOUSEBUTTON
  1945. * specifically, instead of the cMsgss. However, sometimes there are
  1946. * keystroke or mousebutton msgs in the q without those bits set! - IanJa
  1947. */
  1948. if (pq->mlInput.cMsgs == 0) {
  1949. ProcessUpdateKeyStateEvent(pq, gafAsyncKeyState, pq->afKeyRecentDown);
  1950. goto SyncQueue;
  1951. }
  1952. #if DBG
  1953. else if ((!pq->ptiKeyboard || !(pq->ptiKeyboard->pcti->fsWakeBits & QS_KEY)) &&
  1954. (!pq->ptiMouse || !(pq->ptiMouse->pcti->fsWakeBits & QS_MOUSEBUTTON))) {
  1955. /*
  1956. * See if there are any key or mousebutton messages that aren't
  1957. * indicated by QS_KEY or QS_MOUSEBUTTON bits.
  1958. */
  1959. PQMSG pqmsgT;
  1960. for (pqmsgT = pq->mlInput.pqmsgRead; pqmsgT; pqmsgT = pqmsgT->pqmsgNext) {
  1961. if (pqmsgT->msg.message >= WM_KEYFIRST && pqmsgT->msg.message <= WM_KEYLAST) {
  1962. TAGMSG1(DBGTAG_InputWithoutQS,
  1963. "PostUpdateKeyStateEvent() pushing in front of a keystroke: Q %#p", pq);
  1964. } else if (pqmsgT->msg.message >= WM_LBUTTONDOWN && pqmsgT->msg.message <= WM_XBUTTONDBLCLK) {
  1965. TAGMSG1(DBGTAG_InputWithoutQS,
  1966. "PostUpdateKeyStateEvent() pushing in front of a mousebutton: Q %#p", pq);
  1967. }
  1968. }
  1969. }
  1970. #endif
  1971. UserAssert(pq->mlInput.pqmsgWriteLast != NULL);
  1972. /*
  1973. * If the last input message is an UPDATEKEYSTATE event, coalesce with it.
  1974. * (Prevents big memory leaks on apps that don't read input messages)
  1975. */
  1976. pqmsg = pq->mlInput.pqmsgWriteLast;
  1977. if (pqmsg->dwQEvent == QEVENT_UPDATEKEYSTATE) {
  1978. int i;
  1979. DWORD *pdw;
  1980. pb = (PBYTE)(pqmsg->msg.wParam);
  1981. pdw = (DWORD *) (pb + CBKEYSTATE);
  1982. /*
  1983. * Copy in the new key state
  1984. */
  1985. RtlCopyMemory(pb, gafAsyncKeyState, CBKEYSTATE);
  1986. /*
  1987. * Or in the recent key down state (DWORD at a time)
  1988. */
  1989. #if (CBKEYSTATERECENTDOWN % 4) != 0
  1990. #error "CBKEYSTATERECENTDOWN assumed to be an integral number of DWORDs"
  1991. #endif
  1992. for (i = 0; i < CBKEYSTATERECENTDOWN / sizeof(*pdw); i++) {
  1993. *pdw++ |= ((DWORD *)(pq->afKeyRecentDown))[i];
  1994. }
  1995. /*
  1996. * Set QS_EVENTSET as in PostEventMessage, although this is
  1997. * usually, but not always already set
  1998. */
  1999. SetWakeBit(pq->ptiKeyboard, QS_EVENTSET);
  2000. goto SyncQueue;
  2001. }
  2002. /*
  2003. * Make a copy of the async key state buffer, point to it, and add an
  2004. * event to the end of the input queue.
  2005. */
  2006. if ((pb = UserAllocPool(KEYSTATESIZE, TAG_KBDSTATE)) == NULL) {
  2007. return;
  2008. }
  2009. RtlCopyMemory(pb, gafAsyncKeyState, CBKEYSTATE);
  2010. RtlCopyMemory(pb + CBKEYSTATE, pq->afKeyRecentDown, CBKEYSTATERECENTDOWN);
  2011. if (!PostEventMessage(pq->ptiKeyboard, pq, QEVENT_UPDATEKEYSTATE,
  2012. NULL, 0 , (WPARAM)pb, 0)) {
  2013. UserFreePool(pb);
  2014. return;
  2015. }
  2016. /*
  2017. * The key state of the queue is input-synchronized with the user. Erase
  2018. * all 'recent down' flags.
  2019. */
  2020. SyncQueue:
  2021. RtlZeroMemory(pq->afKeyRecentDown, CBKEYSTATERECENTDOWN);
  2022. pq->QF_flags &= ~QF_UPDATEKEYSTATE;
  2023. }
  2024. /***************************************************************************\
  2025. * ProcessUpdateKeyStateEvent
  2026. *
  2027. * This is part two of the above routine, called when the QEVENT_UPDATEKEYSTATE
  2028. * message is read out of the input queue.
  2029. *
  2030. * 06-07-91 ScottLu Created.
  2031. \***************************************************************************/
  2032. void ProcessUpdateKeyStateEvent(
  2033. PQ pq,
  2034. CONST PBYTE pbKeyState,
  2035. CONST PBYTE pbRecentDown)
  2036. {
  2037. int i, j;
  2038. BYTE *pbChange;
  2039. int vk;
  2040. pbChange = pbRecentDown;
  2041. for (i = 0; i < CBKEYSTATERECENTDOWN; i++, pbChange++) {
  2042. /*
  2043. * Find some keys that have changed.
  2044. */
  2045. if (*pbChange == 0)
  2046. continue;
  2047. /*
  2048. * Some keys have changed in this byte. find out which key it is.
  2049. */
  2050. for (j = 0; j < 8; j++) {
  2051. /*
  2052. * Convert our counts to a virtual key index and check to see
  2053. * if this key has changed.
  2054. */
  2055. vk = (i << 3) + j;
  2056. if (!TestKeyRecentDownBit(pbRecentDown, vk))
  2057. continue;
  2058. /*
  2059. * This key has changed. Update it's state in the thread key
  2060. * state table.
  2061. */
  2062. if (TestKeyDownBit(pbKeyState, vk)) {
  2063. SetKeyStateDown(pq, vk);
  2064. } else {
  2065. ClearKeyStateDown(pq, vk);
  2066. }
  2067. if (TestKeyToggleBit(pbKeyState, vk)) {
  2068. SetKeyStateToggle(pq, vk);
  2069. } else {
  2070. ClearKeyStateToggle(pq, vk);
  2071. }
  2072. }
  2073. }
  2074. /*
  2075. * Update the key cache index.
  2076. */
  2077. gpsi->dwKeyCache++;
  2078. /*
  2079. * All updated. Free the key state table if it was posted as an Event Message
  2080. */
  2081. if (pbKeyState != gafAsyncKeyState) {
  2082. UserFreePool(pbKeyState);
  2083. }
  2084. }
  2085. /***************************************************************************\
  2086. * PostEventMessage
  2087. *
  2088. *
  2089. * History:
  2090. * 03-04-91 DavidPe Created.
  2091. \***************************************************************************/
  2092. BOOL PostEventMessage(
  2093. PTHREADINFO pti,
  2094. PQ pq,
  2095. DWORD dwQEvent,
  2096. PWND pwnd,
  2097. UINT message,
  2098. WPARAM wParam,
  2099. LPARAM lParam)
  2100. {
  2101. PQMSG pqmsgEvent;
  2102. CheckCritIn();
  2103. /*
  2104. * If the thread is in cleanup, then it's possible the queue has
  2105. * already been removed for this thread. If this is the case, then
  2106. * we should fail to post the event to a dying thread.
  2107. */
  2108. if (pti && (pti->TIF_flags & TIF_INCLEANUP))
  2109. return FALSE;
  2110. if ((pqmsgEvent = AllocQEntry(&pq->mlInput)) == NULL)
  2111. return FALSE;
  2112. StoreQMessage(pqmsgEvent, pwnd, message, wParam, lParam, 0, dwQEvent, 0);
  2113. StoreQMessagePti(pqmsgEvent, pti);
  2114. /*
  2115. * Let this thread know it has an event message to process.
  2116. */
  2117. if (pti == NULL) {
  2118. UserAssert(pti);
  2119. SetWakeBit(pq->ptiMouse, QS_EVENTSET);
  2120. SetWakeBit(pq->ptiKeyboard, QS_EVENTSET);
  2121. } else {
  2122. SetWakeBit(pti, QS_EVENTSET);
  2123. }
  2124. return TRUE;
  2125. }
  2126. /***************************************************************************\
  2127. * CheckOnTop
  2128. *
  2129. * Usually windows come to the top and activate all at once. Occasionally, a
  2130. * starting app will create a window, pause for awhile, then make itself
  2131. * visible. During that pause, if the user clicks down, the window won't be
  2132. * allowed to activate (because of our foreground activation model). But this
  2133. * still leaves the new window on top of the active window. When this click
  2134. * happens, we get here: if this window is active and is not on top, bring it
  2135. * to the top.
  2136. *
  2137. * Case in point: start winquote, click down. The window
  2138. * you clicked on is active, but winquote is on top.
  2139. *
  2140. * This rarely does anything because 99.99% of the time the active
  2141. * window is already where it should be - on top. Note that
  2142. * CalcForegroundInsertAfter() takes into account owner-based zordering.
  2143. *
  2144. *
  2145. * NOTE: the following was the original function written. However, this
  2146. * function has been disabled for now. in WinWord and Excel especially,
  2147. * this tends to cause savebits to be blown-away on mouse-activation of
  2148. * its dialog-boxes. This could be a problem with GW_HWNDPREV and/or
  2149. * CalcForground not being the same, which causes a SetWindowPos to be
  2150. * called, resulting in the freeing of the SPB. This also solves a
  2151. * problem with the ComboBoxes in MsMoney hiding/freeing the dropdown
  2152. * listbox on activation as well.
  2153. *
  2154. * We need this for ActiveWindowTracking support. xxxBringWindowToTop used to be a call
  2155. * to SetWindowPos but now it's gone.
  2156. *
  2157. * It returns TRUE if the window was brought the top; if no z-order change
  2158. * took place, it returns FALSE.
  2159. *
  2160. * 05-20-93 ScottLu Created.
  2161. * 10-17-94 ChrisWil Made into stub-macro.
  2162. * 05-30-96 GerardoB Brought it back to live for AWT
  2163. \***************************************************************************/
  2164. BOOL CheckOnTop(PTHREADINFO pti, PWND pwndTop, UINT message)
  2165. {
  2166. if (pwndTop != pti->pq->spwndActive)
  2167. return FALSE;
  2168. switch (message) {
  2169. case WM_LBUTTONDOWN:
  2170. case WM_RBUTTONDOWN:
  2171. case WM_MBUTTONDOWN:
  2172. case WM_XBUTTONDOWN:
  2173. if ( TestWF(pwndTop, WEFTOPMOST)
  2174. || (_GetWindow(pwndTop, GW_HWNDPREV) != CalcForegroundInsertAfter(pwndTop))) {
  2175. return xxxSetWindowPos(pwndTop, PWND_TOP, 0, 0, 0, 0,
  2176. SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
  2177. }
  2178. break;
  2179. }
  2180. return FALSE;
  2181. }
  2182. #define MA_PASSTHRU 0
  2183. #define MA_SKIP 1
  2184. #define MA_REHITTEST 2
  2185. /***************************************************************************\
  2186. * zzzActiveCursorTracking
  2187. *
  2188. * If active window tracking is enabled, activation follows
  2189. * the mouse. If the mouse is NOT on the active window
  2190. * (i.e., it was activated by a keyboard operation),
  2191. * activation will change as soon as the mouse moves.
  2192. * So we have to make sure the mouse is on the active window.
  2193. *
  2194. * History
  2195. * 12/07/96 GerardoB Created
  2196. \***************************************************************************/
  2197. void zzzActiveCursorTracking (PWND pwnd)
  2198. {
  2199. BOOL fVisible;
  2200. POINT pt;
  2201. /*
  2202. * If the last input event wasn't from the keyboard, bail
  2203. * The user is probably moving the mouse.
  2204. */
  2205. if (!(glinp.dwFlags & LINP_KEYBOARD)) {
  2206. return;
  2207. }
  2208. /*
  2209. * If we're already there, bail.
  2210. */
  2211. if (PtInRect((LPRECT)&pwnd->rcWindow, gptCursorAsync)) {
  2212. return;
  2213. }
  2214. /*
  2215. * If the window the mouse is on is not "active-trackable", then
  2216. * we can leave the mouse right where it is
  2217. */
  2218. if ((gspwndCursor != NULL) && (GetActiveTrackPwnd(gspwndCursor, NULL) == NULL)) {
  2219. return;
  2220. }
  2221. /*
  2222. * If this window doesn't have a point visible in the screen, bail
  2223. */
  2224. pt.x = pwnd->rcWindow.left + ((pwnd->rcWindow.right - pwnd->rcWindow.left) / 2);
  2225. pt.y = pwnd->rcWindow.top + ((pwnd->rcWindow.bottom - pwnd->rcWindow.top) / 2);
  2226. BoundCursor(&pt);
  2227. if (!PtInRect((LPRECT)&pwnd->rcWindow, pt)) {
  2228. return;
  2229. }
  2230. /*
  2231. * We need to make sure that this window is marked as visible or someone
  2232. * else will be waken up to update the cursor (and might
  2233. * activate itself because of the active tracking).
  2234. *
  2235. * Later5.0 GerardoB: If the window is still not visible when
  2236. * it wakes up, then we're out of luck.
  2237. */
  2238. fVisible = TestWF(pwnd, WFVISIBLE);
  2239. if (!fVisible) {
  2240. SetVisible(pwnd, SV_SET);
  2241. }
  2242. /*
  2243. * Move the cursor to the center of this window
  2244. */
  2245. zzzInternalSetCursorPos(pt.x, pt.y);
  2246. /*
  2247. * Restore visible bit.
  2248. */
  2249. if (!fVisible) {
  2250. SetVisible(pwnd, SV_UNSET);
  2251. }
  2252. }
  2253. /***************************************************************************\
  2254. * GetActiveTrackPwnd
  2255. *
  2256. * History
  2257. * 12/07/96 GerardoB Extracted from xxxActiveWindowTracking.
  2258. \***************************************************************************/
  2259. PWND GetActiveTrackPwnd(PWND pwnd, Q **ppq)
  2260. {
  2261. PWND pwndActivate;
  2262. Q *pq;
  2263. CheckCritIn();
  2264. pwndActivate = pwnd;
  2265. /*
  2266. * Find the top parent
  2267. */
  2268. while (TestwndChild(pwndActivate)) {
  2269. pwndActivate = pwndActivate->spwndParent;
  2270. }
  2271. /*
  2272. * If disabled, get a enabled popup owned by it.
  2273. */
  2274. if (TestWF(pwndActivate, WFDISABLED)) {
  2275. /*
  2276. * This is what we do elsewhere when someone clicks on a
  2277. * disabled non-active window. It might be cheaper to check
  2278. * pwnd->spwndLastActive first (we might need to walk up
  2279. * the owner chain though, as this is where we set spwndLastAcitve
  2280. * when activating a new window. see xxxActivateThisWindow).
  2281. * But let's do the same here; this should be fixed/improved
  2282. * in DWP_GetEnabledPopup anyway. There might be a reason
  2283. * why we don't grab spwndLastActive if OK.... perhaps it has
  2284. * something to do with nested owner windows
  2285. */
  2286. pwndActivate = DWP_GetEnabledPopup(pwndActivate);
  2287. }
  2288. /*
  2289. * Bail if we didn't find a visible window
  2290. */
  2291. if ((pwndActivate == NULL) || !TestWF(pwndActivate, WFVISIBLE)) {
  2292. return NULL;
  2293. }
  2294. /*
  2295. * If already active in the foreground queue, nothing to do
  2296. * Don't activate the modeless menu notification window (it would
  2297. * dismiss the menu)
  2298. */
  2299. pq = GETPTI(pwndActivate)->pq;
  2300. if ((pq == gpqForeground)
  2301. && ((pwndActivate == pq->spwndActive)
  2302. || IsModelessMenuNotificationWindow(pwndActivate))) {
  2303. return NULL;
  2304. }
  2305. /*
  2306. * Don't activate the shell window.
  2307. */
  2308. if (pwndActivate == pwndActivate->head.rpdesk->pDeskInfo->spwndShell) {
  2309. return NULL;
  2310. }
  2311. /*
  2312. * Return the queue if requested
  2313. */
  2314. if (ppq != NULL) {
  2315. *ppq = pq;
  2316. }
  2317. return pwndActivate;
  2318. }
  2319. /***************************************************************************\
  2320. * xxxActivateWindowTracking
  2321. *
  2322. * Activates a window without z-ordering it to the top
  2323. *
  2324. * 06/05/96 GerardoB Created
  2325. \***************************************************************************/
  2326. int xxxActiveWindowTracking(
  2327. PWND pwnd,
  2328. UINT uMsg,
  2329. int iHitTest)
  2330. {
  2331. BOOL fSuccess;
  2332. int iRet;
  2333. PWND pwndActivate;
  2334. Q *pq;
  2335. TL tlpwndActivate;
  2336. CheckLock(pwnd);
  2337. UserAssert(TestUP(ACTIVEWINDOWTRACKING));
  2338. /*
  2339. * If the mouse hasn't been long enough on this queue, bail.
  2340. */
  2341. pq = GETPTI(pwnd)->pq;
  2342. if (!(pq->QF_flags & QF_ACTIVEWNDTRACKING)) {
  2343. return MA_PASSTHRU;
  2344. }
  2345. pq->QF_flags &= ~QF_ACTIVEWNDTRACKING;
  2346. /*
  2347. * If the foreground is locked, bail
  2348. */
  2349. if (IsForegroundLocked()) {
  2350. return MA_PASSTHRU;
  2351. }
  2352. /*
  2353. * Get the window we need to activate. If none, bail.
  2354. */
  2355. pwndActivate = GetActiveTrackPwnd(pwnd, &pq);
  2356. if (pwndActivate == NULL) {
  2357. return MA_PASSTHRU;
  2358. }
  2359. /*
  2360. * Lock if needed because we're about to callback
  2361. */
  2362. if (pwnd != pwndActivate) {
  2363. ThreadLockAlways(pwndActivate, &tlpwndActivate);
  2364. }
  2365. /*
  2366. * Let's ask if it's OK to do this
  2367. *
  2368. * This message is supposed to go to the window the mouse is on.
  2369. * This could be a child window which might return MA_NOACTIVATE*.
  2370. * For mouse clicks (which is what we want to emulate here)
  2371. * xxxButtonEvent calls xxxSetForegroundWindow2 so their
  2372. * pwndActivate gets brought to the foreground regardless.
  2373. * So we send the message to pwndActivate instead.
  2374. */
  2375. iRet = (int)xxxSendMessage(pwndActivate, WM_MOUSEACTIVATE,
  2376. (WPARAM)(HWq(pwndActivate)), MAKELONG((SHORT)iHitTest, uMsg));
  2377. switch (iRet) {
  2378. case MA_ACTIVATE:
  2379. case MA_ACTIVATEANDEAT:
  2380. if (pq == gpqForeground) {
  2381. fSuccess = xxxActivateThisWindow(pwndActivate, 0,
  2382. (TestUP(ACTIVEWNDTRKZORDER) ? 0 : ATW_NOZORDER));
  2383. } else {
  2384. fSuccess = xxxSetForegroundWindow2(pwndActivate, NULL,
  2385. SFW_SWITCH | (TestUP(ACTIVEWNDTRKZORDER) ? 0 : SFW_NOZORDER));
  2386. }
  2387. /*
  2388. * Eat the message if activation failed.
  2389. */
  2390. if (!fSuccess) {
  2391. iRet = MA_SKIP;
  2392. } else if (iRet == MA_ACTIVATEANDEAT) {
  2393. iRet = MA_SKIP;
  2394. }
  2395. break;
  2396. case MA_NOACTIVATEANDEAT:
  2397. iRet = MA_SKIP;
  2398. break;
  2399. case MA_NOACTIVATE:
  2400. default:
  2401. iRet = MA_PASSTHRU;
  2402. break;
  2403. }
  2404. if (pwnd != pwndActivate) {
  2405. ThreadUnlock(&tlpwndActivate);
  2406. }
  2407. return iRet;
  2408. }
  2409. /***************************************************************************\
  2410. * xxxMouseActivate
  2411. *
  2412. * This is where activation due to mouse clicks occurs.
  2413. *
  2414. * IMPLEMENTATION:
  2415. * The message is sent to the specified window. In xxxDefWindowProc, the
  2416. * message is sent to the window's parent. The receiving window may
  2417. * a) process the message,
  2418. * b) skip the message totally, or
  2419. * c) re-hit test message
  2420. *
  2421. * A WM_SETCURSOR message is also sent through the system to set the cursor.
  2422. *
  2423. * History:
  2424. * 11-22-90 DavidPe Ported.
  2425. \***************************************************************************/
  2426. int xxxMouseActivate(
  2427. PTHREADINFO pti,
  2428. PWND pwnd,
  2429. UINT message,
  2430. WPARAM wParam,
  2431. LPPOINT lppt,
  2432. int ht)
  2433. {
  2434. UINT x, y;
  2435. PWND pwndTop;
  2436. int result;
  2437. TL tlpwndTop;
  2438. BOOL fSend;
  2439. CheckLock(pwnd);
  2440. UserAssert(_GETPDESK(pwnd) != NULL);
  2441. /*
  2442. * No mouse activation if the mouse is captured. Must check for the capture
  2443. * ONLY here. 123W depends on it - create a graph, select Rearrange..
  2444. * flip horizontal, click outside the graph. If this code checks for
  2445. * anything beside just capture, 123w will get the below messages and
  2446. * get confused.
  2447. */
  2448. if (pti->pq->spwndCapture != NULL) {
  2449. return MA_PASSTHRU;
  2450. }
  2451. result = MA_PASSTHRU;
  2452. pwndTop = pwnd;
  2453. ThreadLockWithPti(pti, pwndTop, &tlpwndTop);
  2454. /*
  2455. * B#1404
  2456. * Don't send WM_PARENTNOTIFY messages if the child has
  2457. * WS_EX_NOPARENTNOTIFY style.
  2458. *
  2459. * Unfortunately, this breaks people who create controls in
  2460. * MDI children, like WinMail. They don't get WM_PARENTNOTIFY
  2461. * messages, which don't get passed to DefMDIChildProc(), which
  2462. * then can't update the active MDI child. Grrr.
  2463. */
  2464. fSend = (!TestWF(pwnd, WFWIN40COMPAT) || !TestWF(pwnd, WEFNOPARENTNOTIFY));
  2465. /*
  2466. * If it's a buttondown event, send WM_PARENTNOTIFY.
  2467. */
  2468. switch (message) {
  2469. case WM_LBUTTONDOWN:
  2470. case WM_RBUTTONDOWN:
  2471. case WM_MBUTTONDOWN:
  2472. case WM_XBUTTONDOWN:
  2473. while (TestwndChild(pwndTop)) {
  2474. pwndTop = pwndTop->spwndParent;
  2475. if (fSend) {
  2476. ThreadUnlock(&tlpwndTop);
  2477. ThreadLockWithPti(pti, pwndTop, &tlpwndTop);
  2478. x = (UINT)(lppt->x - pwndTop->rcClient.left);
  2479. y = (UINT)(lppt->y - pwndTop->rcClient.top);
  2480. /* Get the xbutton from the hiword of wParam */
  2481. UserAssert(message == WM_XBUTTONDOWN || HIWORD(wParam) == 0);
  2482. UserAssert(LOWORD(wParam) == 0);
  2483. xxxSendMessage(pwndTop, WM_PARENTNOTIFY, (WPARAM)(message | wParam), MAKELPARAM(x, y));
  2484. }
  2485. }
  2486. if (!fSend) {
  2487. ThreadUnlock(&tlpwndTop);
  2488. ThreadLockAlwaysWithPti(pti, pwndTop, &tlpwndTop);
  2489. }
  2490. /*
  2491. * NOTE: We break out of this loop with pwndTop locked.
  2492. */
  2493. break;
  2494. }
  2495. /*
  2496. * The mouse was moved onto this window: make it foreground
  2497. */
  2498. if (TestUP(ACTIVEWINDOWTRACKING) && (message == WM_MOUSEMOVE)) {
  2499. result = xxxActiveWindowTracking(pwnd, WM_MOUSEMOVE, ht);
  2500. }
  2501. /*
  2502. * Are we hitting an inactive top-level window WHICH ISN'T THE DESKTOP(!)?
  2503. *
  2504. * craigc 7-14-89 hitting either inactive top level or any child window,
  2505. * to be compatible with 2.X. Apps apparently needs this message.
  2506. */
  2507. else if ((pti->pq->spwndActive != pwnd || pti->pq->QF_flags & QF_EVENTDEACTIVATEREMOVED) &&
  2508. (pwndTop != PWNDDESKTOP(pwndTop))) {
  2509. switch (message) {
  2510. case WM_LBUTTONDOWN:
  2511. case WM_RBUTTONDOWN:
  2512. case WM_MBUTTONDOWN:
  2513. case WM_XBUTTONDOWN:
  2514. /*
  2515. * Send the MOUSEACTIVATE message.
  2516. */
  2517. result = (int)xxxSendMessage(pwnd, WM_MOUSEACTIVATE,
  2518. (WPARAM)(HW(pwndTop)), MAKELONG((SHORT)ht, message));
  2519. switch (result) {
  2520. case 0:
  2521. case MA_ACTIVATE:
  2522. case MA_ACTIVATEANDEAT:
  2523. /*
  2524. * If activation fails, swallow the message.
  2525. */
  2526. if ((pwndTop != pti->pq->spwndActive ||
  2527. pti->pq->QF_flags & QF_EVENTDEACTIVATEREMOVED) &&
  2528. !xxxActivateWindow(pwndTop,
  2529. (UINT)((pti->pq->codeCapture == NO_CAP_CLIENT) ?
  2530. AW_TRY2 : AW_TRY))) {
  2531. result = MA_SKIP;
  2532. } else if (TestWF(pwndTop, WFDISABLED)) {
  2533. #ifdef NEVER
  2534. /*
  2535. * Althoug this is what win3 does, it is not good: it
  2536. * can easily cause infinite loops. Returning "rehittest"
  2537. * means process this event over again - nothing causes
  2538. * anything different to happen, and we get an infinite
  2539. * loop. This case never gets executed on win3 because if
  2540. * the window is disabled, it got the HTERROR hittest
  2541. * code. This can only be done on Win32 where input is
  2542. * assigned to a window BEFORE process occurs to pull
  2543. * it out of the queue.
  2544. */
  2545. result = MA_REHITTEST;
  2546. #endif
  2547. /*
  2548. * Someone clicked on a window before it was disabled...
  2549. * Since it is disabled now, don't send this message to
  2550. * it: instead eat it.
  2551. */
  2552. result = MA_SKIP;
  2553. } else if (result == MA_ACTIVATEANDEAT) {
  2554. result = MA_SKIP;
  2555. } else {
  2556. result = MA_PASSTHRU;
  2557. goto ItsActiveJustCheckOnTop;
  2558. }
  2559. break;
  2560. case MA_NOACTIVATEANDEAT:
  2561. result = MA_SKIP;
  2562. break;
  2563. }
  2564. }
  2565. } else {
  2566. ItsActiveJustCheckOnTop:
  2567. /*
  2568. * Make sure this active window is on top (see comment
  2569. * in CheckOnTop).
  2570. */
  2571. if (TestUP(ACTIVEWINDOWTRACKING)) {
  2572. if (CheckOnTop(pti, pwndTop, message)) {
  2573. /*
  2574. * The window was z-ordered to the top.
  2575. * If it is a console window, skip the message
  2576. * so it won't go into "selecting" mode
  2577. * Hard error boxes are created by csrss as well
  2578. * If we have topmost console windows someday, this
  2579. * will need to change
  2580. */
  2581. if ((ht == HTCLIENT)
  2582. && (GETPTI(pwndTop)->TIF_flags & TIF_CSRSSTHREAD)
  2583. && !(TestWF(pwndTop, WEFTOPMOST))) {
  2584. RIPMSG2(RIP_WARNING, "xxxMouseActivate: Skipping msg %#lx for pwnd %#p",
  2585. message, pwndTop);
  2586. result = MA_SKIP;
  2587. }
  2588. }
  2589. } /* if (TestUP(ACTIVEWINDOWTRACKING)) */
  2590. }
  2591. /*
  2592. * Now set the cursor shape.
  2593. */
  2594. if (pti->pq->spwndCapture == NULL) {
  2595. xxxSendMessage(pwnd, WM_SETCURSOR, (WPARAM)HW(pwnd),
  2596. MAKELONG((SHORT)ht, message));
  2597. }
  2598. ThreadUnlock(&tlpwndTop);
  2599. return result;
  2600. }
  2601. /***************************************************************************\
  2602. * ResetMouseHover()
  2603. *
  2604. * Resets mouse hover state information.
  2605. *
  2606. * 11/03/95 francish created.
  2607. * 09/04/97 GerardoB Rewritten to use per desktop tracking
  2608. \***************************************************************************/
  2609. void ResetMouseHover(PDESKTOP pdesk, POINT pt)
  2610. {
  2611. /*
  2612. * Reset the timer and hover rect
  2613. */
  2614. InternalSetTimer(pdesk->spwndTrack, IDSYS_MOUSEHOVER,
  2615. pdesk->dwMouseHoverTime,
  2616. xxxSystemTimerProc, TMRF_SYSTEM);
  2617. SetRect(&pdesk->rcMouseHover,
  2618. pt.x - gcxMouseHover / 2,
  2619. pt.y - gcyMouseHover / 2,
  2620. pt.x + gcxMouseHover / 2,
  2621. pt.y + gcyMouseHover / 2);
  2622. }
  2623. /***************************************************************************\
  2624. * QueryTrackMouseEvent()
  2625. *
  2626. * Fills in a TRACKMOUSEEVENT structure describing current tracking state.
  2627. *
  2628. * 11/03/95 francish created.
  2629. * 09/04/97 GerardoB Rewritten to use per desktop tracking
  2630. \***************************************************************************/
  2631. BOOL QueryTrackMouseEvent(
  2632. LPTRACKMOUSEEVENT lpTME)
  2633. {
  2634. PTHREADINFO ptiCurrent = PtiCurrent();
  2635. PDESKTOP pdesk = ptiCurrent->rpdesk;
  2636. /*
  2637. * initialize the struct
  2638. */
  2639. RtlZeroMemory(lpTME, sizeof(*lpTME));
  2640. lpTME->cbSize = sizeof(*lpTME);
  2641. /*
  2642. * Bail if not tracking any mouse event
  2643. * or if the current thread is not in spwndTrack's queue
  2644. */
  2645. if (!(pdesk->dwDTFlags & DF_TRACKMOUSEEVENT)
  2646. || (ptiCurrent->pq != GETPTI(pdesk->spwndTrack)->pq)) {
  2647. return TRUE;
  2648. }
  2649. /*
  2650. * fill in the requested information
  2651. */
  2652. if (pdesk->htEx != HTCLIENT) {
  2653. lpTME->dwFlags |= TME_NONCLIENT;
  2654. }
  2655. if (pdesk->dwDTFlags & DF_TRACKMOUSELEAVE) {
  2656. lpTME->dwFlags |= TME_LEAVE;
  2657. }
  2658. if (pdesk->dwDTFlags & DF_TRACKMOUSEHOVER) {
  2659. lpTME->dwFlags |= TME_HOVER;
  2660. lpTME->dwHoverTime = pdesk->dwMouseHoverTime;
  2661. }
  2662. lpTME->hwndTrack = HWq(pdesk->spwndTrack);
  2663. return TRUE;
  2664. }
  2665. /***************************************************************************\
  2666. * TrackMouseEvent()
  2667. *
  2668. * API for requesting extended mouse notifications (hover, leave, ...)
  2669. *
  2670. * 11/03/95 francish created.
  2671. * 09/04/97 GerardoB Rewritten to use per desktop tracking
  2672. \***************************************************************************/
  2673. BOOL TrackMouseEvent(
  2674. LPTRACKMOUSEEVENT lpTME)
  2675. {
  2676. PDESKTOP pdesk = PtiCurrent()->rpdesk;
  2677. PWND pwnd;
  2678. /*
  2679. * Validate hwndTrack
  2680. */
  2681. pwnd = ValidateHwnd(lpTME->hwndTrack);
  2682. if (pwnd == NULL) {
  2683. return FALSE;
  2684. }
  2685. /*
  2686. * If we're not tracking this window or not in correct hittest, bail
  2687. */
  2688. if ((pwnd != pdesk->spwndTrack)
  2689. || (!!(lpTME->dwFlags & TME_NONCLIENT) ^ (pdesk->htEx != HTCLIENT))) {
  2690. if ((lpTME->dwFlags & TME_LEAVE) && !(lpTME->dwFlags & TME_CANCEL)) {
  2691. _PostMessage(pwnd,
  2692. ((lpTME->dwFlags & TME_NONCLIENT) ? WM_NCMOUSELEAVE : WM_MOUSELEAVE),
  2693. 0, 0);
  2694. }
  2695. return TRUE;
  2696. }
  2697. /*
  2698. * Process cancel request
  2699. */
  2700. if (lpTME->dwFlags & TME_CANCEL) {
  2701. if (lpTME->dwFlags & TME_LEAVE) {
  2702. pdesk->dwDTFlags &= ~DF_TRACKMOUSELEAVE;
  2703. }
  2704. if (lpTME->dwFlags & TME_HOVER) {
  2705. if (pdesk->dwDTFlags & DF_TRACKMOUSEHOVER) {
  2706. _KillSystemTimer(pwnd, IDSYS_MOUSEHOVER);
  2707. pdesk->dwDTFlags &= ~DF_TRACKMOUSEHOVER;
  2708. }
  2709. }
  2710. return TRUE;
  2711. }
  2712. /*
  2713. * Track mouse leave
  2714. */
  2715. if (lpTME->dwFlags & TME_LEAVE) {
  2716. pdesk->dwDTFlags |= DF_TRACKMOUSELEAVE;
  2717. }
  2718. /*
  2719. * Track mouse hover
  2720. */
  2721. if (lpTME->dwFlags & TME_HOVER) {
  2722. pdesk->dwDTFlags |= DF_TRACKMOUSEHOVER;
  2723. pdesk->dwMouseHoverTime = lpTME->dwHoverTime;
  2724. if ((pdesk->dwMouseHoverTime == 0) || (pdesk->dwMouseHoverTime == HOVER_DEFAULT)) {
  2725. pdesk->dwMouseHoverTime = gdtMouseHover;
  2726. }
  2727. ResetMouseHover(pdesk, GETPTI(pwnd)->ptLast);
  2728. }
  2729. return TRUE;
  2730. }
  2731. /***************************************************************************\
  2732. * xxxGetNextSysMsg
  2733. *
  2734. * Returns the queue pointer of the next system message or
  2735. * NULL - no more messages (may be a journal playback delay)
  2736. * PQMSG_PLAYBACK - got a journal playback message
  2737. * (Anything else is a real pointer)
  2738. *
  2739. * 10-23-92 ScottLu Created.
  2740. \***************************************************************************/
  2741. PQMSG xxxGetNextSysMsg(
  2742. PTHREADINFO pti,
  2743. PQMSG pqmsgPrev,
  2744. PQMSG pqmsg)
  2745. {
  2746. DWORD dt;
  2747. PMLIST pml;
  2748. PQMSG pqmsgT;
  2749. /*
  2750. * If there is a journal playback hook, call it to get the next message.
  2751. */
  2752. if (PhkFirstGlobalValid(pti, WH_JOURNALPLAYBACK) != NULL && IsOnInputDesktop(pti)) {
  2753. /*
  2754. * We can't search through journal messages: we only get the current
  2755. * journal message. So if the caller has already called us once
  2756. * before, then exit with no messages.
  2757. */
  2758. if (pqmsgPrev != 0)
  2759. return NULL;
  2760. /*
  2761. * Tell the journal playback hook that we're done
  2762. * with this message now.
  2763. */
  2764. dt = xxxCallJournalPlaybackHook(pqmsg);
  2765. if (dt == 0xFFFFFFFF)
  2766. return NULL;
  2767. /*
  2768. * If dt == 0, then we don't need to wait: set the right wake
  2769. * bits and return this message.
  2770. */
  2771. if (dt == 0) {
  2772. WakeSomeone(pti->pq, pqmsg->msg.message, NULL);
  2773. /*
  2774. * Remember input is coming through journalling so we'll know this is
  2775. * an automation scenario.
  2776. * Note that we don't change any of the glinp information here so it
  2777. * continues to hold what the actual last hardware or SendInput input event was.
  2778. * I'm not changing it now to avoid any unexpected side effects from it since
  2779. * there's no scenario requesting so.
  2780. * This could pontentially be reconsidered so glinp completely reflects
  2781. * what the last input event was, regardless of its source.
  2782. */
  2783. glinp.dwFlags = glinp.dwFlags | LINP_JOURNALLING;
  2784. return PQMSG_PLAYBACK;
  2785. } else {
  2786. /*
  2787. * There is logically no more input in the "queue", so clear the
  2788. * bits so that we will sleep when GetMessage is called.
  2789. */
  2790. pti->pcti->fsWakeBits &= ~QS_INPUT;
  2791. pti->pcti->fsChangeBits &= ~QS_INPUT;
  2792. /*
  2793. * Need to wait before processing this next message. Set
  2794. * a journal timer.
  2795. */
  2796. SetJournalTimer(dt, pqmsg->msg.message);
  2797. return NULL;
  2798. }
  2799. }
  2800. /*
  2801. * No journalling going on... return next message in system queue.
  2802. */
  2803. /*
  2804. * Queue up a mouse move if the mouse has moved.
  2805. */
  2806. if (pti->pq->QF_flags & QF_MOUSEMOVED) {
  2807. PostMove(pti->pq);
  2808. }
  2809. /*
  2810. * If no messages in the input queue, return with 0.
  2811. */
  2812. pml = &pti->pq->mlInput;
  2813. if (pml->cMsgs == 0)
  2814. return NULL;
  2815. /*
  2816. * If this is the first call to xxxGetNextSysMsg(), return the
  2817. * first message.
  2818. */
  2819. if (pqmsgPrev == NULL || pti->pq->idSysPeek <= (ULONG_PTR)PQMSG_PLAYBACK) {
  2820. pqmsgT = pml->pqmsgRead;
  2821. } else {
  2822. /*
  2823. * Otherwise return the next message in the queue. Index with
  2824. * idSysPeek, because that is updated by recursive calls through
  2825. * this code.
  2826. */
  2827. pqmsgT = ((PQMSG)(pti->pq->idSysPeek))->pqmsgNext;
  2828. }
  2829. /*
  2830. * Fill in the structure passed, and return the pointer to the
  2831. * current message in the message list. This will become the new
  2832. * pq->idSysPeek.
  2833. */
  2834. if (pqmsgT != NULL)
  2835. *pqmsg = *pqmsgT;
  2836. return pqmsgT;
  2837. }
  2838. /***************************************************************************\
  2839. * UpdateKeyState
  2840. *
  2841. * Updates queue key state tables.
  2842. *
  2843. * 11-11-92 ScottLu Created.
  2844. \***************************************************************************/
  2845. void UpdateKeyState(
  2846. PQ pq,
  2847. UINT vk,
  2848. BOOL fDown)
  2849. {
  2850. if (vk != 0) {
  2851. /*
  2852. * If we're going down, toggle only if the key isn't
  2853. * already down.
  2854. */
  2855. if (fDown && !TestKeyStateDown(pq, vk)) {
  2856. if (TestKeyStateToggle(pq, vk)) {
  2857. ClearKeyStateToggle(pq, vk);
  2858. } else {
  2859. SetKeyStateToggle(pq, vk);
  2860. }
  2861. }
  2862. /*
  2863. * Now set/clear the key down state.
  2864. */
  2865. if (fDown) {
  2866. SetKeyStateDown(pq, vk);
  2867. } else {
  2868. ClearKeyStateDown(pq, vk);
  2869. }
  2870. /*
  2871. * If this is one of the keys we cache, update the key cache index.
  2872. */
  2873. if (vk < CVKKEYCACHE) {
  2874. gpsi->dwKeyCache++;
  2875. }
  2876. }
  2877. }
  2878. /***************************************************************************\
  2879. * EqualMsg
  2880. *
  2881. * This routine is called in case that idSysPeek points to a message
  2882. * and we are trying to remove a different message
  2883. *
  2884. * 04-25-96 CLupu Created.
  2885. \***************************************************************************/
  2886. BOOL EqualMsg(PQMSG pqmsg1, PQMSG pqmsg2)
  2887. {
  2888. if (pqmsg1->msg.hwnd != pqmsg2->msg.hwnd ||
  2889. pqmsg1->msg.message != pqmsg2->msg.message)
  2890. return FALSE;
  2891. /*
  2892. * This might be a coalesced WM_MOUSEMOVE
  2893. */
  2894. if (pqmsg1->msg.message == WM_MOUSEMOVE)
  2895. return TRUE;
  2896. if (pqmsg1->pti != pqmsg2->pti ||
  2897. pqmsg1->msg.time != pqmsg2->msg.time)
  2898. return FALSE;
  2899. return TRUE;
  2900. }
  2901. /***************************************************************************\
  2902. * xxxSkipSysMsg
  2903. *
  2904. * This routine "skips" an input message: either by calling the journal
  2905. * hooks if we're journalling or by "skipping" the message in the input
  2906. * queue. Internal keystate tables are updated as well.
  2907. *
  2908. * 10-23-92 ScottLu Created.
  2909. \***************************************************************************/
  2910. void xxxSkipSysMsg(
  2911. PTHREADINFO pti,
  2912. PQMSG pqmsg)
  2913. {
  2914. PQMSG pqmsgT;
  2915. BOOL fDown;
  2916. BYTE vk;
  2917. PHOOK phook;
  2918. /*
  2919. * If idSysPeek is 0, then the pqmsg that we were looking at has been
  2920. * deleted, probably because of a callout from ScanSysQueue, and that
  2921. * callout then called PeekMessage(fRemove == TRUE), and then returned.
  2922. */
  2923. if (pti->pq->idSysPeek == 0)
  2924. return;
  2925. phook = PhkFirstGlobalValid(pti, WH_JOURNALPLAYBACK);
  2926. if (phook != NULL && IsOnInputDesktop(pti)) {
  2927. /*
  2928. * Tell the journal playback hook that we're done
  2929. * with this message now.
  2930. */
  2931. phook->flags |= HF_NEEDHC_SKIP;
  2932. } else {
  2933. phook = PhkFirstGlobalValid(pti, WH_JOURNALRECORD);
  2934. if (phook != NULL) {
  2935. /*
  2936. * We've processed a new message: tell the journal record
  2937. * hook what the message is.
  2938. */
  2939. xxxCallJournalRecordHook(pqmsg);
  2940. }
  2941. /*
  2942. * If idSysPeek is 0 now, it means we've been recursed into yet
  2943. * again. This would confuse a journalling app, but it would confuse
  2944. * us more because we'd fault. Return if idSysPeek is 0.
  2945. */
  2946. if ((pqmsgT = (PQMSG)pti->pq->idSysPeek) == NULL)
  2947. return;
  2948. /*
  2949. * Delete this message from the input queue. Make sure pqmsgT isn't
  2950. * 1: this could happen if an app unhooked a journal record hook
  2951. * during a callback from xxxScanSysQueue.
  2952. */
  2953. if (pqmsgT != PQMSG_PLAYBACK) {
  2954. /*
  2955. * There are cases when idSysPeek points to a different message
  2956. * than the one we are trying to remove. This can happen if
  2957. * two threads enters in xxxScanSysQueue, sets the idSysPeek and
  2958. * after this their queues got redistributed. The first thread
  2959. * will have the idSysPeek preserved but the second one has to
  2960. * search the queue for its message. - ask CLupu
  2961. */
  2962. if (!EqualMsg(pqmsgT, pqmsg)) {
  2963. PQMSG pqmsgS;
  2964. #if DBG
  2965. if (IsDbgTagEnabled(DBGTAG_SysPeek)) {
  2966. gnSysPeekSearch++;
  2967. }
  2968. #endif
  2969. TAGMSG0(DBGTAG_SysPeek | RIP_THERESMORE, "Different message than idSysPeek\n");
  2970. TAGMSG2(DBGTAG_SysPeek | RIP_NONAME | RIP_THERESMORE, "pqmsg = %#p idSysPeek = %#p", pqmsg, pqmsgT);
  2971. TAGMSG2(DBGTAG_SysPeek | RIP_NONAME | RIP_THERESMORE, "pti = %#p pti = %#p", pqmsg->pti, pqmsgT->pti);
  2972. TAGMSG2(DBGTAG_SysPeek | RIP_NONAME | RIP_THERESMORE, "msg = %08lx msg = %08lx", pqmsg->msg.message, pqmsgT->msg.message);
  2973. TAGMSG2(DBGTAG_SysPeek | RIP_NONAME | RIP_THERESMORE, "hwnd = %#p hwnd = %#p", pqmsg->msg.hwnd, pqmsgT->msg.hwnd);
  2974. TAGMSG2(DBGTAG_SysPeek | RIP_NONAME | RIP_THERESMORE, "wParam = %#p wParam = %#p", pqmsg->msg.wParam, pqmsgT->msg.wParam);
  2975. TAGMSG2(DBGTAG_SysPeek | RIP_NONAME | RIP_THERESMORE, "lParam = %#p lParam = %#p", pqmsg->msg.lParam, pqmsgT->msg.lParam);
  2976. TAGMSG2(DBGTAG_SysPeek | RIP_NONAME | RIP_THERESMORE, "time = %08lx time = %08lx", pqmsg->msg.time, pqmsgT->msg.time);
  2977. TAGMSG2(DBGTAG_SysPeek | RIP_NONAME | RIP_THERESMORE, "Extra = %08lx Extra = %08lx", pqmsg->ExtraInfo, pqmsgT->ExtraInfo);
  2978. TAGMSG1(DBGTAG_SysPeek | RIP_NONAME, "\npqmsgT = %#p", pqmsgT);
  2979. /*
  2980. * Begin to search for this message
  2981. */
  2982. pqmsgS = pti->pq->mlInput.pqmsgRead;
  2983. while (pqmsgS != NULL) {
  2984. if (EqualMsg(pqmsgS, pqmsg)) {
  2985. TAGMSG2(DBGTAG_SysPeek | RIP_THERESMORE,
  2986. "Deleting pqmsg %#p, pti %#p",
  2987. pqmsgS, pqmsgS->pti);
  2988. TAGMSG4(DBGTAG_SysPeek | RIP_NONAME,
  2989. "m %04lx, w %#p, l %#p, t %lx",
  2990. pqmsgS->msg.message, pqmsgS->msg.hwnd,
  2991. pqmsgS->msg.lParam, pqmsgS->msg.time);
  2992. pqmsgT = pqmsgS;
  2993. break;
  2994. }
  2995. pqmsgS = pqmsgS->pqmsgNext;
  2996. }
  2997. if (pqmsgS == NULL) {
  2998. TAGMSG0(DBGTAG_SysPeek, "Didn't find a matching message. No message removed.");
  2999. return;
  3000. }
  3001. }
  3002. if (pqmsgT == (PQMSG)pti->pq->idSysPeek) {
  3003. /*
  3004. * We'll remove this message from the input queue
  3005. * so set idSysPeek to 0.
  3006. */
  3007. CheckPtiSysPeek(1, pti->pq, 0);
  3008. pti->pq->idSysPeek = 0;
  3009. }
  3010. DelQEntry(&pti->pq->mlInput, pqmsgT);
  3011. }
  3012. }
  3013. fDown = TRUE;
  3014. vk = 0;
  3015. switch (pqmsg->msg.message) {
  3016. case WM_MOUSEMOVE:
  3017. case WM_QUEUESYNC:
  3018. default:
  3019. /*
  3020. * No state change.
  3021. */
  3022. break;
  3023. case WM_KEYUP:
  3024. case WM_SYSKEYUP:
  3025. fDown = FALSE;
  3026. /*
  3027. * Fall through.
  3028. */
  3029. case WM_KEYDOWN:
  3030. case WM_SYSKEYDOWN:
  3031. vk = LOBYTE(LOWORD(pqmsg->msg.wParam));
  3032. break;
  3033. case WM_LBUTTONUP:
  3034. fDown = FALSE;
  3035. /*
  3036. * Fall through.
  3037. */
  3038. case WM_LBUTTONDOWN:
  3039. vk = VK_LBUTTON;
  3040. break;
  3041. case WM_RBUTTONUP:
  3042. fDown = FALSE;
  3043. /*
  3044. * Fall through.
  3045. */
  3046. case WM_RBUTTONDOWN:
  3047. vk = VK_RBUTTON;
  3048. break;
  3049. case WM_MBUTTONUP:
  3050. fDown = FALSE;
  3051. /*
  3052. * Fall through.
  3053. */
  3054. case WM_MBUTTONDOWN:
  3055. vk = VK_MBUTTON;
  3056. break;
  3057. case WM_XBUTTONUP:
  3058. fDown = FALSE;
  3059. /*
  3060. * Fall through.
  3061. */
  3062. case WM_XBUTTONDOWN:
  3063. UserAssert(GET_XBUTTON_WPARAM(pqmsg->msg.wParam) == XBUTTON1 ||
  3064. GET_XBUTTON_WPARAM(pqmsg->msg.wParam) == XBUTTON2);
  3065. switch (GET_XBUTTON_WPARAM(pqmsg->msg.wParam)) {
  3066. case XBUTTON1:
  3067. vk = VK_XBUTTON1;
  3068. break;
  3069. case XBUTTON2:
  3070. vk = VK_XBUTTON2;
  3071. break;
  3072. }
  3073. break;
  3074. }
  3075. /*
  3076. * Set toggle and down bits appropriately.
  3077. */
  3078. if ((vk == VK_SHIFT) || (vk == VK_MENU) || (vk == VK_CONTROL)) {
  3079. BYTE vkHanded, vkOtherHand;
  3080. /*
  3081. * Convert this virtual key into a differentiated (Left/Right) key
  3082. * depending on the extended key bit.
  3083. */
  3084. vkHanded = (vk - VK_SHIFT) * 2 + VK_LSHIFT +
  3085. ((pqmsg->msg.lParam & EXTENDED_BIT) ? 1 : 0);
  3086. vkOtherHand = vkHanded ^ 1;
  3087. if (vk == VK_SHIFT) {
  3088. /*
  3089. * Clear extended bit for r.h. Shift, since it isn't really
  3090. * extended (bit was set to indicate right-handed)
  3091. */
  3092. pqmsg->msg.lParam &= ~EXTENDED_BIT;
  3093. }
  3094. /*
  3095. * Update the key state for the differentiated (Left/Right) key.
  3096. */
  3097. UpdateKeyState(pti->pq, vkHanded, fDown);
  3098. /*
  3099. * Update key state for the undifferentiated (logical) key.
  3100. */
  3101. if (fDown || !TestKeyStateDown(pti->pq, vkOtherHand)) {
  3102. UpdateKeyState(pti->pq, vk, fDown);
  3103. }
  3104. } else {
  3105. UpdateKeyState(pti->pq, vk, fDown);
  3106. }
  3107. }
  3108. #if DBG
  3109. /***************************************************************************\
  3110. * LogPlayback
  3111. *
  3112. *
  3113. * History:
  3114. * 02-13-95 JimA Created.
  3115. \***************************************************************************/
  3116. void LogPlayback(
  3117. PWND pwnd,
  3118. PMSG lpmsg)
  3119. {
  3120. static PWND pwndM = NULL, pwndK = NULL;
  3121. LPCSTR lpszMsg;
  3122. CHAR achBuf[20];
  3123. if ((lpmsg->message >= WM_MOUSEFIRST) && (lpmsg->message <= WM_MOUSELAST)) {
  3124. lpszMsg = aszMouse[lpmsg->message - WM_MOUSEFIRST];
  3125. if (pwnd != pwndM) {
  3126. DbgPrint("*** Mouse input to window \"%ws\" of class \"%s\"\n",
  3127. pwnd->strName.Length ? pwnd->strName.Buffer : L"",
  3128. pwnd->pcls->lpszAnsiClassName);
  3129. pwndM = pwnd;
  3130. }
  3131. } else if ((lpmsg->message >= WM_KEYFIRST) && (lpmsg->message <= WM_KEYLAST)) {
  3132. lpszMsg = aszKey[lpmsg->message - WM_KEYFIRST];
  3133. if (pwnd != pwndK) {
  3134. DbgPrint("*** Kbd input to window \"%ws\" of class \"%s\"\n",
  3135. pwnd->strName.Length ? pwnd->strName.Buffer : L"",
  3136. pwnd->pcls->lpszAnsiClassName);
  3137. pwndK = pwnd;
  3138. }
  3139. } else if (lpmsg->message == WM_QUEUESYNC) {
  3140. lpszMsg = "WM_QUEUESYNC";
  3141. } else {
  3142. sprintf(achBuf, "0x%4x", lpmsg->message);
  3143. lpszMsg = achBuf;
  3144. }
  3145. DbgPrint("msg = %s, wP = %x, lP = %x\n", lpszMsg,
  3146. lpmsg->wParam, lpmsg->lParam);
  3147. }
  3148. #endif // DBG
  3149. /***************************************************************************\
  3150. *
  3151. * GetMouseKeyFlags()
  3152. *
  3153. * Computes MOST of the MK_ flags given a Q.
  3154. * Does not compute MK_MOUSEENTER.
  3155. *
  3156. \***************************************************************************/
  3157. UINT GetMouseKeyFlags(
  3158. PQ pq)
  3159. {
  3160. UINT wParam = 0;
  3161. if (TestKeyStateDown(pq, VK_LBUTTON))
  3162. wParam |= MK_LBUTTON;
  3163. if (TestKeyStateDown(pq, VK_RBUTTON))
  3164. wParam |= MK_RBUTTON;
  3165. if (TestKeyStateDown(pq, VK_MBUTTON))
  3166. wParam |= MK_MBUTTON;
  3167. if (TestKeyStateDown(pq, VK_XBUTTON1))
  3168. wParam |= MK_XBUTTON1;
  3169. if (TestKeyStateDown(pq, VK_XBUTTON2))
  3170. wParam |= MK_XBUTTON2;
  3171. if (TestKeyStateDown(pq, VK_SHIFT))
  3172. wParam |= MK_SHIFT;
  3173. if (TestKeyStateDown(pq, VK_CONTROL))
  3174. wParam |= MK_CONTROL;
  3175. return wParam;
  3176. }
  3177. /***************************************************************************\
  3178. * xxxScanSysQueue
  3179. *
  3180. * This routine looks at the hardware message, determines what
  3181. * window it will be in, determines what the input message will
  3182. * be, and then checks the destination window against hwndFilter,
  3183. * and the input message against msgMinFilter and msgMaxFilter.
  3184. *
  3185. * It also updates various input synchronized states like keystate info.
  3186. *
  3187. * This is almost verbatim from Win3.1.
  3188. *
  3189. * 10-20-92 ScottLu Created.
  3190. \***************************************************************************/
  3191. #ifdef MARKPATH
  3192. #define PATHTAKEN(x) pathTaken |= x
  3193. #define PATHTAKEN2(x) pathTaken2 |= x
  3194. #define PATHTAKEN3(x) pathTaken3 |= x
  3195. #define DUMPPATHTAKEN() if (gfMarkPath) DbgPrint("xxxScanSysQueue path:%08x %08x %08x\n", pathTaken, pathTaken2, pathTaken3)
  3196. #define DUMPSUBPATHTAKEN(p, x) if (gfMarkPath && p & x) { DbgPrint(" %08x %08x %08x\n", pathTaken, pathTaken2, pathTaken3); pathTaken = pathTaken2 = pathTaken3 = 0; }
  3197. #else
  3198. #define PATHTAKEN(x)
  3199. #define PATHTAKEN2(x)
  3200. #define PATHTAKEN3(x)
  3201. #define DUMPPATHTAKEN()
  3202. #define DUMPSUBPATHTAKEN(p, x)
  3203. #endif
  3204. BOOL xxxScanSysQueue(
  3205. PTHREADINFO ptiCurrent,
  3206. LPMSG lpMsg,
  3207. PWND pwndFilter,
  3208. UINT msgMinFilter,
  3209. UINT msgMaxFilter,
  3210. DWORD flags,
  3211. DWORD fsReason)
  3212. {
  3213. QMSG qmsg;
  3214. HWND hwnd;
  3215. PWND pwnd;
  3216. UINT message;
  3217. WPARAM wParam;
  3218. LPARAM lParam;
  3219. PTHREADINFO ptiKeyWake, ptiMouseWake, ptiEventWake;
  3220. #ifdef GENERIC_INPUT
  3221. PTHREADINFO ptiRawInputWake;
  3222. #endif
  3223. POINT pt, ptScreen;
  3224. UINT codeMouseDown;
  3225. BOOL fMouseHookCalled;
  3226. BOOL fKbdHookCalled;
  3227. BOOL fOtherApp;
  3228. int part;
  3229. MOUSEHOOKSTRUCTEX mhs;
  3230. PWND pwndT;
  3231. BOOL fPrevDown;
  3232. BOOL fDown;
  3233. BOOL fAlt;
  3234. TL tlpwnd;
  3235. TL tlpwndT;
  3236. TL tlptiKeyWake;
  3237. TL tlptiMouseWake;
  3238. TL tlptiEventWake;
  3239. #ifdef GENERIC_INPUT
  3240. TL tlptiRawInputWake;
  3241. #endif
  3242. BOOL fRemove = (flags & PM_REMOVE);
  3243. DWORD dwImmRet = 0;
  3244. #ifdef MARKPATH
  3245. DWORD pathTaken = 0;
  3246. DWORD pathTaken2 = 0;
  3247. DWORD pathTaken3 = 0;
  3248. #endif
  3249. UserAssert(IsWinEventNotifyDeferredOK());
  3250. UserAssert((fsReason & ~(QS_EVENT | QS_INPUT)) == 0 &&
  3251. (fsReason & (QS_EVENT | QS_INPUT)) != 0);
  3252. /*
  3253. * If we are looking at a peeked message currently (recursion into this
  3254. * routine) and the only reason we got here was because of an event
  3255. * message (an app was filtering for a non-input message), then just
  3256. * return so we don't mess up idSysPeek. If we do enter this code
  3257. * idSysPeek will get set back to 0, and when we return back into
  3258. * the previous xxxScanSysQueue(), SkipSysMsg() will do nothing, so the
  3259. * message won't get removed. (MS Publisher 2.0 does this).
  3260. */
  3261. if (fsReason == QS_EVENT) {
  3262. if (ptiCurrent->pq->idSysPeek != 0) {
  3263. PATHTAKEN(1);
  3264. DUMPPATHTAKEN();
  3265. return FALSE;
  3266. }
  3267. }
  3268. fDown = FALSE;
  3269. fMouseHookCalled = FALSE;
  3270. fKbdHookCalled = FALSE;
  3271. /*
  3272. * Lock the queue if it's currently unlocked.
  3273. */
  3274. if (ptiCurrent->pq->ptiSysLock == NULL) {
  3275. CheckSysLock(3, ptiCurrent->pq, ptiCurrent);
  3276. ptiCurrent->pq->ptiSysLock = ptiCurrent;
  3277. ptiCurrent->pcti->CTIF_flags |= CTIF_SYSQUEUELOCKED;
  3278. }
  3279. /*
  3280. * Flag to tell if locker was removing messages. If not, then next time
  3281. * Get/PeekMessage is called, the input message list is scanned before the
  3282. * post msg list.
  3283. *
  3284. * Under Win3.1, this flag only gets modified for key and mouse messages.
  3285. * Since under NT ScanSysQueue() can be called to execute event messages,
  3286. * we make this check to be compatible.
  3287. */
  3288. if (fsReason & QS_INPUT) {
  3289. if (fRemove) {
  3290. PATHTAKEN(2);
  3291. ptiCurrent->pq->QF_flags &= ~QF_LOCKNOREMOVE;
  3292. } else {
  3293. PATHTAKEN(4);
  3294. ptiCurrent->pq->QF_flags |= QF_LOCKNOREMOVE;
  3295. }
  3296. }
  3297. /*
  3298. * Return FALSE if the current thread is not the one that lock this queue.
  3299. */
  3300. if (ptiCurrent->pq->ptiSysLock != ptiCurrent) {
  3301. PATHTAKEN(8);
  3302. DUMPPATHTAKEN();
  3303. return FALSE;
  3304. }
  3305. ptiEventWake = ptiKeyWake = ptiMouseWake = NULL;
  3306. #ifdef GENERIC_INPUT
  3307. ptiRawInputWake = NULL;
  3308. #endif
  3309. ThreadLockPti(ptiCurrent, ptiKeyWake, &tlptiKeyWake);
  3310. ThreadLockPti(ptiCurrent, ptiMouseWake, &tlptiMouseWake);
  3311. ThreadLockPti(ptiCurrent, ptiEventWake, &tlptiEventWake);
  3312. #ifdef GENERIC_INPUT
  3313. ThreadLockPti(ptiCurrent, ptiRawInputWake, &tlptiRawInputWake);
  3314. #endif
  3315. /*
  3316. * Initialize the thread lock structure here so we can unlock/lock in
  3317. * the main loop.
  3318. */
  3319. pwnd = NULL;
  3320. ThreadLockWithPti(ptiCurrent, pwnd, &tlpwnd);
  3321. RestartScan:
  3322. CheckPtiSysPeek(2, ptiCurrent->pq, 0);
  3323. ptiCurrent->pq->idSysPeek = 0;
  3324. ContinueScan:
  3325. while (TRUE) {
  3326. ULONG_PTR idSysPeek;
  3327. DUMPSUBPATHTAKEN(pathTaken, 0xf0);
  3328. /*
  3329. * Store idSysPeek in a local which forces pq to be reloaded
  3330. * in case it changed during the xxx call (the compiler can
  3331. * evaluate the LValue at any time)
  3332. */
  3333. idSysPeek = (ULONG_PTR)xxxGetNextSysMsg(ptiCurrent,
  3334. (PQMSG)ptiCurrent->pq->idSysPeek, &qmsg);
  3335. CheckPtiSysPeek(3, ptiCurrent->pq, idSysPeek);
  3336. ptiCurrent->pq->idSysPeek = idSysPeek;
  3337. if (ptiCurrent->pq->idSysPeek == 0) {
  3338. /*
  3339. * If we are only looking for event messages and we didn't
  3340. * find any, then clear the QS_EVENT bit
  3341. */
  3342. if (fsReason == QS_EVENT)
  3343. ClearWakeBit(ptiCurrent, QS_EVENT, FALSE);
  3344. PATHTAKEN(0x10);
  3345. goto NoMessages;
  3346. }
  3347. /*
  3348. * pwnd should be locked for the duration of this routine.
  3349. * For most messages right out of GetNextSysMsg, this is
  3350. * NULL.
  3351. */
  3352. ThreadUnlock(&tlpwnd);
  3353. pwnd = RevalidateHwnd(qmsg.msg.hwnd);
  3354. ThreadLockWithPti(ptiCurrent, pwnd, &tlpwnd);
  3355. /*
  3356. * See if this is an event message. If so, execute it regardless
  3357. * of message and window filters, but only if it is the first element
  3358. * of the input queue.
  3359. */
  3360. if (qmsg.dwQEvent != 0) {
  3361. PTHREADINFO pti;
  3362. PATHTAKEN(0x20);
  3363. /*
  3364. * Most event messages can be executed out of order relative to
  3365. * its place in the queue. There are some examples were this is
  3366. * not allowed, and we check that here. For example, we would not
  3367. * want a keystate synchronization event to be processed before
  3368. * the keystrokes that came before it in the queue!
  3369. *
  3370. * We need to have most event messages be able to get processed
  3371. * out of order because apps can be filtering for message ranges
  3372. * that don't include input (like dde) - those scenarios still
  3373. * need to process events such as deactivate event messages even
  3374. * if there is input in the input queue.
  3375. */
  3376. switch (qmsg.dwQEvent) {
  3377. case QEVENT_UPDATEKEYSTATE:
  3378. /*
  3379. * If the message is not the next message in the queue, don't
  3380. * process it.
  3381. */
  3382. if (ptiCurrent->pq->idSysPeek !=
  3383. (ULONG_PTR)ptiCurrent->pq->mlInput.pqmsgRead) {
  3384. PATHTAKEN(0x40);
  3385. continue;
  3386. }
  3387. break;
  3388. }
  3389. /*
  3390. * If this event isn't for this thread, wake the thread it is
  3391. * for. A NULL qmsg.hti means that any thread can process
  3392. * the event.
  3393. */
  3394. if (qmsg.pti != NULL && (pti = qmsg.pti) != ptiCurrent) {
  3395. /*
  3396. * If somehow this event message got into the wrong queue,
  3397. * then ignore it.
  3398. */
  3399. UserAssert(pti->pq == ptiCurrent->pq);
  3400. if (pti->pq != ptiCurrent->pq) {
  3401. CleanEventMessage((PQMSG)ptiCurrent->pq->idSysPeek);
  3402. DelQEntry(&ptiCurrent->pq->mlInput,
  3403. (PQMSG)ptiCurrent->pq->idSysPeek);
  3404. PATHTAKEN(0x80);
  3405. goto RestartScan;
  3406. }
  3407. /*
  3408. * If ptiEventWake is already set, it means we've already
  3409. * found a thread to wake for event.
  3410. */
  3411. if (ptiEventWake == NULL) {
  3412. ptiEventWake = pti;
  3413. ThreadLockExchangePti(ptiEventWake, &tlptiEventWake);
  3414. }
  3415. /*
  3416. * Clear idSysPeek so that the targeted thread
  3417. * can always get it. Look at the test at the
  3418. * start of this routine for more info.
  3419. */
  3420. CheckPtiSysPeek(4, ptiCurrent->pq, 0);
  3421. ptiCurrent->pq->idSysPeek = 0;
  3422. PATHTAKEN(0x100);
  3423. goto NoMessages;
  3424. }
  3425. /*
  3426. * If this is called with PM_NOYIELD from a 16-bit app, skip
  3427. * processing any event that can generate activation messages. An
  3428. * example is printing from PageMaker 5.0. Bug #12662.
  3429. */
  3430. if ((flags & PM_NOYIELD) && (ptiCurrent->TIF_flags & TIF_16BIT)) {
  3431. PATHTAKEN(0x200);
  3432. switch (qmsg.dwQEvent) {
  3433. /*
  3434. * The following events are safe to process if no yield
  3435. * is to occur.
  3436. */
  3437. case QEVENT_UPDATEKEYSTATE:
  3438. case QEVENT_ASYNCSENDMSG:
  3439. break;
  3440. /*
  3441. * Skip all other events.
  3442. */
  3443. default:
  3444. try {
  3445. ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags | TIF_DELAYEDEVENT;
  3446. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  3447. goto ContinueScan;
  3448. }
  3449. ptiCurrent->TIF_flags |= TIF_DELAYEDEVENT;
  3450. PATHTAKEN(0x400);
  3451. goto ContinueScan;
  3452. }
  3453. }
  3454. /*
  3455. * Delete this before it gets processed so there are no
  3456. * recursion problems.
  3457. */
  3458. DelQEntry(&ptiCurrent->pq->mlInput,
  3459. (PQMSG)ptiCurrent->pq->idSysPeek);
  3460. /*
  3461. * Clear idSysPeek before processing any events messages, because
  3462. * they may recurse and want to use idSysPeek.
  3463. */
  3464. CheckPtiSysPeek(5, ptiCurrent->pq, 0);
  3465. ptiCurrent->pq->idSysPeek = 0;
  3466. xxxProcessEventMessage(ptiCurrent, &qmsg);
  3467. /*
  3468. * Restart the scan from the start so we start with 0 in
  3469. * pq->idSysPeek (since that message is now gone!).
  3470. */
  3471. PATHTAKEN(0x800);
  3472. goto RestartScan;
  3473. }
  3474. /*
  3475. * If the reason we called was just to process event messages, don't
  3476. * enumerate any other mouse or key messages!
  3477. */
  3478. if (fsReason == QS_EVENT) {
  3479. PATHTAKEN(0x1000);
  3480. continue;
  3481. }
  3482. switch (message = qmsg.msg.message) {
  3483. case WM_QUEUESYNC:
  3484. PATHTAKEN(0x2000);
  3485. /*
  3486. * This message is for CBT. Its parameters should already be
  3487. * set up correctly.
  3488. */
  3489. wParam = 0;
  3490. lParam = qmsg.msg.lParam;
  3491. /*
  3492. * Check if this is intended for the current app. Use the mouse
  3493. * bit for WM_QUEUESYNC.
  3494. */
  3495. if (pwnd != NULL && GETPTI(pwnd) != ptiCurrent) {
  3496. /*
  3497. * If this other app isn't going to read from this
  3498. * queue, then skip this message. This can happen with
  3499. * WM_QUEUESYNC if the app passed a window handle
  3500. * to the wrong queue. This isn't likely to happen in
  3501. * this case because WM_QUEUESYNCs come in while journalling,
  3502. * which has all threads sharing the same queue.
  3503. */
  3504. if (GETPTI(pwnd)->pq != ptiCurrent->pq) {
  3505. PATHTAKEN(0x4000);
  3506. goto SkipMessage;
  3507. }
  3508. if (ptiMouseWake == NULL) {
  3509. ptiMouseWake = GETPTI(pwnd);
  3510. ThreadLockExchangePti(ptiMouseWake, &tlptiMouseWake);
  3511. }
  3512. PATHTAKEN(0x8000);
  3513. goto NoMessages;
  3514. }
  3515. if (!CheckMsgFilter(message, msgMinFilter, msgMaxFilter)) {
  3516. PATHTAKEN(0x10000);
  3517. goto NoMessages;
  3518. }
  3519. /*
  3520. * Eat the message.
  3521. */
  3522. if (fRemove) {
  3523. xxxSkipSysMsg(ptiCurrent, &qmsg);
  3524. }
  3525. /*
  3526. * !!HARDWARE HOOK!! goes here.
  3527. */
  3528. /*
  3529. * Return the message.
  3530. */
  3531. PATHTAKEN(0x20000);
  3532. goto ReturnMessage;
  3533. break;
  3534. /*
  3535. * Mouse message or generic hardware messages
  3536. * Key messages are handled in case statements
  3537. * further down in this switch.
  3538. */
  3539. default:
  3540. ReprocessMsg:
  3541. DUMPSUBPATHTAKEN(pathTaken, 0x40000);
  3542. PATHTAKEN(0x40000);
  3543. /*
  3544. * !!GENERIC HARDWARE MESSAGE!! support goes here.
  3545. */
  3546. /*
  3547. * Take the mouse position out of the message.
  3548. */
  3549. pt.x = (int)(short)LOWORD(qmsg.msg.lParam);
  3550. pt.y = (int)(short)HIWORD(qmsg.msg.lParam);
  3551. /*
  3552. * Assume we have a capture.
  3553. */
  3554. part = HTCLIENT;
  3555. /*
  3556. * We have a special global we use for when we're full screen.
  3557. * All mouse input will go to this window.
  3558. */
  3559. if (gspwndScreenCapture != NULL) {
  3560. /*
  3561. * Change the mouse coordinates to full screen.
  3562. */
  3563. pwnd = gspwndScreenCapture;
  3564. lParam = MAKELONG((WORD)qmsg.msg.pt.x,
  3565. (WORD)qmsg.msg.pt.y);
  3566. PATHTAKEN(0x80000);
  3567. } else if ((pwnd = ptiCurrent->pq->spwndCapture) == NULL) {
  3568. PATHTAKEN(0x100000);
  3569. /*
  3570. * We don't have the capture. Figure out which window owns
  3571. * this message.
  3572. *
  3573. * NOTE: Use gptiRit and not ptiCurrent to get the desktop
  3574. * window because if ptiCurrent is the thread that created
  3575. * the main desktop, it's associated desktop is the logon
  3576. * desktop - don't want to hittest against the logon desktop
  3577. * while switched into the main desktop!
  3578. */
  3579. pwndT = gptiRit->rpdesk->pDeskInfo->spwnd;
  3580. ThreadLockWithPti(ptiCurrent, pwndT, &tlpwndT);
  3581. hwnd = xxxWindowHitTest(pwndT, pt, &part, WHT_IGNOREDISABLED);
  3582. ThreadUnlock(&tlpwndT);
  3583. if ((pwnd = RevalidateHwnd(hwnd)) == NULL) {
  3584. pwnd = ptiCurrent->rpdesk->pDeskInfo->spwnd;
  3585. PATHTAKEN(0x200000);
  3586. if (pwnd == NULL) {
  3587. pwnd = gptiRit->rpdesk->pDeskInfo->spwnd;
  3588. }
  3589. }
  3590. if (part == HTCLIENT) {
  3591. /*
  3592. * Part of the client... normal mouse message.
  3593. * NO_CAP_CLIENT means "not captured, in client area
  3594. * of window".
  3595. */
  3596. ptiCurrent->pq->codeCapture = NO_CAP_CLIENT;
  3597. PATHTAKEN(0x400000);
  3598. } else {
  3599. /*
  3600. * Not part of the client... must be an NCMOUSEMESSAGE.
  3601. * NO_CAP_SYS is a creative name by raor which means
  3602. * "not captured, in system area of window."
  3603. */
  3604. ptiCurrent->pq->codeCapture = NO_CAP_SYS;
  3605. PATHTAKEN(0x800000);
  3606. }
  3607. }
  3608. /*
  3609. * We've reassigned pwnd, so lock it.
  3610. */
  3611. ThreadLockExchange(pwnd, &tlpwnd);
  3612. if (fOtherApp = (GETPTI(pwnd) != ptiCurrent)) {
  3613. PATHTAKEN(0x1000000);
  3614. /*
  3615. * If this other app isn't going to read from this
  3616. * queue, then skip this message. This can happen if
  3617. * the RIT queues up a message thinking it goes to
  3618. * a particular hwnd, but then by the time GetMessage()
  3619. * is called for that thread, it doesn't go to that hwnd
  3620. * (like in the case of mouse messages, window rearrangement
  3621. * happens which changes which hwnd the mouse hits on).
  3622. */
  3623. if (GETPTI(pwnd)->pq != ptiCurrent->pq) {
  3624. zzzSetCursor(SYSCUR(ARROW));
  3625. PATHTAKEN(0x2000000);
  3626. goto SkipMessage;
  3627. }
  3628. /*
  3629. * If we haven't already found a message that is intended
  3630. * for another app, remember that we have one.
  3631. */
  3632. if (ptiMouseWake == NULL) {
  3633. ptiMouseWake = GETPTI(pwnd);
  3634. ThreadLockExchangePti(ptiMouseWake, &tlptiMouseWake);
  3635. PATHTAKEN(0x4000000);
  3636. }
  3637. }
  3638. /*
  3639. * Map mouse coordinates based on hit test area code.
  3640. */
  3641. ptScreen = pt;
  3642. switch (ptiCurrent->pq->codeCapture) {
  3643. case CLIENT_CAPTURE:
  3644. case NO_CAP_CLIENT:
  3645. //Screen To Client
  3646. if (TestWF(pwnd, WEFLAYOUTRTL)) {
  3647. pt.x = pwnd->rcClient.right - pt.x;
  3648. } else {
  3649. pt.x -= pwnd->rcClient.left;
  3650. }
  3651. pt.y -= pwnd->rcClient.top;
  3652. PATHTAKEN2(2);
  3653. break;
  3654. case WINDOW_CAPTURE:
  3655. //Screen To Window
  3656. if (TestWF(pwnd, WEFLAYOUTRTL)) {
  3657. pt.x = pwnd->rcWindow.right - pt.x;
  3658. } else {
  3659. pt.x -= pwnd->rcWindow.left;
  3660. }
  3661. pt.y -= pwnd->rcWindow.top;
  3662. PATHTAKEN2(4);
  3663. break;
  3664. }
  3665. /*
  3666. * Track mouse moves when it moves to a different window or
  3667. * a different hit-test area for hot-tracking, tooltips,
  3668. * active window tracking and TrackMouseEvent.
  3669. * Mouse clicks reset tracking state too.
  3670. * Do it only if the message is for the current thread;
  3671. * otherwise, the hit test code (part) is not valid
  3672. * (it's always HTCLIENT; see xxxWindowHitTest2).
  3673. * Tracking will take place when that thread wakes up
  3674. * We also don't do it if this thread is not on pqCursor;
  3675. * that would be the case for a slow app that gets the
  3676. * input message when the mouse has already left its queue
  3677. */
  3678. if (!fOtherApp && (ptiCurrent->pq == gpqCursor)) {
  3679. BOOL fNewpwndTrack = (ptiCurrent->rpdesk->spwndTrack != pwnd);
  3680. int htEx = FindNCHitEx(pwnd, part, pt);
  3681. if ((message != WM_MOUSEMOVE)
  3682. || fNewpwndTrack
  3683. || (ptiCurrent->rpdesk->htEx != htEx)) {
  3684. xxxTrackMouseMove(pwnd, htEx, message);
  3685. ValidateThreadLocks(NULL, ptiCurrent->ptl, (ULONG_PTR)&tlpwnd, TRUE);
  3686. }
  3687. /*
  3688. * Reset mouse hovering if needed.
  3689. *
  3690. */
  3691. if (!fNewpwndTrack && (ptiCurrent->rpdesk->dwDTFlags & DF_TRACKMOUSEHOVER)) {
  3692. if ((message != WM_MOUSEMOVE)
  3693. || !PtInRect(&ptiCurrent->rpdesk->rcMouseHover, ptScreen)) {
  3694. ResetMouseHover(ptiCurrent->rpdesk, ptScreen);
  3695. }
  3696. } else {
  3697. /*
  3698. * Hover must be canceled.
  3699. */
  3700. UserAssert(!(ptiCurrent->rpdesk->dwDTFlags & DF_TRACKMOUSEHOVER));
  3701. }
  3702. } /* if (!fOtherApp.... */
  3703. /*
  3704. * Now see if it matches the window handle filter. If not,
  3705. * get the next message.
  3706. */
  3707. if (!CheckPwndFilter(pwnd, pwndFilter)) {
  3708. PATHTAKEN(0x8000000);
  3709. continue;
  3710. }
  3711. /*
  3712. * See if we need to map to a double click.
  3713. */
  3714. codeMouseDown = 0;
  3715. switch (message) {
  3716. case WM_LBUTTONDOWN:
  3717. case WM_RBUTTONDOWN:
  3718. case WM_MBUTTONDOWN:
  3719. case WM_XBUTTONDOWN:
  3720. if (TestCF(pwnd, CFDBLCLKS) ||
  3721. ptiCurrent->pq->codeCapture == NO_CAP_SYS ||
  3722. IsMenuStarted(ptiCurrent)) {
  3723. codeMouseDown++;
  3724. PATHTAKEN(0x10000000);
  3725. if (qmsg.msg.time <= ptiCurrent->pq->timeDblClk &&
  3726. (!gbClientDoubleClickSupport) &&
  3727. HW(pwnd) == ptiCurrent->pq->hwndDblClk &&
  3728. message == ptiCurrent->pq->msgDblClk &&
  3729. (message != WM_XBUTTONDOWN ||
  3730. GET_XBUTTON_WPARAM(qmsg.msg.wParam) == ptiCurrent->pq->xbtnDblClk)) {
  3731. RECT rcDblClk = {
  3732. ptiCurrent->pq->ptDblClk.x - SYSMET(CXDOUBLECLK) / 2,
  3733. ptiCurrent->pq->ptDblClk.y - SYSMET(CYDOUBLECLK) / 2,
  3734. ptiCurrent->pq->ptDblClk.x + SYSMET(CXDOUBLECLK) / 2,
  3735. ptiCurrent->pq->ptDblClk.y + SYSMET(CYDOUBLECLK) / 2
  3736. };
  3737. if (PtInRect(&rcDblClk, qmsg.msg.pt)) {
  3738. message += (WM_LBUTTONDBLCLK - WM_LBUTTONDOWN);
  3739. codeMouseDown++;
  3740. PATHTAKEN(0x20000000);
  3741. }
  3742. }
  3743. }
  3744. // FALL THROUGH!!!
  3745. case WM_LBUTTONUP:
  3746. case WM_RBUTTONUP:
  3747. case WM_MBUTTONUP:
  3748. case WM_XBUTTONUP:
  3749. /*
  3750. * Note that the mouse button went up or down if we were
  3751. * in menu status mode of alt-key down
  3752. */
  3753. PATHTAKEN(0x40000000);
  3754. if (ptiCurrent->pq->QF_flags & QF_FMENUSTATUS) {
  3755. ptiCurrent->pq->QF_flags |= QF_FMENUSTATUSBREAK;
  3756. PATHTAKEN(0x80000000);
  3757. }
  3758. }
  3759. /*
  3760. * Map message number based on hit test area code.
  3761. */
  3762. if (ptiCurrent->pq->codeCapture == NO_CAP_SYS) {
  3763. message += (UINT)(WM_NCMOUSEMOVE - WM_MOUSEMOVE);
  3764. wParam = (UINT)part;
  3765. PATHTAKEN2(1);
  3766. }
  3767. /*
  3768. * Message number has been mapped: see if it fits the filter.
  3769. * If not, get the next message.
  3770. */
  3771. if (!CheckMsgFilter(message, msgMinFilter, msgMaxFilter)) {
  3772. PATHTAKEN2(8);
  3773. continue;
  3774. }
  3775. /*
  3776. * If message is for another app but it fits our filter, then
  3777. * we should stop looking for messages: this will ensure that
  3778. * we don't keep looking and find and process a message that
  3779. * occured later than the one that should be processed by the
  3780. * other guy.
  3781. */
  3782. if (fOtherApp) {
  3783. PATHTAKEN2(0x10);
  3784. goto NoMessages;
  3785. }
  3786. /*
  3787. * If we're doing full drag, the mouse messages should go to
  3788. * the xxxMoveSize PeekMessage loop. So we get the next message.
  3789. * This can happen when an application does a PeekMessage in
  3790. * response to a message sent inside the movesize dragging loop.
  3791. * This causes the dragging loop to not get the WM_LBUTTONUP
  3792. * message and dragging continues after the button is up
  3793. * (fix for Micrografx Draw). -johannec
  3794. */
  3795. if (message >= WM_MOUSEFIRST && message <= WM_MOUSELAST &&
  3796. ptiCurrent->TIF_flags & TIF_MOVESIZETRACKING) {
  3797. PATHTAKEN2(0x20);
  3798. continue;
  3799. }
  3800. if (ptiCurrent->TIF_flags & TIF_MSGPOSCHANGED) {
  3801. ptiCurrent->TIF_flags &= ~TIF_MSGPOSCHANGED;
  3802. xxxWindowEvent(EVENT_OBJECT_LOCATIONCHANGE, NULL,
  3803. OBJID_CURSOR, INDEXID_CONTAINER, TRUE);
  3804. ValidateThreadLocks(NULL, ptiCurrent->ptl, (ULONG_PTR)&tlpwnd, TRUE);
  3805. }
  3806. /*
  3807. * Let us call the mouse hook to find out if this click is
  3808. * permitted by it.
  3809. *
  3810. * We want to inform the mouse hook before we test for
  3811. * HTNOWHERE and HTERROR; Otherwise, the mouse hook won't
  3812. * get these messages (sankar 12/10/91).
  3813. */
  3814. if (IsHooked(ptiCurrent, WHF_MOUSE)) {
  3815. fMouseHookCalled = TRUE;
  3816. mhs.pt = qmsg.msg.pt;
  3817. mhs.hwnd = HW(pwnd);
  3818. mhs.wHitTestCode = (UINT)part;
  3819. mhs.dwExtraInfo = qmsg.ExtraInfo;
  3820. UserAssert(LOWORD(qmsg.msg.wParam) == 0);
  3821. mhs.mouseData = (DWORD)qmsg.msg.wParam;
  3822. if (xxxCallMouseHook(message, &mhs, fRemove)) {
  3823. /*
  3824. * Not allowed by mouse hook; so skip it.
  3825. */
  3826. PATHTAKEN2(0x40);
  3827. goto SkipMessage;
  3828. }
  3829. PATHTAKEN2(0x80);
  3830. ValidateThreadLocks(NULL, ptiCurrent->ptl, (ULONG_PTR)&tlpwnd, TRUE);
  3831. }
  3832. /*
  3833. * If a HTERROR or HTNOWHERE occured, send the window the
  3834. * WM_SETCURSOR message so it can beep or whatever. Then skip
  3835. * the message and try the next one.
  3836. */
  3837. switch (part) {
  3838. case HTERROR:
  3839. case HTNOWHERE:
  3840. /*
  3841. * Now set the cursor shape.
  3842. */
  3843. xxxSendMessage(pwnd, WM_SETCURSOR, (WPARAM)HW(pwnd),
  3844. MAKELONG(part, qmsg.msg.message));
  3845. /*
  3846. * Skip the message.
  3847. */
  3848. PATHTAKEN2(0x100);
  3849. goto SkipMessage;
  3850. break;
  3851. }
  3852. if (fRemove) {
  3853. PATHTAKEN2(0x200);
  3854. /*
  3855. * Since the processing of a down click may cause the next
  3856. * message to be interpreted as a double click, we only want
  3857. * to do the double click setup if we're actually going to
  3858. * remove the message. Otherwise, the next time we read the
  3859. * same message it would be interpreted as a double click.
  3860. */
  3861. switch (codeMouseDown) {
  3862. case 1:
  3863. /*
  3864. * Down clock: set up for later possible double click.
  3865. */
  3866. ptiCurrent->pq->msgDblClk = qmsg.msg.message;
  3867. /*
  3868. * Note that even if the following assertion were not true,
  3869. * we could still put bogus data in ptiCurrent->pq->xbtnDblClk
  3870. * when the message is not WM_XBUTTONDOWN, since when we check
  3871. * for dblclick we compare the message number before the xbtnDblClk.
  3872. */
  3873. UserAssert(qmsg.msg.message == WM_XBUTTONDOWN || GET_XBUTTON_WPARAM(qmsg.msg.wParam) == 0);
  3874. ptiCurrent->pq->xbtnDblClk = GET_XBUTTON_WPARAM(qmsg.msg.wParam);
  3875. ptiCurrent->pq->timeDblClk = qmsg.msg.time + gdtDblClk;
  3876. ptiCurrent->pq->hwndDblClk = HW(pwnd);
  3877. ptiCurrent->pq->ptDblClk = qmsg.msg.pt;
  3878. PATHTAKEN2(0x400);
  3879. break;
  3880. case 2:
  3881. /*
  3882. * Double click: finish processing.
  3883. */
  3884. ptiCurrent->pq->timeDblClk = 0L;
  3885. PATHTAKEN2(0x800);
  3886. break;
  3887. default:
  3888. PATHTAKEN2(0x1000);
  3889. break;
  3890. }
  3891. /*
  3892. * Set mouse cursor and allow app to activate window
  3893. * only if we're removing the message.
  3894. */
  3895. switch (xxxMouseActivate(ptiCurrent, pwnd,
  3896. qmsg.msg.message, qmsg.msg.wParam, &qmsg.msg.pt, part)) {
  3897. SkipMessage:
  3898. case MA_SKIP:
  3899. DUMPSUBPATHTAKEN(pathTaken2, 0x2000);
  3900. PATHTAKEN2(0x2000);
  3901. xxxSkipSysMsg(ptiCurrent, &qmsg);
  3902. /*
  3903. * Inform the CBT hook that we skipped a mouse click.
  3904. */
  3905. if (fMouseHookCalled) {
  3906. if (IsHooked(ptiCurrent, WHF_CBT)) {
  3907. xxxCallHook(HCBT_CLICKSKIPPED, message,
  3908. (LPARAM)&mhs, WH_CBT);
  3909. PATHTAKEN2(0x4000);
  3910. }
  3911. fMouseHookCalled = FALSE;
  3912. }
  3913. /*
  3914. * Inform the CBT hook that we skipped a key
  3915. */
  3916. if (fKbdHookCalled) {
  3917. if (IsHooked(ptiCurrent, WHF_CBT)) {
  3918. xxxCallHook(HCBT_KEYSKIPPED, wParam, lParam,
  3919. WH_CBT);
  3920. PATHTAKEN2(0x8000);
  3921. }
  3922. fKbdHookCalled = FALSE;
  3923. }
  3924. /*
  3925. * If we aren't removing messages, don't reset idSysPeek
  3926. * otherwise we will go into an infinite loop if
  3927. * the keyboard hook says to ignore the message.
  3928. * (bobgu 4/7/87).
  3929. */
  3930. if (!fRemove) {
  3931. PATHTAKEN2(0x10000);
  3932. goto ContinueScan;
  3933. } else {
  3934. PATHTAKEN2(0x20000);
  3935. goto RestartScan;
  3936. }
  3937. break;
  3938. case MA_REHITTEST:
  3939. /*
  3940. * Reprocess the message.
  3941. */
  3942. PATHTAKEN2(0x40000);
  3943. goto ReprocessMsg;
  3944. }
  3945. }
  3946. /*
  3947. * Eat the message from the input queue (and set the keystate
  3948. * table).
  3949. */
  3950. PATHTAKEN2(0x80000);
  3951. if (fRemove) {
  3952. xxxSkipSysMsg(ptiCurrent, &qmsg);
  3953. }
  3954. if (fRemove && fMouseHookCalled && IsHooked(ptiCurrent, WHF_CBT)) {
  3955. xxxCallHook(HCBT_CLICKSKIPPED, message,
  3956. (LPARAM)&mhs, WH_CBT);
  3957. }
  3958. fMouseHookCalled = FALSE;
  3959. lParam = MAKELONG((short)pt.x, (short)pt.y);
  3960. /*
  3961. * Calculate virtual key state bitmask for wParam.
  3962. */
  3963. if (message >= WM_MOUSEFIRST) {
  3964. /*
  3965. * This is a USER mouse message. Calculate the bit mask for the
  3966. * virtual key state.
  3967. */
  3968. wParam = GetMouseKeyFlags(ptiCurrent->pq);
  3969. PATHTAKEN2(0x100000);
  3970. }
  3971. if ( (WM_NCXBUTTONFIRST <= message && message <= WM_NCXBUTTONLAST) ||
  3972. (WM_XBUTTONFIRST <= message && message <= WM_XBUTTONLAST)) {
  3973. /*
  3974. * The hiword of wParam is assigned the xbutton number when
  3975. * the message is queued.
  3976. */
  3977. UserAssert(LOWORD(qmsg.msg.wParam) == 0);
  3978. UserAssert(HIWORD(wParam) == 0);
  3979. wParam |= qmsg.msg.wParam;
  3980. }
  3981. PATHTAKEN2(0x200000);
  3982. /*
  3983. * If this app has a modeles menu bar,
  3984. * then the menu code should get the first shot at messages on the menu
  3985. * Note that this assumes that xxxHandleMenuMessages
  3986. * doens't need any of the stuff set after ReturnMessage
  3987. */
  3988. if ((part == HTMENU)
  3989. && fRemove
  3990. && (ptiCurrent->pMenuState != NULL)
  3991. && ptiCurrent->pMenuState->fModelessMenu
  3992. && (ptiCurrent->pMenuState->pGlobalPopupMenu != NULL)
  3993. && (ptiCurrent->pMenuState->pGlobalPopupMenu->fIsMenuBar)) {
  3994. if (xxxCallHandleMenuMessages(ptiCurrent->pMenuState, pwnd, message, wParam, lParam)) {
  3995. goto RestartScan;
  3996. }
  3997. }
  3998. goto ReturnMessage;
  3999. break;
  4000. case WM_KEYDOWN:
  4001. case WM_SYSKEYDOWN:
  4002. fDown = TRUE;
  4003. /*
  4004. * If we are sending keyboard input to an app that has been
  4005. * spinning then boost it back up. If we don't you use spinning
  4006. * apps like Write or Project and do two builds in the
  4007. * background. Note the app will also be unboosted again shortly
  4008. * after you stop typing by the old logic. #11188
  4009. */
  4010. if (ptiCurrent->TIF_flags & TIF_SPINNING) {
  4011. if (!NT_SUCCESS(CheckProcessForeground(ptiCurrent))) {
  4012. goto NoMessages;
  4013. }
  4014. }
  4015. /*
  4016. * Apps doing journal playback sometimes put trash in the hiword
  4017. * of wParam... zero it out here.
  4018. */
  4019. wParam = qmsg.msg.wParam & 0xFF;
  4020. /*
  4021. * Clear QF_FMENUSTATUS if a key other than Alt it hit
  4022. * since this means the break of the Alt wouldn't be a
  4023. * menu key anymore.
  4024. */
  4025. if (wParam != VK_MENU)
  4026. ptiCurrent->pq->QF_flags &= ~(QF_FMENUSTATUS|QF_FMENUSTATUSBREAK);
  4027. /*
  4028. * Check for keyboard language toggle. Build the key state
  4029. * here for use during key up processing (where the layout
  4030. * switching takes place. This code is skipped if layout
  4031. * switching via the keyboard is disabled.
  4032. */
  4033. if (gLangToggle[0].bVkey && (gLangToggleKeyState < KLT_NONE)) {
  4034. DWORD i;
  4035. BYTE scancode = LOBYTE(HIWORD(qmsg.msg.lParam));
  4036. BYTE vkey = LOBYTE(qmsg.msg.wParam);
  4037. for (i = 0; i < LANGTOGGLEKEYS_SIZE; i++) {
  4038. if (gLangToggle[i].bScan) {
  4039. if (gLangToggle[i].bScan == scancode) {
  4040. gLangToggleKeyState |= gLangToggle[i].iBitPosition;
  4041. break;
  4042. }
  4043. } else {
  4044. if (gLangToggle[i].bVkey == vkey) {
  4045. gLangToggleKeyState |= gLangToggle[i].iBitPosition;
  4046. break;
  4047. }
  4048. }
  4049. }
  4050. if (i == LANGTOGGLEKEYS_SIZE) {
  4051. gLangToggleKeyState = KLT_NONE; // not a language toggle combination
  4052. }
  4053. }
  4054. /*
  4055. * Check if it is the PrintScrn key.
  4056. */
  4057. fAlt = TestKeyStateDown(ptiCurrent->pq, VK_MENU);
  4058. if (wParam == VK_SNAPSHOT &&
  4059. ((fAlt && !(ptiCurrent->fsReserveKeys & CONSOLE_ALTPRTSC)) ||
  4060. (!fAlt && !(ptiCurrent->fsReserveKeys & CONSOLE_PRTSC)))) {
  4061. /*
  4062. * Remove this message from the input queue.
  4063. */
  4064. PATHTAKEN2(0x400000);
  4065. xxxSkipSysMsg(ptiCurrent, &qmsg);
  4066. /*
  4067. * PRINTSCREEN -> Snap the whole screen.
  4068. * ALT-PRINTSCREEN -> Snap the current window.
  4069. */
  4070. pwndT = ptiCurrent->pq->spwndActive;
  4071. /*
  4072. * check also the scan code to see if we got here
  4073. * through keybd_event(VK_SNAPSHOT, ...
  4074. * the scan code is in lParam bits 16-23
  4075. */
  4076. if (!fAlt && ((qmsg.msg.lParam & 0x00FF0000) != 0x00010000)) {
  4077. pwndT = ptiCurrent->rpdesk->pDeskInfo->spwnd;
  4078. }
  4079. if (pwndT != NULL) {
  4080. ThreadLockAlwaysWithPti(ptiCurrent, pwndT, &tlpwndT);
  4081. xxxSnapWindow(pwndT);
  4082. ThreadUnlock(&tlpwndT);
  4083. }
  4084. PATHTAKEN2(0x800000);
  4085. goto RestartScan;
  4086. }
  4087. /*
  4088. * Check for hot keys being hit if any are defined.
  4089. */
  4090. if (gcHotKey != 0 && (!gfEnableHexNumpad || (gfInNumpadHexInput & NUMPAD_HEXMODE_HL) == 0)) {
  4091. UINT key;
  4092. key = (UINT)wParam;
  4093. if (TestKeyStateDown(ptiCurrent->pq, VK_MENU))
  4094. key |= 0x0400;
  4095. if (TestKeyStateDown(ptiCurrent->pq, VK_CONTROL))
  4096. key |= 0x0200;
  4097. if (TestKeyStateDown(ptiCurrent->pq, VK_SHIFT))
  4098. key |= 0x0100;
  4099. pwndT = HotKeyToWindow(key);
  4100. if (pwndT != NULL) {
  4101. /*
  4102. * VK_PACKET shouldn't be a hot key.
  4103. */
  4104. UserAssert((key & 0xff) != VK_PACKET);
  4105. _PostMessage(ptiCurrent->pq->spwndActive, WM_SYSCOMMAND,
  4106. (WPARAM)SC_HOTKEY, (LPARAM)HWq(pwndT));
  4107. /*
  4108. * Remove this message from the input queue.
  4109. */
  4110. xxxSkipSysMsg(ptiCurrent, &qmsg);
  4111. PATHTAKEN2(0x1000000);
  4112. goto RestartScan;
  4113. }
  4114. PATHTAKEN2(0x2000000);
  4115. }
  4116. #if DBG
  4117. else if (gfInNumpadHexInput & NUMPAD_HEXMODE_HL) {
  4118. RIPMSG0(RIP_VERBOSE, "xxxScanSysQueue: gfInNumpadHexInput is true, so we skipped hotkey.");
  4119. }
  4120. #endif
  4121. if (wParam == VK_PACKET) {
  4122. /*
  4123. * Save the character in thread's cache for TranslateMessage
  4124. */
  4125. ptiCurrent->wchInjected = HIWORD(qmsg.msg.wParam);
  4126. qmsg.msg.wParam = wParam;
  4127. UserAssert(qmsg.msg.wParam == VK_PACKET);
  4128. }
  4129. /*
  4130. * Fall through.
  4131. */
  4132. case WM_SYSKEYUP:
  4133. case WM_KEYUP:
  4134. wParam = qmsg.msg.wParam & 0xFF;
  4135. if (wParam == VK_PACKET) {
  4136. qmsg.msg.wParam = wParam;
  4137. }
  4138. /*
  4139. * Special processing for thai locale toggle using grave accent key
  4140. * Remove key message irrespective of fDown otherwise it will
  4141. * generate WM_CHAR message
  4142. */
  4143. if (gbGraveKeyToggle &&
  4144. //
  4145. // In case of mstsc.exe, should not eat Grave Accent key message.
  4146. // TS client must send Grave Accent key message to server side.
  4147. //
  4148. !(GetAppImeCompatFlags(NULL) & IMECOMPAT_HYDRACLIENT) &&
  4149. LOBYTE(HIWORD(qmsg.msg.lParam)) == SCANCODE_THAI_LAYOUT_TOGGLE &&
  4150. fRemove &&
  4151. !TestKeyStateDown(ptiCurrent->pq, VK_SHIFT) &&
  4152. !TestKeyStateDown(ptiCurrent->pq, VK_MENU) &&
  4153. !TestKeyStateDown(ptiCurrent->pq, VK_CONTROL) &&
  4154. !TestKeyStateDown(ptiCurrent->pq, VK_LWIN) &&
  4155. !TestKeyStateDown(ptiCurrent->pq, VK_RWIN)){
  4156. if ((pwnd = ptiCurrent->pq->spwndFocus) == NULL){
  4157. pwnd = ptiCurrent->pq->spwndActive;
  4158. }
  4159. /*
  4160. * Post message only on WM_KEYUP
  4161. */
  4162. if (!fDown && pwnd){
  4163. PTHREADINFO ptiToggle = GETPTI(pwnd);
  4164. PKL pkl = ptiToggle->spklActive;
  4165. if (pkl && (pkl = HKLtoPKL(ptiToggle, (HKL)HKL_NEXT))) {
  4166. _PostMessage(
  4167. pwnd,
  4168. WM_INPUTLANGCHANGEREQUEST,
  4169. (WPARAM)(((pkl->dwFontSigs & gSystemFS) ? INPUTLANGCHANGE_SYSCHARSET : 0) | INPUTLANGCHANGE_FORWARD),
  4170. (LPARAM)pkl->hkl
  4171. );
  4172. }
  4173. }
  4174. /*
  4175. * eat Accent Grave's key msgs
  4176. */
  4177. xxxSkipSysMsg(ptiCurrent, &qmsg);
  4178. goto RestartScan;
  4179. }
  4180. {
  4181. /*
  4182. * Process keyboard toggle keys only if this is
  4183. * a break event and fRemove == TRUE. Some apps,
  4184. * for instance Word 95, call PeekMessage with
  4185. * PM_NOREMOVE followed by a call with PM_REMOVE.
  4186. * We only want to process this once. Skip all
  4187. * of this is layout switching via the keyboard
  4188. * is disabled.
  4189. */
  4190. #ifdef CUAS_ENABLE
  4191. BOOL bMSCTF;
  4192. try {
  4193. bMSCTF = ((ptiCurrent->pClientInfo->CI_flags & CI_CUAS_MSCTF_RUNNING) != 0);
  4194. } except (W32ExceptionHandler(TRUE, RIP_WARNING)) {
  4195. goto NoMessages;
  4196. }
  4197. #endif // CUAS_ENABLE
  4198. if (
  4199. #ifdef CUAS_ENABLE
  4200. !(bMSCTF) &&
  4201. #endif // CUAS_ENABLE
  4202. !fDown && fRemove && gLangToggle[0].bVkey) {
  4203. BOOL bDropToggle = FALSE;
  4204. DWORD dwDirection = 0;
  4205. PKL pkl;
  4206. PTHREADINFO ptiToggle;
  4207. BOOL bArabicSwitchPresent = FALSE;
  4208. LCID lcid;
  4209. ZwQueryDefaultLocale(FALSE, &lcid);
  4210. pwnd = ptiCurrent->pq->spwndFocus;
  4211. if (pwnd == NULL) {
  4212. pwnd = ptiCurrent->pq->spwndActive;
  4213. if (!pwnd) {
  4214. goto NoLayoutSwitch;
  4215. }
  4216. }
  4217. ptiToggle = GETPTI(pwnd);
  4218. pkl = ptiToggle->spklActive;
  4219. UserAssert(ptiToggle->spklActive != NULL);
  4220. /*
  4221. * Check for Arabic toggle context
  4222. */
  4223. if (gLangToggleKeyState < KLT_NONE && PRIMARYLANGID(lcid) == LANG_ARABIC){
  4224. PKL pkl_next = HKLtoPKL (ptiToggle, (HKL)HKL_NEXT);
  4225. /*
  4226. * test if there are exactly two pkl's and at least one
  4227. * of them is arabic
  4228. */
  4229. if (pkl && pkl_next &&
  4230. pkl->hkl != pkl_next->hkl && pkl_next == HKLtoPKL(ptiToggle, (HKL)HKL_PREV) &&
  4231. (PRIMARYLANGID(HandleToUlong(pkl->hkl)) == LANG_ARABIC || PRIMARYLANGID(HandleToUlong(pkl_next->hkl)) == LANG_ARABIC)){
  4232. bArabicSwitchPresent = TRUE;
  4233. }
  4234. }
  4235. /*
  4236. * NT has always had Alt LShift going forward (down) the list,
  4237. * and Alt RShift going backwards. Windows '95 is different.
  4238. */
  4239. switch (gLangToggleKeyState) {
  4240. case KLT_ALTLEFTSHIFT:
  4241. bDropToggle = TRUE;
  4242. dwDirection = INPUTLANGCHANGE_FORWARD;
  4243. if (!bArabicSwitchPresent || PRIMARYLANGID(HandleToUlong(pkl->hkl)) == LANG_ARABIC){
  4244. pkl = HKLtoPKL(ptiToggle, (HKL)HKL_NEXT);
  4245. }
  4246. break;
  4247. case KLT_ALTRIGHTSHIFT:
  4248. bDropToggle = TRUE;
  4249. dwDirection = INPUTLANGCHANGE_BACKWARD;
  4250. if (!bArabicSwitchPresent || PRIMARYLANGID(HandleToUlong(pkl->hkl)) != LANG_ARABIC){
  4251. pkl = HKLtoPKL(ptiToggle, (HKL)HKL_PREV);
  4252. }
  4253. break;
  4254. case KLT_ALTBOTHSHIFTS:
  4255. pkl = gspklBaseLayout;
  4256. break;
  4257. default:
  4258. goto NoLayoutSwitch;
  4259. break;
  4260. }
  4261. if (pkl == NULL) {
  4262. pkl = GETPTI(pwnd)->spklActive;
  4263. }
  4264. /*
  4265. * If these two are not NULL, then winlogon hasn't loaded
  4266. * any keyboard layouts yet: but nobody should be getting
  4267. * input yet, so Assert but check pkl anyway. #99321
  4268. */
  4269. UserAssert(gspklBaseLayout != NULL);
  4270. UserAssert(pkl);
  4271. if (pkl) {
  4272. /*
  4273. * Not a very satisfactory window to post to, but it's hard
  4274. * to figure out a better window. Just do as Memphis does.
  4275. * Note: The following went up too high, bypassing Word
  4276. * when using wordmail - IanJa bug #64744.
  4277. * if ((pwndTop = GetTopLevelWindow(pwnd)) != NULL) {
  4278. * pwnd = pwndTop;
  4279. * }
  4280. */
  4281. _PostMessage(pwnd, WM_INPUTLANGCHANGEREQUEST,
  4282. (DWORD)(((pkl->dwFontSigs & gSystemFS) ? INPUTLANGCHANGE_SYSCHARSET : 0) | dwDirection),
  4283. (LPARAM)pkl->hkl);
  4284. }
  4285. NoLayoutSwitch:
  4286. if (bDropToggle) {
  4287. /*
  4288. * Clear this key from the key state so that multiple key
  4289. * presses will work (i.e., Alt+Shft+Shft). We don't do
  4290. * this when both shift keys are pressed simultaneously to
  4291. * avoid two activates.
  4292. */
  4293. DWORD i;
  4294. BYTE scancode = LOBYTE(HIWORD(qmsg.msg.lParam));
  4295. BYTE vkey = LOBYTE(qmsg.msg.wParam);
  4296. for (i = 0; i < LANGTOGGLEKEYS_SIZE; i++) {
  4297. if (gLangToggle[i].bScan) {
  4298. if (gLangToggle[i].bScan == scancode) {
  4299. gLangToggleKeyState &= ~(gLangToggle[i].iBitPosition);
  4300. }
  4301. } else {
  4302. if (gLangToggle[i].bVkey == vkey) {
  4303. gLangToggleKeyState &= ~(gLangToggle[i].iBitPosition);
  4304. }
  4305. }
  4306. }
  4307. } else {
  4308. gLangToggleKeyState = 0;
  4309. }
  4310. }
  4311. }
  4312. /*
  4313. * Convert F10 to syskey for new apps.
  4314. */
  4315. if (wParam == VK_F10)
  4316. message |= (WM_SYSKEYDOWN - WM_KEYDOWN);
  4317. if (TestKeyStateDown(ptiCurrent->pq, VK_CONTROL) &&
  4318. wParam == VK_ESCAPE) {
  4319. message |= (WM_SYSKEYDOWN - WM_KEYDOWN);
  4320. }
  4321. /*
  4322. * Clear the 'simulated keystroke' bit for all applications except
  4323. * console so it can pass it to 16-bit vdms. VDM keyboards need to
  4324. * distinguish between AltGr (where Ctrl keystroke is simulated)
  4325. * and a real Ctrl+Alt. Check TIF_CSRSSTHREAD for the console
  4326. * input thread because it lives in the server. This is a cheap
  4327. * way to check for it.
  4328. */
  4329. if (!(ptiCurrent->TIF_flags & TIF_CSRSSTHREAD))
  4330. qmsg.msg.lParam &= ~FAKE_KEYSTROKE;
  4331. PATHTAKEN2(0x4000000);
  4332. /*
  4333. * Fall through.
  4334. */
  4335. /*
  4336. * Some apps want to be able to feed WM_CHAR messages through
  4337. * the playback hook. Why? Because they want to be able to
  4338. * convert a string of characters info key messages
  4339. * and feed them to themselves or other apps. Unfortunately,
  4340. * there are no machine independent virtual key codes for
  4341. * some characters (for example '$'), so they need to send
  4342. * those through as WM_CHARs. (6/10/87).
  4343. */
  4344. case WM_CHAR:
  4345. wParam = qmsg.msg.wParam & 0xFF;
  4346. /*
  4347. * Assign the input to the focus window. If there is no focus
  4348. * window, assign it to the active window as a SYS message.
  4349. */
  4350. pwnd = ptiCurrent->pq->spwndFocus;
  4351. if (ptiCurrent->pq->spwndFocus == NULL) {
  4352. if ((pwnd = ptiCurrent->pq->spwndActive) != NULL) {
  4353. if (CheckMsgFilter(message, WM_KEYDOWN, WM_DEADCHAR)) {
  4354. message += (WM_SYSKEYDOWN - WM_KEYDOWN);
  4355. PATHTAKEN2(0x8000000);
  4356. }
  4357. } else {
  4358. PATHTAKEN2(0x10000000);
  4359. goto SkipMessage;
  4360. }
  4361. }
  4362. /*
  4363. * If there is no active window or focus window, eat this
  4364. * message.
  4365. */
  4366. if (pwnd == NULL) {
  4367. PATHTAKEN2(0x20000000);
  4368. goto SkipMessage;
  4369. }
  4370. ThreadLockExchangeAlways(pwnd, &tlpwnd);
  4371. /*
  4372. * Check if this is intended for the current app.
  4373. */
  4374. if (fOtherApp = (GETPTI(pwnd) != ptiCurrent)) {
  4375. PWND pwndModalLoop;
  4376. /*
  4377. * If this other app isn't going to read from this
  4378. * queue, then skip this message. This can happen if
  4379. * the RIT queues up a message thinking it goes to
  4380. * a particular hwnd, but then by the time GetMessage()
  4381. * is called for that thread, it doesn't go to that hwnd
  4382. * (like in the case of mouse messages, window rearrangement
  4383. * happens which changes which hwnd the mouse hits on).
  4384. */
  4385. if (GETPTI(pwnd)->pq != ptiCurrent->pq) {
  4386. PATHTAKEN2(0x40000000);
  4387. goto SkipMessage;
  4388. }
  4389. /*
  4390. * If the current thread is in the menu or movesize loop
  4391. * then we need to give it the input
  4392. */
  4393. if (IsInsideMenuLoop(ptiCurrent)) {
  4394. pwndModalLoop = ptiCurrent->pMenuState->pGlobalPopupMenu->spwndNotify;
  4395. } else if (ptiCurrent->pmsd != NULL) {
  4396. pwndModalLoop = ptiCurrent->pmsd->spwnd;
  4397. RIPMSG0(RIP_WARNING, "xxxScanSysQueue: returning key to movesize loop");
  4398. } else {
  4399. pwndModalLoop = NULL;
  4400. }
  4401. /*
  4402. * If we're switching windows, lock the new one
  4403. */
  4404. if (pwndModalLoop != NULL) {
  4405. pwnd = pwndModalLoop;
  4406. fOtherApp = (GETPTI(pwnd) != ptiCurrent);
  4407. ThreadLockExchangeAlways(pwnd, &tlpwnd);
  4408. PATHTAKEN2(0x80000000);
  4409. }
  4410. /*
  4411. * If not for us, then remember who it is for.
  4412. */
  4413. if (ptiKeyWake == NULL) {
  4414. PATHTAKEN3(1);
  4415. ptiKeyWake = GETPTI(pwnd);
  4416. ThreadLockExchangePti(ptiKeyWake, &tlptiKeyWake);
  4417. }
  4418. }
  4419. /*
  4420. * See if this thing matches our filter.
  4421. */
  4422. if (!CheckMsgFilter(message, msgMinFilter, msgMaxFilter) ||
  4423. !CheckPwndFilter(pwnd, pwndFilter)) {
  4424. PATHTAKEN3(2);
  4425. continue;
  4426. }
  4427. /*
  4428. * This message matches our filter. If it is not for us then
  4429. * stop searching to make sure the real owner processes this
  4430. * message first.
  4431. */
  4432. if (fOtherApp) {
  4433. PATHTAKEN3(4);
  4434. goto NoMessages;
  4435. }
  4436. /*
  4437. * Generate some special messages if we are removing and we are
  4438. * not inside the menu loop.
  4439. */
  4440. if (fRemove && !IsInsideMenuLoop(ptiCurrent)) {
  4441. /*
  4442. * Generate a WM_CONTEXTMENU for the VK_APPS key
  4443. */
  4444. if ((wParam == VK_APPS) && (message == WM_KEYUP)) {
  4445. _PostMessage(pwnd, WM_CONTEXTMENU, (WPARAM)PtoH(pwnd), KEYBOARD_MENU);
  4446. }
  4447. /*
  4448. * If this is a WM_KEYDOWN message for F1 key then we must generate
  4449. * the WM_KEYF1 message.
  4450. */
  4451. if ((wParam == VK_F1) && (message == WM_KEYDOWN)) {
  4452. _PostMessage(pwnd, WM_KEYF1, 0, 0);
  4453. }
  4454. }
  4455. /*
  4456. * If one Shift key is released while the other Shift key is held
  4457. * down, this keystroke is normally skipped, presumably to prevent
  4458. * applications from thinking that the shift condition no longer
  4459. * applies.
  4460. */
  4461. if (wParam == VK_SHIFT) {
  4462. BYTE vkHanded, vkOtherHand;
  4463. if (qmsg.msg.lParam & EXTENDED_BIT) {
  4464. vkHanded = VK_RSHIFT;
  4465. } else {
  4466. vkHanded = VK_LSHIFT;
  4467. }
  4468. vkOtherHand = vkHanded ^ 1;
  4469. if (!fDown && TestKeyStateDown(ptiCurrent->pq, vkOtherHand)) {
  4470. /*
  4471. * Unlike normal apps, Console MUST be sent a Shift break
  4472. * even when the other Shift key is still down, since it
  4473. * has to be passed on to VDM, which maintains it's own
  4474. * state. Check TIF_CSRSSTHREAD for the console input
  4475. * thread because it lives in the server. This is a cheap
  4476. * way to check for it.
  4477. */
  4478. if ((ptiCurrent->TIF_flags & TIF_CSRSSTHREAD) == 0) {
  4479. /*
  4480. * We ignore this key event, so we must update
  4481. * it's key state whether fRemove is TRUE or not.
  4482. * (ignoring an key event is same as removing it)
  4483. */
  4484. qmsg.msg.wParam = vkHanded;
  4485. xxxSkipSysMsg(ptiCurrent, &qmsg);
  4486. PATHTAKEN3(8);
  4487. goto RestartScan;
  4488. }
  4489. PATHTAKEN3(0x10);
  4490. }
  4491. }
  4492. /*
  4493. * Get the previous up/down state of the key here since
  4494. * SkipSysMsg() sets the key state table and destroys
  4495. * the previous state info.
  4496. */
  4497. fPrevDown = FALSE;
  4498. if (TestKeyStateDown(ptiCurrent->pq, wParam))
  4499. fPrevDown = TRUE;
  4500. /*
  4501. * Eat the message from the input queue and set the keystate
  4502. * table.
  4503. */
  4504. PATHTAKEN3(0x20);
  4505. if (fRemove) {
  4506. xxxSkipSysMsg(ptiCurrent, &qmsg);
  4507. }
  4508. /*
  4509. * This gets us the LOWORD of lParam, the repeat count,
  4510. * the bit in the hi byte indicating whether this is an extended
  4511. * key, and the scan code. We also need to re-get the wParam in
  4512. * case xxxSkipSysMsg called a hook which modified the message.
  4513. * AfterDark's password protection does this.
  4514. */
  4515. lParam = qmsg.msg.lParam;
  4516. wParam = qmsg.msg.wParam;
  4517. /*
  4518. * Indicate if it was previously down.
  4519. */
  4520. if (fPrevDown)
  4521. lParam |= 0x40000000; // KF_REPEAT
  4522. /*
  4523. * Set the transition bit.
  4524. */
  4525. switch (message) {
  4526. case WM_KEYUP:
  4527. case WM_SYSKEYUP:
  4528. lParam |= 0x80000000; // KF_UP
  4529. break;
  4530. }
  4531. /*
  4532. * Set the alt key down bit.
  4533. */
  4534. if (TestKeyStateDown(ptiCurrent->pq, VK_MENU)) {
  4535. lParam |= 0x20000000; // KF_ALTDOWN
  4536. }
  4537. /*
  4538. * Set the menu state flag.
  4539. */
  4540. if (IsMenuStarted(ptiCurrent)) {
  4541. lParam |= 0x10000000; // KF_MENUMODE
  4542. }
  4543. /*
  4544. * Set the dialog state flag.
  4545. */
  4546. if (ptiCurrent->pq->QF_flags & QF_DIALOGACTIVE) {
  4547. lParam |= 0x08000000; // KF_DLGMODE
  4548. }
  4549. /*
  4550. * 0x80000000 is set if up, clear if down
  4551. * 0x40000000 is previous up/down state of key
  4552. * 0x20000000 is whether the alt key is down
  4553. * 0x10000000 is whether currently in menumode.
  4554. * 0x08000000 is whether in dialog mode
  4555. * 0x04000000 is not used
  4556. * 0x02000000 is not used
  4557. * 0x01000000 is whether this is an extended keyboard key
  4558. *
  4559. * Low word is repeat count, low byte hiword is scan code,
  4560. * hi byte hiword is all these bits.
  4561. */
  4562. /*
  4563. * Callback the client IME before calling the keyboard hook.
  4564. * If the vkey is one of the IME hotkeys, the vkey will not
  4565. * be passed to the keyboard hook.
  4566. * If IME needs this vkey, VK_PROCESSKEY will be put into the
  4567. * application queue instead of real vkey.
  4568. */
  4569. UserAssert(ptiCurrent != NULL);
  4570. if (gpImeHotKeyListHeader != NULL &&
  4571. fRemove &&
  4572. !IsMenuStarted(ptiCurrent) &&
  4573. !(ptiCurrent->TIF_flags & TIF_DISABLEIME) &&
  4574. pwnd != NULL) {
  4575. WPARAM wParamTemp = wParam;
  4576. if (wParam == VK_PACKET) {
  4577. wParamTemp = MAKEWPARAM(wParam, ptiCurrent->wchInjected);
  4578. }
  4579. /*
  4580. * xxxImmProcessKey also checks the registered IME hotkeys.
  4581. */
  4582. dwImmRet = xxxImmProcessKey( ptiCurrent->pq,
  4583. pwnd,
  4584. message,
  4585. wParamTemp,
  4586. lParam);
  4587. if ( dwImmRet & (IPHK_HOTKEY | IPHK_SKIPTHISKEY) ) {
  4588. dwImmRet = 0;
  4589. goto SkipMessage;
  4590. }
  4591. }
  4592. /*
  4593. * If we are removing the message, call the keyboard hook
  4594. * with HC_ACTION, otherwise call the hook with HC_NOREMOVE
  4595. * to let it know that the message is not being removed.
  4596. */
  4597. if (IsHooked(ptiCurrent, WHF_KEYBOARD)) {
  4598. fKbdHookCalled = TRUE;
  4599. if (xxxCallHook(fRemove ? HC_ACTION : HC_NOREMOVE,
  4600. wParam, lParam, WH_KEYBOARD)) {
  4601. PATHTAKEN3(0x40);
  4602. goto SkipMessage;
  4603. }
  4604. }
  4605. if (fKbdHookCalled && fRemove && IsHooked(ptiCurrent, WHF_CBT)) {
  4606. xxxCallHook(HCBT_KEYSKIPPED, wParam, lParam, WH_CBT);
  4607. PATHTAKEN3(0x80);
  4608. }
  4609. fKbdHookCalled = FALSE;
  4610. PATHTAKEN3(0x100);
  4611. goto ReturnMessage;
  4612. case WM_MOUSEWHEEL:
  4613. /*
  4614. * If we are sending keyboard input to an app that has been
  4615. * spinning then boost it back up. If we don't you use spinning
  4616. * apps like Write or Project and do two builds in the
  4617. * background. Note the app will also be unboosted again shortly
  4618. * after you stop typing by the old logic. #11188
  4619. */
  4620. if (ptiCurrent->TIF_flags & TIF_SPINNING) {
  4621. if (!NT_SUCCESS(CheckProcessForeground(ptiCurrent))) {
  4622. goto NoMessages;
  4623. }
  4624. }
  4625. /*
  4626. * Assign the input to the focus window. If there is no focus
  4627. * window, or we are in a menu loop, eat this message.
  4628. */
  4629. pwnd = ptiCurrent->pq->spwndFocus;
  4630. if (pwnd == NULL || IsInsideMenuLoop(ptiCurrent)) {
  4631. PATHTAKEN2(0x20000000);
  4632. goto SkipMessage;
  4633. }
  4634. ThreadLockExchangeAlways(pwnd, &tlpwnd);
  4635. /*
  4636. * Check if this is intended for the current app.
  4637. */
  4638. if (fOtherApp = (GETPTI(pwnd) != ptiCurrent)) {
  4639. /*
  4640. * If this other app isn't going to read from this
  4641. * queue, then skip this message. This can happen if
  4642. * the RIT queues up a message thinking it goes to
  4643. * a particular hwnd, but then by the time GetMessage()
  4644. * is called for that thread, it doesn't go to that hwnd
  4645. * (like in the case of mouse messages, window rearrangement
  4646. * happens which changes which hwnd the mouse hits on).
  4647. */
  4648. if (GETPTI(pwnd)->pq != ptiCurrent->pq) {
  4649. PATHTAKEN2(0x40000000);
  4650. goto SkipMessage;
  4651. }
  4652. /*
  4653. * If not for us, then remember who it is for.
  4654. */
  4655. if (ptiKeyWake == NULL) {
  4656. PATHTAKEN3(1);
  4657. ptiKeyWake = GETPTI(pwnd);
  4658. ThreadLockExchangePti(ptiKeyWake, &tlptiKeyWake);
  4659. }
  4660. }
  4661. /*
  4662. * See if this thing matches our filter.
  4663. * NOTE: We need to check whether the caller is filtering
  4664. * for all mouse messages - if so, we assume the caller
  4665. * wants mouse wheel messages too.
  4666. */
  4667. if ( !CheckMsgFilter(WM_MOUSEWHEEL, msgMinFilter, msgMaxFilter) ||
  4668. !CheckPwndFilter(pwnd, pwndFilter)) {
  4669. PATHTAKEN3(2);
  4670. continue;
  4671. }
  4672. /*
  4673. * This message matches our filter. If it is not for us then
  4674. * stop searching to make sure the real owner processes this
  4675. * message first.
  4676. */
  4677. if (fOtherApp) {
  4678. PATHTAKEN3(4);
  4679. goto NoMessages;
  4680. }
  4681. /*
  4682. * Eat the message from the input queue and set the keystate
  4683. * table.
  4684. */
  4685. PATHTAKEN3(0x20);
  4686. if (fRemove) {
  4687. xxxSkipSysMsg(ptiCurrent, &qmsg);
  4688. }
  4689. wParam = GetMouseKeyFlags(ptiCurrent->pq);
  4690. UserAssert(LOWORD(qmsg.msg.wParam) == 0);
  4691. UserAssert(HIWORD(wParam) == 0);
  4692. wParam |= qmsg.msg.wParam;
  4693. lParam = qmsg.msg.lParam;
  4694. /*
  4695. * If we are removing the message, call the mouse hook
  4696. * with HC_ACTION, otherwise call the hook with HC_NOREM
  4697. * to let it know that the message is not being removed.
  4698. */
  4699. if (IsHooked(ptiCurrent, WHF_MOUSE)) {
  4700. fMouseHookCalled = TRUE;
  4701. mhs.pt = qmsg.msg.pt;
  4702. mhs.hwnd = HW(pwnd);
  4703. mhs.wHitTestCode = HTNOWHERE;
  4704. mhs.dwExtraInfo = qmsg.ExtraInfo;
  4705. mhs.mouseData = (DWORD)qmsg.msg.wParam;
  4706. if (xxxCallMouseHook(message, &mhs, fRemove)) {
  4707. /*
  4708. * Not allowed by mouse hook; so skip it.
  4709. */
  4710. PATHTAKEN3(0x40);
  4711. goto SkipMessage;
  4712. }
  4713. }
  4714. if (fMouseHookCalled && fRemove && IsHooked(ptiCurrent, WHF_CBT)) {
  4715. /*
  4716. * CONSIDER: Add new HCBT_ constant for the mouse wheel?
  4717. */
  4718. xxxCallHook(HCBT_CLICKSKIPPED, message, (LPARAM)&mhs, WH_CBT);
  4719. PATHTAKEN3(0x80);
  4720. }
  4721. fMouseHookCalled = FALSE;
  4722. PATHTAKEN3(0x100);
  4723. goto ReturnMessage;
  4724. #ifdef GENERIC_INPUT
  4725. case WM_INPUT:
  4726. /*
  4727. * Generic Input messages.
  4728. * There is not much we should look at here. The best practice is just
  4729. * omit most of the processing and just return the current message.
  4730. */
  4731. wParam = qmsg.msg.wParam;
  4732. lParam = qmsg.msg.lParam;
  4733. /*
  4734. * Assign the input to the focus window. If there is no focus
  4735. * window, assign it to the active window as a SYS message.
  4736. */
  4737. pwnd = NULL;
  4738. if (lParam) {
  4739. PHIDDATA pHidData = HMValidateHandle((LPVOID)lParam, TYPE_HIDDATA);
  4740. if (pHidData) {
  4741. pwnd = pHidData->spwndTarget;
  4742. }
  4743. }
  4744. if (pwnd == NULL) {
  4745. pwnd = ptiCurrent->pq->spwndFocus;
  4746. if (pwnd == NULL) {
  4747. pwnd = ptiCurrent->pq->spwndActive;
  4748. if (pwnd == NULL) {
  4749. PATHTAKEN2(0x10000000);
  4750. goto SkipMessage;
  4751. }
  4752. }
  4753. }
  4754. TAGMSG1(DBGTAG_PNP, "xxxScanSysQueue: pwnd=%p", pwnd);
  4755. UserAssert(pwnd != NULL);
  4756. ThreadLockExchangeAlways(pwnd, &tlpwnd);
  4757. /*
  4758. * Check if this is intended for the current app.
  4759. */
  4760. if (fOtherApp = (GETPTI(pwnd) != ptiCurrent)) {
  4761. PWND pwndModalLoop;
  4762. /*
  4763. * If this other app isn't going to read from this
  4764. * queue, then skip this message. This can happen if
  4765. * the RIT queues up a message thinking it goes to
  4766. * a particular hwnd, but then by the time GetMessage()
  4767. * is called for that thread, it doesn't go to that hwnd
  4768. * (like in the case of mouse messages, window rearrangement
  4769. * happens which changes which hwnd the mouse hits on).
  4770. */
  4771. if (GETPTI(pwnd)->pq != ptiCurrent->pq) {
  4772. PATHTAKEN2(0x40000000);
  4773. goto SkipMessage;
  4774. }
  4775. /*
  4776. * If the current thread is in the menu or movesize loop
  4777. * then we need to give it the input
  4778. */
  4779. if (IsInsideMenuLoop(ptiCurrent)) {
  4780. pwndModalLoop = ptiCurrent->pMenuState->pGlobalPopupMenu->spwndNotify;
  4781. } else if (ptiCurrent->pmsd != NULL) {
  4782. pwndModalLoop = ptiCurrent->pmsd->spwnd;
  4783. RIPMSG0(RIP_WARNING, "xxxScanSysQueue: returning key to movesize loop");
  4784. } else {
  4785. pwndModalLoop = NULL;
  4786. }
  4787. /*
  4788. * If we're switching windows, lock the new one
  4789. */
  4790. if (pwndModalLoop != NULL) {
  4791. pwnd = pwndModalLoop;
  4792. fOtherApp = (GETPTI(pwnd) != ptiCurrent);
  4793. ThreadLockExchangeAlways(pwnd, &tlpwnd);
  4794. PATHTAKEN2(0x80000000);
  4795. }
  4796. /*
  4797. * If not for us, then remember who it is for.
  4798. */
  4799. if (ptiRawInputWake == NULL) {
  4800. PATHTAKEN3(1);
  4801. ptiRawInputWake = GETPTI(pwnd);
  4802. ThreadLockExchangePti(ptiRawInputWake, &tlptiRawInputWake);
  4803. }
  4804. }
  4805. /*
  4806. * See if this thing matches our filter.
  4807. */
  4808. if (!CheckMsgFilter(message, msgMinFilter, msgMaxFilter) ||
  4809. !CheckPwndFilter(pwnd, pwndFilter)) {
  4810. PATHTAKEN3(2);
  4811. continue;
  4812. }
  4813. /*
  4814. * This message matches our filter. If it is not for us then
  4815. * stop searching to make sure the real owner processes this
  4816. * message first.
  4817. */
  4818. if (fOtherApp) {
  4819. PATHTAKEN3(4);
  4820. goto NoMessages;
  4821. }
  4822. /*
  4823. * Remove the message from the input queue.
  4824. */
  4825. if (fRemove) {
  4826. #if LOCK_HIDDATA
  4827. PHIDDATA pHidData = HMValidateHandle((LPVOID)lParam, TYPE_HIDDATA);
  4828. if (pHidData) {
  4829. /*
  4830. * Lock the object so that hRawInput is not destroyed
  4831. * while the message is removed from the input queue.
  4832. */
  4833. HMLockObject(pHidData);
  4834. }
  4835. else {
  4836. RIPMSG1(RIP_WARNING, "xxxScanSysQueue: invalid WM_INPUT's lParam %p", lParam);
  4837. }
  4838. #endif
  4839. xxxSkipSysMsg(ptiCurrent, &qmsg);
  4840. }
  4841. /*
  4842. * N.b.
  4843. * WM_INPUT is not handed to input hooks.
  4844. */
  4845. PATHTAKEN3(0x00010000);
  4846. goto ReturnMessage;
  4847. #endif
  4848. } /* End of switch (message = qmsg.msg.message) */
  4849. } /* End of the GetNextSysMsg() loop */
  4850. ReturnMessage:
  4851. if (!RtlEqualMemory(&ptiCurrent->ptLast, &qmsg.msg.pt, sizeof(POINT))) {
  4852. ptiCurrent->TIF_flags |= TIF_MSGPOSCHANGED;
  4853. }
  4854. ptiCurrent->ptLast = qmsg.msg.pt;
  4855. ptiCurrent->timeLast = qmsg.msg.time;
  4856. ptiCurrent->pq->ExtraInfo = qmsg.ExtraInfo;
  4857. /*
  4858. * idSysLock value of 1 indicates that the message came from the input
  4859. * queue.
  4860. */
  4861. ptiCurrent->idLast = ptiCurrent->pq->idSysLock = 1;
  4862. /*
  4863. * Now see if our input bit is set for this input. If it isn't, set ours
  4864. * and clear the guy who had it previously.
  4865. */
  4866. TransferWakeBit(ptiCurrent, message);
  4867. /*
  4868. * Clear the input bits if no messages in the input queue.
  4869. */
  4870. ClearWakeBit(ptiCurrent, QS_MOUSE | QS_KEY | QS_EVENT |
  4871. #ifdef GENERIC_INPUT
  4872. QS_RAWINPUT |
  4873. #endif
  4874. QS_TRANSFER, TRUE);
  4875. /*
  4876. * Get the message and split.
  4877. */
  4878. lpMsg->hwnd = HW(pwnd);
  4879. lpMsg->message = message;
  4880. /*
  4881. * If the IME claims that it needs this vkey, replace it
  4882. * with VK_PROCESSKEY. The real vkey has been saved in
  4883. * the input context in the client side.
  4884. */
  4885. lpMsg->wParam = (dwImmRet & IPHK_PROCESSBYIME) ? VK_PROCESSKEY : wParam;
  4886. lpMsg->lParam = lParam;
  4887. lpMsg->time = qmsg.msg.time;
  4888. lpMsg->pt = qmsg.msg.pt;
  4889. #if DBG
  4890. if (gfLogPlayback && ptiCurrent->pq->idSysPeek == (LONG_PTR)PQMSG_PLAYBACK)
  4891. LogPlayback(pwnd, lpMsg);
  4892. #endif // DBG
  4893. #ifdef GENERIC_INPUT
  4894. ThreadUnlockPti(ptiCurrent, &tlptiRawInputWake);
  4895. #endif
  4896. ThreadUnlockPti(ptiCurrent, &tlptiEventWake);
  4897. ThreadUnlockPti(ptiCurrent, &tlptiMouseWake);
  4898. ThreadUnlockPti(ptiCurrent, &tlptiKeyWake);
  4899. ThreadUnlock(&tlpwnd);
  4900. PATHTAKEN3(0x200);
  4901. DUMPPATHTAKEN();
  4902. return TRUE;
  4903. NoMessages:
  4904. /*
  4905. * The message was for another app, or none were found that fit the
  4906. * filter.
  4907. */
  4908. /*
  4909. * Unlock the system queue.
  4910. */
  4911. ptiCurrent->pq->idSysLock = 0;
  4912. CheckSysLock(4, ptiCurrent->pq, NULL);
  4913. ptiCurrent->pq->ptiSysLock = NULL;
  4914. ptiCurrent->pcti->CTIF_flags &= ~CTIF_SYSQUEUELOCKED;
  4915. /*
  4916. * Wake up someone else if we found a message for him. QS_TRANSFER
  4917. * signifies that the thread was woken due to input transfer
  4918. * from another thread, rather than from a real input event.
  4919. */
  4920. if (ptiKeyWake != NULL || ptiMouseWake != NULL || ptiEventWake != NULL
  4921. #ifdef GENERIC_INPUT
  4922. || ptiRawInputWake != NULL
  4923. #endif
  4924. ) {
  4925. PATHTAKEN3(0x400);
  4926. if (ptiKeyWake != NULL) {
  4927. SetWakeBit(ptiKeyWake, QS_KEY | QS_TRANSFER);
  4928. ClearWakeBit(ptiCurrent, QS_KEY | QS_TRANSFER, FALSE);
  4929. PATHTAKEN3(0x800);
  4930. }
  4931. if (ptiMouseWake != NULL) {
  4932. SetWakeBit(ptiMouseWake, QS_MOUSE | QS_TRANSFER);
  4933. ClearWakeBit(ptiCurrent, QS_MOUSE | QS_TRANSFER, FALSE);
  4934. PATHTAKEN3(0x1000);
  4935. }
  4936. #ifdef GENERIC_INPUT
  4937. if (ptiRawInputWake != NULL) {
  4938. SetWakeBit(ptiRawInputWake, QS_RAWINPUT | QS_TRANSFER);
  4939. ClearWakeBit(ptiCurrent, QS_RAWINPUT | QS_TRANSFER, FALSE);
  4940. }
  4941. #endif
  4942. if (ptiEventWake != NULL) {
  4943. SetWakeBit(ptiEventWake, QS_EVENTSET);
  4944. ClearWakeBit(ptiCurrent, QS_EVENT, FALSE);
  4945. PATHTAKEN3(0x2000);
  4946. } else if (FJOURNALPLAYBACK()) {
  4947. /*
  4948. * If journal playback is occuring, clear the input bits. This will
  4949. * help prevent a race condition between two threads that call
  4950. * WaitMessage/PeekMessage. This can occur when embedding an OLE
  4951. * object. An example is inserting a Word object into an Excel
  4952. * spreadsheet.
  4953. * Also clear change bits else this thread might not xxxSleepThread.
  4954. */
  4955. ptiCurrent->pcti->fsWakeBitsJournal |= (ptiCurrent->pcti->fsWakeBits &
  4956. (QS_MOUSE | QS_KEY |
  4957. #ifdef GENERIC_INPUT
  4958. QS_RAWINPUT |
  4959. #endif
  4960. QS_TRANSFER));
  4961. ClearWakeBit(ptiCurrent, QS_MOUSE | QS_KEY |
  4962. #ifdef GENERIC_INPUT
  4963. QS_RAWINPUT |
  4964. #endif
  4965. QS_TRANSFER, FALSE);
  4966. ptiCurrent->pcti->fsChangeBits &= ~(QS_MOUSE | QS_KEY |
  4967. #ifdef GENERIC_INPUT
  4968. QS_RAWINPUT |
  4969. #endif
  4970. QS_TRANSFER);
  4971. }
  4972. } else {
  4973. /*
  4974. * Clear the input bits if no messages in the input queue.
  4975. */
  4976. ptiCurrent->pcti->fsWakeBitsJournal = 0;
  4977. ClearWakeBit(ptiCurrent, QS_MOUSE | QS_KEY | QS_EVENT |
  4978. #ifdef GENERIC_INPUT
  4979. QS_RAWINPUT |
  4980. #endif
  4981. QS_TRANSFER, TRUE);
  4982. PATHTAKEN3(0x4000);
  4983. }
  4984. #ifdef GENERIC_INPUT
  4985. ThreadUnlockPti(ptiCurrent, &tlptiRawInputWake);
  4986. #endif
  4987. ThreadUnlockPti(ptiCurrent, &tlptiEventWake);
  4988. ThreadUnlockPti(ptiCurrent, &tlptiMouseWake);
  4989. ThreadUnlockPti(ptiCurrent, &tlptiKeyWake);
  4990. ThreadUnlock(&tlpwnd);
  4991. PATHTAKEN3(0x8000);
  4992. DUMPPATHTAKEN();
  4993. return FALSE;
  4994. }
  4995. #undef PATHTAKEN
  4996. #undef PATHTAKEN2
  4997. #undef PATHTAKEN3
  4998. #undef DUMPPATHTAKEN
  4999. #undef DUMPSUBPATHTAKEN
  5000. /***************************************************************************\
  5001. * IdleTimerProc
  5002. *
  5003. * This will start the screen saver app
  5004. *
  5005. * History:
  5006. * 09-06-91 mikeke Created.
  5007. * 03-26-92 DavidPe Changed to be run from hungapp timer on RIT.
  5008. \***************************************************************************/
  5009. VOID IdleTimerProc(VOID)
  5010. {
  5011. CheckCritIn();
  5012. if ( (TestAsyncKeyStateDown(VK_LBUTTON)) ||
  5013. (TestAsyncKeyStateDown(VK_RBUTTON)) ||
  5014. (TestAsyncKeyStateDown(VK_MBUTTON)) ||
  5015. (TestAsyncKeyStateDown(VK_XBUTTON1)) ||
  5016. (TestAsyncKeyStateDown(VK_XBUTTON2))) {
  5017. return;
  5018. }
  5019. if (giScreenSaveTimeOutMs > 0) {
  5020. if (IsTimeFromLastInput((DWORD)(giScreenSaveTimeOutMs))) {
  5021. if (gppiScreenSaver != NULL) {
  5022. if (!(gppiScreenSaver->W32PF_Flags & W32PF_IDLESCREENSAVER)) {
  5023. /*
  5024. * Bump the priority of the screen saver down to idle.
  5025. */
  5026. gppiScreenSaver->W32PF_Flags |= W32PF_IDLESCREENSAVER;
  5027. SetForegroundPriorityProcess(gppiScreenSaver, gppiScreenSaver->ptiMainThread, TRUE);
  5028. }
  5029. } else {
  5030. /*
  5031. * Tell the system that it needs to bring up a screen saver.
  5032. *
  5033. * Carefull with the case when the active window is hung. If this
  5034. * is the case the screen saver won't be started by winlogon because
  5035. * DefWindowProc won't call StartScreenSaver(FALSE).
  5036. */
  5037. if ((gpqForeground != NULL) &&
  5038. (gpqForeground->spwndActive != NULL) &&
  5039. !FHungApp(GETPTI(gpqForeground->spwndActive), CMSHUNGAPPTIMEOUT)) {
  5040. /*
  5041. * Tell winlogon to start the screen saver if we have a secure
  5042. * screen saver. In case we do have a secure one, the next PostMessage
  5043. * will be ignored in winlogon.
  5044. */
  5045. StartScreenSaver(TRUE);
  5046. _PostMessage(gpqForeground->spwndActive, WM_SYSCOMMAND, SC_SCREENSAVE, 0L);
  5047. } else {
  5048. StartScreenSaver(FALSE);
  5049. }
  5050. }
  5051. }
  5052. }
  5053. if ((giLowPowerTimeOutMs > 0) && ((glinp.dwFlags & LINP_LOWPOWER) == 0)) {
  5054. if (IsTimeFromLastInput((DWORD)(giLowPowerTimeOutMs))) {
  5055. if ((gpqForeground != NULL) && (gpqForeground->spwndActive != NULL)) {
  5056. _PostMessage(gpqForeground->spwndActive, WM_SYSCOMMAND, SC_MONITORPOWER, LOWPOWER_PHASE);
  5057. }
  5058. }
  5059. }
  5060. if ((giPowerOffTimeOutMs > 0) && ((glinp.dwFlags & LINP_POWEROFF) == 0)) {
  5061. if (IsTimeFromLastInput((DWORD)(giPowerOffTimeOutMs))) {
  5062. if ((gpqForeground != NULL) && (gpqForeground->spwndActive != NULL)) {
  5063. _PostMessage(gpqForeground->spwndActive, WM_SYSCOMMAND, SC_MONITORPOWER, POWEROFF_PHASE);
  5064. }
  5065. }
  5066. }
  5067. }
  5068. /***************************************************************************\
  5069. * zzzWakeInputIdle
  5070. *
  5071. * The calling thread is going "idle". Wake up any thread waiting for this.
  5072. *
  5073. * 09-24-91 ScottLu Created.
  5074. \***************************************************************************/
  5075. void zzzWakeInputIdle(
  5076. PTHREADINFO pti)
  5077. {
  5078. PW32PROCESS W32Process = W32GetCurrentProcess();
  5079. /*
  5080. * clear out the TIF_FIRSTIDLE since here we are
  5081. */
  5082. pti->TIF_flags &= ~TIF_FIRSTIDLE;
  5083. /*
  5084. * Shared Wow Apps use the per thread idle event for synchronization.
  5085. * Separate Wow VDMs use the regular mechanism.
  5086. */
  5087. if (pti->TIF_flags & TIF_SHAREDWOW) {
  5088. UserAssert(pti->TIF_flags & TIF_16BIT);
  5089. if (pti->ptdb->pwti) {
  5090. SET_PSEUDO_EVENT(&pti->ptdb->pwti->pIdleEvent);
  5091. }
  5092. } else {
  5093. /*
  5094. * If the main thread is NULL, set it to this queue: it is calling
  5095. * GetMessage().
  5096. */
  5097. if (pti->ppi->ptiMainThread == NULL)
  5098. pti->ppi->ptiMainThread = pti;
  5099. /*
  5100. * Wake up anyone waiting on this event.
  5101. */
  5102. if (pti->ppi->ptiMainThread == pti) {
  5103. SET_PSEUDO_EVENT(&W32Process->InputIdleEvent);
  5104. }
  5105. }
  5106. /*
  5107. * Check to see if the startglass is on, and if so turn it off and update.
  5108. */
  5109. if (W32Process->W32PF_Flags & W32PF_STARTGLASS) {
  5110. /*
  5111. * This app is no longer in "starting" mode. Recalc when to hide
  5112. * the app starting cursor.
  5113. */
  5114. W32Process->W32PF_Flags &= ~W32PF_STARTGLASS;
  5115. zzzCalcStartCursorHide(NULL, 0);
  5116. }
  5117. }
  5118. void SleepInputIdle(
  5119. PTHREADINFO pti)
  5120. {
  5121. PW32PROCESS W32Process;
  5122. /*
  5123. * Shared Wow Apps use the per thread idle event for synchronization.
  5124. * Separate Wow VDMs use the regular mechanism.
  5125. */
  5126. if (pti->TIF_flags & TIF_SHAREDWOW) {
  5127. UserAssert(pti->TIF_flags & TIF_16BIT);
  5128. if (pti->ptdb->pwti) {
  5129. RESET_PSEUDO_EVENT(&pti->ptdb->pwti->pIdleEvent);
  5130. }
  5131. } else {
  5132. /*
  5133. * If the main thread is NULL, set it to this queue: it is calling
  5134. * GetMessage().
  5135. */
  5136. if (pti->ppi->ptiMainThread == NULL)
  5137. pti->ppi->ptiMainThread = pti;
  5138. /*
  5139. * Put to sleep up anyone waiting on this event.
  5140. */
  5141. if (pti->ppi->ptiMainThread == pti) {
  5142. W32Process = W32GetCurrentProcess();
  5143. RESET_PSEUDO_EVENT(&W32Process->InputIdleEvent);
  5144. }
  5145. }
  5146. }
  5147. /***************************************************************************\
  5148. * zzzRecalcThreadAttachment
  5149. * zzzRecalc2
  5150. * zzzAddAttachment
  5151. * CheckAttachment
  5152. *
  5153. * Runs through all the attachinfo fields for all threads and calculates
  5154. * which threads share which queues. Puts calculated result in pqAttach
  5155. * field in each threadinfo structure. This is a difficult problem
  5156. * whose only solution in iterative. The basic algorithm is:
  5157. *
  5158. * 0. Find next unattached thread and attach a queue to it. If none, stop.
  5159. * 1. Loop through all threads: If thread X assigned to this queue or any
  5160. * of X's attach requests assigned to this queue, assign X and all X's
  5161. * attachments to this queue. Remember if we ever attach a 16 bit thread.
  5162. * 2. If thread X is a 16 bit thread and we've already attached another
  5163. * 16 bit thread, assign X and all X's attachments to this queue.
  5164. * 3. If any change found in 1-2, goto 1
  5165. * 4. Goto 0
  5166. *
  5167. * 12-11-92 ScottLu Created.
  5168. * 01-Oct-1993 mikeke Fixed to work with MWOWs
  5169. \***************************************************************************/
  5170. void zzzAddAttachment(
  5171. PTHREADINFO pti,
  5172. PQ pqAttach,
  5173. LPBOOL pfChanged)
  5174. {
  5175. if (pti->pqAttach != pqAttach) {
  5176. /*
  5177. * LATER
  5178. * !!! This is totally messed up, The only reason that this thing
  5179. * could be non null is because two threads are going through
  5180. * zzzAttachThreadInput() at the same time. No one can predict
  5181. * what kind of problems are going to be caused by that.
  5182. * We leave the critical section in one place where we send
  5183. * WM_CANCELMODE below. We should figure out how to remove
  5184. * the sendmessage.
  5185. *
  5186. * If there already is a queue there, as there may be, destroy it.
  5187. * Note that zzzDestroyQueue() will only get rid of the queue if the
  5188. * thread reference count goes to 0.
  5189. */
  5190. PQ pqDestroy = pti->pqAttach;
  5191. pti->pqAttach = pqAttach;
  5192. if (pqDestroy != NULL)
  5193. zzzDestroyQueue(pqDestroy, pti);
  5194. pqAttach->cThreads++;
  5195. *pfChanged = TRUE;
  5196. }
  5197. }
  5198. void zzzRecalc2(
  5199. PQ pqAttach)
  5200. {
  5201. PATTACHINFO pai;
  5202. PTHREADINFO pti;
  5203. BOOL fChanged;
  5204. PLIST_ENTRY pHead, pEntry;
  5205. /*
  5206. * Defer Win Event notifications so we can traverse PtiList with impunity
  5207. * #bug number from shiflet
  5208. */
  5209. DeferWinEventNotify();
  5210. BEGINATOMICCHECK();
  5211. /*
  5212. * Keep adding attachments until everything that should be attached to this
  5213. * queue is attached
  5214. */
  5215. do {
  5216. fChanged = FALSE;
  5217. /*
  5218. * If a thread is attached to this Q attach all of it's attachments
  5219. * and MWOW buddies if they aren't already attached.
  5220. */
  5221. pHead = &PtiCurrent()->rpdesk->PtiList;
  5222. for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink) {
  5223. pti = CONTAINING_RECORD(pEntry, THREADINFO, PtiLink);
  5224. if (pti->pqAttach == pqAttach) {
  5225. /*
  5226. * check each of the attachments to see if this thread is attached
  5227. * to any other threads
  5228. */
  5229. for (pai = gpai; pai != NULL; pai = pai->paiNext) {
  5230. /*
  5231. * if they weren't attached already, attach them
  5232. */
  5233. if (pai->pti1 == pti || pai->pti2 == pti) {
  5234. zzzAddAttachment((pai->pti1 == pti) ? pai->pti2 : pai->pti1,
  5235. pqAttach, &fChanged);
  5236. }
  5237. }
  5238. /*
  5239. * If this is a 16bit thread attach to all other threads in
  5240. * it's MWOW
  5241. */
  5242. if (pti->TIF_flags & TIF_16BIT) {
  5243. PTHREADINFO ptiAttach;
  5244. PLIST_ENTRY pHeadAttach, pEntryAttach;
  5245. pHeadAttach = &pti->rpdesk->PtiList;
  5246. for (pEntryAttach = pHeadAttach->Flink;
  5247. pEntryAttach != pHeadAttach;
  5248. pEntryAttach = pEntryAttach->Flink) {
  5249. ptiAttach = CONTAINING_RECORD(pEntryAttach, THREADINFO, PtiLink);
  5250. if (ptiAttach->TIF_flags & TIF_16BIT &&
  5251. ptiAttach->ppi == pti->ppi) {
  5252. zzzAddAttachment(ptiAttach, pqAttach, &fChanged);
  5253. }
  5254. }
  5255. }
  5256. }
  5257. }
  5258. } while (fChanged);
  5259. ENDATOMICCHECK();
  5260. zzzEndDeferWinEventNotify();
  5261. }
  5262. void zzzRecalcThreadAttachment()
  5263. {
  5264. PTHREADINFO pti;
  5265. PLIST_ENTRY pHead, pEntry;
  5266. /*
  5267. * Win Event notifications must be defered so we can traverse PtiList with impunity
  5268. */
  5269. UserAssert(IsWinEventNotifyDeferred());
  5270. /*
  5271. * For all threads, start an attach queue if a thread hasn't been
  5272. * attached yet.
  5273. */
  5274. pHead = &PtiCurrent()->rpdesk->PtiList;
  5275. for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink) {
  5276. pti = CONTAINING_RECORD(pEntry, THREADINFO, PtiLink);
  5277. /*
  5278. * Assert: We should not leave the critsect from xxxCreateThreadInfo
  5279. * with the new thread in the rpdesk->PtiList but not yet with a queue.
  5280. */
  5281. UserAssert(pti->pq != NULL);
  5282. if (pti->pqAttach == NULL) {
  5283. /*
  5284. * Allocate a new queue for this thread if more than
  5285. * one thread references it.
  5286. */
  5287. if (pti->pq->cThreads > 1) {
  5288. pti->pqAttach = AllocQueue(NULL, NULL);
  5289. if (pti->pqAttach == NULL) {
  5290. RIPMSG0(RIP_WARNING, "zzzRecalcThreadAttachment: AllocQueue failed");
  5291. break;
  5292. }
  5293. pti->pqAttach->cThreads++;
  5294. } else {
  5295. pti->pqAttach = pti->pq;
  5296. }
  5297. /*
  5298. * Attach every thread that is directly or indirectly attached
  5299. * to this thread.
  5300. */
  5301. zzzRecalc2(pti->pqAttach);
  5302. }
  5303. }
  5304. }
  5305. /***************************************************************************\
  5306. * RedistributeInput
  5307. *
  5308. * This routine takes a input stream from the queue being left, and
  5309. * redistributes it. This effectively filters out the messages destined
  5310. * to the thread that left the queue.
  5311. *
  5312. * 12-10-92 ScottLu Created.
  5313. \***************************************************************************/
  5314. void RedistributeInput(
  5315. PQMSG pqmsgS,
  5316. PQ pqRedist)
  5317. {
  5318. PTHREADINFO ptiSave;
  5319. PTHREADINFO ptiT;
  5320. PQMSG *ppqmsgD;
  5321. PQMSG pqmsgT;
  5322. PMLIST pmlInput;
  5323. /*
  5324. * Since the thread attaching or unattaching may have left a queue
  5325. * shared by other threads, the messages we are going to requeue
  5326. * may have multiple destinations. On top of this, once we find
  5327. * a home queue for a message, it needs to be inserted in the
  5328. * list ordered by its time stamp (older messages go at the end).
  5329. */
  5330. /*
  5331. * Loop through a given dest's messages to find where to insert
  5332. * the source messages, based on message time stamp. Be sure
  5333. * to deal with empty message lists (meaning, check for NULL).
  5334. */
  5335. ptiT = NULL;
  5336. ppqmsgD = NULL;
  5337. pmlInput = NULL;
  5338. while (pqmsgS != NULL) {
  5339. /*
  5340. * Find out where this message should go.
  5341. */
  5342. ptiSave = ptiT;
  5343. ptiT = pqmsgS->pti;
  5344. /*
  5345. * Get rid of some event messages.
  5346. *
  5347. * QEVENT_UPDATEKEYSTATE: key state already up to date
  5348. */
  5349. if (pqmsgS->dwQEvent == QEVENT_UPDATEKEYSTATE) {
  5350. ptiT = NULL;
  5351. }
  5352. if (ptiT == NULL) {
  5353. /*
  5354. * Unlink it. pqmsgS should be the first in the list
  5355. */
  5356. UserAssert(!pqmsgS->pqmsgPrev);
  5357. if (pqmsgS->pqmsgNext != NULL) {
  5358. pqmsgS->pqmsgNext->pqmsgPrev = NULL;
  5359. }
  5360. pqmsgT = pqmsgS;
  5361. pqmsgS = pqmsgS->pqmsgNext;
  5362. /*
  5363. * Clean it / free it.
  5364. */
  5365. CleanEventMessage(pqmsgT);
  5366. FreeQEntry(pqmsgT);
  5367. ptiT = ptiSave;
  5368. continue;
  5369. }
  5370. /*
  5371. * Point to the pointer that points to the first message
  5372. * that this message should go to, so that pointer is easy to
  5373. * update, no matter where it is.
  5374. */
  5375. if (ppqmsgD == NULL || ptiSave != ptiT) {
  5376. /*
  5377. * If the source is younger than the last message in the
  5378. * destination, go to the end. Otherwise, start at the
  5379. * head of the desination list and find a place to insert
  5380. * the message.
  5381. */
  5382. if (ptiT->pq->mlInput.pqmsgWriteLast != NULL &&
  5383. pqmsgS->msg.time >= ptiT->pq->mlInput.pqmsgWriteLast->msg.time) {
  5384. ppqmsgD = &ptiT->pq->mlInput.pqmsgWriteLast->pqmsgNext;
  5385. } else {
  5386. ppqmsgD = &ptiT->pq->mlInput.pqmsgRead;
  5387. }
  5388. pmlInput = &ptiT->pq->mlInput;
  5389. }
  5390. /*
  5391. * If we're not at the end of the destination AND the destination
  5392. * message time is younger than the source time, go on to
  5393. * the next message.
  5394. */
  5395. while (*ppqmsgD != NULL && ((*ppqmsgD)->msg.time <= pqmsgS->msg.time)) {
  5396. ppqmsgD = &((*ppqmsgD)->pqmsgNext);
  5397. }
  5398. /*
  5399. * Link in the source before the dest message. Update
  5400. * it's next and prev pointers. Update the dest prev
  5401. * pointer.
  5402. */
  5403. pqmsgT = pqmsgS;
  5404. pqmsgS = pqmsgS->pqmsgNext;
  5405. pqmsgT->pqmsgNext = *ppqmsgD;
  5406. if (*ppqmsgD != NULL) {
  5407. pqmsgT->pqmsgPrev = (*ppqmsgD)->pqmsgPrev;
  5408. (*ppqmsgD)->pqmsgPrev = pqmsgT;
  5409. } else {
  5410. pqmsgT->pqmsgPrev = pmlInput->pqmsgWriteLast;
  5411. pmlInput->pqmsgWriteLast = pqmsgT;
  5412. }
  5413. *ppqmsgD = pqmsgT;
  5414. ppqmsgD = &pqmsgT->pqmsgNext;
  5415. pmlInput->cMsgs++;
  5416. /*
  5417. * If the thread has an event message, make sure it's going to wake
  5418. * up to process it. The QS_EVENT flag might not be set if the thread
  5419. * previously found an event message for another thread and passed
  5420. * control over to him.
  5421. */
  5422. if (pqmsgT->dwQEvent != 0 && !(ptiT->pcti->fsWakeBits & QS_EVENT)) {
  5423. SetWakeBit(ptiT, QS_EVENTSET);
  5424. }
  5425. /*
  5426. * Preserve the 'idSysPeek' from the old queue, checking if the
  5427. * redistributed queue is the same as ptiT->pq.
  5428. */
  5429. if (pqmsgT == (PQMSG)(pqRedist->idSysPeek) && pqRedist != ptiT->pq) {
  5430. if (ptiT->pq->idSysPeek == 0) {
  5431. CheckPtiSysPeek(6, ptiT->pq, pqRedist->idSysPeek);
  5432. ptiT->pq->idSysPeek = pqRedist->idSysPeek;
  5433. } else {
  5434. TAGMSG2(DBGTAG_SysPeek,
  5435. "idSysPeek %#p already set in pq %#p",
  5436. ptiT->pq->idSysPeek, ptiT->pq);
  5437. }
  5438. /*
  5439. * Set the 'idSysPeek' of this queue to 0 since
  5440. * we moved the idSysPeek to other queue
  5441. */
  5442. CheckPtiSysPeek(7, pqRedist, 0);
  5443. pqRedist->idSysPeek = 0;
  5444. /*
  5445. * Preserve also 'ptiSysLock'.
  5446. * Set ptiSysLock to the ptiT->pq only if it points to
  5447. * that queue.
  5448. */
  5449. if (ptiT->pq->ptiSysLock == NULL &&
  5450. pqRedist->ptiSysLock != NULL &&
  5451. pqRedist->ptiSysLock->pq == ptiT->pq) {
  5452. CheckSysLock(4, ptiT->pq, pqRedist->ptiSysLock);
  5453. ptiT->pq->ptiSysLock = pqRedist->ptiSysLock;
  5454. CheckSysLock(5, pqRedist, NULL);
  5455. pqRedist->ptiSysLock = NULL;
  5456. } else {
  5457. TAGMSG2(DBGTAG_SysPeek,
  5458. "ptiSysLock %#p already set in pq %#p\n",
  5459. ptiT->pq->ptiSysLock, ptiT->pq);
  5460. }
  5461. }
  5462. /*
  5463. * Don't want the prev pointer on our message list to point
  5464. * to this message which is on a different list (doesn't
  5465. * really matter because we're about to link it anyway,
  5466. * but completeness shouldn't hurt).
  5467. */
  5468. if (pqmsgS != NULL) {
  5469. pqmsgS->pqmsgPrev = NULL;
  5470. }
  5471. }
  5472. }
  5473. /***************************************************************************\
  5474. * CancelInputState
  5475. *
  5476. * This routine takes a queue and "cancels" input state in it - i.e., if the
  5477. * app thinks it is active, make it think it is not active, etc.
  5478. *
  5479. * 12-10-92 ScottLu Created.
  5480. \***************************************************************************/
  5481. VOID CancelInputState(
  5482. PTHREADINFO pti,
  5483. DWORD cmd)
  5484. {
  5485. PTHREADINFO ptiCurrent = PtiCurrent();
  5486. PWND pwndT;
  5487. TL tlpwndT;
  5488. TL tlpwndChild;
  5489. AAS aas;
  5490. /*
  5491. * In all cases, do not leave do any send messages or any callbacks!
  5492. * This is because this code is called from
  5493. * SetWindowsHook(WH_JOURNALPLAYBACK | WH_JOURNALRECORD). No app currently
  5494. * calling this routine expects to be called before this routine returns.
  5495. * (If you do callback before it returns, you'll break at least Access
  5496. * for Windows). - scottlu
  5497. */
  5498. switch (cmd) {
  5499. case CANCEL_ACTIVESTATE:
  5500. /*
  5501. * Active state.
  5502. */
  5503. pwndT = pti->pq->spwndActive;
  5504. ThreadLockWithPti(ptiCurrent, pwndT, &tlpwndT);
  5505. QueueNotifyMessage(pwndT, WM_NCACTIVATE, FALSE, 0);
  5506. QueueNotifyMessage(pwndT, WM_ACTIVATE,
  5507. MAKELONG(WA_INACTIVE, TestWF(pwndT, WFMINIMIZED)),
  5508. 0);
  5509. if (pwndT == pti->pq->spwndActive)
  5510. Unlock(&pti->pq->spwndActive);
  5511. aas.ptiNotify = GETPTI(pwndT);
  5512. aas.tidActDeact = TIDq(GETPTI(pwndT));
  5513. aas.fActivating = FALSE;
  5514. aas.fQueueNotify = TRUE;
  5515. /*
  5516. * Even though this in an xxx call, it does NOT leave any critical
  5517. * sections (because fQueueNotify is TRUE).
  5518. */
  5519. ThreadLockWithPti(ptiCurrent, GETPTI(pwndT)->rpdesk->pDeskInfo->spwnd->spwndChild, &tlpwndChild);
  5520. xxxInternalEnumWindow(GETPTI(pwndT)->rpdesk->pDeskInfo->spwnd->spwndChild,
  5521. (WNDENUMPROC_PWND)xxxActivateApp, (LPARAM)&aas, BWL_ENUMLIST);
  5522. ThreadUnlock(&tlpwndChild);
  5523. ThreadUnlock(&tlpwndT);
  5524. break;
  5525. case CANCEL_FOCUSSTATE:
  5526. /*
  5527. * Focus state.
  5528. */
  5529. pwndT = pti->pq->spwndFocus;
  5530. ThreadLockWithPti(ptiCurrent, pwndT, &tlpwndT);
  5531. QueueNotifyMessage(pwndT, WM_KILLFOCUS, 0, 0);
  5532. #ifdef FE_IME
  5533. if (IS_IME_ENABLED()) {
  5534. /*
  5535. * Even though this in an xxx call, it does NOT leave any
  5536. * critical section (because fQueueMsg is TRUE).
  5537. */
  5538. xxxFocusSetInputContext(pwndT, FALSE, TRUE);
  5539. }
  5540. #endif
  5541. if (pwndT == pti->pq->spwndFocus)
  5542. Unlock(&pti->pq->spwndFocus);
  5543. ThreadUnlock(&tlpwndT);
  5544. break;
  5545. case CANCEL_CAPTURESTATE:
  5546. /*
  5547. * Capture state.
  5548. */
  5549. /*
  5550. * We shouldn't be nuking the capture of a modal menu mode.
  5551. */
  5552. UserAssert((pti->pMenuState == NULL)
  5553. || pti->pMenuState->fModelessMenu
  5554. || pti->pMenuState->fInDoDragDrop);
  5555. pti->pq->QF_flags &= ~QF_CAPTURELOCKED;
  5556. pwndT = pti->pq->spwndCapture;
  5557. ThreadLockWithPti(ptiCurrent, pwndT, &tlpwndT);
  5558. QueueNotifyMessage(pwndT, WM_CANCELMODE, 0, 0);
  5559. if (pwndT == pti->pq->spwndCapture)
  5560. UnlockCaptureWindow(pti->pq);
  5561. ThreadUnlock(&tlpwndT);
  5562. break;
  5563. }
  5564. }
  5565. /***************************************************************************\
  5566. * DBGValidateQueueStates
  5567. *
  5568. * Verifies that all queues point to stuff owned by a thread attached to
  5569. * the queue.
  5570. *
  5571. * 07/29/97 GerardoB Created
  5572. \***************************************************************************/
  5573. #if DBG
  5574. #define VALIDATEQSPWND(spwnd) \
  5575. if (pq-> ## spwnd != NULL) { \
  5576. ptiwnd = GETPTI(pq-> ## spwnd); \
  5577. fDestroyedOK = (TestWF(pq-> ## spwnd, WFDESTROYED) && (ptiwnd == gptiRit)); \
  5578. UserAssert((pti->rpdesk == ptiwnd->rpdesk) || fDestroyedOK); \
  5579. UserAssert((pti == ptiwnd) \
  5580. || (fAttached && (pq == ptiwnd->pq)) \
  5581. || fDestroyedOK); \
  5582. }
  5583. void DBGValidateQueueStates (PDESKTOP pdesk)
  5584. {
  5585. BOOL fAttached, fDestroyedOK;
  5586. PQ pq;
  5587. PLIST_ENTRY pHead, pEntry;
  5588. PTHREADINFO pti, ptiwnd;
  5589. DWORD dwInForeground = 0;
  5590. UserAssert((gpqForeground == NULL)
  5591. || ((gpqForeground->ptiMouse->rpdesk == grpdeskRitInput)
  5592. && (gpqForeground->cThreads != 0)));
  5593. if (pdesk == NULL) {
  5594. RIPMSG0(RIP_WARNING, "DBGValidateQueueStates: Null pdesk parameter");
  5595. return;
  5596. }
  5597. pHead = &pdesk->PtiList;
  5598. for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink) {
  5599. pti = CONTAINING_RECORD(pEntry, THREADINFO, PtiLink);
  5600. pq = pti->pq;
  5601. if (pq == NULL) {
  5602. RIPMSG2(RIP_WARNING, "DBGValidateQueueStates: Null pq. pti:%#p. pdesk:%#p", pti, pdesk);
  5603. continue;
  5604. }
  5605. /*
  5606. * The queue should have a non-null cThreads excepting when it is
  5607. * QF_INDESTROY
  5608. */
  5609. if (!(pq->QF_flags & QF_INDESTROY)) {
  5610. UserAssert(pq->cThreads != 0);
  5611. }
  5612. fAttached = (pq->cThreads > 1);
  5613. if (pti->pq == gpqForeground) {
  5614. dwInForeground++;
  5615. }
  5616. /*
  5617. * pti's
  5618. */
  5619. UserAssert((pti == pq->ptiMouse)
  5620. || (fAttached && (pq == pq->ptiMouse->pq)));
  5621. UserAssert(pti->rpdesk == pq->ptiMouse->rpdesk);
  5622. UserAssert((pti == pq->ptiKeyboard)
  5623. || (fAttached && (pq == pq->ptiKeyboard->pq)));
  5624. UserAssert(pti->rpdesk == pq->ptiKeyboard->rpdesk);
  5625. if (pq->ptiSysLock != NULL) {
  5626. UserAssert((pti == pq->ptiSysLock)
  5627. || (fAttached && (pq == pq->ptiSysLock->pq)));
  5628. }
  5629. /*
  5630. * pwnd's.
  5631. */
  5632. VALIDATEQSPWND(spwndActive);
  5633. VALIDATEQSPWND(spwndFocus);
  5634. VALIDATEQSPWND(spwndCapture);
  5635. VALIDATEQSPWND(spwndActivePrev);
  5636. }
  5637. UserAssert((gpqForeground == NULL)
  5638. || (dwInForeground == 0)
  5639. || (gpqForeground->cThreads == dwInForeground));
  5640. }
  5641. /***************************************************************************\
  5642. * DBGValidateQueue
  5643. *
  5644. * Verifies that queue is readable and fields are valid.
  5645. *
  5646. * 02-Sep-1999 JerrySh Created.
  5647. \***************************************************************************/
  5648. void DBGValidateQueue(PQ pq)
  5649. {
  5650. if (pq != NULL) {
  5651. Q q = *pq;
  5652. UserAssert(q.spwndActive == HtoP(PtoH(q.spwndActive)));
  5653. UserAssert(q.spwndFocus == HtoP(PtoH(q.spwndFocus)));
  5654. UserAssert(q.spwndCapture == HtoP(PtoH(q.spwndCapture)));
  5655. UserAssert(q.spwndActivePrev == HtoP(PtoH(q.spwndActivePrev)));
  5656. UserAssert(q.spcurCurrent == HtoP(PtoH(q.spcurCurrent)));
  5657. }
  5658. }
  5659. #endif /* DBG */
  5660. /***************************************************************************\
  5661. * zzzAttachThreadInput (API)
  5662. * zzzReattachThreads
  5663. * zzzAttachToQueue
  5664. * CheckTransferState
  5665. *
  5666. * Attaches a given thread to another input queue, either by attaching to
  5667. * a queue (referenced by another thread id), or detaching from one.
  5668. *
  5669. * 12-09-92 ScottLu Created.
  5670. \***************************************************************************/
  5671. #define CTS_DONOTHING 0
  5672. #define CTS_CANCELOLD 1
  5673. #define CTS_TRANSFER 2
  5674. DWORD CheckTransferState(
  5675. PTHREADINFO pti,
  5676. PQ pqAttach,
  5677. LONG offset,
  5678. BOOL fJoiningForeground)
  5679. {
  5680. PWND pwndOld, pwndNew, pwndForegroundState;
  5681. /*
  5682. * return 0: do nothing.
  5683. * return 1: cancel the old state.
  5684. * return 2: transfer the old state to the new state
  5685. */
  5686. pwndOld = *(PWND *)(((BYTE *)pti->pq) + offset);
  5687. pwndNew = *(PWND *)(((BYTE *)pqAttach) + offset);
  5688. /*
  5689. * Make sure the old state even exists, and that the old state is
  5690. * owned by this thread. If not, nothing happens.
  5691. */
  5692. if (pwndOld == NULL || GETPTI(pwndOld) != pti)
  5693. return CTS_DONOTHING;
  5694. /*
  5695. * If the new state already exists, cancel the old state.
  5696. */
  5697. if (pwndNew != NULL)
  5698. return CTS_CANCELOLD;
  5699. /*
  5700. * Transfer this old state if this thread is not joining the foreground.
  5701. */
  5702. if (gpqForeground == NULL || !fJoiningForeground)
  5703. return CTS_TRANSFER;
  5704. /*
  5705. * We're joining the foreground - only transfer the old state if we own
  5706. * that foreground state or if there is no foreground state.
  5707. */
  5708. pwndForegroundState = *(PWND *)(((BYTE *)gpqForeground) + offset);
  5709. if (pwndForegroundState == NULL || pwndOld == pwndForegroundState)
  5710. return CTS_TRANSFER;
  5711. /*
  5712. * We're joining the foreground but we didn't set that foreground state.
  5713. * Don't allow the transfer of that state.
  5714. */
  5715. return CTS_CANCELOLD;
  5716. }
  5717. VOID zzzAttachToQueue(
  5718. PTHREADINFO pti,
  5719. PQ pqAttach,
  5720. PQ pqJournal,
  5721. BOOL fJoiningForeground)
  5722. {
  5723. PQMSG pqmsgT;
  5724. PQ pqDestroy;
  5725. /*
  5726. * Check active state.
  5727. */
  5728. switch (CheckTransferState(pti, pqAttach,
  5729. FIELD_OFFSET(Q, spwndActive), fJoiningForeground)) {
  5730. case CTS_CANCELOLD:
  5731. CancelInputState(pti, CANCEL_ACTIVESTATE);
  5732. break;
  5733. case CTS_TRANSFER:
  5734. Lock(&pqAttach->spwndActive, pti->pq->spwndActive);
  5735. Unlock(&pti->pq->spwndActive);
  5736. /*
  5737. * The caret usually follows the focus window, which follows
  5738. * the active window...
  5739. */
  5740. if (pti->pq->caret.spwnd != NULL) {
  5741. if (GETPTI(pti->pq->caret.spwnd) == pti) {
  5742. /*
  5743. * Just copy the entire caret structure... that way we
  5744. * don't need to deal with locking/unlocking the spwnd.
  5745. */
  5746. if (pqAttach->caret.spwnd == NULL) {
  5747. pqAttach->caret = pti->pq->caret;
  5748. pti->pq->caret.spwnd = NULL;
  5749. }
  5750. }
  5751. }
  5752. break;
  5753. }
  5754. /*
  5755. * Check focus state.
  5756. */
  5757. switch (CheckTransferState(pti, pqAttach,
  5758. FIELD_OFFSET(Q, spwndFocus), fJoiningForeground)) {
  5759. case CTS_CANCELOLD:
  5760. CancelInputState(pti, CANCEL_FOCUSSTATE);
  5761. break;
  5762. case CTS_TRANSFER:
  5763. Lock(&pqAttach->spwndFocus, pti->pq->spwndFocus);
  5764. Unlock(&pti->pq->spwndFocus);
  5765. break;
  5766. }
  5767. /*
  5768. * Check capture state.
  5769. */
  5770. switch (CheckTransferState(pti, pqAttach,
  5771. FIELD_OFFSET(Q, spwndCapture), fJoiningForeground)) {
  5772. case CTS_CANCELOLD:
  5773. CancelInputState(pti, CANCEL_CAPTURESTATE);
  5774. break;
  5775. case CTS_TRANSFER:
  5776. LockCaptureWindow(pqAttach, pti->pq->spwndCapture);
  5777. UnlockCaptureWindow(pti->pq);
  5778. pqAttach->codeCapture = pti->pq->codeCapture;
  5779. pqAttach->QF_flags ^= ((pqAttach->QF_flags ^ pti->pq->QF_flags) & QF_CAPTURELOCKED);
  5780. break;
  5781. #if DBG
  5782. case CTS_DONOTHING:
  5783. /*
  5784. * We should always transfer the capture state of a thread
  5785. * in modal menu mode.
  5786. */
  5787. UserAssert((pti->pMenuState == NULL)
  5788. || ExitMenuLoop(pti->pMenuState, pti->pMenuState->pGlobalPopupMenu)
  5789. || pti->pMenuState->fModelessMenu
  5790. || pti->pMenuState->fInDoDragDrop);
  5791. break;
  5792. #endif
  5793. }
  5794. /*
  5795. * Check spwndActivePrev state. This check has some considerations.
  5796. * If the CTS_TRANSFER is returned, it usually means there was no
  5797. * prev-active in the attach-queue, and it will use the first
  5798. * window it encounters. Since we walk the thread-list, a out-of-zorder
  5799. * window could be chosen. So, to counter this, we'll check the
  5800. * attach-queue-next-prev against the thread-previous window to see
  5801. * if it is truly the next-zorder window.
  5802. */
  5803. switch (CheckTransferState(pti, pqAttach,
  5804. FIELD_OFFSET(Q, spwndActivePrev), fJoiningForeground)) {
  5805. case CTS_TRANSFER:
  5806. Lock(&pqAttach->spwndActivePrev, pti->pq->spwndActivePrev);
  5807. Unlock(&pti->pq->spwndActivePrev);
  5808. break;
  5809. case CTS_CANCELOLD:
  5810. /*
  5811. * Check to see if the previous window is what we would expect it
  5812. * to be.
  5813. */
  5814. if (pqAttach->spwndActive &&
  5815. (pqAttach->spwndActivePrev && pti->pq->spwndActivePrev) &&
  5816. (pqAttach->spwndActive->spwndNext == pti->pq->spwndActivePrev)) {
  5817. Lock(&pqAttach->spwndActivePrev, pti->pq->spwndActivePrev);
  5818. Unlock(&pti->pq->spwndActivePrev);
  5819. }
  5820. break;
  5821. }
  5822. if (pti == pti->pq->ptiSysLock) {
  5823. /*
  5824. * Preserve any of the flags we might have already been set on pqAttach.
  5825. * Note that these flags might have been set on a previous call
  5826. * to this function that received the same pqAttach.
  5827. */
  5828. pqAttach->QF_flags ^= ((pqAttach->QF_flags ^ pti->pq->QF_flags)
  5829. & ~(QF_CAPTURELOCKED));
  5830. /*
  5831. * Fix for 29967 "Start menu disappears when clicked and Office
  5832. * taskbar has focus!". Win95 uses a global counter instead of this
  5833. * flag. In NT when we click on Office taskbar and then on the Start
  5834. * Menu, MSoffice calls zzzAttachThreadInput() which changes the Start
  5835. * Menu's queue and the new queue has the QF_ACTIVATIONCHANGE flag on.
  5836. * Inside the xxxMNLoop we test if this flag is on and if it is we
  5837. * exit the menu.
  5838. */
  5839. if (!IsInsideMenuLoop(pti)) {
  5840. pqAttach->QF_flags &= ~QF_ACTIVATIONCHANGE;
  5841. }
  5842. /*
  5843. * Unlock the queue since pti is moving to anotther queue.
  5844. */
  5845. /* CheckSysLock(6, pq, NULL); what number? */
  5846. pti->pq->ptiSysLock = NULL;
  5847. }
  5848. if (gspwndCursor != NULL && pti == GETPTI(gspwndCursor)) {
  5849. LockQCursor(pqAttach, pti->pq->spcurCurrent);
  5850. }
  5851. /*
  5852. * Each thread has its own cursor level, which is a count of the number
  5853. * of times that app has called show/hide cursor. This gets added into
  5854. * the queue's count for a completely accurate count every time this
  5855. * queue recalculation is done.
  5856. */
  5857. /*
  5858. * LATER
  5859. * We need to adjust the iCursorLevel of the old queue to reflect
  5860. * the fact that a thread is departing.
  5861. * FritzS
  5862. */
  5863. pqAttach->iCursorLevel += pti->iCursorLevel;
  5864. /*
  5865. * Pump up the new queue with the right input variables.
  5866. */
  5867. pqAttach->ptiMouse = pti;
  5868. pqAttach->ptiKeyboard = pti;
  5869. pqDestroy = pti->pq;
  5870. /*
  5871. * Don't increment the thread count here because we already incremented
  5872. * it when we put it in pti->pqAttach. Since we're moving it from pqAttach
  5873. * to pq, we don't mess with the reference count.
  5874. */
  5875. pti->pq = pqAttach;
  5876. /*
  5877. * If the thread is using the journal queue, leave the message list
  5878. * alone. Otherwise, redistribute the messages.
  5879. */
  5880. if (pqDestroy != pqJournal) {
  5881. /*
  5882. * Remember the current message list so it can get redistributed taking
  5883. * into account ptiAttach's new queue.
  5884. */
  5885. pqmsgT = pqDestroy->mlInput.pqmsgRead;
  5886. pqDestroy->mlInput.pqmsgRead = NULL;
  5887. pqDestroy->mlInput.pqmsgWriteLast = NULL;
  5888. pqDestroy->mlInput.cMsgs = 0;
  5889. /*
  5890. * Now redistribute the input messages from the old queue they go into the
  5891. * right queues.
  5892. *
  5893. * Preserve the 'idSysPeek' when redistributing the queue
  5894. */
  5895. RedistributeInput(pqmsgT, pqDestroy);
  5896. /*
  5897. * Officially attach the new queue to this thread. Note that zzzDestroyQueue()
  5898. * doesn't actually destroy anything until the thread reference count goes
  5899. * to 0.
  5900. */
  5901. zzzDestroyQueue(pqDestroy, pti);
  5902. } else {
  5903. UserAssert(pqDestroy->cThreads);
  5904. pqDestroy->cThreads--;
  5905. }
  5906. }
  5907. BOOL zzzReattachThreads(
  5908. BOOL fJournalAttach)
  5909. {
  5910. PTHREADINFO ptiCurrent = PtiCurrent();
  5911. PTHREADINFO pti;
  5912. PQ pqForegroundPrevNew;
  5913. PQ pqForegroundNew;
  5914. PQ pqAttach;
  5915. PQ pqJournal;
  5916. PLIST_ENTRY pHead, pEntry;
  5917. BOOL bHadAnActiveForegroundWindow;
  5918. /*
  5919. * In all cases, do not leave do any send messages or any callbacks!
  5920. * This is because this code is called from
  5921. * SetWindowsHook(WH_JOURNALPLAYBACK | WH_JOURNALRECORD). No app currently
  5922. * calling this routine expects to be called before this routine returns.
  5923. * (If you do callback before it returns, you'll break at least Access
  5924. * for Windows). - scottlu
  5925. */
  5926. #if DBG
  5927. DBGValidateQueueStates(ptiCurrent->rpdesk);
  5928. #endif
  5929. /*
  5930. * Defer Win Event notifications so we can traverse PtiList with impunity
  5931. * Also, we don't want to callback while we're half way attached
  5932. */
  5933. DeferWinEventNotify();
  5934. BEGINATOMICCHECK();
  5935. /*
  5936. * Don't recalc attach info if this is a journal attach, because
  5937. * the journal attach code has already done this for us.
  5938. */
  5939. if (!fJournalAttach) {
  5940. /*
  5941. * Now recalculate all the different queue groups, based on the
  5942. * attach requests. This fills in the pqAttach of each thread info
  5943. * with the new queue this thread belongs to. Always takes into
  5944. * account all attachment requests.
  5945. */
  5946. zzzRecalcThreadAttachment();
  5947. /*
  5948. * Make a guess about which queue is the journal queue.
  5949. */
  5950. pqJournal = gpqForeground;
  5951. if (pqJournal == NULL)
  5952. pqJournal = ptiCurrent->pq;
  5953. /*
  5954. * If the queue is only used by one thread, perform normal processing.
  5955. */
  5956. if (pqJournal->cThreads == 1) {
  5957. pqJournal = NULL;
  5958. } else {
  5959. /*
  5960. * Lock the queue to ensure that it stays valid
  5961. * until we have redistributed the input.
  5962. */
  5963. (pqJournal->cLockCount)++;
  5964. }
  5965. } else {
  5966. pqJournal = NULL;
  5967. }
  5968. /*
  5969. * What will be the new foreground queue?
  5970. */
  5971. pqForegroundNew = NULL;
  5972. /*
  5973. * Remember if there is a foreground window so we don't force one
  5974. * at the end if there wasn't one before the attach
  5975. */
  5976. if (gpqForeground != NULL && gpqForeground->spwndActive != NULL) {
  5977. bHadAnActiveForegroundWindow = TRUE;
  5978. pqForegroundNew = GETPTI(gpqForeground->spwndActive)->pqAttach;
  5979. } else {
  5980. bHadAnActiveForegroundWindow = FALSE;
  5981. }
  5982. pqForegroundPrevNew = NULL;
  5983. if (gpqForegroundPrev != NULL && gpqForegroundPrev->spwndActivePrev != NULL) {
  5984. pqForegroundPrevNew = GETPTI(gpqForegroundPrev->spwndActivePrev)->pqAttach;
  5985. }
  5986. pHead = &ptiCurrent->rpdesk->PtiList;
  5987. for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink) {
  5988. pti = CONTAINING_RECORD(pEntry, THREADINFO, PtiLink);
  5989. if (pti->pqAttach == pti->pq) {
  5990. pti->pqAttach = NULL;
  5991. } else if(pti->pqAttach != NULL) {
  5992. /*
  5993. * It is crucial that we NULL out pqAttach for this queue once
  5994. * we have it in a local variable because the NULL-ness of this
  5995. * field is checked in attach operations.
  5996. */
  5997. pqAttach = pti->pqAttach;
  5998. pti->pqAttach = NULL;
  5999. zzzAttachToQueue(pti, pqAttach, pqJournal, pqForegroundNew == pqAttach);
  6000. }
  6001. }
  6002. /*
  6003. * If we are doing a journal detach, redistribute the input messages
  6004. * from the old queue.
  6005. */
  6006. if (pqJournal != NULL) {
  6007. PQMSG pqmsgRedist;
  6008. UserAssert(pqJournal->cLockCount);
  6009. (pqJournal->cLockCount)--;
  6010. pqmsgRedist = pqJournal->mlInput.pqmsgRead;
  6011. pqJournal->mlInput.pqmsgRead = NULL;
  6012. pqJournal->mlInput.pqmsgWriteLast = NULL;
  6013. pqJournal->mlInput.cMsgs = 0;
  6014. RedistributeInput(pqmsgRedist, pqJournal);
  6015. /*
  6016. * Only destroy the queue if it is no longer is use.
  6017. */
  6018. if (pqJournal->cThreads == 0) {
  6019. pqJournal->cThreads = 1; // prevent underflow
  6020. zzzDestroyQueue(pqJournal, pti); // DeferWinEventNotify() ?? IANJA ??
  6021. } else {
  6022. /*
  6023. * Make sure that this queue doesn't point to a pti
  6024. * no longer attached to it.
  6025. * Hopefully we'll go to zzzDestroyQueue only once
  6026. * Increment cThreads so the queue won't be destroyed
  6027. * but we'll simply reassign the pti fields.
  6028. */
  6029. if ((pqJournal->ptiMouse != NULL)
  6030. && (pqJournal != pqJournal->ptiMouse->pq)) {
  6031. pqJournal->cThreads++;
  6032. zzzDestroyQueue(pqJournal, pqJournal->ptiMouse);
  6033. }
  6034. if ((pqJournal->ptiKeyboard != NULL)
  6035. && (pqJournal != pqJournal->ptiKeyboard->pq)) {
  6036. pqJournal->cThreads++;
  6037. zzzDestroyQueue(pqJournal, pqJournal->ptiKeyboard);
  6038. }
  6039. }
  6040. }
  6041. #if DBG
  6042. DBGValidateQueueStates(ptiCurrent->rpdesk);
  6043. #endif
  6044. /*
  6045. * If the current thread is not on the active desktop, do not
  6046. * change the global foreground state.
  6047. */
  6048. if (PtiCurrent()->rpdesk != grpdeskRitInput) {
  6049. EXITATOMICCHECK();
  6050. zzzEndDeferWinEventNotify();
  6051. return TRUE;
  6052. }
  6053. /*
  6054. * We're done attaching. gptiForeground hasn't changed... but
  6055. * gpqForeground has! Try not to leave NULL as the foreground.
  6056. */
  6057. #if DBG
  6058. DBGValidateQueue(pqForegroundNew);
  6059. #endif
  6060. gpqForeground = pqForegroundNew;
  6061. // So we can Alt-Esc xxxNextWindow without an AV
  6062. // If we have a non-NULL gpqForeground, its kbd input thread better have an rpdesk!
  6063. UserAssert(!gpqForeground || (gpqForeground->ptiKeyboard && gpqForeground->ptiKeyboard->rpdesk));
  6064. gpqForegroundPrev = pqForegroundPrevNew;
  6065. ENDATOMICCHECK();
  6066. zzzEndDeferWinEventNotify();
  6067. if ((gpqForeground == NULL) && (bHadAnActiveForegroundWindow)) {
  6068. PWND pwndNewForeground;
  6069. PTHREADINFO pti = PtiCurrent();
  6070. pwndNewForeground = _GetNextQueueWindow(pti->rpdesk->pDeskInfo->spwnd->spwndChild, 0, FALSE);
  6071. /*
  6072. * Don't use xxxSetForegroundWindow2 because we must not leave
  6073. * the critical section. There is no currently active foreground
  6074. * so all that is needed is to post an activate event to the
  6075. * new foreground queue.
  6076. */
  6077. if (pwndNewForeground != NULL) {
  6078. PostEventMessage(GETPTI(pwndNewForeground),
  6079. GETPTI(pwndNewForeground)->pq, QEVENT_ACTIVATE, NULL, 0,
  6080. 0, (LPARAM)HWq(pwndNewForeground));
  6081. }
  6082. }
  6083. zzzSetFMouseMoved();
  6084. UserAssert(gpqForeground == NULL || gpqForeground->ptiMouse->rpdesk == grpdeskRitInput);
  6085. return TRUE;
  6086. }
  6087. BOOL zzzAttachThreadInput(
  6088. PTHREADINFO ptiAttach,
  6089. PTHREADINFO ptiAttachTo,
  6090. BOOL fAttach)
  6091. {
  6092. CheckCritIn();
  6093. /*
  6094. * Attaching to yourself doesn't make any sense.
  6095. */
  6096. if (ptiAttach == ptiAttachTo)
  6097. return FALSE;
  6098. #if defined(FE_IME)
  6099. /*
  6100. * For console IME issue
  6101. *
  6102. * Console IME do attach to console input thread message queue.
  6103. * So needs share message queue for synchronize a key state.
  6104. */
  6105. if (IS_IME_ENABLED()) {
  6106. PTHREADINFO ptiConsoleIME;
  6107. PTHREADINFO ptiConsoleInput;
  6108. if ( ((ptiConsoleIME = PtiFromThreadId(ptiAttach->rpdesk->dwConsoleIMEThreadId)) != NULL) &&
  6109. ((ptiConsoleInput = PtiFromThreadId(ptiAttach->rpdesk->dwConsoleThreadId)) != NULL) &&
  6110. (ptiAttach == ptiConsoleIME) &&
  6111. (ptiAttachTo == ptiConsoleInput) &&
  6112. (ptiConsoleIME->TIF_flags & TIF_DONTATTACHQUEUE)
  6113. )
  6114. {
  6115. goto SkipCheck;
  6116. }
  6117. }
  6118. #endif
  6119. /*
  6120. * Will this thread allow attaching? Shell threads and system threads
  6121. * won't allow attaching.
  6122. */
  6123. if (ptiAttachTo->TIF_flags & TIF_DONTATTACHQUEUE) {
  6124. return FALSE;
  6125. }
  6126. if (ptiAttach->TIF_flags & TIF_DONTATTACHQUEUE) {
  6127. return FALSE;
  6128. }
  6129. #if defined(FE_IME)
  6130. SkipCheck:
  6131. #endif
  6132. /*
  6133. * Don't allow attaching across desktops, either.
  6134. */
  6135. if (ptiAttachTo->rpdesk != ptiAttach->rpdesk) {
  6136. return FALSE;
  6137. }
  6138. /*
  6139. * If attaching, make a new attachinfo structure for this thread.
  6140. * If not attaching, remove an existing attach reference.
  6141. */
  6142. if (fAttach) {
  6143. PATTACHINFO pai;
  6144. /*
  6145. * Alloc a new attachinfo struct, fill it in, link it in.
  6146. */
  6147. if ((pai = (PATTACHINFO)UserAllocPool(sizeof(ATTACHINFO), TAG_ATTACHINFO)) == NULL)
  6148. return FALSE;
  6149. pai->pti1 = ptiAttach;
  6150. pai->pti2 = ptiAttachTo;;
  6151. pai->paiNext = gpai;
  6152. gpai = pai;
  6153. } else {
  6154. PATTACHINFO *ppai;
  6155. BOOL fFound = FALSE;
  6156. /*
  6157. * Search for this attachinfo struct. If we can't find it, fail.
  6158. * If we do find it, unlink it and free it.
  6159. */
  6160. for (ppai = &gpai; (*ppai) != NULL; ppai = &(*ppai)->paiNext) {
  6161. if (((*ppai)->pti2 == ptiAttachTo) && ((*ppai)->pti1 == ptiAttach)) {
  6162. PATTACHINFO paiKill = *ppai;
  6163. fFound = TRUE;
  6164. *ppai = (*ppai)->paiNext;
  6165. UserFreePool((HLOCAL)paiKill);
  6166. break;
  6167. }
  6168. }
  6169. /*
  6170. * If we couldn't find this reference, then fail.
  6171. */
  6172. if (!fFound) {
  6173. return FALSE;
  6174. }
  6175. }
  6176. /*
  6177. * Now do the actual reattachment work for all threads - unless we're
  6178. * journalling. If we did by mistake do attachment while journalling
  6179. * was occuring, journalling would be hosed because journalling requires
  6180. * all threads to be attached - but it is also treated as a special
  6181. * case so it doesn't affect the ATTACHINFO structures. Therefore
  6182. * recalcing attach info based on ATTACHINFO structures would break
  6183. * the attachment required for journalling.
  6184. */
  6185. if (!FJOURNALRECORD() && !FJOURNALPLAYBACK()) {
  6186. return zzzReattachThreads(FALSE);
  6187. }
  6188. return TRUE;
  6189. }
  6190. /***************************************************************************\
  6191. * _SetMessageExtraInfo (API)
  6192. *
  6193. * History:
  6194. * 1-May-1995 FritzS
  6195. \***************************************************************************/
  6196. LONG_PTR _SetMessageExtraInfo(LONG_PTR lData)
  6197. {
  6198. LONG_PTR lRet;
  6199. PTHREADINFO pti = PtiCurrent();
  6200. lRet = pti->pq->ExtraInfo;
  6201. pti->pq->ExtraInfo = lData;
  6202. return lRet;
  6203. }