xref: /reactos/win32ss/user/ntuser/msgqueue.c (revision 9c544a55)
1 /*
2  * COPYRIGHT:        See COPYING in the top level directory
3  * PROJECT:          ReactOS Win32k subsystem
4  * PURPOSE:          Message queues
5  * FILE:             win32ss/user/ntuser/msgqueue.c
6  * PROGRAMER:        Casper S. Hornstrup (chorns@users.sourceforge.net)
7                      Alexandre Julliard
8                      Maarten Lankhorst
9  */
10 
11 #include <win32k.h>
12 DBG_DEFAULT_CHANNEL(UserMsgQ);
13 
14 /* GLOBALS *******************************************************************/
15 
16 static PPAGED_LOOKASIDE_LIST pgMessageLookasideList;
17 static PPAGED_LOOKASIDE_LIST pgSendMsgLookasideList;
18 INT PostMsgCount = 0;
19 INT SendMsgCount = 0;
20 PUSER_MESSAGE_QUEUE gpqCursor;
21 ULONG_PTR gdwMouseMoveExtraInfo = 0;
22 DWORD gdwMouseMoveTimeStamp = 0;
23 LIST_ENTRY usmList;
24 
25 /* FUNCTIONS *****************************************************************/
26 
27 CODE_SEG("INIT")
28 NTSTATUS
29 NTAPI
30 MsqInitializeImpl(VOID)
31 {
32    // Setup Post Messages
33    pgMessageLookasideList = ExAllocatePoolWithTag(NonPagedPool, sizeof(PAGED_LOOKASIDE_LIST), TAG_USRMSG);
34    if (!pgMessageLookasideList)
35       return STATUS_NO_MEMORY;
36    ExInitializePagedLookasideList(pgMessageLookasideList,
37                                   NULL,
38                                   NULL,
39                                   0,
40                                   sizeof(USER_MESSAGE),
41                                   TAG_USRMSG,
42                                   256);
43    // Setup Send Messages
44    pgSendMsgLookasideList = ExAllocatePoolWithTag(NonPagedPool, sizeof(PAGED_LOOKASIDE_LIST), TAG_USRMSG);
45    if (!pgSendMsgLookasideList)
46       return STATUS_NO_MEMORY;
47    ExInitializePagedLookasideList(pgSendMsgLookasideList,
48                                   NULL,
49                                   NULL,
50                                   0,
51                                   sizeof(USER_SENT_MESSAGE),
52                                   TAG_USRMSG,
53                                   16);
54 
55    InitializeListHead(&usmList);
56 
57    return(STATUS_SUCCESS);
58 }
59 
60 PWND FASTCALL
61 IntTopLevelWindowFromPoint(INT x, INT y)
62 {
63     PWND pWnd, pwndDesktop;
64 
65     /* Get the desktop window */
66     pwndDesktop = UserGetDesktopWindow();
67     if (!pwndDesktop)
68         return NULL;
69 
70     /* Loop all top level windows */
71     for (pWnd = pwndDesktop->spwndChild;
72          pWnd != NULL;
73          pWnd = pWnd->spwndNext)
74     {
75         if (pWnd->state2 & WNDS2_INDESTROY || pWnd->state & WNDS_DESTROYED)
76         {
77             TRACE("The Window is in DESTROY!\n");
78             continue;
79         }
80 
81         if ((pWnd->style & WS_VISIBLE) &&
82             (pWnd->ExStyle & (WS_EX_LAYERED|WS_EX_TRANSPARENT)) != (WS_EX_LAYERED|WS_EX_TRANSPARENT) &&
83             IntPtInWindow(pWnd, x, y))
84             return pWnd;
85     }
86 
87     /* Window has not been found */
88     return pwndDesktop;
89 }
90 
91 PCURICON_OBJECT
92 FASTCALL
93 UserSetCursor(
94     PCURICON_OBJECT NewCursor,
95     BOOL ForceChange)
96 {
97     PCURICON_OBJECT OldCursor;
98     HDC hdcScreen;
99     PTHREADINFO pti;
100     PUSER_MESSAGE_QUEUE MessageQueue;
101     PWND pWnd;
102 
103     pti = PsGetCurrentThreadWin32Thread();
104     MessageQueue = pti->MessageQueue;
105 
106     OldCursor = MessageQueue->CursorObject;
107 
108     /* Check if cursors are different */
109     if (OldCursor == NewCursor)
110         return OldCursor;
111 
112     /* Update cursor for this message queue */
113     MessageQueue->CursorObject = NewCursor;
114 
115     /* If cursor is not visible we have nothing to do */
116     if (MessageQueue->iCursorLevel < 0)
117         return OldCursor;
118 
119     // Fixes the error message "Not the same cursor!".
120     if (gpqCursor == NULL)
121     {
122        gpqCursor = MessageQueue;
123     }
124 
125     /* Update cursor if this message queue controls it */
126     pWnd = IntTopLevelWindowFromPoint(gpsi->ptCursor.x, gpsi->ptCursor.y);
127     if (pWnd && pWnd->head.pti->MessageQueue == MessageQueue)
128     {
129        /* Get the screen DC */
130         if (!(hdcScreen = IntGetScreenDC()))
131         {
132             return NULL;
133         }
134 
135         if (NewCursor)
136         {
137             /* Call GDI to set the new screen cursor */
138             PCURICON_OBJECT CursorFrame = NewCursor;
139             if(NewCursor->CURSORF_flags & CURSORF_ACON)
140             {
141                 FIXME("Should animate the cursor, using only the first frame now.\n");
142                 CursorFrame = ((PACON)NewCursor)->aspcur[0];
143             }
144             GreSetPointerShape(hdcScreen,
145                                CursorFrame->hbmAlpha ? NULL : NewCursor->hbmMask,
146                                CursorFrame->hbmAlpha ? NewCursor->hbmAlpha : NewCursor->hbmColor,
147                                CursorFrame->xHotspot,
148                                CursorFrame->yHotspot,
149                                gpsi->ptCursor.x,
150                                gpsi->ptCursor.y,
151                                CursorFrame->hbmAlpha ? SPS_ALPHA : 0);
152         }
153         else /* Note: OldCursor != NewCursor so we have to hide cursor */
154         {
155             /* Remove the cursor */
156             GreMovePointer(hdcScreen, -1, -1);
157             TRACE("Removing pointer!\n");
158         }
159         IntGetSysCursorInfo()->CurrentCursorObject = NewCursor;
160     }
161 
162     /* Return the old cursor */
163     return OldCursor;
164 }
165 
166 /* Called from NtUserCallOneParam with Routine ONEPARAM_ROUTINE_SHOWCURSOR
167  * User32 macro NtUserShowCursor */
168 int UserShowCursor(BOOL bShow)
169 {
170     HDC hdcScreen;
171     PTHREADINFO pti;
172     PUSER_MESSAGE_QUEUE MessageQueue;
173     PWND pWnd;
174 
175     if (!(hdcScreen = IntGetScreenDC()))
176     {
177         return -1; /* No mouse */
178     }
179 
180     pti = PsGetCurrentThreadWin32Thread();
181     MessageQueue = pti->MessageQueue;
182 
183     /* Update counter */
184     MessageQueue->iCursorLevel += bShow ? 1 : -1;
185     pti->iCursorLevel += bShow ? 1 : -1;
186 
187     /* Check for trivial cases */
188     if ((bShow && MessageQueue->iCursorLevel != 0) ||
189         (!bShow && MessageQueue->iCursorLevel != -1))
190     {
191         /* Note: w don't update global info here because it is used only
192           internally to check if cursor is visible */
193         return MessageQueue->iCursorLevel;
194     }
195 
196     /* Check if cursor is above window owned by this MessageQueue */
197     pWnd = IntTopLevelWindowFromPoint(gpsi->ptCursor.x, gpsi->ptCursor.y);
198     if (pWnd && pWnd->head.pti->MessageQueue == MessageQueue)
199     {
200         if (bShow)
201         {
202             /* Show the pointer */
203             GreMovePointer(hdcScreen, gpsi->ptCursor.x, gpsi->ptCursor.y);
204             TRACE("Showing pointer!\n");
205         }
206         else
207         {
208             /* Remove the pointer */
209             GreMovePointer(hdcScreen, -1, -1);
210             TRACE("Removing pointer!\n");
211         }
212 
213         /* Update global info */
214         IntGetSysCursorInfo()->ShowingCursor = MessageQueue->iCursorLevel;
215     }
216 
217     return MessageQueue->iCursorLevel;
218 }
219 
220 DWORD FASTCALL
221 UserGetKeyState(DWORD dwKey)
222 {
223    DWORD dwRet = 0;
224    PTHREADINFO pti;
225    PUSER_MESSAGE_QUEUE MessageQueue;
226 
227    pti = PsGetCurrentThreadWin32Thread();
228    MessageQueue = pti->MessageQueue;
229 
230    if (dwKey < 0x100)
231    {
232        if (IS_KEY_DOWN(MessageQueue->afKeyState, dwKey))
233            dwRet |= 0xFF80; // If down, windows returns 0xFF80.
234        if (IS_KEY_LOCKED(MessageQueue->afKeyState, dwKey))
235            dwRet |= 0x1;
236    }
237    else
238    {
239       EngSetLastError(ERROR_INVALID_PARAMETER);
240    }
241    return dwRet;
242 }
243 
244 /* change the input key state for a given key */
245 static VOID
246 UpdateKeyState(PUSER_MESSAGE_QUEUE MessageQueue, WORD wVk, BOOL bIsDown)
247 {
248     TRACE("UpdateKeyState wVk: %u, bIsDown: %d\n", wVk, bIsDown);
249 
250     if (bIsDown)
251     {
252         /* If it's first key down event, xor lock bit */
253         if (!IS_KEY_DOWN(MessageQueue->afKeyState, wVk))
254             SET_KEY_LOCKED(MessageQueue->afKeyState, wVk, !IS_KEY_LOCKED(MessageQueue->afKeyState, wVk));
255 
256         SET_KEY_DOWN(MessageQueue->afKeyState, wVk, TRUE);
257         MessageQueue->afKeyRecentDown[wVk / 8] |= (1 << (wVk % 8));
258     }
259     else
260         SET_KEY_DOWN(MessageQueue->afKeyState, wVk, FALSE);
261 }
262 
263 /* update the input key state for a keyboard message */
264 static VOID
265 UpdateKeyStateFromMsg(PUSER_MESSAGE_QUEUE MessageQueue, MSG* msg)
266 {
267     UCHAR key;
268     BOOL down = FALSE;
269 
270     TRACE("UpdateKeyStateFromMsg message:%u\n", msg->message);
271 
272     switch (msg->message)
273     {
274     case WM_LBUTTONDOWN:
275         down = TRUE;
276         /* fall through */
277     case WM_LBUTTONUP:
278         UpdateKeyState(MessageQueue, VK_LBUTTON, down);
279         break;
280     case WM_MBUTTONDOWN:
281         down = TRUE;
282         /* fall through */
283     case WM_MBUTTONUP:
284         UpdateKeyState(MessageQueue, VK_MBUTTON, down);
285         break;
286     case WM_RBUTTONDOWN:
287         down = TRUE;
288         /* fall through */
289     case WM_RBUTTONUP:
290         UpdateKeyState(MessageQueue, VK_RBUTTON, down);
291         break;
292     case WM_XBUTTONDOWN:
293         down = TRUE;
294         /* fall through */
295     case WM_XBUTTONUP:
296         if (msg->wParam == XBUTTON1)
297             UpdateKeyState(MessageQueue, VK_XBUTTON1, down);
298         else if (msg->wParam == XBUTTON2)
299             UpdateKeyState(MessageQueue, VK_XBUTTON2, down);
300         break;
301     case WM_KEYDOWN:
302     case WM_SYSKEYDOWN:
303         down = TRUE;
304         /* fall through */
305     case WM_KEYUP:
306     case WM_SYSKEYUP:
307         key = (UCHAR)msg->wParam;
308         UpdateKeyState(MessageQueue, key, down);
309         switch(key)
310         {
311         case VK_LCONTROL:
312         case VK_RCONTROL:
313             down = IS_KEY_DOWN(MessageQueue->afKeyState, VK_LCONTROL) || IS_KEY_DOWN(MessageQueue->afKeyState, VK_RCONTROL);
314             UpdateKeyState(MessageQueue, VK_CONTROL, down);
315             break;
316         case VK_LMENU:
317         case VK_RMENU:
318             down = IS_KEY_DOWN(MessageQueue->afKeyState, VK_LMENU) || IS_KEY_DOWN(MessageQueue->afKeyState, VK_RMENU);
319             UpdateKeyState(MessageQueue, VK_MENU, down);
320             break;
321         case VK_LSHIFT:
322         case VK_RSHIFT:
323             down = IS_KEY_DOWN(MessageQueue->afKeyState, VK_LSHIFT) || IS_KEY_DOWN(MessageQueue->afKeyState, VK_RSHIFT);
324             UpdateKeyState(MessageQueue, VK_SHIFT, down);
325             break;
326         }
327         break;
328     }
329 }
330 
331 /*
332     Get down key states from the queue of prior processed input message key states.
333 
334     This fixes the left button dragging on the desktop and release sticking outline issue.
335     USB Tablet pointer seems to stick the most and leaves the box outline displayed.
336  */
337 WPARAM FASTCALL
338 MsqGetDownKeyState(PUSER_MESSAGE_QUEUE MessageQueue)
339 {
340     WPARAM ret = 0;
341 
342     if (gspv.bMouseBtnSwap)
343     {
344        if (IS_KEY_DOWN(MessageQueue->afKeyState, VK_RBUTTON)) ret |= MK_LBUTTON;
345        if (IS_KEY_DOWN(MessageQueue->afKeyState, VK_LBUTTON)) ret |= MK_RBUTTON;
346     }
347     else
348     {
349        if (IS_KEY_DOWN(MessageQueue->afKeyState, VK_LBUTTON)) ret |= MK_LBUTTON;
350        if (IS_KEY_DOWN(MessageQueue->afKeyState, VK_RBUTTON)) ret |= MK_RBUTTON;
351     }
352 
353     if (IS_KEY_DOWN(MessageQueue->afKeyState, VK_MBUTTON))  ret |= MK_MBUTTON;
354     if (IS_KEY_DOWN(MessageQueue->afKeyState, VK_SHIFT))    ret |= MK_SHIFT;
355     if (IS_KEY_DOWN(MessageQueue->afKeyState, VK_CONTROL))  ret |= MK_CONTROL;
356     if (IS_KEY_DOWN(MessageQueue->afKeyState, VK_XBUTTON1)) ret |= MK_XBUTTON1;
357     if (IS_KEY_DOWN(MessageQueue->afKeyState, VK_XBUTTON2)) ret |= MK_XBUTTON2;
358     return ret;
359 }
360 
361 HANDLE FASTCALL
362 IntMsqSetWakeMask(DWORD WakeMask)
363 {
364    PTHREADINFO Win32Thread;
365    HANDLE MessageEventHandle;
366    DWORD dwFlags = HIWORD(WakeMask);
367 
368    Win32Thread = PsGetCurrentThreadWin32Thread();
369    if (Win32Thread == NULL || Win32Thread->MessageQueue == NULL)
370       return 0;
371 
372 // Win32Thread->pEventQueueServer; IntMsqSetWakeMask returns Win32Thread->hEventQueueClient
373    MessageEventHandle = Win32Thread->hEventQueueClient;
374 
375    if (Win32Thread->pcti)
376    {
377       if ( (Win32Thread->pcti->fsChangeBits & LOWORD(WakeMask)) ||
378            ( (dwFlags & MWMO_INPUTAVAILABLE) && (Win32Thread->pcti->fsWakeBits & LOWORD(WakeMask)) ) )
379       {
380          ERR("Chg 0x%x Wake 0x%x Mask 0x%x\n",Win32Thread->pcti->fsChangeBits, Win32Thread->pcti->fsWakeBits, WakeMask);
381          KeSetEvent(Win32Thread->pEventQueueServer, IO_NO_INCREMENT, FALSE); // Wake it up!
382          return MessageEventHandle;
383       }
384    }
385 
386    IdlePing();
387 
388    return MessageEventHandle;
389 }
390 
391 BOOL FASTCALL
392 IntMsqClearWakeMask(VOID)
393 {
394    PTHREADINFO Win32Thread;
395 
396    Win32Thread = PsGetCurrentThreadWin32Thread();
397    if (Win32Thread == NULL || Win32Thread->MessageQueue == NULL)
398       return FALSE;
399    // Very hacky, but that is what they do.
400    Win32Thread->pcti->fsWakeBits = 0;
401 
402    IdlePong();
403 
404    return TRUE;
405 }
406 
407 /*
408    Due to the uncertainty of knowing what was set in our multilevel message queue,
409    and even if the bits are all cleared. The same as cTimers/cPaintsReady.
410    I think this is the best solution... (jt) */
411 VOID FASTCALL
412 MsqWakeQueue(PTHREADINFO pti, DWORD MessageBits, BOOL KeyEvent)
413 {
414    PUSER_MESSAGE_QUEUE Queue;
415 
416    Queue = pti->MessageQueue;
417 
418    if (Queue->QF_flags & QF_INDESTROY)
419    {
420       ERR("This Message Queue is in Destroy!\n");
421    }
422    pti->pcti->fsWakeBits |= MessageBits;
423    pti->pcti->fsChangeBits |= MessageBits;
424 
425    // Start bit accounting to help clear the main set of bits.
426    if (MessageBits & QS_KEY)
427    {
428       pti->nCntsQBits[QSRosKey]++;
429    }
430    if (MessageBits & QS_MOUSE)
431    {
432       if (MessageBits & QS_MOUSEMOVE)   pti->nCntsQBits[QSRosMouseMove]++;
433       if (MessageBits & QS_MOUSEBUTTON) pti->nCntsQBits[QSRosMouseButton]++;
434    }
435    if (MessageBits & QS_POSTMESSAGE) pti->nCntsQBits[QSRosPostMessage]++;
436    if (MessageBits & QS_SENDMESSAGE) pti->nCntsQBits[QSRosSendMessage]++;
437    if (MessageBits & QS_HOTKEY)      pti->nCntsQBits[QSRosHotKey]++;
438    if (MessageBits & QS_EVENT)       pti->nCntsQBits[QSRosEvent]++;
439 
440    if (KeyEvent)
441       KeSetEvent(pti->pEventQueueServer, IO_NO_INCREMENT, FALSE);
442 }
443 
444 VOID FASTCALL
445 ClearMsgBitsMask(PTHREADINFO pti, UINT MessageBits)
446 {
447    UINT ClrMask = 0;
448 
449    if (MessageBits & QS_KEY)
450    {
451       if (--pti->nCntsQBits[QSRosKey] == 0) ClrMask |= QS_KEY;
452    }
453    if (MessageBits & QS_MOUSEMOVE)
454    {  // Account for tracking mouse moves..
455       if (pti->nCntsQBits[QSRosMouseMove])
456       {
457          pti->nCntsQBits[QSRosMouseMove] = 0; // Throttle down count. Up to > 3:1 entries are ignored.
458          ClrMask |= QS_MOUSEMOVE;
459       }
460    }
461    if (MessageBits & QS_MOUSEBUTTON)
462    {
463       if (--pti->nCntsQBits[QSRosMouseButton] == 0) ClrMask |= QS_MOUSEBUTTON;
464    }
465    if (MessageBits & QS_POSTMESSAGE)
466    {
467       if (--pti->nCntsQBits[QSRosPostMessage] == 0) ClrMask |= QS_POSTMESSAGE;
468    }
469    if (MessageBits & QS_TIMER) // ReactOS hard coded.
470    {  // Handle timer bits here.
471       if ( pti->cTimersReady )
472       {
473          if (--pti->cTimersReady == 0) ClrMask |= QS_TIMER;
474       }
475    }
476    if (MessageBits & QS_PAINT) // ReactOS hard coded.
477    {  // Handle paint bits here.
478       if ( pti->cPaintsReady )
479       {
480          if (--pti->cPaintsReady == 0) ClrMask |= QS_PAINT;
481       }
482    }
483    if (MessageBits & QS_SENDMESSAGE)
484    {
485       if (--pti->nCntsQBits[QSRosSendMessage] == 0) ClrMask |= QS_SENDMESSAGE;
486    }
487    if (MessageBits & QS_HOTKEY)
488    {
489       if (--pti->nCntsQBits[QSRosHotKey] == 0) ClrMask |= QS_HOTKEY;
490    }
491    if (MessageBits & QS_EVENT)
492    {
493       if (--pti->nCntsQBits[QSRosEvent] == 0) ClrMask |= QS_EVENT;
494    }
495 
496    pti->pcti->fsWakeBits &= ~ClrMask;
497    pti->pcti->fsChangeBits &= ~ClrMask;
498 }
499 
500 VOID FASTCALL
501 MsqIncPaintCountQueue(PTHREADINFO pti)
502 {
503    pti->cPaintsReady++;
504    MsqWakeQueue(pti, QS_PAINT, TRUE);
505 }
506 
507 VOID FASTCALL
508 MsqDecPaintCountQueue(PTHREADINFO pti)
509 {
510    ClearMsgBitsMask(pti, QS_PAINT);
511 }
512 
513 /*
514     Post the move or update the message still pending to be processed.
515     Do not overload the queue with mouse move messages.
516  */
517 VOID FASTCALL
518 MsqPostMouseMove(PTHREADINFO pti, MSG* Msg, LONG_PTR ExtraInfo)
519 {
520     PUSER_MESSAGE Message;
521     PLIST_ENTRY ListHead;
522     PUSER_MESSAGE_QUEUE MessageQueue = pti->MessageQueue;
523 
524     ListHead = &MessageQueue->HardwareMessagesListHead;
525 
526     // Do nothing if empty.
527     if (!IsListEmpty(ListHead->Flink))
528     {
529        // Look at the end of the list,
530        Message = CONTAINING_RECORD(ListHead->Blink, USER_MESSAGE, ListEntry);
531 
532        // If the mouse move message is existing on the list,
533        if (Message->Msg.message == WM_MOUSEMOVE)
534        {
535           // Overwrite the message with updated data!
536           Message->Msg = *Msg;
537 
538           MsqWakeQueue(pti, QS_MOUSEMOVE, TRUE);
539           return;
540        }
541     }
542 
543     MsqPostMessage(pti, Msg, TRUE, QS_MOUSEMOVE, 0, ExtraInfo);
544 }
545 
546 /*
547     Bring together the mouse move message.
548     Named "Coalesce" from Amine email ;^) (jt).
549  */
550 VOID FASTCALL
551 IntCoalesceMouseMove(PTHREADINFO pti)
552 {
553     MSG Msg;
554 
555     // Force time stamp to update, keeping message time in sync.
556     if (gdwMouseMoveTimeStamp == 0)
557     {
558         gdwMouseMoveTimeStamp = EngGetTickCount32();
559     }
560 
561     // Build mouse move message.
562     Msg.hwnd    = NULL;
563     Msg.message = WM_MOUSEMOVE;
564     Msg.wParam  = 0;
565     Msg.lParam  = MAKELONG(gpsi->ptCursor.x, gpsi->ptCursor.y);
566     Msg.time    = gdwMouseMoveTimeStamp;
567     Msg.pt      = gpsi->ptCursor;
568 
569     // Post the move.
570     MsqPostMouseMove(pti, &Msg, gdwMouseMoveExtraInfo);
571 
572     // Zero the time stamp.
573     gdwMouseMoveTimeStamp = 0;
574 
575     // Clear flag since the move was posted.
576     pti->MessageQueue->QF_flags &= ~QF_MOUSEMOVED;
577 }
578 
579 VOID FASTCALL
580 co_MsqInsertMouseMessage(MSG* Msg, DWORD flags, ULONG_PTR dwExtraInfo, BOOL Hook)
581 {
582    MSLLHOOKSTRUCT MouseHookData;
583 //   PDESKTOP pDesk;
584    PWND pwnd, pwndDesktop;
585    HDC hdcScreen;
586    PTHREADINFO pti;
587    PUSER_MESSAGE_QUEUE MessageQueue;
588    PSYSTEM_CURSORINFO CurInfo;
589 
590    Msg->time = EngGetTickCount32();
591 
592    MouseHookData.pt.x = LOWORD(Msg->lParam);
593    MouseHookData.pt.y = HIWORD(Msg->lParam);
594    switch (Msg->message)
595    {
596       case WM_MOUSEWHEEL:
597          MouseHookData.mouseData = MAKELONG(0, GET_WHEEL_DELTA_WPARAM(Msg->wParam));
598          break;
599       case WM_XBUTTONDOWN:
600       case WM_XBUTTONUP:
601       case WM_XBUTTONDBLCLK:
602       case WM_NCXBUTTONDOWN:
603       case WM_NCXBUTTONUP:
604       case WM_NCXBUTTONDBLCLK:
605          MouseHookData.mouseData = MAKELONG(0, HIWORD(Msg->wParam));
606          break;
607       default:
608          MouseHookData.mouseData = 0;
609          break;
610    }
611 
612    MouseHookData.flags = flags; // LLMHF_INJECTED
613    MouseHookData.time = Msg->time;
614    MouseHookData.dwExtraInfo = dwExtraInfo;
615 
616    /* If the hook procedure returned non zero, dont send the message */
617    if (Hook)
618    {
619       if (co_HOOK_CallHooks(WH_MOUSE_LL, HC_ACTION, Msg->message, (LPARAM) &MouseHookData))
620          return;
621    }
622 
623    /* Get the desktop window */
624    pwndDesktop = UserGetDesktopWindow();
625    if (!pwndDesktop) return;
626 //   pDesk = pwndDesktop->head.rpdesk;
627 
628    /* Check if the mouse is captured */
629    Msg->hwnd = IntGetCaptureWindow();
630    if (Msg->hwnd != NULL)
631    {
632        pwnd = UserGetWindowObject(Msg->hwnd);
633    }
634    else
635    {
636        pwnd = IntTopLevelWindowFromPoint(Msg->pt.x, Msg->pt.y);
637        if (pwnd) Msg->hwnd = UserHMGetHandle(pwnd);
638    }
639 
640    hdcScreen = IntGetScreenDC();
641    CurInfo = IntGetSysCursorInfo();
642 
643    /* Check if we found a window */
644    if (Msg->hwnd != NULL && pwnd != NULL)
645    {
646        pti = pwnd->head.pti;
647        MessageQueue = pti->MessageQueue;
648 
649        if (MessageQueue->QF_flags & QF_INDESTROY)
650        {
651           ERR("Mouse is over a Window with a Dead Message Queue!\n");
652           return;
653        }
654 
655        // Check to see if this is attached.
656        if ( pti != MessageQueue->ptiMouse &&
657             MessageQueue->cThreads > 1 )
658        {
659           // Set the send pti to the message queue mouse pti.
660           pti = MessageQueue->ptiMouse;
661        }
662 
663        if (Msg->message == WM_MOUSEMOVE)
664        {
665           /* Check if cursor should be visible */
666            if(hdcScreen &&
667               MessageQueue->CursorObject &&
668               MessageQueue->iCursorLevel >= 0)
669            {
670                /* Check if shape has changed */
671                if(CurInfo->CurrentCursorObject != MessageQueue->CursorObject)
672                {
673                    /* Call GDI to set the new screen cursor */
674                     GreSetPointerShape(hdcScreen,
675                                        MessageQueue->CursorObject->hbmAlpha ?
676                                            NULL : MessageQueue->CursorObject->hbmMask,
677                                        MessageQueue->CursorObject->hbmAlpha ?
678                                            MessageQueue->CursorObject->hbmAlpha : MessageQueue->CursorObject->hbmColor,
679                                        MessageQueue->CursorObject->xHotspot,
680                                        MessageQueue->CursorObject->yHotspot,
681                                        gpsi->ptCursor.x,
682                                        gpsi->ptCursor.y,
683                                        MessageQueue->CursorObject->hbmAlpha ? SPS_ALPHA : 0);
684 
685                } else
686                    GreMovePointer(hdcScreen, Msg->pt.x, Msg->pt.y);
687            }
688            /* Check if we have to hide cursor */
689            else if (CurInfo->ShowingCursor >= 0)
690                GreMovePointer(hdcScreen, -1, -1);
691 
692            /* Update global cursor info */
693            CurInfo->ShowingCursor = MessageQueue->iCursorLevel;
694            CurInfo->CurrentCursorObject = MessageQueue->CursorObject;
695            gpqCursor = MessageQueue;
696 
697            /* Mouse move is a special case */
698            MessageQueue->QF_flags |= QF_MOUSEMOVED;
699            gdwMouseMoveExtraInfo = dwExtraInfo;
700            gdwMouseMoveTimeStamp = Msg->time;
701            MsqWakeQueue(pti, QS_MOUSEMOVE, TRUE);
702        }
703        else
704        {
705            if (!IntGetCaptureWindow())
706            {
707              // ERR("ptiLastInput is set\n");
708              // ptiLastInput = pti; // Once this is set during Reboot or Shutdown, this prevents the exit window having foreground.
709              // Find all the Move Mouse calls and fix mouse set active focus issues......
710            }
711 
712            // Post mouse move before posting mouse buttons, keep it in sync.
713            if (pti->MessageQueue->QF_flags & QF_MOUSEMOVED)
714            {
715               IntCoalesceMouseMove(pti);
716            }
717 
718            TRACE("Posting mouse message to hwnd=%p!\n", UserHMGetHandle(pwnd));
719            MsqPostMessage(pti, Msg, TRUE, QS_MOUSEBUTTON, 0, dwExtraInfo);
720        }
721    }
722    else if (hdcScreen)
723    {
724        /* always show cursor on background; FIXME: set default pointer */
725        GreMovePointer(hdcScreen, Msg->pt.x, Msg->pt.y);
726        CurInfo->ShowingCursor = 0;
727    }
728 }
729 
730 PUSER_MESSAGE FASTCALL
731 MsqCreateMessage(LPMSG Msg)
732 {
733    PUSER_MESSAGE Message;
734 
735    Message = ExAllocateFromPagedLookasideList(pgMessageLookasideList);
736    if (!Message)
737    {
738       return NULL;
739    }
740 
741    RtlZeroMemory(Message, sizeof(*Message));
742    RtlMoveMemory(&Message->Msg, Msg, sizeof(MSG));
743    PostMsgCount++;
744    return Message;
745 }
746 
747 VOID FASTCALL
748 MsqDestroyMessage(PUSER_MESSAGE Message)
749 {
750    TRACE("Post Destroy %d\n",PostMsgCount);
751    if (Message->pti == NULL)
752    {
753       ERR("Double Free Message\n");
754       return;
755    }
756    RemoveEntryList(&Message->ListEntry);
757    Message->pti = NULL;
758    ExFreeToPagedLookasideList(pgMessageLookasideList, Message);
759    PostMsgCount--;
760 }
761 
762 PUSER_SENT_MESSAGE FASTCALL
763 AllocateUserMessage(BOOL KEvent)
764 {
765    PUSER_SENT_MESSAGE Message;
766 
767    if(!(Message = ExAllocateFromPagedLookasideList(pgSendMsgLookasideList)))
768    {
769        ERR("AllocateUserMessage(): Not enough memory to allocate a message\n");
770        return NULL;
771    }
772    RtlZeroMemory(Message, sizeof(USER_SENT_MESSAGE));
773 
774    if (KEvent)
775    {
776       Message->pkCompletionEvent = &Message->CompletionEvent;
777 
778       KeInitializeEvent(Message->pkCompletionEvent, NotificationEvent, FALSE);
779    }
780    SendMsgCount++;
781    TRACE("AUM pti %p msg %p\n",PsGetCurrentThreadWin32Thread(),Message);
782    return Message;
783 }
784 
785 VOID FASTCALL
786 FreeUserMessage(PUSER_SENT_MESSAGE Message)
787 {
788    Message->pkCompletionEvent = NULL;
789 
790    /* Remove it from the list */
791    RemoveEntryList(&Message->ListEntry);
792 
793    ExFreeToPagedLookasideList(pgSendMsgLookasideList, Message);
794    SendMsgCount--;
795 }
796 
797 VOID APIENTRY
798 MsqRemoveWindowMessagesFromQueue(PWND Window)
799 {
800    PTHREADINFO pti;
801    PUSER_SENT_MESSAGE SentMessage;
802    PUSER_MESSAGE PostedMessage;
803    PLIST_ENTRY CurrentEntry, ListHead;
804 
805    ASSERT(Window);
806 
807    pti = Window->head.pti;
808 
809    /* remove the posted messages for this window */
810    CurrentEntry = pti->PostedMessagesListHead.Flink;
811    ListHead = &pti->PostedMessagesListHead;
812    while (CurrentEntry != ListHead)
813    {
814       PostedMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, ListEntry);
815 
816       if (PostedMessage->Msg.hwnd == UserHMGetHandle(Window))
817       {
818          if (PostedMessage->Msg.message == WM_QUIT && pti->QuitPosted == 0)
819          {
820             pti->QuitPosted = 1;
821             pti->exitCode = PostedMessage->Msg.wParam;
822          }
823          ClearMsgBitsMask(pti, PostedMessage->QS_Flags);
824          MsqDestroyMessage(PostedMessage);
825          CurrentEntry = pti->PostedMessagesListHead.Flink;
826       }
827       else
828       {
829          CurrentEntry = CurrentEntry->Flink;
830       }
831    }
832 
833    /* remove the sent messages for this window */
834    CurrentEntry = pti->SentMessagesListHead.Flink;
835    ListHead = &pti->SentMessagesListHead;
836    while (CurrentEntry != ListHead)
837    {
838       SentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE, ListEntry);
839 
840       if(SentMessage->Msg.hwnd == UserHMGetHandle(Window))
841       {
842          ERR("Remove Window Messages %p From Sent Queue\n",SentMessage);
843 #if 0 // Should mark these as invalid and allow the rest clean up, so far no harm by just commenting out. See CORE-9210.
844          ClearMsgBitsMask(pti, SentMessage->QS_Flags);
845 
846          /* wake the sender's thread */
847          if (SentMessage->pkCompletionEvent != NULL)
848          {
849             KeSetEvent(SentMessage->pkCompletionEvent, IO_NO_INCREMENT, FALSE);
850          }
851 
852          if (SentMessage->HasPackedLParam)
853          {
854             if (SentMessage->Msg.lParam)
855                ExFreePool((PVOID)SentMessage->Msg.lParam);
856          }
857 
858          /* free the message */
859          FreeUserMessage(SentMessage);
860 
861          CurrentEntry = pti->SentMessagesListHead.Flink;
862 #endif
863          CurrentEntry = CurrentEntry->Flink;
864       }
865       else
866       {
867          CurrentEntry = CurrentEntry->Flink;
868       }
869    }
870 }
871 
872 BOOLEAN FASTCALL
873 co_MsqDispatchOneSentMessage(
874     _In_ PTHREADINFO pti)
875 {
876    PUSER_SENT_MESSAGE SaveMsg, Message;
877    PLIST_ENTRY Entry;
878    BOOL Ret;
879    LRESULT Result = 0;
880 
881    ASSERT(pti == PsGetCurrentThreadWin32Thread());
882 
883    if (IsListEmpty(&pti->SentMessagesListHead))
884    {
885       return(FALSE);
886    }
887 
888    /* remove it from the list of pending messages */
889    Entry = RemoveHeadList(&pti->SentMessagesListHead);
890    Message = CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry);
891 
892    // Signal this message is being processed.
893    Message->flags |= SMF_RECEIVERBUSY|SMF_RECEIVEDMESSAGE;
894 
895    SaveMsg = pti->pusmCurrent;
896    pti->pusmCurrent = Message;
897 
898    // Processing a message sent to it from another thread.
899    if ( ( Message->ptiSender && pti != Message->ptiSender) ||
900         ( Message->ptiCallBackSender && pti != Message->ptiCallBackSender ))
901    {  // most likely, but, to be sure.
902       pti->pcti->CTI_flags |= CTI_INSENDMESSAGE; // Let the user know...
903    }
904 
905    /* Now insert it to the global list of messages that can be removed Justin Case there's Trouble */
906    InsertTailList(&usmList, &Message->ListEntry);
907 
908    ClearMsgBitsMask(pti, Message->QS_Flags);
909 
910    if (Message->HookMessage == MSQ_ISHOOK)
911    {  // Direct Hook Call processor
912       Result = co_CallHook( Message->Msg.message,           // HookId
913                             HandleToLong(Message->Msg.hwnd), // Code
914                             Message->Msg.wParam,
915                             Message->Msg.lParam);
916    }
917    else if(Message->HookMessage == MSQ_INJECTMODULE)
918    {
919        Result = IntLoadHookModule(Message->Msg.message,
920                                   (HHOOK)Message->Msg.lParam,
921                                   Message->Msg.wParam);
922    }
923    else if ((Message->CompletionCallback) &&
924             (Message->ptiCallBackSender == pti))
925    {   /* Call the callback routine */
926       if (Message->QS_Flags & QS_SMRESULT)
927       {
928          co_IntCallSentMessageCallback(Message->CompletionCallback,
929                                        Message->Msg.hwnd,
930                                        Message->Msg.message,
931                                        Message->CompletionCallbackContext,
932                                        Message->lResult);
933          /* Set callback to NULL to prevent reentry */
934          Message->CompletionCallback = NULL;
935       }
936       else
937       {
938          /* The message has not been processed yet, reinsert it. */
939          RemoveEntryList(&Message->ListEntry);
940          InsertTailList(&Message->ptiCallBackSender->SentMessagesListHead, &Message->ListEntry);
941          // List is occupied need to set the bit.
942          MsqWakeQueue(Message->ptiCallBackSender, QS_SENDMESSAGE, TRUE);
943          ERR("Callback Message not processed yet. Requeuing the message\n"); //// <---- Need to see if this happens.
944          Ret = FALSE;
945          goto Exit;
946       }
947    }
948    else
949    {  /* Call the window procedure. */
950       Result = co_IntSendMessage( Message->Msg.hwnd,
951                                   Message->Msg.message,
952                                   Message->Msg.wParam,
953                                   Message->Msg.lParam);
954    }
955 
956    /* If the message is a callback, insert it in the callback senders MessageQueue */
957    if (Message->CompletionCallback)
958    {
959       if (Message->ptiCallBackSender)
960       {
961          Message->lResult = Result;
962          Message->QS_Flags |= QS_SMRESULT;
963 
964          /* insert it in the callers message queue */
965          RemoveEntryList(&Message->ListEntry);
966          InsertTailList(&Message->ptiCallBackSender->SentMessagesListHead, &Message->ListEntry);
967          MsqWakeQueue(Message->ptiCallBackSender, QS_SENDMESSAGE, TRUE);
968       }
969       Ret = TRUE;
970       goto Exit;
971    }
972 
973    // Retrieve the result from callback.
974    if (Message->QS_Flags & QS_SMRESULT)
975    {
976       Result = Message->lResult;
977    }
978 
979    /* Let the sender know the result. */
980    Message->lResult = Result;
981 
982    if (Message->HasPackedLParam)
983    {
984       if (Message->Msg.lParam)
985          ExFreePool((PVOID)Message->Msg.lParam);
986    }
987 
988    // Clear busy signal.
989    Message->flags &= ~SMF_RECEIVERBUSY;
990 
991    /* Notify the sender. */
992    if (Message->pkCompletionEvent != NULL)
993    {
994       KeSetEvent(Message->pkCompletionEvent, IO_NO_INCREMENT, FALSE);
995    }
996 
997    /* free the message */
998    if (Message->flags & SMF_RECEIVERFREE)
999    {
1000       TRACE("Receiver Freeing Message %p\n",Message);
1001       FreeUserMessage(Message);
1002    }
1003 
1004    Ret = TRUE;
1005 Exit:
1006    /* do not hangup on the user if this is reentering */
1007    if (!SaveMsg) pti->pcti->CTI_flags &= ~CTI_INSENDMESSAGE;
1008    pti->pusmCurrent = SaveMsg;
1009 
1010    return Ret;
1011 }
1012 
1013 BOOL FASTCALL
1014 co_MsqSendMessageAsync(PTHREADINFO ptiReceiver,
1015                        HWND hwnd,
1016                        UINT Msg,
1017                        WPARAM wParam,
1018                        LPARAM lParam,
1019                        SENDASYNCPROC CompletionCallback,
1020                        ULONG_PTR CompletionCallbackContext,
1021                        BOOL HasPackedLParam,
1022                        INT HookMessage)
1023 {
1024     PTHREADINFO ptiSender;
1025     PUSER_SENT_MESSAGE Message;
1026 
1027     if(!(Message = AllocateUserMessage(FALSE)))
1028     {
1029         ERR("MsqSendMessageAsync(): Not enough memory to allocate a message\n");
1030         return FALSE;
1031     }
1032 
1033     ptiSender = PsGetCurrentThreadWin32Thread();
1034 
1035     Message->Msg.hwnd = hwnd;
1036     Message->Msg.message = Msg;
1037     Message->Msg.wParam = wParam;
1038     Message->Msg.lParam = lParam;
1039     Message->pkCompletionEvent = NULL; // No event needed.
1040     Message->ptiReceiver = ptiReceiver;
1041     Message->ptiCallBackSender = ptiSender;
1042     Message->CompletionCallback = CompletionCallback;
1043     Message->CompletionCallbackContext = CompletionCallbackContext;
1044     Message->HookMessage = HookMessage;
1045     Message->HasPackedLParam = HasPackedLParam;
1046     Message->QS_Flags = QS_SENDMESSAGE;
1047     Message->flags = SMF_RECEIVERFREE;
1048 
1049     InsertTailList(&ptiReceiver->SentMessagesListHead, &Message->ListEntry);
1050     MsqWakeQueue(ptiReceiver, QS_SENDMESSAGE, TRUE);
1051 
1052     return TRUE;
1053 }
1054 
1055 NTSTATUS FASTCALL
1056 co_MsqSendMessage(PTHREADINFO ptirec,
1057                   HWND Wnd,
1058                   UINT Msg,
1059                   WPARAM wParam,
1060                   LPARAM lParam,
1061                   UINT uTimeout,
1062                   BOOL Block,
1063                   INT HookMessage,
1064                   ULONG_PTR *uResult)
1065 {
1066    PTHREADINFO pti;
1067    PUSER_SENT_MESSAGE SaveMsg, Message;
1068    NTSTATUS WaitStatus;
1069    LARGE_INTEGER Timeout;
1070    PLIST_ENTRY Entry;
1071    PWND pWnd;
1072    BOOLEAN SwapStateEnabled;
1073    LRESULT Result = 0;   //// Result could be trashed. ////
1074 
1075    pti = PsGetCurrentThreadWin32Thread();
1076    ASSERT(pti != ptirec);
1077    ASSERT(ptirec->pcti); // Send must have a client side to receive it!!!!
1078 
1079    /* Don't send from or to a dying thread */
1080    if (pti->TIF_flags & TIF_INCLEANUP || ptirec->TIF_flags & TIF_INCLEANUP)
1081    {
1082        // Unless we are dying and need to tell our parents.
1083        if (pti->TIF_flags & TIF_INCLEANUP && !(ptirec->TIF_flags & TIF_INCLEANUP))
1084        {
1085            // Parent notify is the big one. Fire and forget!
1086            TRACE("Send message from dying thread %u\n", Msg);
1087            co_MsqSendMessageAsync(ptirec, Wnd, Msg, wParam, lParam, NULL, 0, FALSE, HookMessage);
1088        }
1089        if (uResult) *uResult = -1;
1090        TRACE("MsqSM: Msg %u Current pti %lu or Rec pti %lu\n", Msg, pti->TIF_flags & TIF_INCLEANUP, ptirec->TIF_flags & TIF_INCLEANUP);
1091        return STATUS_UNSUCCESSFUL;
1092    }
1093 
1094    if (IsThreadSuspended(ptirec))
1095    {
1096       ERR("Sending to Suspended Thread Msg %lx\n",Msg);
1097       if (uResult) *uResult = -1;
1098       return STATUS_UNSUCCESSFUL;
1099    }
1100 
1101    // Should we do the same for No Wait?
1102    if ( HookMessage == MSQ_NORMAL )
1103    {
1104       pWnd = ValidateHwndNoErr(Wnd);
1105 
1106       // These can not cross International Border lines!
1107       if ( pti->ppi != ptirec->ppi && pWnd )
1108       {
1109          switch(Msg)
1110          {
1111              // Handle the special case when working with password transfers across bordering processes.
1112              case EM_GETLINE:
1113              case EM_SETPASSWORDCHAR:
1114              case WM_GETTEXT:
1115                 // Look for edit controls setup for passwords.
1116                 if ( gpsi->atomSysClass[ICLS_EDIT] == pWnd->pcls->atomClassName && // Use atomNVClassName.
1117                      pWnd->style & ES_PASSWORD )
1118                 {
1119                    if (uResult) *uResult = -1;
1120                    ERR("Running across the border without a passport!\n");
1121                    EngSetLastError(ERROR_ACCESS_DENIED);
1122                    return STATUS_UNSUCCESSFUL;
1123                 }
1124                 break;
1125              case WM_NOTIFY:
1126                 if (uResult) *uResult = -1;
1127                 ERR("Running across the border without a passport!\n");
1128                 return STATUS_UNSUCCESSFUL;
1129          }
1130       }
1131 
1132       // These can not cross State lines!
1133       if ( Msg == WM_CREATE || Msg == WM_NCCREATE )
1134       {
1135          if (uResult) *uResult = -1;
1136          ERR("Can not tell the other State we have Create!\n");
1137          return STATUS_UNSUCCESSFUL;
1138       }
1139    }
1140 
1141    if(!(Message = AllocateUserMessage(TRUE)))
1142    {
1143       ERR("MsqSendMessage(): Not enough memory to allocate a message\n");
1144       if (uResult) *uResult = -1;
1145       return STATUS_INSUFFICIENT_RESOURCES;
1146    }
1147 
1148    Timeout.QuadPart = Int32x32To64(-10000,uTimeout); // Pass SMTO test with a TO of 0x80000000.
1149    TRACE("Timeout val %lld\n",Timeout.QuadPart);
1150 
1151    Message->Msg.hwnd = Wnd;
1152    Message->Msg.message = Msg;
1153    Message->Msg.wParam = wParam;
1154    Message->Msg.lParam = lParam;
1155    Message->ptiReceiver = ptirec;
1156    Message->ptiSender = pti;
1157    Message->HookMessage = HookMessage;
1158    Message->QS_Flags = QS_SENDMESSAGE;
1159 
1160    SaveMsg = pti->pusmSent;
1161    pti->pusmSent = Message;
1162 
1163    /* Queue it in the destination's message queue */
1164    InsertTailList(&ptirec->SentMessagesListHead, &Message->ListEntry);
1165 
1166    MsqWakeQueue(ptirec, QS_SENDMESSAGE, TRUE);
1167 
1168    // First time in, turn off swapping of the stack.
1169    if (pti->cEnterCount == 0)
1170    {
1171       SwapStateEnabled = KeSetKernelStackSwapEnable(FALSE);
1172    }
1173    pti->cEnterCount++;
1174 
1175    if (Block)
1176    {
1177       PVOID WaitObjects[2];
1178 
1179       WaitObjects[0] = Message->pkCompletionEvent; // Wait 0
1180       WaitObjects[1] = ptirec->pEThread;           // Wait 1
1181 
1182       UserLeaveCo();
1183 
1184       WaitStatus = KeWaitForMultipleObjects( 2,
1185                                              WaitObjects,
1186                                              WaitAny,
1187                                              UserRequest,
1188                                              UserMode,
1189                                              FALSE,
1190                                             (uTimeout ? &Timeout : NULL),
1191                                              NULL );
1192 
1193       UserEnterCo();
1194 
1195       if (WaitStatus == STATUS_TIMEOUT)
1196       {
1197          /* Look up if the message has not yet dispatched, if so
1198             make sure it can't pass a result and it must not set the completion event anymore */
1199          Entry = ptirec->SentMessagesListHead.Flink;
1200          while (Entry != &ptirec->SentMessagesListHead)
1201          {
1202             if (CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry) == Message)
1203             {
1204                Message->pkCompletionEvent = NULL;
1205                RemoveEntryList(&Message->ListEntry);
1206                ClearMsgBitsMask(ptirec, Message->QS_Flags);
1207                InsertTailList(&usmList, &Message->ListEntry);
1208                break;
1209             }
1210             Entry = Entry->Flink;
1211          }
1212 
1213          ERR("MsqSendMessage (blocked) timed out 1 Status %lx\n", WaitStatus);
1214       }
1215       // Receiving thread passed on and left us hanging with issues still pending.
1216       else if (WaitStatus == STATUS_WAIT_1)
1217       {
1218          ERR("Bk Receiving Thread woken up dead!\n");
1219          Message->flags |= SMF_RECEIVERDIED;
1220       }
1221 
1222       while (co_MsqDispatchOneSentMessage(pti))
1223          ;
1224    }
1225    else
1226    {
1227       PVOID WaitObjects[3];
1228 
1229       WaitObjects[0] = Message->pkCompletionEvent; // Wait 0
1230       WaitObjects[1] = pti->pEventQueueServer;     // Wait 1
1231       WaitObjects[2] = ptirec->pEThread;           // Wait 2
1232 
1233       do
1234       {
1235          UserLeaveCo();
1236 
1237          WaitStatus = KeWaitForMultipleObjects( 3,
1238                                                 WaitObjects,
1239                                                 WaitAny,
1240                                                 UserRequest,
1241                                                 UserMode,
1242                                                 FALSE,
1243                                                (uTimeout ? &Timeout : NULL),
1244                                                 NULL);
1245 
1246          UserEnterCo();
1247 
1248          if (WaitStatus == STATUS_TIMEOUT)
1249          {
1250             /* Look up if the message has not yet been dispatched, if so
1251                make sure it can't pass a result and it must not set the completion event anymore */
1252             Entry = ptirec->SentMessagesListHead.Flink;
1253             while (Entry != &ptirec->SentMessagesListHead)
1254             {
1255                if (CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry) == Message)
1256                {
1257                   Message->pkCompletionEvent = NULL;
1258                   RemoveEntryList(&Message->ListEntry);
1259                   ClearMsgBitsMask(ptirec, Message->QS_Flags);
1260                   InsertTailList(&usmList, &Message->ListEntry);
1261                   break;
1262                }
1263                Entry = Entry->Flink;
1264             }
1265 
1266             WARN("MsqSendMessage timed out 2 Status %lx\n", WaitStatus);
1267             break;
1268          }
1269          // Receiving thread passed on and left us hanging with issues still pending.
1270          else if (WaitStatus == STATUS_WAIT_2)
1271          {
1272             ERR("NB Receiving Thread woken up dead!\n");
1273             Message->flags |= SMF_RECEIVERDIED;
1274             break;
1275          }
1276 
1277          if (WaitStatus == STATUS_USER_APC) break;
1278 
1279          while (co_MsqDispatchOneSentMessage(pti))
1280             ;
1281       } while (WaitStatus == STATUS_WAIT_1);
1282    }
1283 
1284    // Count is nil, restore swapping of the stack.
1285    if (--pti->cEnterCount == 0 )
1286    {
1287       KeSetKernelStackSwapEnable(SwapStateEnabled);
1288    }
1289 
1290    // Handle User APC
1291    if (WaitStatus == STATUS_USER_APC)
1292    {
1293      // The current thread is dying!
1294      TRACE("User APC\n");
1295 
1296      // The Message will be on the Trouble list until Thread cleanup.
1297      Message->flags |= SMF_SENDERDIED;
1298 
1299      co_IntDeliverUserAPC();
1300      ERR("User APC Returned\n"); // Should not see this message.
1301    }
1302 
1303    // Force this thread to wake up for the next go around.
1304    KeSetEvent(pti->pEventQueueServer, IO_NO_INCREMENT, FALSE);
1305 
1306    Result = Message->lResult;
1307 
1308    // Determine whether this message is being processed or not.
1309    if ((Message->flags & (SMF_RECEIVERBUSY|SMF_RECEIVEDMESSAGE)) != SMF_RECEIVEDMESSAGE)
1310    {
1311       Message->flags |= SMF_RECEIVERFREE;
1312    }
1313 
1314    if (!(Message->flags & SMF_RECEIVERFREE))
1315    {
1316       TRACE("Sender Freeing Message %p ptirec %p bit %d list empty %d\n",Message,ptirec,!!(ptirec->pcti->fsChangeBits & QS_SENDMESSAGE),IsListEmpty(&ptirec->SentMessagesListHead));
1317       // Make it to this point, the message was received.
1318       FreeUserMessage(Message);
1319    }
1320 
1321    pti->pusmSent = SaveMsg;
1322 
1323    TRACE("MSM Allocation Count %d Status %lx Result %d\n",SendMsgCount,WaitStatus,Result);
1324 
1325    if (WaitStatus != STATUS_TIMEOUT)
1326    {
1327       if (uResult)
1328       {
1329          *uResult = (STATUS_WAIT_0 == WaitStatus ? Result : 0);
1330       }
1331    }
1332 
1333    return WaitStatus;
1334 }
1335 
1336 VOID FASTCALL
1337 MsqPostMessage(PTHREADINFO pti,
1338                MSG* Msg,
1339                BOOLEAN HardwareMessage,
1340                DWORD MessageBits,
1341                DWORD dwQEvent,
1342                LONG_PTR ExtraInfo)
1343 {
1344    PUSER_MESSAGE Message;
1345    PUSER_MESSAGE_QUEUE MessageQueue;
1346 
1347    MessageQueue = pti->MessageQueue;
1348 
1349    if ((pti->TIF_flags & TIF_INCLEANUP) || (MessageQueue->QF_flags & QF_INDESTROY))
1350    {
1351       ERR("Post Msg; Thread or Q is Dead!\n");
1352       return;
1353    }
1354 
1355    Message = MsqCreateMessage(Msg);
1356    if (!Message)
1357       return;
1358 
1359    if (Msg->message == WM_HOTKEY)
1360       MessageBits |= QS_HOTKEY;
1361 
1362    Message->dwQEvent = dwQEvent;
1363    Message->ExtraInfo = ExtraInfo;
1364    Message->QS_Flags = MessageBits;
1365    Message->pti = pti;
1366 
1367    if (!HardwareMessage)
1368    {
1369        InsertTailList(&pti->PostedMessagesListHead, &Message->ListEntry);
1370    }
1371    else
1372    {
1373        InsertTailList(&MessageQueue->HardwareMessagesListHead, &Message->ListEntry);
1374    }
1375 
1376    MsqWakeQueue(pti, MessageBits, TRUE);
1377    TRACE("Post Message %d\n", PostMsgCount);
1378 }
1379 
1380 VOID FASTCALL
1381 MsqPostQuitMessage(PTHREADINFO pti, ULONG ExitCode)
1382 {
1383    pti->QuitPosted = TRUE;
1384    pti->exitCode = ExitCode;
1385    MsqWakeQueue(pti, QS_POSTMESSAGE|QS_ALLPOSTMESSAGE, TRUE);
1386 }
1387 
1388 /***********************************************************************
1389  *           MsqSendParentNotify
1390  *
1391  * Send a WM_PARENTNOTIFY to all ancestors of the given window, unless
1392  * the window has the WS_EX_NOPARENTNOTIFY style.
1393  */
1394 static void MsqSendParentNotify( PWND pwnd, WORD event, WORD idChild, POINT pt )
1395 {
1396     PWND pwndDesktop = UserGetDesktopWindow();
1397 
1398     /* pt has to be in the client coordinates of the parent window */
1399     pt.x += pwndDesktop->rcClient.left - pwnd->rcClient.left;
1400     pt.y += pwndDesktop->rcClient.top - pwnd->rcClient.top;
1401 
1402     for (;;)
1403     {
1404         PWND pwndParent;
1405 
1406         if (!(pwnd->style & WS_CHILD)) break;
1407         if (pwnd->ExStyle & WS_EX_NOPARENTNOTIFY) break;
1408         if (!(pwndParent = IntGetParent(pwnd))) break;
1409         if (pwndParent == pwndDesktop) break;
1410         pt.x += pwnd->rcClient.left - pwndParent->rcClient.left;
1411         pt.y += pwnd->rcClient.top - pwndParent->rcClient.top;
1412 
1413         pwnd = pwndParent;
1414         co_IntSendMessage( UserHMGetHandle(pwnd), WM_PARENTNOTIFY,
1415                       MAKEWPARAM( event, idChild ), MAKELPARAM( pt.x, pt.y ) );
1416     }
1417 }
1418 
1419 VOID
1420 FASTCALL
1421 IntTrackMouseMove(PWND pwndTrack, PDESKTOP pDesk, PMSG msg, USHORT hittest)
1422 {
1423 //   PWND pwndTrack = IntChildrenWindowFromPoint(pwndMsg, msg->pt.x, msg->pt.y);
1424 //   hittest = (USHORT)GetNCHitEx(pwndTrack, msg->pt); /// @todo WTF is this???
1425 
1426    if ( pDesk->spwndTrack != pwndTrack || // Change with tracking window or
1427         msg->message != WM_MOUSEMOVE   || // Mouse click changes or
1428         pDesk->htEx != hittest)           // Change in current hit test states.
1429    {
1430       TRACE("ITMM: Track Mouse Move!\n");
1431 
1432       /* Handle only the changing window track and mouse move across a border. */
1433       if ( pDesk->spwndTrack != pwndTrack ||
1434           (pDesk->htEx == HTCLIENT) ^ (hittest == HTCLIENT) )
1435       {
1436          TRACE("ITMM: Another Wnd %d or Across Border %d\n",
1437               pDesk->spwndTrack != pwndTrack,(pDesk->htEx == HTCLIENT) ^ (hittest == HTCLIENT));
1438 
1439          if ( pDesk->dwDTFlags & DF_TME_LEAVE )
1440             UserPostMessage( UserHMGetHandle(pDesk->spwndTrack),
1441                             (pDesk->htEx != HTCLIENT) ? WM_NCMOUSELEAVE : WM_MOUSELEAVE,
1442                              0, 0);
1443 
1444          if ( pDesk->dwDTFlags & DF_TME_HOVER )
1445             IntKillTimer(pDesk->spwndTrack, ID_EVENT_SYSTIMER_MOUSEHOVER, TRUE);
1446 
1447          /* Clear the flags to sign a change. */
1448          pDesk->dwDTFlags &= ~(DF_TME_LEAVE|DF_TME_HOVER);
1449       }
1450       /* Set the Track window and hit test. */
1451       pDesk->spwndTrack = pwndTrack;
1452       pDesk->htEx = hittest;
1453    }
1454 
1455    /* Reset, Same Track window, Hover set and Mouse Clicks or Clobbered Hover box. */
1456    if ( pDesk->spwndTrack == pwndTrack &&
1457        ( msg->message != WM_MOUSEMOVE || !RECTL_bPointInRect(&pDesk->rcMouseHover, msg->pt.x, msg->pt.y)) &&
1458         pDesk->dwDTFlags & DF_TME_HOVER )
1459    {
1460       TRACE("ITMM: Reset Hover points!\n");
1461       // Restart timer for the hover period.
1462       IntSetTimer(pDesk->spwndTrack, ID_EVENT_SYSTIMER_MOUSEHOVER, pDesk->dwMouseHoverTime, SystemTimerProc, TMRF_SYSTEM);
1463       // Reset desktop mouse hover from the system default hover rectangle.
1464       RECTL_vSetRect(&pDesk->rcMouseHover,
1465                       msg->pt.x - gspv.iMouseHoverWidth  / 2,
1466                       msg->pt.y - gspv.iMouseHoverHeight / 2,
1467                       msg->pt.x + gspv.iMouseHoverWidth  / 2,
1468                       msg->pt.y + gspv.iMouseHoverHeight / 2);
1469    }
1470 }
1471 
1472 BOOL co_IntProcessMouseMessage(MSG* msg, BOOL* RemoveMessages, BOOL* NotForUs, LONG_PTR ExtraInfo, UINT first, UINT last)
1473 {
1474     MSG clk_msg;
1475     POINT pt;
1476     UINT message;
1477     USHORT hittest;
1478     EVENTMSG event;
1479     MOUSEHOOKSTRUCT hook;
1480     BOOL eatMsg = FALSE;
1481 
1482     PWND pwndMsg, pwndDesktop;
1483     PUSER_MESSAGE_QUEUE MessageQueue;
1484     PTHREADINFO pti;
1485     PSYSTEM_CURSORINFO CurInfo;
1486     PDESKTOP pDesk;
1487 
1488     pti = PsGetCurrentThreadWin32Thread();
1489     pwndDesktop = UserGetDesktopWindow();
1490     if (pwndDesktop == NULL)
1491     {
1492         ERR("No desktop window!\n");
1493         return FALSE;
1494     }
1495 
1496     MessageQueue = pti->MessageQueue;
1497     CurInfo = IntGetSysCursorInfo();
1498     pwndMsg = ValidateHwndNoErr(msg->hwnd);
1499     clk_msg = MessageQueue->msgDblClk;
1500     pDesk = pwndDesktop->head.rpdesk;
1501 
1502     /* find the window to dispatch this mouse message to */
1503     if (MessageQueue->spwndCapture)
1504     {
1505         hittest = HTCLIENT;
1506         pwndMsg = MessageQueue->spwndCapture;
1507     }
1508     else
1509     {
1510         /*
1511            Start with null window. See wine win.c:test_mouse_input:WM_COMMAND tests.
1512         */
1513         pwndMsg = co_WinPosWindowFromPoint( NULL, &msg->pt, &hittest, FALSE);
1514     }
1515 
1516     TRACE("Got mouse message for %p, hittest: 0x%x\n", msg->hwnd, hittest);
1517 
1518     // Null window or not the same "Hardware" message queue.
1519     if (pwndMsg == NULL || pwndMsg->head.pti->MessageQueue != MessageQueue)
1520     {
1521         // Crossing a boundary, so set cursor. See default message queue cursor.
1522         IntSystemSetCursor(SYSTEMCUR(ARROW));
1523         /* Remove and ignore the message */
1524         *RemoveMessages = TRUE;
1525         return FALSE;
1526     }
1527 
1528     // Check to see if this is attached,
1529     if ( pwndMsg->head.pti != pti &&  // window thread is not current,
1530          MessageQueue->cThreads > 1 ) // and is attached...
1531     {
1532         // This is not for us and we should leave so the other thread can check for messages!!!
1533         *NotForUs = TRUE;
1534         *RemoveMessages = FALSE;
1535         return FALSE;
1536     }
1537 
1538     if ( MessageQueue == gpqCursor ) // Cursor must use the same Queue!
1539     {
1540        IntTrackMouseMove(pwndMsg, pDesk, msg, hittest);
1541     }
1542     else
1543     {
1544        WARN("Not the same cursor!\n");
1545     }
1546 
1547     msg->hwnd = UserHMGetHandle(pwndMsg);
1548 
1549     pt = msg->pt;
1550     message = msg->message;
1551 
1552     /* Note: windows has no concept of a non-client wheel message */
1553     if (message != WM_MOUSEWHEEL)
1554     {
1555         if (hittest != HTCLIENT)
1556         {
1557             message += WM_NCMOUSEMOVE - WM_MOUSEMOVE;
1558             msg->wParam = hittest; // Caution! This might break wParam check in DblClk.
1559         }
1560         else
1561         {
1562             /* coordinates don't get translated while tracking a menu */
1563             /* FIXME: should differentiate popups and top-level menus */
1564             if (!(MessageQueue->MenuOwner))
1565             {
1566                 pt.x += pwndDesktop->rcClient.left - pwndMsg->rcClient.left;
1567                 pt.y += pwndDesktop->rcClient.top - pwndMsg->rcClient.top;
1568             }
1569         }
1570     }
1571     msg->lParam = MAKELONG( pt.x, pt.y );
1572 
1573     /* translate double-clicks */
1574 
1575     if ((msg->message == WM_LBUTTONDOWN) ||
1576         (msg->message == WM_RBUTTONDOWN) ||
1577         (msg->message == WM_MBUTTONDOWN) ||
1578         (msg->message == WM_XBUTTONDOWN))
1579     {
1580         BOOL update = *RemoveMessages;
1581 
1582         /* translate double-clicks -
1583          * note that ...MOUSEMOVEs can slip in between
1584          * ...BUTTONDOWN and ...BUTTONDBLCLK messages */
1585 
1586         if ((MessageQueue->MenuOwner || MessageQueue->MoveSize) ||
1587             hittest != HTCLIENT ||
1588             (pwndMsg->pcls->style & CS_DBLCLKS))
1589         {
1590            if ((msg->message == clk_msg.message) &&
1591                (msg->hwnd == clk_msg.hwnd) &&
1592                // Only worry about XButton wParam.
1593                (msg->message != WM_XBUTTONDOWN || GET_XBUTTON_WPARAM(msg->wParam) == GET_XBUTTON_WPARAM(clk_msg.wParam)) &&
1594                ((msg->time - clk_msg.time) < (ULONG)gspv.iDblClickTime) &&
1595                (abs(msg->pt.x - clk_msg.pt.x) < UserGetSystemMetrics(SM_CXDOUBLECLK)/2) &&
1596                (abs(msg->pt.y - clk_msg.pt.y) < UserGetSystemMetrics(SM_CYDOUBLECLK)/2))
1597            {
1598                message += (WM_LBUTTONDBLCLK - WM_LBUTTONDOWN);
1599                if (update)
1600                {
1601                    MessageQueue->msgDblClk.message = 0;  /* clear the double-click conditions */
1602                    update = FALSE;
1603                }
1604            }
1605         }
1606 
1607         if (!((first ==  0 && last == 0) || (message >= first || message <= last)))
1608         {
1609             TRACE("Message out of range!!!\n");
1610             return FALSE;
1611         }
1612 
1613         /* update static double-click conditions */
1614         if (update) MessageQueue->msgDblClk = *msg;
1615     }
1616     else
1617     {
1618         if (!((first ==  0 && last == 0) || (message >= first || message <= last)))
1619         {
1620             TRACE("Message out of range!!!\n");
1621             return FALSE;
1622         }
1623 
1624         // Update mouse move down keys.
1625         if (message == WM_MOUSEMOVE)
1626         {
1627            msg->wParam = MsqGetDownKeyState(MessageQueue);
1628         }
1629     }
1630 
1631     if (gspv.bMouseClickLock)
1632     {
1633         BOOL IsClkLck = FALSE;
1634 
1635         if(msg->message == WM_LBUTTONUP)
1636         {
1637             IsClkLck = ((msg->time - CurInfo->ClickLockTime) >= gspv.dwMouseClickLockTime);
1638             if (IsClkLck && (!CurInfo->ClickLockActive))
1639             {
1640                 CurInfo->ClickLockActive = TRUE;
1641             }
1642         }
1643         else if (msg->message == WM_LBUTTONDOWN)
1644         {
1645             if (CurInfo->ClickLockActive)
1646             {
1647                 IsClkLck = TRUE;
1648                 CurInfo->ClickLockActive = FALSE;
1649             }
1650 
1651             CurInfo->ClickLockTime = msg->time;
1652         }
1653 
1654         if(IsClkLck)
1655         {
1656             /* Remove and ignore the message */
1657             *RemoveMessages = TRUE;
1658             TRACE("Remove and ignore the message\n");
1659             return FALSE;
1660         }
1661     }
1662 
1663     if (pti->TIF_flags & TIF_MSGPOSCHANGED)
1664     {
1665         pti->TIF_flags &= ~TIF_MSGPOSCHANGED;
1666         IntNotifyWinEvent(EVENT_OBJECT_LOCATIONCHANGE, NULL, OBJID_CLIENT, CHILDID_SELF, 0);
1667     }
1668 
1669     /* message is accepted now (but still get dropped) */
1670 
1671     event.message = msg->message;
1672     event.time    = msg->time;
1673     event.hwnd    = msg->hwnd;
1674     event.paramL  = msg->pt.x;
1675     event.paramH  = msg->pt.y;
1676     co_HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&event );
1677 
1678     hook.pt           = msg->pt;
1679     hook.hwnd         = msg->hwnd;
1680     hook.wHitTestCode = hittest;
1681     hook.dwExtraInfo  = ExtraInfo;
1682     if (co_HOOK_CallHooks( WH_MOUSE, *RemoveMessages ? HC_ACTION : HC_NOREMOVE,
1683                         message, (LPARAM)&hook ))
1684     {
1685         hook.pt           = msg->pt;
1686         hook.hwnd         = msg->hwnd;
1687         hook.wHitTestCode = hittest;
1688         hook.dwExtraInfo  = ExtraInfo;
1689         co_HOOK_CallHooks( WH_CBT, HCBT_CLICKSKIPPED, message, (LPARAM)&hook );
1690 
1691         ERR("WH_MOUSE dropped mouse message!\n");
1692 
1693         /* Remove and skip message */
1694         *RemoveMessages = TRUE;
1695         return FALSE;
1696     }
1697 
1698     if ((hittest == (USHORT)HTERROR) || (hittest == (USHORT)HTNOWHERE))
1699     {
1700         co_IntSendMessage( msg->hwnd, WM_SETCURSOR, (WPARAM)msg->hwnd, MAKELONG( hittest, msg->message ));
1701 
1702         /* Remove and skip message */
1703         *RemoveMessages = TRUE;
1704         return FALSE;
1705     }
1706 
1707     if ((*RemoveMessages == FALSE) || MessageQueue->spwndCapture)
1708     {
1709         /* Accept the message */
1710         msg->message = message;
1711         return TRUE;
1712     }
1713 
1714     if ((msg->message == WM_LBUTTONDOWN) ||
1715         (msg->message == WM_RBUTTONDOWN) ||
1716         (msg->message == WM_MBUTTONDOWN) ||
1717         (msg->message == WM_XBUTTONDOWN))
1718     {
1719         /* Send the WM_PARENTNOTIFY,
1720          * note that even for double/nonclient clicks
1721          * notification message is still WM_L/M/RBUTTONDOWN.
1722          */
1723         MsqSendParentNotify(pwndMsg, msg->message, 0, msg->pt );
1724 
1725         /* Activate the window if needed */
1726 
1727         if (pwndMsg != MessageQueue->spwndActive)
1728         {
1729             PWND pwndTop = pwndMsg;
1730             pwndTop = IntGetNonChildAncestor(pwndTop);
1731 
1732             TRACE("Mouse pti %p pwndMsg pti %p pwndTop pti %p\n",MessageQueue->ptiMouse,pwndMsg->head.pti,pwndTop->head.pti);
1733 
1734             if (pwndTop && pwndTop != pwndDesktop)
1735             {
1736                 LONG ret = co_IntSendMessage( msg->hwnd,
1737                                               WM_MOUSEACTIVATE,
1738                                               (WPARAM)UserHMGetHandle(pwndTop),
1739                                               MAKELONG( hittest, msg->message));
1740                 switch(ret)
1741                 {
1742                 case MA_NOACTIVATEANDEAT:
1743                     eatMsg = TRUE;
1744                     /* fall through */
1745                 case MA_NOACTIVATE:
1746                     break;
1747                 case MA_ACTIVATEANDEAT:
1748                     eatMsg = TRUE;
1749                     /* fall through */
1750                 case MA_ACTIVATE:
1751                 case 0:
1752                     if (!co_IntMouseActivateWindow( pwndTop )) eatMsg = TRUE;
1753                     break;
1754                 default:
1755                     ERR( "unknown WM_MOUSEACTIVATE code %d\n", ret );
1756                     break;
1757                 }
1758             }
1759         }
1760     }
1761 
1762     /* send the WM_SETCURSOR message */
1763 
1764     /* Windows sends the normal mouse message as the message parameter
1765        in the WM_SETCURSOR message even if it's non-client mouse message */
1766     co_IntSendMessage( msg->hwnd, WM_SETCURSOR, (WPARAM)msg->hwnd, MAKELONG( hittest, msg->message ));
1767 
1768     msg->message = message;
1769     return !eatMsg;
1770 }
1771 
1772 BOOL co_IntProcessKeyboardMessage(MSG* Msg, BOOL* RemoveMessages)
1773 {
1774     EVENTMSG Event;
1775     USER_REFERENCE_ENTRY Ref;
1776     PWND pWnd;
1777     UINT ImmRet;
1778     BOOL Ret = TRUE, bKeyUpDown = FALSE;
1779     PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
1780     const UINT uMsg = Msg->message;
1781 
1782     if (uMsg == VK_PACKET)
1783         pti->wchInjected = HIWORD(Msg->wParam);
1784 
1785     if (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN || uMsg == WM_KEYUP || uMsg == WM_SYSKEYUP)
1786     {
1787         bKeyUpDown = TRUE;
1788         switch (Msg->wParam)
1789         {
1790             case VK_LSHIFT: case VK_RSHIFT:
1791                 Msg->wParam = VK_SHIFT;
1792                 break;
1793             case VK_LCONTROL: case VK_RCONTROL:
1794                 Msg->wParam = VK_CONTROL;
1795                 break;
1796             case VK_LMENU: case VK_RMENU:
1797                 Msg->wParam = VK_MENU;
1798                 break;
1799         }
1800     }
1801 
1802     pWnd = ValidateHwndNoErr(Msg->hwnd);
1803     if (pWnd) UserRefObjectCo(pWnd, &Ref);
1804 
1805     Event.message = uMsg;
1806     Event.hwnd    = Msg->hwnd;
1807     Event.time    = Msg->time;
1808     Event.paramL  = (Msg->wParam & 0xFF) | (HIWORD(Msg->lParam) << 8);
1809     Event.paramH  = Msg->lParam & 0x7FFF;
1810     if (HIWORD(Msg->lParam) & 0x0100) Event.paramH |= 0x8000;
1811     co_HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&Event);
1812 
1813     if (*RemoveMessages)
1814     {
1815         if ((uMsg == WM_KEYDOWN) &&
1816             (Msg->hwnd != IntGetDesktopWindow()))
1817         {
1818             /* Handle F1 key by sending out WM_HELP message */
1819             if (Msg->wParam == VK_F1)
1820             {
1821                 UserPostMessage( Msg->hwnd, WM_KEYF1, 0, 0 );
1822             }
1823             else if (Msg->wParam >= VK_BROWSER_BACK &&
1824                      Msg->wParam <= VK_LAUNCH_APP2)
1825             {
1826                 /* FIXME: Process keystate */
1827                 co_IntSendMessage(Msg->hwnd, WM_APPCOMMAND, (WPARAM)Msg->hwnd, MAKELPARAM(0, (FAPPCOMMAND_KEY | (Msg->wParam - VK_BROWSER_BACK + 1))));
1828             }
1829         }
1830         else if (uMsg == WM_KEYUP)
1831         {
1832             /* Handle VK_APPS key by posting a WM_CONTEXTMENU message */
1833             if (Msg->wParam == VK_APPS && pti->MessageQueue->MenuOwner == NULL)
1834                 UserPostMessage( Msg->hwnd, WM_CONTEXTMENU, (WPARAM)Msg->hwnd, -1 );
1835         }
1836     }
1837 
1838     //// Key Down!
1839     if (*RemoveMessages && uMsg == WM_SYSKEYDOWN)
1840     {
1841         if ( HIWORD(Msg->lParam) & KF_ALTDOWN )
1842         {
1843             if ( Msg->wParam == VK_ESCAPE || Msg->wParam == VK_TAB ) // Alt-Tab/ESC Alt-Shift-Tab/ESC
1844             {
1845                 WPARAM wParamTmp;
1846 
1847                 wParamTmp = UserGetKeyState(VK_SHIFT) & 0x8000 ? SC_PREVWINDOW : SC_NEXTWINDOW;
1848                 TRACE("Send WM_SYSCOMMAND Alt-Tab/ESC Alt-Shift-Tab/ESC\n");
1849                 co_IntSendMessage( Msg->hwnd, WM_SYSCOMMAND, wParamTmp, Msg->wParam );
1850 
1851                 //// Keep looping.
1852                 Ret = FALSE;
1853                 //// Skip the rest.
1854                 goto Exit;
1855             }
1856         }
1857     }
1858 
1859     if (co_HOOK_CallHooks( WH_KEYBOARD,
1860                           *RemoveMessages ? HC_ACTION : HC_NOREMOVE,
1861                            LOWORD(Msg->wParam),
1862                            Msg->lParam))
1863     {
1864         /* skip this message */
1865         co_HOOK_CallHooks( WH_CBT,
1866                            HCBT_KEYSKIPPED,
1867                            LOWORD(Msg->wParam),
1868                            Msg->lParam );
1869 
1870         ERR("KeyboardMessage WH_KEYBOARD Call Hook return!\n");
1871 
1872         *RemoveMessages = TRUE;
1873 
1874         Ret = FALSE;
1875     }
1876 
1877     if (pWnd && Ret && *RemoveMessages && bKeyUpDown && !(pti->TIF_flags & TIF_DISABLEIME))
1878     {
1879         ImmRet = IntImmProcessKey(pti->MessageQueue, pWnd, uMsg, Msg->wParam, Msg->lParam);
1880         if (ImmRet)
1881         {
1882             if ( ImmRet & (IPHK_HOTKEY|IPHK_SKIPTHISKEY) )
1883             {
1884                ImmRet = 0;
1885             }
1886             if ( ImmRet & IPHK_PROCESSBYIME )
1887             {
1888                Msg->wParam = VK_PROCESSKEY;
1889             }
1890         }
1891     }
1892 Exit:
1893     if (pWnd) UserDerefObjectCo(pWnd);
1894     return Ret;
1895 }
1896 
1897 BOOL co_IntProcessHardwareMessage(MSG* Msg, BOOL* RemoveMessages, BOOL* NotForUs, LONG_PTR ExtraInfo, UINT first, UINT last)
1898 {
1899     if ( IS_MOUSE_MESSAGE(Msg->message))
1900     {
1901         return co_IntProcessMouseMessage(Msg, RemoveMessages, NotForUs, ExtraInfo, first, last);
1902     }
1903     else if ( IS_KBD_MESSAGE(Msg->message))
1904     {
1905         return co_IntProcessKeyboardMessage(Msg, RemoveMessages);
1906     }
1907 
1908     return TRUE;
1909 }
1910 
1911 /* check whether a message filter contains at least one potential hardware message */
1912 static INT FASTCALL
1913 filter_contains_hw_range( UINT first, UINT last )
1914 {
1915    /* hardware message ranges are (in numerical order):
1916     *   WM_NCMOUSEFIRST .. WM_NCMOUSELAST
1917     *   WM_KEYFIRST .. WM_KEYLAST
1918     *   WM_MOUSEFIRST .. WM_MOUSELAST
1919     */
1920     if (!last) --last;
1921     if (last < WM_NCMOUSEFIRST) return 0;
1922     if (first > WM_NCMOUSELAST && last < WM_KEYFIRST) return 0;
1923     if (first > WM_KEYLAST && last < WM_MOUSEFIRST) return 0;
1924     if (first > WM_MOUSELAST) return 0;
1925     return 1;
1926 }
1927 
1928 /* check whether message is in the range of mouse messages */
1929 static inline BOOL is_mouse_message( UINT message )
1930 {
1931     return ( //( message >= WM_NCMOUSEFIRST && message <= WM_NCMOUSELAST )   || This seems to break tests...
1932              ( message >= WM_MOUSEFIRST   && message <= WM_MOUSELAST )     ||
1933              ( message >= WM_XBUTTONDOWN  && message <= WM_XBUTTONDBLCLK ) ||
1934              ( message >= WM_MBUTTONDOWN  && message <= WM_MBUTTONDBLCLK ) ||
1935              ( message >= WM_LBUTTONDOWN  && message <= WM_RBUTTONDBLCLK ) );
1936 }
1937 
1938 BOOL APIENTRY
1939 co_MsqPeekHardwareMessage(IN PTHREADINFO pti,
1940                          IN BOOL Remove,
1941                          IN PWND Window,
1942                          IN UINT MsgFilterLow,
1943                          IN UINT MsgFilterHigh,
1944                          IN UINT QSflags,
1945                          OUT MSG* pMsg)
1946 {
1947    BOOL AcceptMessage, NotForUs;
1948    PUSER_MESSAGE CurrentMessage;
1949    PLIST_ENTRY ListHead;
1950    MSG msg;
1951    ULONG_PTR idSave;
1952    DWORD QS_Flags;
1953    LONG_PTR ExtraInfo;
1954    MSG clk_msg;
1955    BOOL Ret = FALSE;
1956    PUSER_MESSAGE_QUEUE MessageQueue = pti->MessageQueue;
1957 
1958    if (!filter_contains_hw_range( MsgFilterLow, MsgFilterHigh )) return FALSE;
1959 
1960    ListHead = MessageQueue->HardwareMessagesListHead.Flink;
1961 
1962    if (IsListEmpty(ListHead)) return FALSE;
1963 
1964    if (!MessageQueue->ptiSysLock)
1965    {
1966       MessageQueue->ptiSysLock = pti;
1967       pti->pcti->CTI_flags |= CTI_THREADSYSLOCK;
1968    }
1969 
1970    if (MessageQueue->ptiSysLock != pti)
1971    {
1972       ERR("Thread Q is locked to ptiSysLock 0x%p pti 0x%p\n",MessageQueue->ptiSysLock,pti);
1973       return FALSE;
1974    }
1975 
1976    while (ListHead != &MessageQueue->HardwareMessagesListHead)
1977    {
1978       CurrentMessage = CONTAINING_RECORD(ListHead, USER_MESSAGE, ListEntry);
1979       ListHead = ListHead->Flink;
1980 
1981       if (MessageQueue->idSysPeek == (ULONG_PTR)CurrentMessage)
1982       {
1983          TRACE("Skip this message due to it is in play!\n");
1984          continue;
1985       }
1986 /*
1987  MSDN:
1988  1: any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL.
1989  2: retrieves only messages on the current thread's message queue whose hwnd value is NULL.
1990  3: handle to the window whose messages are to be retrieved.
1991  */
1992       if ( ( !Window || // 1
1993             ( Window == PWND_BOTTOM && CurrentMessage->Msg.hwnd == NULL ) || // 2
1994             ( Window != PWND_BOTTOM && UserHMGetHandle(Window) == CurrentMessage->Msg.hwnd ) || // 3
1995             ( is_mouse_message(CurrentMessage->Msg.message) ) ) && // Null window for anything mouse.
1996             ( CurrentMessage->QS_Flags & QSflags ) )
1997       {
1998          idSave = MessageQueue->idSysPeek;
1999          MessageQueue->idSysPeek = (ULONG_PTR)CurrentMessage;
2000 
2001          msg = CurrentMessage->Msg;
2002          ExtraInfo = CurrentMessage->ExtraInfo;
2003          QS_Flags = CurrentMessage->QS_Flags;
2004          clk_msg = MessageQueue->msgDblClk;
2005 
2006          NotForUs = FALSE;
2007 
2008          UpdateKeyStateFromMsg(MessageQueue, &msg);
2009          AcceptMessage = co_IntProcessHardwareMessage(&msg, &Remove, &NotForUs, ExtraInfo, MsgFilterLow, MsgFilterHigh);
2010 
2011          if (!NotForUs && (MsgFilterLow != 0 || MsgFilterHigh != 0))
2012          {
2013              /* Don't return message if not in range */
2014              if (msg.message < MsgFilterLow || msg.message > MsgFilterHigh)
2015              {
2016                  MessageQueue->msgDblClk = clk_msg;
2017                  MessageQueue->idSysPeek = idSave;
2018                  continue;
2019              }
2020          }
2021 
2022          if (Remove)
2023          {
2024              if (CurrentMessage->pti != NULL && (MessageQueue->idSysPeek == (ULONG_PTR)CurrentMessage))
2025              {
2026                 MsqDestroyMessage(CurrentMessage);
2027              }
2028              ClearMsgBitsMask(pti, QS_Flags);
2029          }
2030 
2031          MessageQueue->idSysPeek = idSave;
2032 
2033          if (NotForUs)
2034          {
2035             Ret = FALSE;
2036             break;
2037          }
2038 
2039          if (AcceptMessage)
2040          {
2041             *pMsg = msg;
2042             // Fix all but one wine win:test_GetMessagePos WM_TIMER tests. See PostTimerMessages.
2043             if (!RtlEqualMemory(&pti->ptLast, &msg.pt, sizeof(POINT)))
2044             {
2045                pti->TIF_flags |= TIF_MSGPOSCHANGED;
2046             }
2047             pti->timeLast = msg.time;
2048             pti->ptLast   = msg.pt;
2049             MessageQueue->ExtraInfo = ExtraInfo;
2050             Ret = TRUE;
2051             break;
2052          }
2053       }
2054    }
2055 
2056    MessageQueue->ptiSysLock = NULL;
2057    pti->pcti->CTI_flags &= ~CTI_THREADSYSLOCK;
2058    return Ret;
2059 }
2060 
2061 BOOLEAN APIENTRY
2062 MsqPeekMessage(IN PTHREADINFO pti,
2063                   IN BOOLEAN Remove,
2064                   IN PWND Window,
2065                   IN UINT MsgFilterLow,
2066                   IN UINT MsgFilterHigh,
2067                   IN UINT QSflags,
2068                   OUT LONG_PTR *ExtraInfo,
2069                   OUT DWORD *dwQEvent,
2070                   OUT PMSG Message)
2071 {
2072    PUSER_MESSAGE CurrentMessage;
2073    PLIST_ENTRY ListHead;
2074    DWORD QS_Flags;
2075    BOOL Ret = FALSE;
2076 
2077    ListHead = pti->PostedMessagesListHead.Flink;
2078 
2079    if (IsListEmpty(ListHead)) return FALSE;
2080 
2081    while(ListHead != &pti->PostedMessagesListHead)
2082    {
2083       CurrentMessage = CONTAINING_RECORD(ListHead, USER_MESSAGE, ListEntry);
2084       ListHead = ListHead->Flink;
2085 /*
2086  MSDN:
2087  1: any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL.
2088  2: retrieves only messages on the current thread's message queue whose hwnd value is NULL.
2089  3: handle to the window whose messages are to be retrieved.
2090  */
2091       if ( ( !Window || // 1
2092             ( Window == PWND_BOTTOM && CurrentMessage->Msg.hwnd == NULL ) || // 2
2093             ( Window != PWND_BOTTOM && UserHMGetHandle(Window) == CurrentMessage->Msg.hwnd ) ) && // 3
2094             ( ( ( MsgFilterLow == 0 && MsgFilterHigh == 0 ) && CurrentMessage->QS_Flags & QSflags ) ||
2095               ( MsgFilterLow <= CurrentMessage->Msg.message && MsgFilterHigh >= CurrentMessage->Msg.message ) ) )
2096       {
2097          *Message   = CurrentMessage->Msg;
2098          *ExtraInfo = CurrentMessage->ExtraInfo;
2099          QS_Flags   = CurrentMessage->QS_Flags;
2100          if (dwQEvent) *dwQEvent = CurrentMessage->dwQEvent;
2101 
2102          if (Remove)
2103          {
2104              if (CurrentMessage->pti != NULL)
2105              {
2106                 MsqDestroyMessage(CurrentMessage);
2107              }
2108              ClearMsgBitsMask(pti, QS_Flags);
2109          }
2110          Ret = TRUE;
2111          break;
2112       }
2113    }
2114 
2115    return Ret;
2116 }
2117 
2118 NTSTATUS FASTCALL
2119 co_MsqWaitForNewMessages(PTHREADINFO pti, PWND WndFilter,
2120                          UINT MsgFilterMin, UINT MsgFilterMax)
2121 {
2122    NTSTATUS ret = STATUS_SUCCESS;
2123 
2124    // Post mouse moves before waiting for messages.
2125    if (pti->MessageQueue->QF_flags & QF_MOUSEMOVED)
2126    {
2127       IntCoalesceMouseMove(pti);
2128    }
2129 
2130    UserLeaveCo();
2131 
2132    ZwYieldExecution(); // Let someone else run!
2133 
2134    ret = KeWaitForSingleObject( pti->pEventQueueServer,
2135                                 UserRequest,
2136                                 UserMode,
2137                                 FALSE,
2138                                 NULL );
2139    UserEnterCo();
2140    if ( ret == STATUS_USER_APC )
2141    {
2142       TRACE("MWFNW User APC\n");
2143       co_IntDeliverUserAPC();
2144    }
2145    return ret;
2146 }
2147 
2148 BOOL FASTCALL
2149 MsqIsHung(PTHREADINFO pti, DWORD TimeOut)
2150 {
2151     DWORD dwTimeStamp = EngGetTickCount32();
2152     if (dwTimeStamp - pti->pcti->timeLastRead > TimeOut &&
2153        !(pti->pcti->fsWakeMask & QS_INPUT) &&
2154        !PsGetThreadFreezeCount(pti->pEThread) &&
2155        !(pti->ppi->W32PF_flags & W32PF_APPSTARTING))
2156     {
2157         TRACE("\nMsqIsHung(pti %p, TimeOut %lu)\n"
2158             "pEThread %p, ThreadsProcess %p, ImageFileName '%s'\n"
2159             "dwTimeStamp = %lu\n"
2160             "pti->pcti->timeLastRead = %lu\n"
2161             "pti->timeLast = %lu\n"
2162             "PsGetThreadFreezeCount(pti->pEThread) = %lu\n",
2163             pti, TimeOut,
2164             pti->pEThread,
2165             pti->pEThread ? pti->pEThread->ThreadsProcess : NULL,
2166             (pti->pEThread && pti->pEThread->ThreadsProcess)
2167                 ? pti->pEThread->ThreadsProcess->ImageFileName : "(None)",
2168             dwTimeStamp,
2169             pti->pcti->timeLastRead,
2170             pti->timeLast,
2171             PsGetThreadFreezeCount(pti->pEThread));
2172 
2173         return TRUE;
2174     }
2175 
2176    return FALSE;
2177 }
2178 
2179 BOOL FASTCALL
2180 IsThreadSuspended(PTHREADINFO pti)
2181 {
2182    if (pti->pEThread)
2183    {
2184       BOOL Ret = TRUE;
2185       if (!(pti->pEThread->Tcb.SuspendCount) && !PsGetThreadFreezeCount(pti->pEThread)) Ret = FALSE;
2186       return Ret;
2187    }
2188    return FALSE;
2189 }
2190 
2191 VOID
2192 CALLBACK
2193 HungAppSysTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
2194 {
2195    DoTheScreenSaver();
2196    TRACE("HungAppSysTimerProc\n");
2197    // Process list of windows that are hung and waiting.
2198 }
2199 
2200 BOOLEAN FASTCALL
2201 MsqInitializeMessageQueue(PTHREADINFO pti, PUSER_MESSAGE_QUEUE MessageQueue)
2202 {
2203    InitializeListHead(&MessageQueue->HardwareMessagesListHead); // Keep here!
2204    MessageQueue->spwndFocus = NULL;
2205    MessageQueue->iCursorLevel = 0;
2206    MessageQueue->CursorObject = SYSTEMCUR(WAIT); // See test_initial_cursor.
2207    if (MessageQueue->CursorObject)
2208    {
2209       TRACE("Default cursor hcur %p\n",UserHMGetHandle(MessageQueue->CursorObject));
2210       UserReferenceObject(MessageQueue->CursorObject);
2211    }
2212    RtlCopyMemory(MessageQueue->afKeyState, gafAsyncKeyState, sizeof(gafAsyncKeyState));
2213    MessageQueue->ptiMouse = pti;
2214    MessageQueue->ptiKeyboard = pti;
2215    MessageQueue->cThreads++;
2216 
2217    return TRUE;
2218 }
2219 
2220 VOID FASTCALL
2221 MsqCleanupThreadMsgs(PTHREADINFO pti)
2222 {
2223    PLIST_ENTRY CurrentEntry;
2224    PUSER_MESSAGE CurrentMessage;
2225    PUSER_SENT_MESSAGE CurrentSentMessage;
2226 
2227    TRACE("MsqCleanupThreadMsgs %p\n",pti);
2228 
2229    // Clear it all out.
2230    if (pti->pcti)
2231    {
2232        pti->pcti->fsWakeBits = 0;
2233        pti->pcti->fsChangeBits = 0;
2234    }
2235 
2236    pti->nCntsQBits[QSRosKey] = 0;
2237    pti->nCntsQBits[QSRosMouseMove] = 0;
2238    pti->nCntsQBits[QSRosMouseButton] = 0;
2239    pti->nCntsQBits[QSRosPostMessage] = 0;
2240    pti->nCntsQBits[QSRosSendMessage] = 0;
2241    pti->nCntsQBits[QSRosHotKey] = 0;
2242    pti->nCntsQBits[QSRosEvent] = 0;
2243 
2244    /* cleanup posted messages */
2245    while (!IsListEmpty(&pti->PostedMessagesListHead))
2246    {
2247       CurrentEntry = pti->PostedMessagesListHead.Flink;
2248       CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, ListEntry);
2249       ERR("Thread Cleanup Post Messages %p\n",CurrentMessage);
2250       if (CurrentMessage->dwQEvent)
2251       {
2252          if (CurrentMessage->dwQEvent == POSTEVENT_NWE)
2253          {
2254             ExFreePoolWithTag( (PVOID)CurrentMessage->ExtraInfo, TAG_HOOK);
2255          }
2256       }
2257       MsqDestroyMessage(CurrentMessage);
2258    }
2259 
2260    /* remove the messages that have not yet been dispatched */
2261    while (!IsListEmpty(&pti->SentMessagesListHead))
2262    {
2263       CurrentEntry = pti->SentMessagesListHead.Flink;
2264       CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE, ListEntry);
2265 
2266       ERR("Thread Cleanup Sent Messages %p\n",CurrentSentMessage);
2267 
2268       /* wake the sender's thread */
2269       if (CurrentSentMessage->pkCompletionEvent != NULL)
2270       {
2271          KeSetEvent(CurrentSentMessage->pkCompletionEvent, IO_NO_INCREMENT, FALSE);
2272       }
2273 
2274       if (CurrentSentMessage->HasPackedLParam)
2275       {
2276          if (CurrentSentMessage->Msg.lParam)
2277             ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
2278       }
2279 
2280       /* free the message */
2281       FreeUserMessage(CurrentSentMessage);
2282    }
2283 
2284    // Process Trouble Message List
2285    if (!IsListEmpty(&usmList))
2286    {
2287       CurrentEntry = usmList.Flink;
2288       while (CurrentEntry != &usmList)
2289       {
2290          CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE, ListEntry);
2291          CurrentEntry = CurrentEntry->Flink;
2292 
2293          TRACE("Found troubled messages %p on the list\n",CurrentSentMessage);
2294 
2295          if ( pti == CurrentSentMessage->ptiReceiver )
2296          {
2297             if (CurrentSentMessage->HasPackedLParam)
2298             {
2299                if (CurrentSentMessage->Msg.lParam)
2300                   ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
2301             }
2302 
2303             /* free the message */
2304             FreeUserMessage(CurrentSentMessage);
2305          }
2306          else if ( pti == CurrentSentMessage->ptiSender ||
2307                    pti == CurrentSentMessage->ptiCallBackSender )
2308          {
2309             // Determine whether this message is being processed or not.
2310             if ((CurrentSentMessage->flags & (SMF_RECEIVERBUSY|SMF_RECEIVEDMESSAGE)) != SMF_RECEIVEDMESSAGE)
2311             {
2312                CurrentSentMessage->flags |= SMF_RECEIVERFREE;
2313             }
2314 
2315             if (!(CurrentSentMessage->flags & SMF_RECEIVERFREE))
2316             {
2317 
2318                if (CurrentSentMessage->HasPackedLParam)
2319                {
2320                   if (CurrentSentMessage->Msg.lParam)
2321                      ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
2322                }
2323 
2324                /* free the message */
2325                FreeUserMessage(CurrentSentMessage);
2326             }
2327          }
2328       }
2329    }
2330 }
2331 
2332 VOID FASTCALL
2333 MsqCleanupMessageQueue(PTHREADINFO pti)
2334 {
2335    PUSER_MESSAGE_QUEUE MessageQueue;
2336    PLIST_ENTRY CurrentEntry;
2337    PUSER_MESSAGE CurrentMessage;
2338 
2339    MessageQueue = pti->MessageQueue;
2340    MessageQueue->cThreads--;
2341 
2342    if (MessageQueue->cThreads)
2343    {
2344       if (MessageQueue->ptiSysLock == pti) MessageQueue->ptiSysLock = NULL;
2345    }
2346 
2347    if (MessageQueue->cThreads == 0) //// Fix a crash related to CORE-10471 testing.
2348    {
2349       /* cleanup posted messages */
2350       while (!IsListEmpty(&MessageQueue->HardwareMessagesListHead))
2351       {
2352          CurrentEntry = MessageQueue->HardwareMessagesListHead.Flink;
2353          CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, ListEntry);
2354          ERR("MQ Cleanup Post Messages %p\n",CurrentMessage);
2355          MsqDestroyMessage(CurrentMessage);
2356       }
2357    } ////
2358 
2359    if (MessageQueue->CursorObject)
2360    {
2361        PCURICON_OBJECT pCursor = MessageQueue->CursorObject;
2362 
2363        /* Change to another cursor if we going to dereference current one
2364           Note: we can't use UserSetCursor because it uses current thread
2365                 message queue instead of queue given for cleanup */
2366        if (IntGetSysCursorInfo()->CurrentCursorObject == pCursor)
2367        {
2368            HDC hdcScreen;
2369 
2370            /* Get the screen DC */
2371            hdcScreen = IntGetScreenDC();
2372            if (hdcScreen)
2373                GreMovePointer(hdcScreen, -1, -1);
2374            IntGetSysCursorInfo()->CurrentCursorObject = NULL;
2375        }
2376 
2377        TRACE("DereferenceObject pCursor\n");
2378        UserDereferenceObject(pCursor);
2379    }
2380 
2381    if (gpqForeground == MessageQueue)
2382    {
2383       IntSetFocusMessageQueue(NULL);
2384    }
2385    if (gpqForegroundPrev == MessageQueue)
2386    {
2387       gpqForegroundPrev = NULL;
2388    }
2389    if (gpqCursor == MessageQueue)
2390    {
2391       gpqCursor = NULL;
2392    }
2393 }
2394 
2395 PUSER_MESSAGE_QUEUE FASTCALL
2396 MsqCreateMessageQueue(PTHREADINFO pti)
2397 {
2398    PUSER_MESSAGE_QUEUE MessageQueue;
2399 
2400    MessageQueue = ExAllocatePoolWithTag(NonPagedPool,
2401                                         sizeof(*MessageQueue),
2402                                         USERTAG_Q);
2403 
2404    if (!MessageQueue)
2405    {
2406       return NULL;
2407    }
2408 
2409    RtlZeroMemory(MessageQueue, sizeof(*MessageQueue));
2410    /* hold at least one reference until it'll be destroyed */
2411    IntReferenceMessageQueue(MessageQueue);
2412    /* initialize the queue */
2413    if (!MsqInitializeMessageQueue(pti, MessageQueue))
2414    {
2415       IntDereferenceMessageQueue(MessageQueue);
2416       return NULL;
2417    }
2418 
2419    return MessageQueue;
2420 }
2421 
2422 VOID FASTCALL
2423 MsqDestroyMessageQueue(_In_ PTHREADINFO pti)
2424 {
2425    PDESKTOP desk;
2426    PUSER_MESSAGE_QUEUE MessageQueue = pti->MessageQueue;
2427 
2428    NT_ASSERT(MessageQueue != NULL);
2429    MessageQueue->QF_flags |= QF_INDESTROY;
2430 
2431    /* remove the message queue from any desktops */
2432    if ((desk = InterlockedExchangePointer((PVOID*)&MessageQueue->Desktop, 0)))
2433    {
2434       (void)InterlockedExchangePointer((PVOID*)&desk->ActiveMessageQueue, 0);
2435       IntDereferenceMessageQueue(MessageQueue);
2436    }
2437 
2438    /* clean it up */
2439    MsqCleanupMessageQueue(pti);
2440 
2441    /* decrease the reference counter, if it hits zero, the queue will be freed */
2442    _PRAGMA_WARNING_SUPPRESS(__WARNING_USING_UNINIT_VAR);
2443    IntDereferenceMessageQueue(MessageQueue);
2444 }
2445 
2446 LPARAM FASTCALL
2447 MsqSetMessageExtraInfo(LPARAM lParam)
2448 {
2449    LPARAM Ret;
2450    PTHREADINFO pti;
2451    PUSER_MESSAGE_QUEUE MessageQueue;
2452 
2453    pti = PsGetCurrentThreadWin32Thread();
2454    MessageQueue = pti->MessageQueue;
2455    if(!MessageQueue)
2456    {
2457       return 0;
2458    }
2459 
2460    Ret = MessageQueue->ExtraInfo;
2461    MessageQueue->ExtraInfo = lParam;
2462 
2463    return Ret;
2464 }
2465 
2466 LPARAM FASTCALL
2467 MsqGetMessageExtraInfo(VOID)
2468 {
2469    PTHREADINFO pti;
2470    PUSER_MESSAGE_QUEUE MessageQueue;
2471 
2472    pti = PsGetCurrentThreadWin32Thread();
2473    MessageQueue = pti->MessageQueue;
2474    if(!MessageQueue)
2475    {
2476       return 0;
2477    }
2478 
2479    return MessageQueue->ExtraInfo;
2480 }
2481 
2482 // ReplyMessage is called by the thread receiving the window message.
2483 BOOL FASTCALL
2484 co_MsqReplyMessage( LRESULT lResult )
2485 {
2486    PUSER_SENT_MESSAGE Message;
2487    PTHREADINFO pti;
2488 
2489    pti = PsGetCurrentThreadWin32Thread();
2490    Message = pti->pusmCurrent;
2491 
2492    if (!Message) return FALSE;
2493 
2494    if (Message->QS_Flags & QS_SMRESULT) return FALSE;
2495 
2496    //     SendMessageXxx  || Callback msg and not a notify msg
2497    if (Message->ptiSender || Message->CompletionCallback)
2498    {
2499       Message->lResult = lResult;
2500       Message->QS_Flags |= QS_SMRESULT;
2501    // See co_MsqDispatchOneSentMessage, change bits already accounted for and cleared and this msg is going away..
2502    }
2503    return TRUE;
2504 }
2505 
2506 HWND FASTCALL
2507 MsqSetStateWindow(PTHREADINFO pti, ULONG Type, HWND hWnd)
2508 {
2509    HWND Prev;
2510    PUSER_MESSAGE_QUEUE MessageQueue;
2511 
2512    MessageQueue = pti->MessageQueue;
2513 
2514    switch(Type)
2515    {
2516       case MSQ_STATE_CAPTURE:
2517          Prev = MessageQueue->spwndCapture ? UserHMGetHandle(MessageQueue->spwndCapture) : 0;
2518          MessageQueue->spwndCapture = ValidateHwndNoErr(hWnd);
2519          return Prev;
2520       case MSQ_STATE_ACTIVE:
2521          Prev = MessageQueue->spwndActive ? UserHMGetHandle(MessageQueue->spwndActive) : 0;
2522          MessageQueue->spwndActive = ValidateHwndNoErr(hWnd);
2523          return Prev;
2524       case MSQ_STATE_FOCUS:
2525          Prev = MessageQueue->spwndFocus ? UserHMGetHandle(MessageQueue->spwndFocus) : 0;
2526          MessageQueue->spwndFocus = ValidateHwndNoErr(hWnd);
2527          return Prev;
2528       case MSQ_STATE_MENUOWNER:
2529          Prev = MessageQueue->MenuOwner;
2530          MessageQueue->MenuOwner = hWnd;
2531          return Prev;
2532       case MSQ_STATE_MOVESIZE:
2533          Prev = MessageQueue->MoveSize;
2534          MessageQueue->MoveSize = hWnd;
2535          return Prev;
2536       case MSQ_STATE_CARET:
2537          Prev = MessageQueue->CaretInfo.hWnd;
2538          MessageQueue->CaretInfo.hWnd = hWnd;
2539          return Prev;
2540    }
2541 
2542    return NULL;
2543 }
2544 
2545 VOID FASTCALL
2546 MsqReleaseModifierKeys(PUSER_MESSAGE_QUEUE MessageQueue)
2547 {
2548     WORD ModifierKeys[] = { VK_LCONTROL, VK_RCONTROL, VK_CONTROL,
2549                             VK_LMENU,    VK_RMENU,    VK_MENU,
2550                             VK_LSHIFT,   VK_RSHIFT,   VK_SHIFT };
2551     UINT i;
2552 
2553     for (i = 0; i < _countof(ModifierKeys); ++i)
2554     {
2555         if (IS_KEY_DOWN(MessageQueue->afKeyState, ModifierKeys[i]))
2556             SET_KEY_DOWN(MessageQueue->afKeyState, ModifierKeys[i], FALSE);
2557     }
2558 }
2559 
2560 SHORT
2561 APIENTRY
2562 NtUserGetKeyState(INT key)
2563 {
2564    DWORD Ret;
2565 
2566    UserEnterShared();
2567 
2568    Ret = UserGetKeyState(key);
2569 
2570    UserLeave();
2571 
2572    return (SHORT)Ret;
2573 }
2574 
2575 
2576 DWORD
2577 APIENTRY
2578 NtUserGetKeyboardState(LPBYTE lpKeyState)
2579 {
2580    DWORD i, ret = TRUE;
2581    PTHREADINFO pti;
2582    PUSER_MESSAGE_QUEUE MessageQueue;
2583 
2584    UserEnterShared();
2585 
2586    pti = PsGetCurrentThreadWin32Thread();
2587    MessageQueue = pti->MessageQueue;
2588 
2589    _SEH2_TRY
2590    {
2591        /* Probe and copy key state to an array */
2592        ProbeForWrite(lpKeyState, 256 * sizeof(BYTE), 1);
2593        for (i = 0; i < 256; ++i)
2594        {
2595            lpKeyState[i] = 0;
2596            if (IS_KEY_DOWN(MessageQueue->afKeyState, i))
2597                lpKeyState[i] |= KS_DOWN_BIT;
2598            if (IS_KEY_LOCKED(MessageQueue->afKeyState, i))
2599                lpKeyState[i] |= KS_LOCK_BIT;
2600        }
2601    }
2602    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2603    {
2604        SetLastNtError(_SEH2_GetExceptionCode());
2605        ret = FALSE;
2606    }
2607    _SEH2_END;
2608 
2609    UserLeave();
2610 
2611    return ret;
2612 }
2613 
2614 BOOL
2615 APIENTRY
2616 NtUserSetKeyboardState(LPBYTE pKeyState)
2617 {
2618    UINT i;
2619    BOOL bRet = TRUE;
2620    PTHREADINFO pti;
2621    PUSER_MESSAGE_QUEUE MessageQueue;
2622 
2623    UserEnterExclusive();
2624 
2625    pti = PsGetCurrentThreadWin32Thread();
2626    MessageQueue = pti->MessageQueue;
2627 
2628    _SEH2_TRY
2629    {
2630        ProbeForRead(pKeyState, 256 * sizeof(BYTE), 1);
2631        for (i = 0; i < 256; ++i)
2632        {
2633             SET_KEY_DOWN(MessageQueue->afKeyState, i, pKeyState[i] & KS_DOWN_BIT);
2634             SET_KEY_LOCKED(MessageQueue->afKeyState, i, pKeyState[i] & KS_LOCK_BIT);
2635        }
2636    }
2637    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2638    {
2639        SetLastNtError(_SEH2_GetExceptionCode());
2640        bRet = FALSE;
2641    }
2642    _SEH2_END;
2643 
2644    UserLeave();
2645 
2646    return bRet;
2647 }
2648 
2649 /* EOF */
2650