xref: /reactos/win32ss/user/ntuser/msgqueue.c (revision c948ea85)
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     MessageQueue = pti->MessageQueue;
1491     CurInfo = IntGetSysCursorInfo();
1492     pwndMsg = ValidateHwndNoErr(msg->hwnd);
1493     clk_msg = MessageQueue->msgDblClk;
1494     pDesk = pwndDesktop->head.rpdesk;
1495 
1496     /* find the window to dispatch this mouse message to */
1497     if (MessageQueue->spwndCapture)
1498     {
1499         hittest = HTCLIENT;
1500         pwndMsg = MessageQueue->spwndCapture;
1501     }
1502     else
1503     {
1504         /*
1505            Start with null window. See wine win.c:test_mouse_input:WM_COMMAND tests.
1506         */
1507         pwndMsg = co_WinPosWindowFromPoint( NULL, &msg->pt, &hittest, FALSE);
1508     }
1509 
1510     TRACE("Got mouse message for %p, hittest: 0x%x\n", msg->hwnd, hittest);
1511 
1512     // Null window or not the same "Hardware" message queue.
1513     if (pwndMsg == NULL || pwndMsg->head.pti->MessageQueue != MessageQueue)
1514     {
1515         // Crossing a boundary, so set cursor. See default message queue cursor.
1516         IntSystemSetCursor(SYSTEMCUR(ARROW));
1517         /* Remove and ignore the message */
1518         *RemoveMessages = TRUE;
1519         return FALSE;
1520     }
1521 
1522     // Check to see if this is attached,
1523     if ( pwndMsg->head.pti != pti &&  // window thread is not current,
1524          MessageQueue->cThreads > 1 ) // and is attached...
1525     {
1526         // This is not for us and we should leave so the other thread can check for messages!!!
1527         *NotForUs = TRUE;
1528         *RemoveMessages = FALSE;
1529         return FALSE;
1530     }
1531 
1532     if ( MessageQueue == gpqCursor ) // Cursor must use the same Queue!
1533     {
1534        IntTrackMouseMove(pwndMsg, pDesk, msg, hittest);
1535     }
1536     else
1537     {
1538        WARN("Not the same cursor!\n");
1539     }
1540 
1541     msg->hwnd = UserHMGetHandle(pwndMsg);
1542 
1543     pt = msg->pt;
1544     message = msg->message;
1545 
1546     /* Note: windows has no concept of a non-client wheel message */
1547     if (message != WM_MOUSEWHEEL)
1548     {
1549         if (hittest != HTCLIENT)
1550         {
1551             message += WM_NCMOUSEMOVE - WM_MOUSEMOVE;
1552             msg->wParam = hittest; // Caution! This might break wParam check in DblClk.
1553         }
1554         else
1555         {
1556             /* coordinates don't get translated while tracking a menu */
1557             /* FIXME: should differentiate popups and top-level menus */
1558             if (!(MessageQueue->MenuOwner))
1559             {
1560                 pt.x += pwndDesktop->rcClient.left - pwndMsg->rcClient.left;
1561                 pt.y += pwndDesktop->rcClient.top - pwndMsg->rcClient.top;
1562             }
1563         }
1564     }
1565     msg->lParam = MAKELONG( pt.x, pt.y );
1566 
1567     /* translate double-clicks */
1568 
1569     if ((msg->message == WM_LBUTTONDOWN) ||
1570         (msg->message == WM_RBUTTONDOWN) ||
1571         (msg->message == WM_MBUTTONDOWN) ||
1572         (msg->message == WM_XBUTTONDOWN))
1573     {
1574         BOOL update = *RemoveMessages;
1575 
1576         /* translate double-clicks -
1577          * note that ...MOUSEMOVEs can slip in between
1578          * ...BUTTONDOWN and ...BUTTONDBLCLK messages */
1579 
1580         if ((MessageQueue->MenuOwner || MessageQueue->MoveSize) ||
1581             hittest != HTCLIENT ||
1582             (pwndMsg->pcls->style & CS_DBLCLKS))
1583         {
1584            if ((msg->message == clk_msg.message) &&
1585                (msg->hwnd == clk_msg.hwnd) &&
1586                // Only worry about XButton wParam.
1587                (msg->message != WM_XBUTTONDOWN || GET_XBUTTON_WPARAM(msg->wParam) == GET_XBUTTON_WPARAM(clk_msg.wParam)) &&
1588                ((msg->time - clk_msg.time) < (ULONG)gspv.iDblClickTime) &&
1589                (abs(msg->pt.x - clk_msg.pt.x) < UserGetSystemMetrics(SM_CXDOUBLECLK)/2) &&
1590                (abs(msg->pt.y - clk_msg.pt.y) < UserGetSystemMetrics(SM_CYDOUBLECLK)/2))
1591            {
1592                message += (WM_LBUTTONDBLCLK - WM_LBUTTONDOWN);
1593                if (update)
1594                {
1595                    MessageQueue->msgDblClk.message = 0;  /* clear the double-click conditions */
1596                    update = FALSE;
1597                }
1598            }
1599         }
1600 
1601         if (!((first ==  0 && last == 0) || (message >= first || message <= last)))
1602         {
1603             TRACE("Message out of range!!!\n");
1604             return FALSE;
1605         }
1606 
1607         /* update static double-click conditions */
1608         if (update) MessageQueue->msgDblClk = *msg;
1609     }
1610     else
1611     {
1612         if (!((first ==  0 && last == 0) || (message >= first || message <= last)))
1613         {
1614             TRACE("Message out of range!!!\n");
1615             return FALSE;
1616         }
1617 
1618         // Update mouse move down keys.
1619         if (message == WM_MOUSEMOVE)
1620         {
1621            msg->wParam = MsqGetDownKeyState(MessageQueue);
1622         }
1623     }
1624 
1625     if (gspv.bMouseClickLock)
1626     {
1627         BOOL IsClkLck = FALSE;
1628 
1629         if(msg->message == WM_LBUTTONUP)
1630         {
1631             IsClkLck = ((msg->time - CurInfo->ClickLockTime) >= gspv.dwMouseClickLockTime);
1632             if (IsClkLck && (!CurInfo->ClickLockActive))
1633             {
1634                 CurInfo->ClickLockActive = TRUE;
1635             }
1636         }
1637         else if (msg->message == WM_LBUTTONDOWN)
1638         {
1639             if (CurInfo->ClickLockActive)
1640             {
1641                 IsClkLck = TRUE;
1642                 CurInfo->ClickLockActive = FALSE;
1643             }
1644 
1645             CurInfo->ClickLockTime = msg->time;
1646         }
1647 
1648         if(IsClkLck)
1649         {
1650             /* Remove and ignore the message */
1651             *RemoveMessages = TRUE;
1652             TRACE("Remove and ignore the message\n");
1653             return FALSE;
1654         }
1655     }
1656 
1657     if (pti->TIF_flags & TIF_MSGPOSCHANGED)
1658     {
1659         pti->TIF_flags &= ~TIF_MSGPOSCHANGED;
1660         IntNotifyWinEvent(EVENT_OBJECT_LOCATIONCHANGE, NULL, OBJID_CLIENT, CHILDID_SELF, 0);
1661     }
1662 
1663     /* message is accepted now (but still get dropped) */
1664 
1665     event.message = msg->message;
1666     event.time    = msg->time;
1667     event.hwnd    = msg->hwnd;
1668     event.paramL  = msg->pt.x;
1669     event.paramH  = msg->pt.y;
1670     co_HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&event );
1671 
1672     hook.pt           = msg->pt;
1673     hook.hwnd         = msg->hwnd;
1674     hook.wHitTestCode = hittest;
1675     hook.dwExtraInfo  = ExtraInfo;
1676     if (co_HOOK_CallHooks( WH_MOUSE, *RemoveMessages ? HC_ACTION : HC_NOREMOVE,
1677                         message, (LPARAM)&hook ))
1678     {
1679         hook.pt           = msg->pt;
1680         hook.hwnd         = msg->hwnd;
1681         hook.wHitTestCode = hittest;
1682         hook.dwExtraInfo  = ExtraInfo;
1683         co_HOOK_CallHooks( WH_CBT, HCBT_CLICKSKIPPED, message, (LPARAM)&hook );
1684 
1685         ERR("WH_MOUSE dropped mouse message!\n");
1686 
1687         /* Remove and skip message */
1688         *RemoveMessages = TRUE;
1689         return FALSE;
1690     }
1691 
1692     if ((hittest == (USHORT)HTERROR) || (hittest == (USHORT)HTNOWHERE))
1693     {
1694         co_IntSendMessage( msg->hwnd, WM_SETCURSOR, (WPARAM)msg->hwnd, MAKELONG( hittest, msg->message ));
1695 
1696         /* Remove and skip message */
1697         *RemoveMessages = TRUE;
1698         return FALSE;
1699     }
1700 
1701     if ((*RemoveMessages == FALSE) || MessageQueue->spwndCapture)
1702     {
1703         /* Accept the message */
1704         msg->message = message;
1705         return TRUE;
1706     }
1707 
1708     if ((msg->message == WM_LBUTTONDOWN) ||
1709         (msg->message == WM_RBUTTONDOWN) ||
1710         (msg->message == WM_MBUTTONDOWN) ||
1711         (msg->message == WM_XBUTTONDOWN))
1712     {
1713         /* Send the WM_PARENTNOTIFY,
1714          * note that even for double/nonclient clicks
1715          * notification message is still WM_L/M/RBUTTONDOWN.
1716          */
1717         MsqSendParentNotify(pwndMsg, msg->message, 0, msg->pt );
1718 
1719         /* Activate the window if needed */
1720 
1721         if (pwndMsg != MessageQueue->spwndActive)
1722         {
1723             PWND pwndTop = pwndMsg;
1724             pwndTop = IntGetNonChildAncestor(pwndTop);
1725 
1726             TRACE("Mouse pti %p pwndMsg pti %p pwndTop pti %p\n",MessageQueue->ptiMouse,pwndMsg->head.pti,pwndTop->head.pti);
1727 
1728             if (pwndTop && pwndTop != pwndDesktop)
1729             {
1730                 LONG ret = co_IntSendMessage( msg->hwnd,
1731                                               WM_MOUSEACTIVATE,
1732                                               (WPARAM)UserHMGetHandle(pwndTop),
1733                                               MAKELONG( hittest, msg->message));
1734                 switch(ret)
1735                 {
1736                 case MA_NOACTIVATEANDEAT:
1737                     eatMsg = TRUE;
1738                     /* fall through */
1739                 case MA_NOACTIVATE:
1740                     break;
1741                 case MA_ACTIVATEANDEAT:
1742                     eatMsg = TRUE;
1743                     /* fall through */
1744                 case MA_ACTIVATE:
1745                 case 0:
1746                     if (!co_IntMouseActivateWindow( pwndTop )) eatMsg = TRUE;
1747                     break;
1748                 default:
1749                     ERR( "unknown WM_MOUSEACTIVATE code %d\n", ret );
1750                     break;
1751                 }
1752             }
1753         }
1754     }
1755 
1756     /* send the WM_SETCURSOR message */
1757 
1758     /* Windows sends the normal mouse message as the message parameter
1759        in the WM_SETCURSOR message even if it's non-client mouse message */
1760     co_IntSendMessage( msg->hwnd, WM_SETCURSOR, (WPARAM)msg->hwnd, MAKELONG( hittest, msg->message ));
1761 
1762     msg->message = message;
1763     return !eatMsg;
1764 }
1765 
1766 BOOL co_IntProcessKeyboardMessage(MSG* Msg, BOOL* RemoveMessages)
1767 {
1768     EVENTMSG Event;
1769     USER_REFERENCE_ENTRY Ref;
1770     PWND pWnd;
1771     UINT ImmRet;
1772     BOOL Ret = TRUE, bKeyUpDown = FALSE;
1773     PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
1774     const UINT uMsg = Msg->message;
1775 
1776     if (uMsg == VK_PACKET)
1777         pti->wchInjected = HIWORD(Msg->wParam);
1778 
1779     if (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN || uMsg == WM_KEYUP || uMsg == WM_SYSKEYUP)
1780     {
1781         bKeyUpDown = TRUE;
1782         switch (Msg->wParam)
1783         {
1784             case VK_LSHIFT: case VK_RSHIFT:
1785                 Msg->wParam = VK_SHIFT;
1786                 break;
1787             case VK_LCONTROL: case VK_RCONTROL:
1788                 Msg->wParam = VK_CONTROL;
1789                 break;
1790             case VK_LMENU: case VK_RMENU:
1791                 Msg->wParam = VK_MENU;
1792                 break;
1793         }
1794     }
1795 
1796     pWnd = ValidateHwndNoErr(Msg->hwnd);
1797     if (pWnd) UserRefObjectCo(pWnd, &Ref);
1798 
1799     Event.message = uMsg;
1800     Event.hwnd    = Msg->hwnd;
1801     Event.time    = Msg->time;
1802     Event.paramL  = (Msg->wParam & 0xFF) | (HIWORD(Msg->lParam) << 8);
1803     Event.paramH  = Msg->lParam & 0x7FFF;
1804     if (HIWORD(Msg->lParam) & 0x0100) Event.paramH |= 0x8000;
1805     co_HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&Event);
1806 
1807     if (*RemoveMessages)
1808     {
1809         if ((uMsg == WM_KEYDOWN) &&
1810             (Msg->hwnd != IntGetDesktopWindow()))
1811         {
1812             /* Handle F1 key by sending out WM_HELP message */
1813             if (Msg->wParam == VK_F1)
1814             {
1815                 UserPostMessage( Msg->hwnd, WM_KEYF1, 0, 0 );
1816             }
1817             else if (Msg->wParam >= VK_BROWSER_BACK &&
1818                      Msg->wParam <= VK_LAUNCH_APP2)
1819             {
1820                 /* FIXME: Process keystate */
1821                 co_IntSendMessage(Msg->hwnd, WM_APPCOMMAND, (WPARAM)Msg->hwnd, MAKELPARAM(0, (FAPPCOMMAND_KEY | (Msg->wParam - VK_BROWSER_BACK + 1))));
1822             }
1823         }
1824         else if (uMsg == WM_KEYUP)
1825         {
1826             /* Handle VK_APPS key by posting a WM_CONTEXTMENU message */
1827             if (Msg->wParam == VK_APPS && pti->MessageQueue->MenuOwner == NULL)
1828                 UserPostMessage( Msg->hwnd, WM_CONTEXTMENU, (WPARAM)Msg->hwnd, -1 );
1829         }
1830     }
1831 
1832     //// Key Down!
1833     if (*RemoveMessages && uMsg == WM_SYSKEYDOWN)
1834     {
1835         if ( HIWORD(Msg->lParam) & KF_ALTDOWN )
1836         {
1837             if ( Msg->wParam == VK_ESCAPE || Msg->wParam == VK_TAB ) // Alt-Tab/ESC Alt-Shift-Tab/ESC
1838             {
1839                 WPARAM wParamTmp;
1840 
1841                 wParamTmp = UserGetKeyState(VK_SHIFT) & 0x8000 ? SC_PREVWINDOW : SC_NEXTWINDOW;
1842                 TRACE("Send WM_SYSCOMMAND Alt-Tab/ESC Alt-Shift-Tab/ESC\n");
1843                 co_IntSendMessage( Msg->hwnd, WM_SYSCOMMAND, wParamTmp, Msg->wParam );
1844 
1845                 //// Keep looping.
1846                 Ret = FALSE;
1847                 //// Skip the rest.
1848                 goto Exit;
1849             }
1850         }
1851     }
1852 
1853     if (co_HOOK_CallHooks( WH_KEYBOARD,
1854                           *RemoveMessages ? HC_ACTION : HC_NOREMOVE,
1855                            LOWORD(Msg->wParam),
1856                            Msg->lParam))
1857     {
1858         /* skip this message */
1859         co_HOOK_CallHooks( WH_CBT,
1860                            HCBT_KEYSKIPPED,
1861                            LOWORD(Msg->wParam),
1862                            Msg->lParam );
1863 
1864         ERR("KeyboardMessage WH_KEYBOARD Call Hook return!\n");
1865 
1866         *RemoveMessages = TRUE;
1867 
1868         Ret = FALSE;
1869     }
1870 
1871     if (pWnd && Ret && *RemoveMessages && bKeyUpDown && !(pti->TIF_flags & TIF_DISABLEIME))
1872     {
1873         ImmRet = IntImmProcessKey(pti->MessageQueue, pWnd, uMsg, Msg->wParam, Msg->lParam);
1874         if (ImmRet)
1875         {
1876             if ( ImmRet & (IPHK_HOTKEY|IPHK_SKIPTHISKEY) )
1877             {
1878                ImmRet = 0;
1879             }
1880             if ( ImmRet & IPHK_PROCESSBYIME )
1881             {
1882                Msg->wParam = VK_PROCESSKEY;
1883             }
1884         }
1885     }
1886 Exit:
1887     if (pWnd) UserDerefObjectCo(pWnd);
1888     return Ret;
1889 }
1890 
1891 BOOL co_IntProcessHardwareMessage(MSG* Msg, BOOL* RemoveMessages, BOOL* NotForUs, LONG_PTR ExtraInfo, UINT first, UINT last)
1892 {
1893     if ( IS_MOUSE_MESSAGE(Msg->message))
1894     {
1895         return co_IntProcessMouseMessage(Msg, RemoveMessages, NotForUs, ExtraInfo, first, last);
1896     }
1897     else if ( IS_KBD_MESSAGE(Msg->message))
1898     {
1899         return co_IntProcessKeyboardMessage(Msg, RemoveMessages);
1900     }
1901 
1902     return TRUE;
1903 }
1904 
1905 /* check whether a message filter contains at least one potential hardware message */
1906 static INT FASTCALL
1907 filter_contains_hw_range( UINT first, UINT last )
1908 {
1909    /* hardware message ranges are (in numerical order):
1910     *   WM_NCMOUSEFIRST .. WM_NCMOUSELAST
1911     *   WM_KEYFIRST .. WM_KEYLAST
1912     *   WM_MOUSEFIRST .. WM_MOUSELAST
1913     */
1914     if (!last) --last;
1915     if (last < WM_NCMOUSEFIRST) return 0;
1916     if (first > WM_NCMOUSELAST && last < WM_KEYFIRST) return 0;
1917     if (first > WM_KEYLAST && last < WM_MOUSEFIRST) return 0;
1918     if (first > WM_MOUSELAST) return 0;
1919     return 1;
1920 }
1921 
1922 /* check whether message is in the range of mouse messages */
1923 static inline BOOL is_mouse_message( UINT message )
1924 {
1925     return ( //( message >= WM_NCMOUSEFIRST && message <= WM_NCMOUSELAST )   || This seems to break tests...
1926              ( message >= WM_MOUSEFIRST   && message <= WM_MOUSELAST )     ||
1927              ( message >= WM_XBUTTONDOWN  && message <= WM_XBUTTONDBLCLK ) ||
1928              ( message >= WM_MBUTTONDOWN  && message <= WM_MBUTTONDBLCLK ) ||
1929              ( message >= WM_LBUTTONDOWN  && message <= WM_RBUTTONDBLCLK ) );
1930 }
1931 
1932 BOOL APIENTRY
1933 co_MsqPeekHardwareMessage(IN PTHREADINFO pti,
1934                          IN BOOL Remove,
1935                          IN PWND Window,
1936                          IN UINT MsgFilterLow,
1937                          IN UINT MsgFilterHigh,
1938                          IN UINT QSflags,
1939                          OUT MSG* pMsg)
1940 {
1941    BOOL AcceptMessage, NotForUs;
1942    PUSER_MESSAGE CurrentMessage;
1943    PLIST_ENTRY ListHead;
1944    MSG msg;
1945    ULONG_PTR idSave;
1946    DWORD QS_Flags;
1947    LONG_PTR ExtraInfo;
1948    MSG clk_msg;
1949    BOOL Ret = FALSE;
1950    PUSER_MESSAGE_QUEUE MessageQueue = pti->MessageQueue;
1951 
1952    if (!filter_contains_hw_range( MsgFilterLow, MsgFilterHigh )) return FALSE;
1953 
1954    ListHead = MessageQueue->HardwareMessagesListHead.Flink;
1955 
1956    if (IsListEmpty(ListHead)) return FALSE;
1957 
1958    if (!MessageQueue->ptiSysLock)
1959    {
1960       MessageQueue->ptiSysLock = pti;
1961       pti->pcti->CTI_flags |= CTI_THREADSYSLOCK;
1962    }
1963 
1964    if (MessageQueue->ptiSysLock != pti)
1965    {
1966       ERR("Thread Q is locked to ptiSysLock 0x%p pti 0x%p\n",MessageQueue->ptiSysLock,pti);
1967       return FALSE;
1968    }
1969 
1970    while (ListHead != &MessageQueue->HardwareMessagesListHead)
1971    {
1972       CurrentMessage = CONTAINING_RECORD(ListHead, USER_MESSAGE, ListEntry);
1973       ListHead = ListHead->Flink;
1974 
1975       if (MessageQueue->idSysPeek == (ULONG_PTR)CurrentMessage)
1976       {
1977          TRACE("Skip this message due to it is in play!\n");
1978          continue;
1979       }
1980 /*
1981  MSDN:
1982  1: any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL.
1983  2: retrieves only messages on the current thread's message queue whose hwnd value is NULL.
1984  3: handle to the window whose messages are to be retrieved.
1985  */
1986       if ( ( !Window || // 1
1987             ( Window == PWND_BOTTOM && CurrentMessage->Msg.hwnd == NULL ) || // 2
1988             ( Window != PWND_BOTTOM && UserHMGetHandle(Window) == CurrentMessage->Msg.hwnd ) || // 3
1989             ( is_mouse_message(CurrentMessage->Msg.message) ) ) && // Null window for anything mouse.
1990             ( CurrentMessage->QS_Flags & QSflags ) )
1991       {
1992          idSave = MessageQueue->idSysPeek;
1993          MessageQueue->idSysPeek = (ULONG_PTR)CurrentMessage;
1994 
1995          msg = CurrentMessage->Msg;
1996          ExtraInfo = CurrentMessage->ExtraInfo;
1997          QS_Flags = CurrentMessage->QS_Flags;
1998          clk_msg = MessageQueue->msgDblClk;
1999 
2000          NotForUs = FALSE;
2001 
2002          UpdateKeyStateFromMsg(MessageQueue, &msg);
2003          AcceptMessage = co_IntProcessHardwareMessage(&msg, &Remove, &NotForUs, ExtraInfo, MsgFilterLow, MsgFilterHigh);
2004 
2005          if (!NotForUs && (MsgFilterLow != 0 || MsgFilterHigh != 0))
2006          {
2007              /* Don't return message if not in range */
2008              if (msg.message < MsgFilterLow || msg.message > MsgFilterHigh)
2009              {
2010                  MessageQueue->msgDblClk = clk_msg;
2011                  MessageQueue->idSysPeek = idSave;
2012                  continue;
2013              }
2014          }
2015 
2016          if (Remove)
2017          {
2018              if (CurrentMessage->pti != NULL && (MessageQueue->idSysPeek == (ULONG_PTR)CurrentMessage))
2019              {
2020                 MsqDestroyMessage(CurrentMessage);
2021              }
2022              ClearMsgBitsMask(pti, QS_Flags);
2023          }
2024 
2025          MessageQueue->idSysPeek = idSave;
2026 
2027          if (NotForUs)
2028          {
2029             Ret = FALSE;
2030             break;
2031          }
2032 
2033          if (AcceptMessage)
2034          {
2035             *pMsg = msg;
2036             // Fix all but one wine win:test_GetMessagePos WM_TIMER tests. See PostTimerMessages.
2037             if (!RtlEqualMemory(&pti->ptLast, &msg.pt, sizeof(POINT)))
2038             {
2039                pti->TIF_flags |= TIF_MSGPOSCHANGED;
2040             }
2041             pti->timeLast = msg.time;
2042             pti->ptLast   = msg.pt;
2043             MessageQueue->ExtraInfo = ExtraInfo;
2044             Ret = TRUE;
2045             break;
2046          }
2047       }
2048    }
2049 
2050    MessageQueue->ptiSysLock = NULL;
2051    pti->pcti->CTI_flags &= ~CTI_THREADSYSLOCK;
2052    return Ret;
2053 }
2054 
2055 BOOLEAN APIENTRY
2056 MsqPeekMessage(IN PTHREADINFO pti,
2057                   IN BOOLEAN Remove,
2058                   IN PWND Window,
2059                   IN UINT MsgFilterLow,
2060                   IN UINT MsgFilterHigh,
2061                   IN UINT QSflags,
2062                   OUT LONG_PTR *ExtraInfo,
2063                   OUT DWORD *dwQEvent,
2064                   OUT PMSG Message)
2065 {
2066    PUSER_MESSAGE CurrentMessage;
2067    PLIST_ENTRY ListHead;
2068    DWORD QS_Flags;
2069    BOOL Ret = FALSE;
2070 
2071    ListHead = pti->PostedMessagesListHead.Flink;
2072 
2073    if (IsListEmpty(ListHead)) return FALSE;
2074 
2075    while(ListHead != &pti->PostedMessagesListHead)
2076    {
2077       CurrentMessage = CONTAINING_RECORD(ListHead, USER_MESSAGE, ListEntry);
2078       ListHead = ListHead->Flink;
2079 /*
2080  MSDN:
2081  1: any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL.
2082  2: retrieves only messages on the current thread's message queue whose hwnd value is NULL.
2083  3: handle to the window whose messages are to be retrieved.
2084  */
2085       if ( ( !Window || // 1
2086             ( Window == PWND_BOTTOM && CurrentMessage->Msg.hwnd == NULL ) || // 2
2087             ( Window != PWND_BOTTOM && UserHMGetHandle(Window) == CurrentMessage->Msg.hwnd ) ) && // 3
2088             ( ( ( MsgFilterLow == 0 && MsgFilterHigh == 0 ) && CurrentMessage->QS_Flags & QSflags ) ||
2089               ( MsgFilterLow <= CurrentMessage->Msg.message && MsgFilterHigh >= CurrentMessage->Msg.message ) ) )
2090       {
2091          *Message   = CurrentMessage->Msg;
2092          *ExtraInfo = CurrentMessage->ExtraInfo;
2093          QS_Flags   = CurrentMessage->QS_Flags;
2094          if (dwQEvent) *dwQEvent = CurrentMessage->dwQEvent;
2095 
2096          if (Remove)
2097          {
2098              if (CurrentMessage->pti != NULL)
2099              {
2100                 MsqDestroyMessage(CurrentMessage);
2101              }
2102              ClearMsgBitsMask(pti, QS_Flags);
2103          }
2104          Ret = TRUE;
2105          break;
2106       }
2107    }
2108 
2109    return Ret;
2110 }
2111 
2112 NTSTATUS FASTCALL
2113 co_MsqWaitForNewMessages(PTHREADINFO pti, PWND WndFilter,
2114                          UINT MsgFilterMin, UINT MsgFilterMax)
2115 {
2116    NTSTATUS ret = STATUS_SUCCESS;
2117 
2118    // Post mouse moves before waiting for messages.
2119    if (pti->MessageQueue->QF_flags & QF_MOUSEMOVED)
2120    {
2121       IntCoalesceMouseMove(pti);
2122    }
2123 
2124    UserLeaveCo();
2125 
2126    ZwYieldExecution(); // Let someone else run!
2127 
2128    ret = KeWaitForSingleObject( pti->pEventQueueServer,
2129                                 UserRequest,
2130                                 UserMode,
2131                                 FALSE,
2132                                 NULL );
2133    UserEnterCo();
2134    if ( ret == STATUS_USER_APC )
2135    {
2136       TRACE("MWFNW User APC\n");
2137       co_IntDeliverUserAPC();
2138    }
2139    return ret;
2140 }
2141 
2142 BOOL FASTCALL
2143 MsqIsHung(PTHREADINFO pti, DWORD TimeOut)
2144 {
2145     DWORD dwTimeStamp = EngGetTickCount32();
2146     if (dwTimeStamp - pti->pcti->timeLastRead > TimeOut &&
2147        !(pti->pcti->fsWakeMask & QS_INPUT) &&
2148        !PsGetThreadFreezeCount(pti->pEThread) &&
2149        !(pti->ppi->W32PF_flags & W32PF_APPSTARTING))
2150     {
2151         TRACE("\nMsqIsHung(pti %p, TimeOut %lu)\n"
2152             "pEThread %p, ThreadsProcess %p, ImageFileName '%s'\n"
2153             "dwTimeStamp = %lu\n"
2154             "pti->pcti->timeLastRead = %lu\n"
2155             "pti->timeLast = %lu\n"
2156             "PsGetThreadFreezeCount(pti->pEThread) = %lu\n",
2157             pti, TimeOut,
2158             pti->pEThread,
2159             pti->pEThread ? pti->pEThread->ThreadsProcess : NULL,
2160             (pti->pEThread && pti->pEThread->ThreadsProcess)
2161                 ? pti->pEThread->ThreadsProcess->ImageFileName : "(None)",
2162             dwTimeStamp,
2163             pti->pcti->timeLastRead,
2164             pti->timeLast,
2165             PsGetThreadFreezeCount(pti->pEThread));
2166 
2167         return TRUE;
2168     }
2169 
2170    return FALSE;
2171 }
2172 
2173 BOOL FASTCALL
2174 IsThreadSuspended(PTHREADINFO pti)
2175 {
2176    if (pti->pEThread)
2177    {
2178       BOOL Ret = TRUE;
2179       if (!(pti->pEThread->Tcb.SuspendCount) && !PsGetThreadFreezeCount(pti->pEThread)) Ret = FALSE;
2180       return Ret;
2181    }
2182    return FALSE;
2183 }
2184 
2185 VOID
2186 CALLBACK
2187 HungAppSysTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
2188 {
2189    DoTheScreenSaver();
2190    TRACE("HungAppSysTimerProc\n");
2191    // Process list of windows that are hung and waiting.
2192 }
2193 
2194 BOOLEAN FASTCALL
2195 MsqInitializeMessageQueue(PTHREADINFO pti, PUSER_MESSAGE_QUEUE MessageQueue)
2196 {
2197    InitializeListHead(&MessageQueue->HardwareMessagesListHead); // Keep here!
2198    MessageQueue->spwndFocus = NULL;
2199    MessageQueue->iCursorLevel = 0;
2200    MessageQueue->CursorObject = SYSTEMCUR(WAIT); // See test_initial_cursor.
2201    if (MessageQueue->CursorObject)
2202    {
2203       TRACE("Default cursor hcur %p\n",UserHMGetHandle(MessageQueue->CursorObject));
2204       UserReferenceObject(MessageQueue->CursorObject);
2205    }
2206    RtlCopyMemory(MessageQueue->afKeyState, gafAsyncKeyState, sizeof(gafAsyncKeyState));
2207    MessageQueue->ptiMouse = pti;
2208    MessageQueue->ptiKeyboard = pti;
2209    MessageQueue->cThreads++;
2210 
2211    return TRUE;
2212 }
2213 
2214 VOID FASTCALL
2215 MsqCleanupThreadMsgs(PTHREADINFO pti)
2216 {
2217    PLIST_ENTRY CurrentEntry;
2218    PUSER_MESSAGE CurrentMessage;
2219    PUSER_SENT_MESSAGE CurrentSentMessage;
2220 
2221    TRACE("MsqCleanupThreadMsgs %p\n",pti);
2222 
2223    // Clear it all out.
2224    if (pti->pcti)
2225    {
2226        pti->pcti->fsWakeBits = 0;
2227        pti->pcti->fsChangeBits = 0;
2228    }
2229 
2230    pti->nCntsQBits[QSRosKey] = 0;
2231    pti->nCntsQBits[QSRosMouseMove] = 0;
2232    pti->nCntsQBits[QSRosMouseButton] = 0;
2233    pti->nCntsQBits[QSRosPostMessage] = 0;
2234    pti->nCntsQBits[QSRosSendMessage] = 0;
2235    pti->nCntsQBits[QSRosHotKey] = 0;
2236    pti->nCntsQBits[QSRosEvent] = 0;
2237 
2238    /* cleanup posted messages */
2239    while (!IsListEmpty(&pti->PostedMessagesListHead))
2240    {
2241       CurrentEntry = pti->PostedMessagesListHead.Flink;
2242       CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, ListEntry);
2243       ERR("Thread Cleanup Post Messages %p\n",CurrentMessage);
2244       if (CurrentMessage->dwQEvent)
2245       {
2246          if (CurrentMessage->dwQEvent == POSTEVENT_NWE)
2247          {
2248             ExFreePoolWithTag( (PVOID)CurrentMessage->ExtraInfo, TAG_HOOK);
2249          }
2250       }
2251       MsqDestroyMessage(CurrentMessage);
2252    }
2253 
2254    /* remove the messages that have not yet been dispatched */
2255    while (!IsListEmpty(&pti->SentMessagesListHead))
2256    {
2257       CurrentEntry = pti->SentMessagesListHead.Flink;
2258       CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE, ListEntry);
2259 
2260       ERR("Thread Cleanup Sent Messages %p\n",CurrentSentMessage);
2261 
2262       /* wake the sender's thread */
2263       if (CurrentSentMessage->pkCompletionEvent != NULL)
2264       {
2265          KeSetEvent(CurrentSentMessage->pkCompletionEvent, IO_NO_INCREMENT, FALSE);
2266       }
2267 
2268       if (CurrentSentMessage->HasPackedLParam)
2269       {
2270          if (CurrentSentMessage->Msg.lParam)
2271             ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
2272       }
2273 
2274       /* free the message */
2275       FreeUserMessage(CurrentSentMessage);
2276    }
2277 
2278    // Process Trouble Message List
2279    if (!IsListEmpty(&usmList))
2280    {
2281       CurrentEntry = usmList.Flink;
2282       while (CurrentEntry != &usmList)
2283       {
2284          CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE, ListEntry);
2285          CurrentEntry = CurrentEntry->Flink;
2286 
2287          TRACE("Found troubled messages %p on the list\n",CurrentSentMessage);
2288 
2289          if ( pti == CurrentSentMessage->ptiReceiver )
2290          {
2291             if (CurrentSentMessage->HasPackedLParam)
2292             {
2293                if (CurrentSentMessage->Msg.lParam)
2294                   ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
2295             }
2296 
2297             /* free the message */
2298             FreeUserMessage(CurrentSentMessage);
2299          }
2300          else if ( pti == CurrentSentMessage->ptiSender ||
2301                    pti == CurrentSentMessage->ptiCallBackSender )
2302          {
2303             // Determine whether this message is being processed or not.
2304             if ((CurrentSentMessage->flags & (SMF_RECEIVERBUSY|SMF_RECEIVEDMESSAGE)) != SMF_RECEIVEDMESSAGE)
2305             {
2306                CurrentSentMessage->flags |= SMF_RECEIVERFREE;
2307             }
2308 
2309             if (!(CurrentSentMessage->flags & SMF_RECEIVERFREE))
2310             {
2311 
2312                if (CurrentSentMessage->HasPackedLParam)
2313                {
2314                   if (CurrentSentMessage->Msg.lParam)
2315                      ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
2316                }
2317 
2318                /* free the message */
2319                FreeUserMessage(CurrentSentMessage);
2320             }
2321          }
2322       }
2323    }
2324 }
2325 
2326 VOID FASTCALL
2327 MsqCleanupMessageQueue(PTHREADINFO pti)
2328 {
2329    PUSER_MESSAGE_QUEUE MessageQueue;
2330    PLIST_ENTRY CurrentEntry;
2331    PUSER_MESSAGE CurrentMessage;
2332 
2333    MessageQueue = pti->MessageQueue;
2334    MessageQueue->cThreads--;
2335 
2336    if (MessageQueue->cThreads)
2337    {
2338       if (MessageQueue->ptiSysLock == pti) MessageQueue->ptiSysLock = NULL;
2339    }
2340 
2341    if (MessageQueue->cThreads == 0) //// Fix a crash related to CORE-10471 testing.
2342    {
2343       /* cleanup posted messages */
2344       while (!IsListEmpty(&MessageQueue->HardwareMessagesListHead))
2345       {
2346          CurrentEntry = MessageQueue->HardwareMessagesListHead.Flink;
2347          CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, ListEntry);
2348          ERR("MQ Cleanup Post Messages %p\n",CurrentMessage);
2349          MsqDestroyMessage(CurrentMessage);
2350       }
2351    } ////
2352 
2353    if (MessageQueue->CursorObject)
2354    {
2355        PCURICON_OBJECT pCursor = MessageQueue->CursorObject;
2356 
2357        /* Change to another cursor if we going to dereference current one
2358           Note: we can't use UserSetCursor because it uses current thread
2359                 message queue instead of queue given for cleanup */
2360        if (IntGetSysCursorInfo()->CurrentCursorObject == pCursor)
2361        {
2362            HDC hdcScreen;
2363 
2364            /* Get the screen DC */
2365            hdcScreen = IntGetScreenDC();
2366            if (hdcScreen)
2367                GreMovePointer(hdcScreen, -1, -1);
2368            IntGetSysCursorInfo()->CurrentCursorObject = NULL;
2369        }
2370 
2371        TRACE("DereferenceObject pCursor\n");
2372        UserDereferenceObject(pCursor);
2373    }
2374 
2375    if (gpqForeground == MessageQueue)
2376    {
2377       IntSetFocusMessageQueue(NULL);
2378    }
2379    if (gpqForegroundPrev == MessageQueue)
2380    {
2381       gpqForegroundPrev = NULL;
2382    }
2383    if (gpqCursor == MessageQueue)
2384    {
2385       gpqCursor = NULL;
2386    }
2387 }
2388 
2389 PUSER_MESSAGE_QUEUE FASTCALL
2390 MsqCreateMessageQueue(PTHREADINFO pti)
2391 {
2392    PUSER_MESSAGE_QUEUE MessageQueue;
2393 
2394    MessageQueue = ExAllocatePoolWithTag(NonPagedPool,
2395                                         sizeof(*MessageQueue),
2396                                         USERTAG_Q);
2397 
2398    if (!MessageQueue)
2399    {
2400       return NULL;
2401    }
2402 
2403    RtlZeroMemory(MessageQueue, sizeof(*MessageQueue));
2404    /* hold at least one reference until it'll be destroyed */
2405    IntReferenceMessageQueue(MessageQueue);
2406    /* initialize the queue */
2407    if (!MsqInitializeMessageQueue(pti, MessageQueue))
2408    {
2409       IntDereferenceMessageQueue(MessageQueue);
2410       return NULL;
2411    }
2412 
2413    return MessageQueue;
2414 }
2415 
2416 VOID FASTCALL
2417 MsqDestroyMessageQueue(_In_ PTHREADINFO pti)
2418 {
2419    PDESKTOP desk;
2420    PUSER_MESSAGE_QUEUE MessageQueue = pti->MessageQueue;
2421 
2422    NT_ASSERT(MessageQueue != NULL);
2423    MessageQueue->QF_flags |= QF_INDESTROY;
2424 
2425    /* remove the message queue from any desktops */
2426    if ((desk = InterlockedExchangePointer((PVOID*)&MessageQueue->Desktop, 0)))
2427    {
2428       (void)InterlockedExchangePointer((PVOID*)&desk->ActiveMessageQueue, 0);
2429       IntDereferenceMessageQueue(MessageQueue);
2430    }
2431 
2432    /* clean it up */
2433    MsqCleanupMessageQueue(pti);
2434 
2435    /* decrease the reference counter, if it hits zero, the queue will be freed */
2436    _PRAGMA_WARNING_SUPPRESS(__WARNING_USING_UNINIT_VAR);
2437    IntDereferenceMessageQueue(MessageQueue);
2438 }
2439 
2440 LPARAM FASTCALL
2441 MsqSetMessageExtraInfo(LPARAM lParam)
2442 {
2443    LPARAM Ret;
2444    PTHREADINFO pti;
2445    PUSER_MESSAGE_QUEUE MessageQueue;
2446 
2447    pti = PsGetCurrentThreadWin32Thread();
2448    MessageQueue = pti->MessageQueue;
2449    if(!MessageQueue)
2450    {
2451       return 0;
2452    }
2453 
2454    Ret = MessageQueue->ExtraInfo;
2455    MessageQueue->ExtraInfo = lParam;
2456 
2457    return Ret;
2458 }
2459 
2460 LPARAM FASTCALL
2461 MsqGetMessageExtraInfo(VOID)
2462 {
2463    PTHREADINFO pti;
2464    PUSER_MESSAGE_QUEUE MessageQueue;
2465 
2466    pti = PsGetCurrentThreadWin32Thread();
2467    MessageQueue = pti->MessageQueue;
2468    if(!MessageQueue)
2469    {
2470       return 0;
2471    }
2472 
2473    return MessageQueue->ExtraInfo;
2474 }
2475 
2476 // ReplyMessage is called by the thread receiving the window message.
2477 BOOL FASTCALL
2478 co_MsqReplyMessage( LRESULT lResult )
2479 {
2480    PUSER_SENT_MESSAGE Message;
2481    PTHREADINFO pti;
2482 
2483    pti = PsGetCurrentThreadWin32Thread();
2484    Message = pti->pusmCurrent;
2485 
2486    if (!Message) return FALSE;
2487 
2488    if (Message->QS_Flags & QS_SMRESULT) return FALSE;
2489 
2490    //     SendMessageXxx  || Callback msg and not a notify msg
2491    if (Message->ptiSender || Message->CompletionCallback)
2492    {
2493       Message->lResult = lResult;
2494       Message->QS_Flags |= QS_SMRESULT;
2495    // See co_MsqDispatchOneSentMessage, change bits already accounted for and cleared and this msg is going away..
2496    }
2497    return TRUE;
2498 }
2499 
2500 HWND FASTCALL
2501 MsqSetStateWindow(PTHREADINFO pti, ULONG Type, HWND hWnd)
2502 {
2503    HWND Prev;
2504    PUSER_MESSAGE_QUEUE MessageQueue;
2505 
2506    MessageQueue = pti->MessageQueue;
2507 
2508    switch(Type)
2509    {
2510       case MSQ_STATE_CAPTURE:
2511          Prev = MessageQueue->spwndCapture ? UserHMGetHandle(MessageQueue->spwndCapture) : 0;
2512          MessageQueue->spwndCapture = ValidateHwndNoErr(hWnd);
2513          return Prev;
2514       case MSQ_STATE_ACTIVE:
2515          Prev = MessageQueue->spwndActive ? UserHMGetHandle(MessageQueue->spwndActive) : 0;
2516          MessageQueue->spwndActive = ValidateHwndNoErr(hWnd);
2517          return Prev;
2518       case MSQ_STATE_FOCUS:
2519          Prev = MessageQueue->spwndFocus ? UserHMGetHandle(MessageQueue->spwndFocus) : 0;
2520          MessageQueue->spwndFocus = ValidateHwndNoErr(hWnd);
2521          return Prev;
2522       case MSQ_STATE_MENUOWNER:
2523          Prev = MessageQueue->MenuOwner;
2524          MessageQueue->MenuOwner = hWnd;
2525          return Prev;
2526       case MSQ_STATE_MOVESIZE:
2527          Prev = MessageQueue->MoveSize;
2528          MessageQueue->MoveSize = hWnd;
2529          return Prev;
2530       case MSQ_STATE_CARET:
2531          Prev = MessageQueue->CaretInfo.hWnd;
2532          MessageQueue->CaretInfo.hWnd = hWnd;
2533          return Prev;
2534    }
2535 
2536    return NULL;
2537 }
2538 
2539 VOID FASTCALL
2540 MsqReleaseModifierKeys(PUSER_MESSAGE_QUEUE MessageQueue)
2541 {
2542     WORD ModifierKeys[] = { VK_LCONTROL, VK_RCONTROL, VK_CONTROL,
2543                             VK_LMENU,    VK_RMENU,    VK_MENU,
2544                             VK_LSHIFT,   VK_RSHIFT,   VK_SHIFT };
2545     UINT i;
2546 
2547     for (i = 0; i < _countof(ModifierKeys); ++i)
2548     {
2549         if (IS_KEY_DOWN(MessageQueue->afKeyState, ModifierKeys[i]))
2550             SET_KEY_DOWN(MessageQueue->afKeyState, ModifierKeys[i], FALSE);
2551     }
2552 }
2553 
2554 SHORT
2555 APIENTRY
2556 NtUserGetKeyState(INT key)
2557 {
2558    DWORD Ret;
2559 
2560    UserEnterShared();
2561 
2562    Ret = UserGetKeyState(key);
2563 
2564    UserLeave();
2565 
2566    return (SHORT)Ret;
2567 }
2568 
2569 
2570 DWORD
2571 APIENTRY
2572 NtUserGetKeyboardState(LPBYTE lpKeyState)
2573 {
2574    DWORD i, ret = TRUE;
2575    PTHREADINFO pti;
2576    PUSER_MESSAGE_QUEUE MessageQueue;
2577 
2578    UserEnterShared();
2579 
2580    pti = PsGetCurrentThreadWin32Thread();
2581    MessageQueue = pti->MessageQueue;
2582 
2583    _SEH2_TRY
2584    {
2585        /* Probe and copy key state to an array */
2586        ProbeForWrite(lpKeyState, 256 * sizeof(BYTE), 1);
2587        for (i = 0; i < 256; ++i)
2588        {
2589            lpKeyState[i] = 0;
2590            if (IS_KEY_DOWN(MessageQueue->afKeyState, i))
2591                lpKeyState[i] |= KS_DOWN_BIT;
2592            if (IS_KEY_LOCKED(MessageQueue->afKeyState, i))
2593                lpKeyState[i] |= KS_LOCK_BIT;
2594        }
2595    }
2596    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2597    {
2598        SetLastNtError(_SEH2_GetExceptionCode());
2599        ret = FALSE;
2600    }
2601    _SEH2_END;
2602 
2603    UserLeave();
2604 
2605    return ret;
2606 }
2607 
2608 BOOL
2609 APIENTRY
2610 NtUserSetKeyboardState(LPBYTE pKeyState)
2611 {
2612    UINT i;
2613    BOOL bRet = TRUE;
2614    PTHREADINFO pti;
2615    PUSER_MESSAGE_QUEUE MessageQueue;
2616 
2617    UserEnterExclusive();
2618 
2619    pti = PsGetCurrentThreadWin32Thread();
2620    MessageQueue = pti->MessageQueue;
2621 
2622    _SEH2_TRY
2623    {
2624        ProbeForRead(pKeyState, 256 * sizeof(BYTE), 1);
2625        for (i = 0; i < 256; ++i)
2626        {
2627             SET_KEY_DOWN(MessageQueue->afKeyState, i, pKeyState[i] & KS_DOWN_BIT);
2628             SET_KEY_LOCKED(MessageQueue->afKeyState, i, pKeyState[i] & KS_LOCK_BIT);
2629        }
2630    }
2631    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2632    {
2633        SetLastNtError(_SEH2_GetExceptionCode());
2634        bRet = FALSE;
2635    }
2636    _SEH2_END;
2637 
2638    UserLeave();
2639 
2640    return bRet;
2641 }
2642 
2643 /* EOF */
2644