xref: /reactos/win32ss/user/ntuser/msgqueue.c (revision 53221834)
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 = pwnd->head.h;
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");
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 == Window->head.h)
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 == Window->head.h)
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                            (INT)(INT_PTR)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 
1025     PTHREADINFO ptiSender;
1026     PUSER_SENT_MESSAGE Message;
1027 
1028     if(!(Message = AllocateUserMessage(FALSE)))
1029     {
1030         ERR("MsqSendMessageAsync(): Not enough memory to allocate a message");
1031         return FALSE;
1032     }
1033 
1034     ptiSender = PsGetCurrentThreadWin32Thread();
1035 
1036     Message->Msg.hwnd = hwnd;
1037     Message->Msg.message = Msg;
1038     Message->Msg.wParam = wParam;
1039     Message->Msg.lParam = lParam;
1040     Message->pkCompletionEvent = NULL; // No event needed.
1041     Message->ptiReceiver = ptiReceiver;
1042     Message->ptiCallBackSender = ptiSender;
1043     Message->CompletionCallback = CompletionCallback;
1044     Message->CompletionCallbackContext = CompletionCallbackContext;
1045     Message->HookMessage = HookMessage;
1046     Message->HasPackedLParam = HasPackedLParam;
1047     Message->QS_Flags = QS_SENDMESSAGE;
1048     Message->flags = SMF_RECEIVERFREE;
1049 
1050     InsertTailList(&ptiReceiver->SentMessagesListHead, &Message->ListEntry);
1051     MsqWakeQueue(ptiReceiver, QS_SENDMESSAGE, TRUE);
1052 
1053     return TRUE;
1054 }
1055 
1056 NTSTATUS FASTCALL
1057 co_MsqSendMessage(PTHREADINFO ptirec,
1058                   HWND Wnd,
1059                   UINT Msg,
1060                   WPARAM wParam,
1061                   LPARAM lParam,
1062                   UINT uTimeout,
1063                   BOOL Block,
1064                   INT HookMessage,
1065                   ULONG_PTR *uResult)
1066 {
1067    PTHREADINFO pti;
1068    PUSER_SENT_MESSAGE SaveMsg, Message;
1069    NTSTATUS WaitStatus;
1070    LARGE_INTEGER Timeout;
1071    PLIST_ENTRY Entry;
1072    PWND pWnd;
1073    BOOLEAN SwapStateEnabled;
1074    LRESULT Result = 0;   //// Result could be trashed. ////
1075 
1076    pti = PsGetCurrentThreadWin32Thread();
1077    ASSERT(pti != ptirec);
1078    ASSERT(ptirec->pcti); // Send must have a client side to receive it!!!!
1079 
1080    /* Don't send from or to a dying thread */
1081    if (pti->TIF_flags & TIF_INCLEANUP || ptirec->TIF_flags & TIF_INCLEANUP)
1082    {
1083        // Unless we are dying and need to tell our parents.
1084        if (pti->TIF_flags & TIF_INCLEANUP && !(ptirec->TIF_flags & TIF_INCLEANUP))
1085        {
1086            // Parent notify is the big one. Fire and forget!
1087            TRACE("Send message from dying thread %u\n", Msg);
1088            co_MsqSendMessageAsync(ptirec, Wnd, Msg, wParam, lParam, NULL, 0, FALSE, HookMessage);
1089        }
1090        if (uResult) *uResult = -1;
1091        TRACE("MsqSM: Msg %u Current pti %lu or Rec pti %lu\n", Msg, pti->TIF_flags & TIF_INCLEANUP, ptirec->TIF_flags & TIF_INCLEANUP);
1092        return STATUS_UNSUCCESSFUL;
1093    }
1094 
1095    if (IsThreadSuspended(ptirec))
1096    {
1097       ERR("Sending to Suspended Thread Msg %lx\n",Msg);
1098       if (uResult) *uResult = -1;
1099       return STATUS_UNSUCCESSFUL;
1100    }
1101 
1102    // Should we do the same for No Wait?
1103    if ( HookMessage == MSQ_NORMAL )
1104    {
1105       pWnd = ValidateHwndNoErr(Wnd);
1106 
1107       // These can not cross International Border lines!
1108       if ( pti->ppi != ptirec->ppi && pWnd )
1109       {
1110          switch(Msg)
1111          {
1112              // Handle the special case when working with password transfers across bordering processes.
1113              case EM_GETLINE:
1114              case EM_SETPASSWORDCHAR:
1115              case WM_GETTEXT:
1116                 // Look for edit controls setup for passwords.
1117                 if ( gpsi->atomSysClass[ICLS_EDIT] == pWnd->pcls->atomClassName && // Use atomNVClassName.
1118                      pWnd->style & ES_PASSWORD )
1119                 {
1120                    if (uResult) *uResult = -1;
1121                    ERR("Running across the border without a passport!\n");
1122                    EngSetLastError(ERROR_ACCESS_DENIED);
1123                    return STATUS_UNSUCCESSFUL;
1124                 }
1125                 break;
1126              case WM_NOTIFY:
1127                 if (uResult) *uResult = -1;
1128                 ERR("Running across the border without a passport!\n");
1129                 return STATUS_UNSUCCESSFUL;
1130          }
1131       }
1132 
1133       // These can not cross State lines!
1134       if ( Msg == WM_CREATE || Msg == WM_NCCREATE )
1135       {
1136          if (uResult) *uResult = -1;
1137          ERR("Can not tell the other State we have Create!\n");
1138          return STATUS_UNSUCCESSFUL;
1139       }
1140    }
1141 
1142    if(!(Message = AllocateUserMessage(TRUE)))
1143    {
1144       ERR("MsqSendMessage(): Not enough memory to allocate a message\n");
1145       if (uResult) *uResult = -1;
1146       return STATUS_INSUFFICIENT_RESOURCES;
1147    }
1148 
1149    Timeout.QuadPart = Int32x32To64(-10000,uTimeout); // Pass SMTO test with a TO of 0x80000000.
1150    TRACE("Timeout val %lld\n",Timeout.QuadPart);
1151 
1152    Message->Msg.hwnd = Wnd;
1153    Message->Msg.message = Msg;
1154    Message->Msg.wParam = wParam;
1155    Message->Msg.lParam = lParam;
1156    Message->ptiReceiver = ptirec;
1157    Message->ptiSender = pti;
1158    Message->HookMessage = HookMessage;
1159    Message->QS_Flags = QS_SENDMESSAGE;
1160 
1161    SaveMsg = pti->pusmSent;
1162    pti->pusmSent = Message;
1163 
1164    /* Queue it in the destination's message queue */
1165    InsertTailList(&ptirec->SentMessagesListHead, &Message->ListEntry);
1166 
1167    MsqWakeQueue(ptirec, QS_SENDMESSAGE, TRUE);
1168 
1169    // First time in, turn off swapping of the stack.
1170    if (pti->cEnterCount == 0)
1171    {
1172       SwapStateEnabled = KeSetKernelStackSwapEnable(FALSE);
1173    }
1174    pti->cEnterCount++;
1175 
1176    if (Block)
1177    {
1178       PVOID WaitObjects[2];
1179 
1180       WaitObjects[0] = Message->pkCompletionEvent; // Wait 0
1181       WaitObjects[1] = ptirec->pEThread;           // Wait 1
1182 
1183       UserLeaveCo();
1184 
1185       WaitStatus = KeWaitForMultipleObjects( 2,
1186                                              WaitObjects,
1187                                              WaitAny,
1188                                              UserRequest,
1189                                              UserMode,
1190                                              FALSE,
1191                                             (uTimeout ? &Timeout : NULL),
1192                                              NULL );
1193 
1194       UserEnterCo();
1195 
1196       if (WaitStatus == STATUS_TIMEOUT)
1197       {
1198          /* Look up if the message has not yet dispatched, if so
1199             make sure it can't pass a result and it must not set the completion event anymore */
1200          Entry = ptirec->SentMessagesListHead.Flink;
1201          while (Entry != &ptirec->SentMessagesListHead)
1202          {
1203             if (CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry) == Message)
1204             {
1205                Message->pkCompletionEvent = NULL;
1206                RemoveEntryList(&Message->ListEntry);
1207                ClearMsgBitsMask(ptirec, Message->QS_Flags);
1208                InsertTailList(&usmList, &Message->ListEntry);
1209                break;
1210             }
1211             Entry = Entry->Flink;
1212          }
1213 
1214          ERR("MsqSendMessage (blocked) timed out 1 Status %lx\n", WaitStatus);
1215       }
1216       // Receiving thread passed on and left us hanging with issues still pending.
1217       else if (WaitStatus == STATUS_WAIT_1)
1218       {
1219          ERR("Bk Receiving Thread woken up dead!\n");
1220          Message->flags |= SMF_RECEIVERDIED;
1221       }
1222 
1223       while (co_MsqDispatchOneSentMessage(pti))
1224          ;
1225    }
1226    else
1227    {
1228       PVOID WaitObjects[3];
1229 
1230       WaitObjects[0] = Message->pkCompletionEvent; // Wait 0
1231       WaitObjects[1] = pti->pEventQueueServer;     // Wait 1
1232       WaitObjects[2] = ptirec->pEThread;           // Wait 2
1233 
1234       do
1235       {
1236          UserLeaveCo();
1237 
1238          WaitStatus = KeWaitForMultipleObjects( 3,
1239                                                 WaitObjects,
1240                                                 WaitAny,
1241                                                 UserRequest,
1242                                                 UserMode,
1243                                                 FALSE,
1244                                                (uTimeout ? &Timeout : NULL),
1245                                                 NULL);
1246 
1247          UserEnterCo();
1248 
1249          if (WaitStatus == STATUS_TIMEOUT)
1250          {
1251             /* Look up if the message has not yet been dispatched, if so
1252                make sure it can't pass a result and it must not set the completion event anymore */
1253             Entry = ptirec->SentMessagesListHead.Flink;
1254             while (Entry != &ptirec->SentMessagesListHead)
1255             {
1256                if (CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry) == Message)
1257                {
1258                   Message->pkCompletionEvent = NULL;
1259                   RemoveEntryList(&Message->ListEntry);
1260                   ClearMsgBitsMask(ptirec, Message->QS_Flags);
1261                   InsertTailList(&usmList, &Message->ListEntry);
1262                   break;
1263                }
1264                Entry = Entry->Flink;
1265             }
1266 
1267             ERR("MsqSendMessage timed out 2 Status %lx\n", WaitStatus);
1268             break;
1269          }
1270          // Receiving thread passed on and left us hanging with issues still pending.
1271          else if (WaitStatus == STATUS_WAIT_2)
1272          {
1273             ERR("NB Receiving Thread woken up dead!\n");
1274             Message->flags |= SMF_RECEIVERDIED;
1275             break;
1276          }
1277 
1278          if (WaitStatus == STATUS_USER_APC) break;
1279 
1280          while (co_MsqDispatchOneSentMessage(pti))
1281             ;
1282       } while (WaitStatus == STATUS_WAIT_1);
1283    }
1284 
1285    // Count is nil, restore swapping of the stack.
1286    if (--pti->cEnterCount == 0 )
1287    {
1288       KeSetKernelStackSwapEnable(SwapStateEnabled);
1289    }
1290 
1291    // Handle User APC
1292    if (WaitStatus == STATUS_USER_APC)
1293    {
1294      // The current thread is dying!
1295      TRACE("User APC\n");
1296 
1297      // The Message will be on the Trouble list until Thread cleanup.
1298      Message->flags |= SMF_SENDERDIED;
1299 
1300      co_IntDeliverUserAPC();
1301      ERR("User APC Returned\n"); // Should not see this message.
1302    }
1303 
1304    // Force this thread to wake up for the next go around.
1305    KeSetEvent(pti->pEventQueueServer, IO_NO_INCREMENT, FALSE);
1306 
1307    Result = Message->lResult;
1308 
1309    // Determine whether this message is being processed or not.
1310    if ((Message->flags & (SMF_RECEIVERBUSY|SMF_RECEIVEDMESSAGE)) != SMF_RECEIVEDMESSAGE)
1311    {
1312       Message->flags |= SMF_RECEIVERFREE;
1313    }
1314 
1315    if (!(Message->flags & SMF_RECEIVERFREE))
1316    {
1317       TRACE("Sender Freeing Message %p ptirec %p bit %d list empty %d\n",Message,ptirec,!!(ptirec->pcti->fsChangeBits & QS_SENDMESSAGE),IsListEmpty(&ptirec->SentMessagesListHead));
1318       // Make it to this point, the message was received.
1319       FreeUserMessage(Message);
1320    }
1321 
1322    pti->pusmSent = SaveMsg;
1323 
1324    TRACE("MSM Allocation Count %d Status %lx Result %d\n",SendMsgCount,WaitStatus,Result);
1325 
1326    if (WaitStatus != STATUS_TIMEOUT)
1327    {
1328       if (uResult)
1329       {
1330          *uResult = (STATUS_WAIT_0 == WaitStatus ? Result : 0);
1331       }
1332    }
1333 
1334    return WaitStatus;
1335 }
1336 
1337 VOID FASTCALL
1338 MsqPostMessage(PTHREADINFO pti,
1339                MSG* Msg,
1340                BOOLEAN HardwareMessage,
1341                DWORD MessageBits,
1342                DWORD dwQEvent,
1343                LONG_PTR ExtraInfo)
1344 {
1345    PUSER_MESSAGE Message;
1346    PUSER_MESSAGE_QUEUE MessageQueue;
1347 
1348    if ( pti->TIF_flags & TIF_INCLEANUP || pti->MessageQueue->QF_flags & QF_INDESTROY )
1349    {
1350       ERR("Post Msg; Thread or Q is Dead!\n");
1351       return;
1352    }
1353 
1354    if(!(Message = MsqCreateMessage(Msg)))
1355    {
1356       return;
1357    }
1358 
1359    MessageQueue = pti->MessageQueue;
1360 
1361    if (!HardwareMessage)
1362    {
1363        InsertTailList(&pti->PostedMessagesListHead, &Message->ListEntry);
1364    }
1365    else
1366    {
1367        InsertTailList(&MessageQueue->HardwareMessagesListHead, &Message->ListEntry);
1368    }
1369 
1370    if (Msg->message == WM_HOTKEY) MessageBits |= QS_HOTKEY; // Justin Case, just set it.
1371    Message->dwQEvent = dwQEvent;
1372    Message->ExtraInfo = ExtraInfo;
1373    Message->QS_Flags = MessageBits;
1374    Message->pti = pti;
1375    MsqWakeQueue(pti, MessageBits, TRUE);
1376    TRACE("Post Message %d\n",PostMsgCount);
1377 }
1378 
1379 VOID FASTCALL
1380 MsqPostQuitMessage(PTHREADINFO pti, ULONG ExitCode)
1381 {
1382    pti->QuitPosted = TRUE;
1383    pti->exitCode = ExitCode;
1384    MsqWakeQueue(pti, QS_POSTMESSAGE|QS_ALLPOSTMESSAGE, TRUE);
1385 }
1386 
1387 /***********************************************************************
1388  *           MsqSendParentNotify
1389  *
1390  * Send a WM_PARENTNOTIFY to all ancestors of the given window, unless
1391  * the window has the WS_EX_NOPARENTNOTIFY style.
1392  */
1393 static void MsqSendParentNotify( PWND pwnd, WORD event, WORD idChild, POINT pt )
1394 {
1395     PWND pwndDesktop = UserGetDesktopWindow();
1396 
1397     /* pt has to be in the client coordinates of the parent window */
1398     pt.x += pwndDesktop->rcClient.left - pwnd->rcClient.left;
1399     pt.y += pwndDesktop->rcClient.top - pwnd->rcClient.top;
1400 
1401     for (;;)
1402     {
1403         PWND pwndParent;
1404 
1405         if (!(pwnd->style & WS_CHILD)) break;
1406         if (pwnd->ExStyle & WS_EX_NOPARENTNOTIFY) break;
1407         if (!(pwndParent = IntGetParent(pwnd))) break;
1408         if (pwndParent == pwndDesktop) break;
1409         pt.x += pwnd->rcClient.left - pwndParent->rcClient.left;
1410         pt.y += pwnd->rcClient.top - pwndParent->rcClient.top;
1411 
1412         pwnd = pwndParent;
1413         co_IntSendMessage( UserHMGetHandle(pwnd), WM_PARENTNOTIFY,
1414                       MAKEWPARAM( event, idChild ), MAKELPARAM( pt.x, pt.y ) );
1415     }
1416 }
1417 
1418 VOID
1419 FASTCALL
1420 IntTrackMouseMove(PWND pwndTrack, PDESKTOP pDesk, PMSG msg, USHORT hittest)
1421 {
1422 //   PWND pwndTrack = IntChildrenWindowFromPoint(pwndMsg, msg->pt.x, msg->pt.y);
1423 //   hittest = (USHORT)GetNCHitEx(pwndTrack, msg->pt); /// @todo WTF is this???
1424 
1425    if ( pDesk->spwndTrack != pwndTrack || // Change with tracking window or
1426         msg->message != WM_MOUSEMOVE   || // Mouse click changes or
1427         pDesk->htEx != hittest)           // Change in current hit test states.
1428    {
1429       TRACE("ITMM: Track Mouse Move!\n");
1430 
1431       /* Handle only the changing window track and mouse move across a border. */
1432       if ( pDesk->spwndTrack != pwndTrack ||
1433           (pDesk->htEx == HTCLIENT) ^ (hittest == HTCLIENT) )
1434       {
1435          TRACE("ITMM: Another Wnd %d or Across Border %d\n",
1436               pDesk->spwndTrack != pwndTrack,(pDesk->htEx == HTCLIENT) ^ (hittest == HTCLIENT));
1437 
1438          if ( pDesk->dwDTFlags & DF_TME_LEAVE )
1439             UserPostMessage( UserHMGetHandle(pDesk->spwndTrack),
1440                             (pDesk->htEx != HTCLIENT) ? WM_NCMOUSELEAVE : WM_MOUSELEAVE,
1441                              0, 0);
1442 
1443          if ( pDesk->dwDTFlags & DF_TME_HOVER )
1444             IntKillTimer(pDesk->spwndTrack, ID_EVENT_SYSTIMER_MOUSEHOVER, TRUE);
1445 
1446          /* Clear the flags to sign a change. */
1447          pDesk->dwDTFlags &= ~(DF_TME_LEAVE|DF_TME_HOVER);
1448       }
1449       /* Set the Track window and hit test. */
1450       pDesk->spwndTrack = pwndTrack;
1451       pDesk->htEx = hittest;
1452    }
1453 
1454    /* Reset, Same Track window, Hover set and Mouse Clicks or Clobbered Hover box. */
1455    if ( pDesk->spwndTrack == pwndTrack &&
1456        ( msg->message != WM_MOUSEMOVE || !RECTL_bPointInRect(&pDesk->rcMouseHover, msg->pt.x, msg->pt.y)) &&
1457         pDesk->dwDTFlags & DF_TME_HOVER )
1458    {
1459       TRACE("ITMM: Reset Hover points!\n");
1460       // Restart timer for the hover period.
1461       IntSetTimer(pDesk->spwndTrack, ID_EVENT_SYSTIMER_MOUSEHOVER, pDesk->dwMouseHoverTime, SystemTimerProc, TMRF_SYSTEM);
1462       // Reset desktop mouse hover from the system default hover rectangle.
1463       RECTL_vSetRect(&pDesk->rcMouseHover,
1464                       msg->pt.x - gspv.iMouseHoverWidth  / 2,
1465                       msg->pt.y - gspv.iMouseHoverHeight / 2,
1466                       msg->pt.x + gspv.iMouseHoverWidth  / 2,
1467                       msg->pt.y + gspv.iMouseHoverHeight / 2);
1468    }
1469 }
1470 
1471 BOOL co_IntProcessMouseMessage(MSG* msg, BOOL* RemoveMessages, BOOL* NotForUs, LONG_PTR ExtraInfo, UINT first, UINT last)
1472 {
1473     MSG clk_msg;
1474     POINT pt;
1475     UINT message;
1476     USHORT hittest;
1477     EVENTMSG event;
1478     MOUSEHOOKSTRUCT hook;
1479     BOOL eatMsg = FALSE;
1480 
1481     PWND pwndMsg, pwndDesktop;
1482     PUSER_MESSAGE_QUEUE MessageQueue;
1483     PTHREADINFO pti;
1484     PSYSTEM_CURSORINFO CurInfo;
1485     PDESKTOP pDesk;
1486 
1487     pti = PsGetCurrentThreadWin32Thread();
1488     pwndDesktop = UserGetDesktopWindow();
1489     MessageQueue = pti->MessageQueue;
1490     CurInfo = IntGetSysCursorInfo();
1491     pwndMsg = ValidateHwndNoErr(msg->hwnd);
1492     clk_msg = MessageQueue->msgDblClk;
1493     pDesk = pwndDesktop->head.rpdesk;
1494 
1495     /* find the window to dispatch this mouse message to */
1496     if (MessageQueue->spwndCapture)
1497     {
1498         hittest = HTCLIENT;
1499         pwndMsg = MessageQueue->spwndCapture;
1500     }
1501     else
1502     {
1503         /*
1504            Start with null window. See wine win.c:test_mouse_input:WM_COMMAND tests.
1505         */
1506         pwndMsg = co_WinPosWindowFromPoint( NULL, &msg->pt, &hittest, FALSE);
1507     }
1508 
1509     TRACE("Got mouse message for %p, hittest: 0x%x\n", msg->hwnd, hittest);
1510 
1511     // Null window or not the same "Hardware" message queue.
1512     if (pwndMsg == NULL || pwndMsg->head.pti->MessageQueue != MessageQueue)
1513     {
1514         // Crossing a boundary, so set cursor. See default message queue cursor.
1515         IntSystemSetCursor(SYSTEMCUR(ARROW));
1516         /* Remove and ignore the message */
1517         *RemoveMessages = TRUE;
1518         return FALSE;
1519     }
1520 
1521     // Check to see if this is attached,
1522     if ( pwndMsg->head.pti != pti &&  // window thread is not current,
1523          MessageQueue->cThreads > 1 ) // and is attached...
1524     {
1525         // This is not for us and we should leave so the other thread can check for messages!!!
1526         *NotForUs = TRUE;
1527         *RemoveMessages = TRUE;
1528         return FALSE;
1529     }
1530 
1531     if ( MessageQueue == gpqCursor ) // Cursor must use the same Queue!
1532     {
1533        IntTrackMouseMove(pwndMsg, pDesk, msg, hittest);
1534     }
1535     else
1536     {
1537        ERR("Not the same cursor!\n");
1538     }
1539 
1540     msg->hwnd = UserHMGetHandle(pwndMsg);
1541 
1542     pt = msg->pt;
1543     message = msg->message;
1544 
1545     /* Note: windows has no concept of a non-client wheel message */
1546     if (message != WM_MOUSEWHEEL)
1547     {
1548         if (hittest != HTCLIENT)
1549         {
1550             message += WM_NCMOUSEMOVE - WM_MOUSEMOVE;
1551             msg->wParam = hittest; // Caution! This might break wParam check in DblClk.
1552         }
1553         else
1554         {
1555             /* coordinates don't get translated while tracking a menu */
1556             /* FIXME: should differentiate popups and top-level menus */
1557             if (!(MessageQueue->MenuOwner))
1558             {
1559                 pt.x += pwndDesktop->rcClient.left - pwndMsg->rcClient.left;
1560                 pt.y += pwndDesktop->rcClient.top - pwndMsg->rcClient.top;
1561             }
1562         }
1563     }
1564     msg->lParam = MAKELONG( pt.x, pt.y );
1565 
1566     /* translate double clicks */
1567 
1568     if ((msg->message == WM_LBUTTONDOWN) ||
1569         (msg->message == WM_RBUTTONDOWN) ||
1570         (msg->message == WM_MBUTTONDOWN) ||
1571         (msg->message == WM_XBUTTONDOWN))
1572     {
1573         BOOL update = *RemoveMessages;
1574 
1575         /* translate double clicks -
1576          * note that ...MOUSEMOVEs can slip in between
1577          * ...BUTTONDOWN and ...BUTTONDBLCLK messages */
1578 
1579         if ((MessageQueue->MenuOwner || MessageQueue->MoveSize) ||
1580             hittest != HTCLIENT ||
1581             (pwndMsg->pcls->style & CS_DBLCLKS))
1582         {
1583            if ((msg->message == clk_msg.message) &&
1584                (msg->hwnd == clk_msg.hwnd) &&
1585                // Only worry about XButton wParam.
1586                (msg->message != WM_XBUTTONDOWN || GET_XBUTTON_WPARAM(msg->wParam) == GET_XBUTTON_WPARAM(clk_msg.wParam)) &&
1587                ((msg->time - clk_msg.time) < (ULONG)gspv.iDblClickTime) &&
1588                (abs(msg->pt.x - clk_msg.pt.x) < UserGetSystemMetrics(SM_CXDOUBLECLK)/2) &&
1589                (abs(msg->pt.y - clk_msg.pt.y) < UserGetSystemMetrics(SM_CYDOUBLECLK)/2))
1590            {
1591                message += (WM_LBUTTONDBLCLK - WM_LBUTTONDOWN);
1592                if (update)
1593                {
1594                    MessageQueue->msgDblClk.message = 0;  /* clear the double click conditions */
1595                    update = FALSE;
1596                }
1597            }
1598         }
1599 
1600         if (!((first ==  0 && last == 0) || (message >= first || message <= last)))
1601         {
1602             TRACE("Message out of range!!!\n");
1603             return FALSE;
1604         }
1605 
1606         /* update static double click conditions */
1607         if (update) MessageQueue->msgDblClk = *msg;
1608     }
1609     else
1610     {
1611         if (!((first ==  0 && last == 0) || (message >= first || message <= last)))
1612         {
1613             TRACE("Message out of range!!!\n");
1614             return FALSE;
1615         }
1616 
1617         // Update mouse move down keys.
1618         if (message == WM_MOUSEMOVE)
1619         {
1620            msg->wParam = MsqGetDownKeyState(MessageQueue);
1621         }
1622     }
1623 
1624     if (gspv.bMouseClickLock)
1625     {
1626         BOOL IsClkLck = FALSE;
1627 
1628         if(msg->message == WM_LBUTTONUP)
1629         {
1630             IsClkLck = ((msg->time - CurInfo->ClickLockTime) >= gspv.dwMouseClickLockTime);
1631             if (IsClkLck && (!CurInfo->ClickLockActive))
1632             {
1633                 CurInfo->ClickLockActive = TRUE;
1634             }
1635         }
1636         else if (msg->message == WM_LBUTTONDOWN)
1637         {
1638             if (CurInfo->ClickLockActive)
1639             {
1640                 IsClkLck = TRUE;
1641                 CurInfo->ClickLockActive = FALSE;
1642             }
1643 
1644             CurInfo->ClickLockTime = msg->time;
1645         }
1646 
1647         if(IsClkLck)
1648         {
1649             /* Remove and ignore the message */
1650             *RemoveMessages = TRUE;
1651             TRACE("Remove and ignore the message\n");
1652             return FALSE;
1653         }
1654     }
1655 
1656     if (pti->TIF_flags & TIF_MSGPOSCHANGED)
1657     {
1658         pti->TIF_flags &= ~TIF_MSGPOSCHANGED;
1659         IntNotifyWinEvent(EVENT_OBJECT_LOCATIONCHANGE, NULL, OBJID_CLIENT, CHILDID_SELF, 0);
1660     }
1661 
1662     /* message is accepted now (but still get dropped) */
1663 
1664     event.message = msg->message;
1665     event.time    = msg->time;
1666     event.hwnd    = msg->hwnd;
1667     event.paramL  = msg->pt.x;
1668     event.paramH  = msg->pt.y;
1669     co_HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&event );
1670 
1671     hook.pt           = msg->pt;
1672     hook.hwnd         = msg->hwnd;
1673     hook.wHitTestCode = hittest;
1674     hook.dwExtraInfo  = ExtraInfo;
1675     if (co_HOOK_CallHooks( WH_MOUSE, *RemoveMessages ? HC_ACTION : HC_NOREMOVE,
1676                         message, (LPARAM)&hook ))
1677     {
1678         hook.pt           = msg->pt;
1679         hook.hwnd         = msg->hwnd;
1680         hook.wHitTestCode = hittest;
1681         hook.dwExtraInfo  = ExtraInfo;
1682         co_HOOK_CallHooks( WH_CBT, HCBT_CLICKSKIPPED, message, (LPARAM)&hook );
1683 
1684         ERR("WH_MOUSE dropped mouse message!\n");
1685 
1686         /* Remove and skip message */
1687         *RemoveMessages = TRUE;
1688         return FALSE;
1689     }
1690 
1691     if ((hittest == (USHORT)HTERROR) || (hittest == (USHORT)HTNOWHERE))
1692     {
1693         co_IntSendMessage( msg->hwnd, WM_SETCURSOR, (WPARAM)msg->hwnd, MAKELONG( hittest, msg->message ));
1694 
1695         /* Remove and skip message */
1696         *RemoveMessages = TRUE;
1697         return FALSE;
1698     }
1699 
1700     if ((*RemoveMessages == FALSE) || MessageQueue->spwndCapture)
1701     {
1702         /* Accept the message */
1703         msg->message = message;
1704         return TRUE;
1705     }
1706 
1707     if ((msg->message == WM_LBUTTONDOWN) ||
1708         (msg->message == WM_RBUTTONDOWN) ||
1709         (msg->message == WM_MBUTTONDOWN) ||
1710         (msg->message == WM_XBUTTONDOWN))
1711     {
1712         /* Send the WM_PARENTNOTIFY,
1713          * note that even for double/nonclient clicks
1714          * notification message is still WM_L/M/RBUTTONDOWN.
1715          */
1716         MsqSendParentNotify(pwndMsg, msg->message, 0, msg->pt );
1717 
1718         /* Activate the window if needed */
1719 
1720         if (pwndMsg != MessageQueue->spwndActive)
1721         {
1722             PWND pwndTop = pwndMsg;
1723             pwndTop = IntGetNonChildAncestor(pwndTop);
1724 
1725             TRACE("Mouse pti %p pwndMsg pti %p pwndTop pti %p\n",MessageQueue->ptiMouse,pwndMsg->head.pti,pwndTop->head.pti);
1726 
1727             if (pwndTop && pwndTop != pwndDesktop)
1728             {
1729                 LONG ret = co_IntSendMessage( msg->hwnd,
1730                                               WM_MOUSEACTIVATE,
1731                                               (WPARAM)UserHMGetHandle(pwndTop),
1732                                               MAKELONG( hittest, msg->message));
1733                 switch(ret)
1734                 {
1735                 case MA_NOACTIVATEANDEAT:
1736                     eatMsg = TRUE;
1737                     /* fall through */
1738                 case MA_NOACTIVATE:
1739                     break;
1740                 case MA_ACTIVATEANDEAT:
1741                     eatMsg = TRUE;
1742                     /* fall through */
1743                 case MA_ACTIVATE:
1744                 case 0:
1745                     if (!co_IntMouseActivateWindow( pwndTop )) eatMsg = TRUE;
1746                     break;
1747                 default:
1748                     ERR( "unknown WM_MOUSEACTIVATE code %d\n", ret );
1749                     break;
1750                 }
1751             }
1752         }
1753     }
1754 
1755     /* send the WM_SETCURSOR message */
1756 
1757     /* Windows sends the normal mouse message as the message parameter
1758        in the WM_SETCURSOR message even if it's non-client mouse message */
1759     co_IntSendMessage( msg->hwnd, WM_SETCURSOR, (WPARAM)msg->hwnd, MAKELONG( hittest, msg->message ));
1760 
1761     msg->message = message;
1762     return !eatMsg;
1763 }
1764 
1765 BOOL co_IntProcessKeyboardMessage(MSG* Msg, BOOL* RemoveMessages)
1766 {
1767     EVENTMSG Event;
1768     USER_REFERENCE_ENTRY Ref;
1769     PWND pWnd;
1770     UINT ImmRet;
1771     BOOL Ret = TRUE;
1772     WPARAM wParam = Msg->wParam;
1773     PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
1774 
1775     if (Msg->message == VK_PACKET)
1776     {
1777        pti->wchInjected = HIWORD(Msg->wParam);
1778     }
1779 
1780     if (Msg->message == WM_KEYDOWN || Msg->message == WM_SYSKEYDOWN ||
1781         Msg->message == WM_KEYUP || Msg->message == WM_SYSKEYUP)
1782     {
1783         switch (Msg->wParam)
1784         {
1785             case VK_LSHIFT: case VK_RSHIFT:
1786                 Msg->wParam = VK_SHIFT;
1787                 break;
1788             case VK_LCONTROL: case VK_RCONTROL:
1789                 Msg->wParam = VK_CONTROL;
1790                 break;
1791             case VK_LMENU: case VK_RMENU:
1792                 Msg->wParam = VK_MENU;
1793                 break;
1794         }
1795     }
1796 
1797     pWnd = ValidateHwndNoErr(Msg->hwnd);
1798     if (pWnd) UserRefObjectCo(pWnd, &Ref);
1799 
1800     Event.message = Msg->message;
1801     Event.hwnd    = Msg->hwnd;
1802     Event.time    = Msg->time;
1803     Event.paramL  = (Msg->wParam & 0xFF) | (HIWORD(Msg->lParam) << 8);
1804     Event.paramH  = Msg->lParam & 0x7FFF;
1805     if (HIWORD(Msg->lParam) & 0x0100) Event.paramH |= 0x8000;
1806     co_HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&Event);
1807 
1808     if (*RemoveMessages)
1809     {
1810         if ((Msg->message == WM_KEYDOWN) &&
1811             (Msg->hwnd != IntGetDesktopWindow()))
1812         {
1813             /* Handle F1 key by sending out WM_HELP message */
1814             if (Msg->wParam == VK_F1)
1815             {
1816                 UserPostMessage( Msg->hwnd, WM_KEYF1, 0, 0 );
1817             }
1818             else if (Msg->wParam >= VK_BROWSER_BACK &&
1819                      Msg->wParam <= VK_LAUNCH_APP2)
1820             {
1821                 /* FIXME: Process keystate */
1822                 co_IntSendMessage(Msg->hwnd, WM_APPCOMMAND, (WPARAM)Msg->hwnd, MAKELPARAM(0, (FAPPCOMMAND_KEY | (Msg->wParam - VK_BROWSER_BACK + 1))));
1823             }
1824         }
1825         else if (Msg->message == WM_KEYUP)
1826         {
1827             /* Handle VK_APPS key by posting a WM_CONTEXTMENU message */
1828             if (Msg->wParam == VK_APPS && pti->MessageQueue->MenuOwner == NULL)
1829                 UserPostMessage( Msg->hwnd, WM_CONTEXTMENU, (WPARAM)Msg->hwnd, -1 );
1830         }
1831     }
1832 
1833     //// Key Down!
1834     if ( *RemoveMessages && Msg->message == WM_SYSKEYDOWN )
1835     {
1836         if ( HIWORD(Msg->lParam) & KF_ALTDOWN )
1837         {
1838             if ( Msg->wParam == VK_ESCAPE || Msg->wParam == VK_TAB ) // Alt-Tab/ESC Alt-Shift-Tab/ESC
1839             {
1840                 WPARAM wParamTmp;
1841 
1842                 wParamTmp = UserGetKeyState(VK_SHIFT) & 0x8000 ? SC_PREVWINDOW : SC_NEXTWINDOW;
1843                 TRACE("Send WM_SYSCOMMAND Alt-Tab/ESC Alt-Shift-Tab/ESC\n");
1844                 co_IntSendMessage( Msg->hwnd, WM_SYSCOMMAND, wParamTmp, Msg->wParam );
1845 
1846                 //// Keep looping.
1847                 Ret = FALSE;
1848                 //// Skip the rest.
1849                 goto Exit;
1850             }
1851         }
1852     }
1853 
1854     if ( *RemoveMessages && (Msg->message == WM_SYSKEYDOWN || Msg->message == WM_KEYDOWN) )
1855     {
1856         if (gdwLanguageToggleKey < 3)
1857         {
1858             if (IS_KEY_DOWN(gafAsyncKeyState, gdwLanguageToggleKey == 1 ? VK_LMENU : VK_CONTROL)) // L Alt 1 or Ctrl 2 .
1859             {
1860                 if ( wParam == VK_LSHIFT ) gLanguageToggleKeyState = INPUTLANGCHANGE_FORWARD;  // Left Alt - Left Shift, Next
1861                 //// FIXME : It seems to always be VK_LSHIFT.
1862                 if ( wParam == VK_RSHIFT ) gLanguageToggleKeyState = INPUTLANGCHANGE_BACKWARD; // Left Alt - Right Shift, Previous
1863             }
1864          }
1865     }
1866 
1867     //// Key Up!                             Alt Key                        Ctrl Key
1868     if ( *RemoveMessages && (Msg->message == WM_SYSKEYUP || Msg->message == WM_KEYUP) )
1869     {
1870         // When initializing win32k: Reading from the registry hotkey combination
1871         // to switch the keyboard layout and store it to global variable.
1872         // Using this combination of hotkeys in this function
1873 
1874         if ( gdwLanguageToggleKey < 3 &&
1875              IS_KEY_DOWN(gafAsyncKeyState, gdwLanguageToggleKey == 1 ? VK_LMENU : VK_CONTROL) )
1876         {
1877             if ( Msg->wParam == VK_SHIFT && !(IS_KEY_DOWN(gafAsyncKeyState, VK_SHIFT)))
1878             {
1879                 WPARAM wParamILR;
1880                 PKL pkl = pti->KeyboardLayout;
1881 
1882                 if (pWnd) UserDerefObjectCo(pWnd);
1883 
1884                 //// Seems to override message window.
1885                 if (!(pWnd = pti->MessageQueue->spwndFocus))
1886                 {
1887                      pWnd = pti->MessageQueue->spwndActive;
1888                 }
1889                 if (pWnd) UserRefObjectCo(pWnd, &Ref);
1890 
1891                 if (pkl != NULL && gLanguageToggleKeyState)
1892                 {
1893                     TRACE("Posting WM_INPUTLANGCHANGEREQUEST KeyState %d\n", gLanguageToggleKeyState );
1894 
1895                     wParamILR = gLanguageToggleKeyState;
1896                     // If system character set and font signature send flag.
1897                     if ( gSystemFS & pkl->dwFontSigs )
1898                     {
1899                        wParamILR |= INPUTLANGCHANGE_SYSCHARSET;
1900                     }
1901 
1902                     UserPostMessage( UserHMGetHandle(pWnd),
1903                                      WM_INPUTLANGCHANGEREQUEST,
1904                                      wParamILR,
1905                                     (LPARAM)pkl->hkl );
1906 
1907                     gLanguageToggleKeyState = 0;
1908                     //// Keep looping.
1909                     Ret = FALSE;
1910                     //// Skip the rest.
1911                     goto Exit;
1912                 }
1913             }
1914         }
1915     }
1916 
1917     if (co_HOOK_CallHooks( WH_KEYBOARD,
1918                           *RemoveMessages ? HC_ACTION : HC_NOREMOVE,
1919                            LOWORD(Msg->wParam),
1920                            Msg->lParam))
1921     {
1922         /* skip this message */
1923         co_HOOK_CallHooks( WH_CBT,
1924                            HCBT_KEYSKIPPED,
1925                            LOWORD(Msg->wParam),
1926                            Msg->lParam );
1927 
1928         ERR("KeyboardMessage WH_KEYBOARD Call Hook return!\n");
1929 
1930         *RemoveMessages = TRUE;
1931 
1932         Ret = FALSE;
1933     }
1934 
1935     if ( pWnd && Ret && *RemoveMessages && Msg->message == WM_KEYDOWN && !(pti->TIF_flags & TIF_DISABLEIME))
1936     {
1937         if ( (ImmRet = IntImmProcessKey(pti->MessageQueue, pWnd, Msg->message, Msg->wParam, Msg->lParam)) )
1938         {
1939             if ( ImmRet & (IPHK_HOTKEY|IPHK_SKIPTHISKEY) )
1940             {
1941                ImmRet = 0;
1942             }
1943             if ( ImmRet & IPHK_PROCESSBYIME )
1944             {
1945                Msg->wParam = VK_PROCESSKEY;
1946             }
1947         }
1948     }
1949 Exit:
1950     if (pWnd) UserDerefObjectCo(pWnd);
1951     return Ret;
1952 }
1953 
1954 BOOL co_IntProcessHardwareMessage(MSG* Msg, BOOL* RemoveMessages, BOOL* NotForUs, LONG_PTR ExtraInfo, UINT first, UINT last)
1955 {
1956     if ( IS_MOUSE_MESSAGE(Msg->message))
1957     {
1958         return co_IntProcessMouseMessage(Msg, RemoveMessages, NotForUs, ExtraInfo, first, last);
1959     }
1960     else if ( IS_KBD_MESSAGE(Msg->message))
1961     {
1962         return co_IntProcessKeyboardMessage(Msg, RemoveMessages);
1963     }
1964 
1965     return TRUE;
1966 }
1967 
1968 /* check whether a message filter contains at least one potential hardware message */
1969 static INT FASTCALL
1970 filter_contains_hw_range( UINT first, UINT last )
1971 {
1972    /* hardware message ranges are (in numerical order):
1973     *   WM_NCMOUSEFIRST .. WM_NCMOUSELAST
1974     *   WM_KEYFIRST .. WM_KEYLAST
1975     *   WM_MOUSEFIRST .. WM_MOUSELAST
1976     */
1977     if (!last) --last;
1978     if (last < WM_NCMOUSEFIRST) return 0;
1979     if (first > WM_NCMOUSELAST && last < WM_KEYFIRST) return 0;
1980     if (first > WM_KEYLAST && last < WM_MOUSEFIRST) return 0;
1981     if (first > WM_MOUSELAST) return 0;
1982     return 1;
1983 }
1984 
1985 /* check whether message is in the range of mouse messages */
1986 static inline BOOL is_mouse_message( UINT message )
1987 {
1988     return ( //( message >= WM_NCMOUSEFIRST && message <= WM_NCMOUSELAST )   || This seems to break tests...
1989              ( message >= WM_MOUSEFIRST   && message <= WM_MOUSELAST )     ||
1990              ( message >= WM_XBUTTONDOWN  && message <= WM_XBUTTONDBLCLK ) ||
1991              ( message >= WM_MBUTTONDOWN  && message <= WM_MBUTTONDBLCLK ) ||
1992              ( message >= WM_LBUTTONDOWN  && message <= WM_RBUTTONDBLCLK ) );
1993 }
1994 
1995 BOOL APIENTRY
1996 co_MsqPeekHardwareMessage(IN PTHREADINFO pti,
1997                          IN BOOL Remove,
1998                          IN PWND Window,
1999                          IN UINT MsgFilterLow,
2000                          IN UINT MsgFilterHigh,
2001                          IN UINT QSflags,
2002                          OUT MSG* pMsg)
2003 {
2004    BOOL AcceptMessage, NotForUs;
2005    PUSER_MESSAGE CurrentMessage;
2006    PLIST_ENTRY ListHead;
2007    MSG msg;
2008    ULONG_PTR idSave;
2009    DWORD QS_Flags;
2010    LONG_PTR ExtraInfo;
2011    BOOL Ret = FALSE;
2012    PUSER_MESSAGE_QUEUE MessageQueue = pti->MessageQueue;
2013 
2014    if (!filter_contains_hw_range( MsgFilterLow, MsgFilterHigh )) return FALSE;
2015 
2016    ListHead = MessageQueue->HardwareMessagesListHead.Flink;
2017 
2018    if (IsListEmpty(ListHead)) return FALSE;
2019 
2020    if (!MessageQueue->ptiSysLock)
2021    {
2022       MessageQueue->ptiSysLock = pti;
2023       pti->pcti->CTI_flags |= CTI_THREADSYSLOCK;
2024    }
2025 
2026    if (MessageQueue->ptiSysLock != pti)
2027    {
2028       ERR("Thread Q is locked to ptiSysLock 0x%p pti 0x%p\n",MessageQueue->ptiSysLock,pti);
2029       return FALSE;
2030    }
2031 
2032    while (ListHead != &MessageQueue->HardwareMessagesListHead)
2033    {
2034       CurrentMessage = CONTAINING_RECORD(ListHead, USER_MESSAGE, ListEntry);
2035       ListHead = ListHead->Flink;
2036 
2037       if (MessageQueue->idSysPeek == (ULONG_PTR)CurrentMessage)
2038       {
2039          TRACE("Skip this message due to it is in play!\n");
2040          continue;
2041       }
2042 /*
2043  MSDN:
2044  1: any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL.
2045  2: retrieves only messages on the current thread's message queue whose hwnd value is NULL.
2046  3: handle to the window whose messages are to be retrieved.
2047  */
2048       if ( ( !Window || // 1
2049             ( Window == PWND_BOTTOM && CurrentMessage->Msg.hwnd == NULL ) || // 2
2050             ( Window != PWND_BOTTOM && Window->head.h == CurrentMessage->Msg.hwnd ) || // 3
2051             ( is_mouse_message(CurrentMessage->Msg.message) ) ) && // Null window for anything mouse.
2052             ( ( ( MsgFilterLow == 0 && MsgFilterHigh == 0 ) && CurrentMessage->QS_Flags & QSflags ) ||
2053               ( MsgFilterLow <= CurrentMessage->Msg.message && MsgFilterHigh >= CurrentMessage->Msg.message ) ) )
2054       {
2055          idSave = MessageQueue->idSysPeek;
2056          MessageQueue->idSysPeek = (ULONG_PTR)CurrentMessage;
2057 
2058          msg = CurrentMessage->Msg;
2059          ExtraInfo = CurrentMessage->ExtraInfo;
2060          QS_Flags = CurrentMessage->QS_Flags;
2061 
2062          NotForUs = FALSE;
2063 
2064          UpdateKeyStateFromMsg(MessageQueue, &msg);
2065          AcceptMessage = co_IntProcessHardwareMessage(&msg, &Remove, &NotForUs, ExtraInfo, MsgFilterLow, MsgFilterHigh);
2066 
2067          if (Remove)
2068          {
2069              if (CurrentMessage->pti != NULL && (MessageQueue->idSysPeek == (ULONG_PTR)CurrentMessage))
2070              {
2071                 MsqDestroyMessage(CurrentMessage);
2072              }
2073              ClearMsgBitsMask(pti, QS_Flags);
2074          }
2075 
2076          MessageQueue->idSysPeek = idSave;
2077 
2078          if (NotForUs)
2079          {
2080             Ret = FALSE;
2081             break;
2082          }
2083 
2084          if (AcceptMessage)
2085          {
2086             *pMsg = msg;
2087             // Fix all but one wine win:test_GetMessagePos WM_TIMER tests. See PostTimerMessages.
2088             if (!RtlEqualMemory(&pti->ptLast, &msg.pt, sizeof(POINT)))
2089             {
2090                pti->TIF_flags |= TIF_MSGPOSCHANGED;
2091             }
2092             pti->timeLast = msg.time;
2093             pti->ptLast   = msg.pt;
2094             MessageQueue->ExtraInfo = ExtraInfo;
2095             Ret = TRUE;
2096             break;
2097          }
2098       }
2099    }
2100 
2101    MessageQueue->ptiSysLock = NULL;
2102    pti->pcti->CTI_flags &= ~CTI_THREADSYSLOCK;
2103    return Ret;
2104 }
2105 
2106 BOOLEAN APIENTRY
2107 MsqPeekMessage(IN PTHREADINFO pti,
2108                   IN BOOLEAN Remove,
2109                   IN PWND Window,
2110                   IN UINT MsgFilterLow,
2111                   IN UINT MsgFilterHigh,
2112                   IN UINT QSflags,
2113                   OUT LONG_PTR *ExtraInfo,
2114                   OUT DWORD *dwQEvent,
2115                   OUT PMSG Message)
2116 {
2117    PUSER_MESSAGE CurrentMessage;
2118    PLIST_ENTRY ListHead;
2119    DWORD QS_Flags;
2120    BOOL Ret = FALSE;
2121 
2122    ListHead = pti->PostedMessagesListHead.Flink;
2123 
2124    if (IsListEmpty(ListHead)) return FALSE;
2125 
2126    while(ListHead != &pti->PostedMessagesListHead)
2127    {
2128       CurrentMessage = CONTAINING_RECORD(ListHead, USER_MESSAGE, ListEntry);
2129       ListHead = ListHead->Flink;
2130 /*
2131  MSDN:
2132  1: any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL.
2133  2: retrieves only messages on the current thread's message queue whose hwnd value is NULL.
2134  3: handle to the window whose messages are to be retrieved.
2135  */
2136       if ( ( !Window || // 1
2137             ( Window == PWND_BOTTOM && CurrentMessage->Msg.hwnd == NULL ) || // 2
2138             ( Window != PWND_BOTTOM && Window->head.h == CurrentMessage->Msg.hwnd ) ) && // 3
2139             ( ( ( MsgFilterLow == 0 && MsgFilterHigh == 0 ) && CurrentMessage->QS_Flags & QSflags ) ||
2140               ( MsgFilterLow <= CurrentMessage->Msg.message && MsgFilterHigh >= CurrentMessage->Msg.message ) ) )
2141       {
2142          *Message   = CurrentMessage->Msg;
2143          *ExtraInfo = CurrentMessage->ExtraInfo;
2144          QS_Flags   = CurrentMessage->QS_Flags;
2145          if (dwQEvent) *dwQEvent = CurrentMessage->dwQEvent;
2146 
2147          if (Remove)
2148          {
2149              if (CurrentMessage->pti != NULL)
2150              {
2151                 MsqDestroyMessage(CurrentMessage);
2152              }
2153              ClearMsgBitsMask(pti, QS_Flags);
2154          }
2155          Ret = TRUE;
2156          break;
2157       }
2158    }
2159 
2160    return Ret;
2161 }
2162 
2163 NTSTATUS FASTCALL
2164 co_MsqWaitForNewMessages(PTHREADINFO pti, PWND WndFilter,
2165                          UINT MsgFilterMin, UINT MsgFilterMax)
2166 {
2167    NTSTATUS ret = STATUS_SUCCESS;
2168 
2169    // Post mouse moves before waiting for messages.
2170    if (pti->MessageQueue->QF_flags & QF_MOUSEMOVED)
2171    {
2172       IntCoalesceMouseMove(pti);
2173    }
2174 
2175    UserLeaveCo();
2176 
2177    ZwYieldExecution(); // Let someone else run!
2178 
2179    ret = KeWaitForSingleObject( pti->pEventQueueServer,
2180                                 UserRequest,
2181                                 UserMode,
2182                                 FALSE,
2183                                 NULL );
2184    UserEnterCo();
2185    if ( ret == STATUS_USER_APC )
2186    {
2187       TRACE("MWFNW User APC\n");
2188       co_IntDeliverUserAPC();
2189    }
2190    return ret;
2191 }
2192 
2193 BOOL FASTCALL
2194 MsqIsHung(PTHREADINFO pti, DWORD TimeOut)
2195 {
2196     DWORD dwTimeStamp = EngGetTickCount32();
2197     if (dwTimeStamp - pti->pcti->timeLastRead > TimeOut &&
2198        !(pti->pcti->fsWakeMask & QS_INPUT) &&
2199        !PsGetThreadFreezeCount(pti->pEThread) &&
2200        !(pti->ppi->W32PF_flags & W32PF_APPSTARTING))
2201     {
2202         TRACE("\nMsqIsHung(pti %p, TimeOut %lu)\n"
2203             "pEThread %p, ThreadsProcess %p, ImageFileName '%s'\n"
2204             "dwTimeStamp = %lu\n"
2205             "pti->pcti->timeLastRead = %lu\n"
2206             "pti->timeLast = %lu\n"
2207             "PsGetThreadFreezeCount(pti->pEThread) = %lu\n",
2208             pti, TimeOut,
2209             pti->pEThread,
2210             pti->pEThread ? pti->pEThread->ThreadsProcess : NULL,
2211             (pti->pEThread && pti->pEThread->ThreadsProcess)
2212                 ? pti->pEThread->ThreadsProcess->ImageFileName : "(None)",
2213             dwTimeStamp,
2214             pti->pcti->timeLastRead,
2215             pti->timeLast,
2216             PsGetThreadFreezeCount(pti->pEThread));
2217 
2218         return TRUE;
2219     }
2220 
2221    return FALSE;
2222 }
2223 
2224 BOOL FASTCALL
2225 IsThreadSuspended(PTHREADINFO pti)
2226 {
2227    if (pti->pEThread)
2228    {
2229       BOOL Ret = TRUE;
2230       if (!(pti->pEThread->Tcb.SuspendCount) && !PsGetThreadFreezeCount(pti->pEThread)) Ret = FALSE;
2231       return Ret;
2232    }
2233    return FALSE;
2234 }
2235 
2236 VOID
2237 CALLBACK
2238 HungAppSysTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
2239 {
2240    DoTheScreenSaver();
2241    TRACE("HungAppSysTimerProc\n");
2242    // Process list of windows that are hung and waiting.
2243 }
2244 
2245 BOOLEAN FASTCALL
2246 MsqInitializeMessageQueue(PTHREADINFO pti, PUSER_MESSAGE_QUEUE MessageQueue)
2247 {
2248    InitializeListHead(&MessageQueue->HardwareMessagesListHead); // Keep here!
2249    MessageQueue->spwndFocus = NULL;
2250    MessageQueue->iCursorLevel = 0;
2251    MessageQueue->CursorObject = SYSTEMCUR(WAIT); // See test_initial_cursor.
2252    if (MessageQueue->CursorObject)
2253    {
2254       TRACE("Default cursor hcur %p\n",UserHMGetHandle(MessageQueue->CursorObject));
2255       UserReferenceObject(MessageQueue->CursorObject);
2256    }
2257    RtlCopyMemory(MessageQueue->afKeyState, gafAsyncKeyState, sizeof(gafAsyncKeyState));
2258    MessageQueue->ptiMouse = pti;
2259    MessageQueue->ptiKeyboard = pti;
2260    MessageQueue->cThreads++;
2261 
2262    return TRUE;
2263 }
2264 
2265 VOID FASTCALL
2266 MsqCleanupThreadMsgs(PTHREADINFO pti)
2267 {
2268    PLIST_ENTRY CurrentEntry;
2269    PUSER_MESSAGE CurrentMessage;
2270    PUSER_SENT_MESSAGE CurrentSentMessage;
2271 
2272    TRACE("MsqCleanupThreadMsgs %p\n",pti);
2273 
2274    // Clear it all out.
2275    if (pti->pcti)
2276    {
2277        pti->pcti->fsWakeBits = 0;
2278        pti->pcti->fsChangeBits = 0;
2279    }
2280 
2281    pti->nCntsQBits[QSRosKey] = 0;
2282    pti->nCntsQBits[QSRosMouseMove] = 0;
2283    pti->nCntsQBits[QSRosMouseButton] = 0;
2284    pti->nCntsQBits[QSRosPostMessage] = 0;
2285    pti->nCntsQBits[QSRosSendMessage] = 0;
2286    pti->nCntsQBits[QSRosHotKey] = 0;
2287    pti->nCntsQBits[QSRosEvent] = 0;
2288 
2289    /* cleanup posted messages */
2290    while (!IsListEmpty(&pti->PostedMessagesListHead))
2291    {
2292       CurrentEntry = pti->PostedMessagesListHead.Flink;
2293       CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, ListEntry);
2294       ERR("Thread Cleanup Post Messages %p\n",CurrentMessage);
2295       if (CurrentMessage->dwQEvent)
2296       {
2297          if (CurrentMessage->dwQEvent == POSTEVENT_NWE)
2298          {
2299             ExFreePoolWithTag( (PVOID)CurrentMessage->ExtraInfo, TAG_HOOK);
2300          }
2301       }
2302       MsqDestroyMessage(CurrentMessage);
2303    }
2304 
2305    /* remove the messages that have not yet been dispatched */
2306    while (!IsListEmpty(&pti->SentMessagesListHead))
2307    {
2308       CurrentEntry = pti->SentMessagesListHead.Flink;
2309       CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE, ListEntry);
2310 
2311       ERR("Thread Cleanup Sent Messages %p\n",CurrentSentMessage);
2312 
2313       /* wake the sender's thread */
2314       if (CurrentSentMessage->pkCompletionEvent != NULL)
2315       {
2316          KeSetEvent(CurrentSentMessage->pkCompletionEvent, IO_NO_INCREMENT, FALSE);
2317       }
2318 
2319       if (CurrentSentMessage->HasPackedLParam)
2320       {
2321          if (CurrentSentMessage->Msg.lParam)
2322             ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
2323       }
2324 
2325       /* free the message */
2326       FreeUserMessage(CurrentSentMessage);
2327    }
2328 
2329    // Process Trouble Message List
2330    if (!IsListEmpty(&usmList))
2331    {
2332       CurrentEntry = usmList.Flink;
2333       while (CurrentEntry != &usmList)
2334       {
2335          CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE, ListEntry);
2336          CurrentEntry = CurrentEntry->Flink;
2337 
2338          TRACE("Found troubled messages %p on the list\n",CurrentSentMessage);
2339 
2340          if ( pti == CurrentSentMessage->ptiReceiver )
2341          {
2342             if (CurrentSentMessage->HasPackedLParam)
2343             {
2344                if (CurrentSentMessage->Msg.lParam)
2345                   ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
2346             }
2347 
2348             /* free the message */
2349             FreeUserMessage(CurrentSentMessage);
2350          }
2351          else if ( pti == CurrentSentMessage->ptiSender ||
2352                    pti == CurrentSentMessage->ptiCallBackSender )
2353          {
2354             // Determine whether this message is being processed or not.
2355             if ((CurrentSentMessage->flags & (SMF_RECEIVERBUSY|SMF_RECEIVEDMESSAGE)) != SMF_RECEIVEDMESSAGE)
2356             {
2357                CurrentSentMessage->flags |= SMF_RECEIVERFREE;
2358             }
2359 
2360             if (!(CurrentSentMessage->flags & SMF_RECEIVERFREE))
2361             {
2362 
2363                if (CurrentSentMessage->HasPackedLParam)
2364                {
2365                   if (CurrentSentMessage->Msg.lParam)
2366                      ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
2367                }
2368 
2369                /* free the message */
2370                FreeUserMessage(CurrentSentMessage);
2371             }
2372          }
2373       }
2374    }
2375 }
2376 
2377 VOID FASTCALL
2378 MsqCleanupMessageQueue(PTHREADINFO pti)
2379 {
2380    PUSER_MESSAGE_QUEUE MessageQueue;
2381    PLIST_ENTRY CurrentEntry;
2382    PUSER_MESSAGE CurrentMessage;
2383 
2384    MessageQueue = pti->MessageQueue;
2385    MessageQueue->cThreads--;
2386 
2387    if (MessageQueue->cThreads)
2388    {
2389       if (MessageQueue->ptiSysLock == pti) MessageQueue->ptiSysLock = NULL;
2390    }
2391 
2392    if (MessageQueue->cThreads == 0) //// Fix a crash related to CORE-10471 testing.
2393    {
2394       /* cleanup posted messages */
2395       while (!IsListEmpty(&MessageQueue->HardwareMessagesListHead))
2396       {
2397          CurrentEntry = MessageQueue->HardwareMessagesListHead.Flink;
2398          CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, ListEntry);
2399          ERR("MQ Cleanup Post Messages %p\n",CurrentMessage);
2400          MsqDestroyMessage(CurrentMessage);
2401       }
2402    } ////
2403 
2404    if (MessageQueue->CursorObject)
2405    {
2406        PCURICON_OBJECT pCursor = MessageQueue->CursorObject;
2407 
2408        /* Change to another cursor if we going to dereference current one
2409           Note: we can't use UserSetCursor because it uses current thread
2410                 message queue instead of queue given for cleanup */
2411        if (IntGetSysCursorInfo()->CurrentCursorObject == pCursor)
2412        {
2413            HDC hdcScreen;
2414 
2415            /* Get the screen DC */
2416            hdcScreen = IntGetScreenDC();
2417            if (hdcScreen)
2418                GreMovePointer(hdcScreen, -1, -1);
2419            IntGetSysCursorInfo()->CurrentCursorObject = NULL;
2420        }
2421 
2422        TRACE("DereferenceObject pCursor\n");
2423        UserDereferenceObject(pCursor);
2424    }
2425 
2426    if (gpqForeground == MessageQueue)
2427    {
2428       IntSetFocusMessageQueue(NULL);
2429    }
2430    if (gpqForegroundPrev == MessageQueue)
2431    {
2432       gpqForegroundPrev = NULL;
2433    }
2434    if (gpqCursor == MessageQueue)
2435    {
2436       gpqCursor = NULL;
2437    }
2438 }
2439 
2440 PUSER_MESSAGE_QUEUE FASTCALL
2441 MsqCreateMessageQueue(PTHREADINFO pti)
2442 {
2443    PUSER_MESSAGE_QUEUE MessageQueue;
2444 
2445    MessageQueue = ExAllocatePoolWithTag(NonPagedPool,
2446                                         sizeof(*MessageQueue),
2447                                         USERTAG_Q);
2448 
2449    if (!MessageQueue)
2450    {
2451       return NULL;
2452    }
2453 
2454    RtlZeroMemory(MessageQueue, sizeof(*MessageQueue));
2455    /* hold at least one reference until it'll be destroyed */
2456    IntReferenceMessageQueue(MessageQueue);
2457    /* initialize the queue */
2458    if (!MsqInitializeMessageQueue(pti, MessageQueue))
2459    {
2460       IntDereferenceMessageQueue(MessageQueue);
2461       return NULL;
2462    }
2463 
2464    return MessageQueue;
2465 }
2466 
2467 VOID FASTCALL
2468 MsqDestroyMessageQueue(_In_ PTHREADINFO pti)
2469 {
2470    PDESKTOP desk;
2471    PUSER_MESSAGE_QUEUE MessageQueue = pti->MessageQueue;
2472 
2473    NT_ASSERT(MessageQueue != NULL);
2474    MessageQueue->QF_flags |= QF_INDESTROY;
2475 
2476    /* remove the message queue from any desktops */
2477    if ((desk = InterlockedExchangePointer((PVOID*)&MessageQueue->Desktop, 0)))
2478    {
2479       (void)InterlockedExchangePointer((PVOID*)&desk->ActiveMessageQueue, 0);
2480       IntDereferenceMessageQueue(MessageQueue);
2481    }
2482 
2483    /* clean it up */
2484    MsqCleanupMessageQueue(pti);
2485 
2486    /* decrease the reference counter, if it hits zero, the queue will be freed */
2487    _PRAGMA_WARNING_SUPPRESS(__WARNING_USING_UNINIT_VAR);
2488    IntDereferenceMessageQueue(MessageQueue);
2489 }
2490 
2491 LPARAM FASTCALL
2492 MsqSetMessageExtraInfo(LPARAM lParam)
2493 {
2494    LPARAM Ret;
2495    PTHREADINFO pti;
2496    PUSER_MESSAGE_QUEUE MessageQueue;
2497 
2498    pti = PsGetCurrentThreadWin32Thread();
2499    MessageQueue = pti->MessageQueue;
2500    if(!MessageQueue)
2501    {
2502       return 0;
2503    }
2504 
2505    Ret = MessageQueue->ExtraInfo;
2506    MessageQueue->ExtraInfo = lParam;
2507 
2508    return Ret;
2509 }
2510 
2511 LPARAM FASTCALL
2512 MsqGetMessageExtraInfo(VOID)
2513 {
2514    PTHREADINFO pti;
2515    PUSER_MESSAGE_QUEUE MessageQueue;
2516 
2517    pti = PsGetCurrentThreadWin32Thread();
2518    MessageQueue = pti->MessageQueue;
2519    if(!MessageQueue)
2520    {
2521       return 0;
2522    }
2523 
2524    return MessageQueue->ExtraInfo;
2525 }
2526 
2527 // ReplyMessage is called by the thread receiving the window message.
2528 BOOL FASTCALL
2529 co_MsqReplyMessage( LRESULT lResult )
2530 {
2531    PUSER_SENT_MESSAGE Message;
2532    PTHREADINFO pti;
2533 
2534    pti = PsGetCurrentThreadWin32Thread();
2535    Message = pti->pusmCurrent;
2536 
2537    if (!Message) return FALSE;
2538 
2539    if (Message->QS_Flags & QS_SMRESULT) return FALSE;
2540 
2541    //     SendMessageXxx  || Callback msg and not a notify msg
2542    if (Message->ptiSender || Message->CompletionCallback)
2543    {
2544       Message->lResult = lResult;
2545       Message->QS_Flags |= QS_SMRESULT;
2546    // See co_MsqDispatchOneSentMessage, change bits already accounted for and cleared and this msg is going away..
2547    }
2548    return TRUE;
2549 }
2550 
2551 HWND FASTCALL
2552 MsqSetStateWindow(PTHREADINFO pti, ULONG Type, HWND hWnd)
2553 {
2554    HWND Prev;
2555    PUSER_MESSAGE_QUEUE MessageQueue;
2556 
2557    MessageQueue = pti->MessageQueue;
2558 
2559    switch(Type)
2560    {
2561       case MSQ_STATE_CAPTURE:
2562          Prev = MessageQueue->spwndCapture ? UserHMGetHandle(MessageQueue->spwndCapture) : 0;
2563          MessageQueue->spwndCapture = ValidateHwndNoErr(hWnd);
2564          return Prev;
2565       case MSQ_STATE_ACTIVE:
2566          Prev = MessageQueue->spwndActive ? UserHMGetHandle(MessageQueue->spwndActive) : 0;
2567          MessageQueue->spwndActive = ValidateHwndNoErr(hWnd);
2568          return Prev;
2569       case MSQ_STATE_FOCUS:
2570          Prev = MessageQueue->spwndFocus ? UserHMGetHandle(MessageQueue->spwndFocus) : 0;
2571          MessageQueue->spwndFocus = ValidateHwndNoErr(hWnd);
2572          return Prev;
2573       case MSQ_STATE_MENUOWNER:
2574          Prev = MessageQueue->MenuOwner;
2575          MessageQueue->MenuOwner = hWnd;
2576          return Prev;
2577       case MSQ_STATE_MOVESIZE:
2578          Prev = MessageQueue->MoveSize;
2579          MessageQueue->MoveSize = hWnd;
2580          return Prev;
2581       case MSQ_STATE_CARET:
2582          Prev = MessageQueue->CaretInfo.hWnd;
2583          MessageQueue->CaretInfo.hWnd = hWnd;
2584          return Prev;
2585    }
2586 
2587    return NULL;
2588 }
2589 
2590 SHORT
2591 APIENTRY
2592 NtUserGetKeyState(INT key)
2593 {
2594    DWORD Ret;
2595 
2596    UserEnterShared();
2597 
2598    Ret = UserGetKeyState(key);
2599 
2600    UserLeave();
2601 
2602    return (SHORT)Ret;
2603 }
2604 
2605 
2606 DWORD
2607 APIENTRY
2608 NtUserGetKeyboardState(LPBYTE lpKeyState)
2609 {
2610    DWORD i, ret = TRUE;
2611    PTHREADINFO pti;
2612    PUSER_MESSAGE_QUEUE MessageQueue;
2613 
2614    UserEnterShared();
2615 
2616    pti = PsGetCurrentThreadWin32Thread();
2617    MessageQueue = pti->MessageQueue;
2618 
2619    _SEH2_TRY
2620    {
2621        /* Probe and copy key state to an array */
2622        ProbeForWrite(lpKeyState, 256 * sizeof(BYTE), 1);
2623        for (i = 0; i < 256; ++i)
2624        {
2625            lpKeyState[i] = 0;
2626            if (IS_KEY_DOWN(MessageQueue->afKeyState, i))
2627                lpKeyState[i] |= KS_DOWN_BIT;
2628            if (IS_KEY_LOCKED(MessageQueue->afKeyState, i))
2629                lpKeyState[i] |= KS_LOCK_BIT;
2630        }
2631    }
2632    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2633    {
2634        SetLastNtError(_SEH2_GetExceptionCode());
2635        ret = FALSE;
2636    }
2637    _SEH2_END;
2638 
2639    UserLeave();
2640 
2641    return ret;
2642 }
2643 
2644 BOOL
2645 APIENTRY
2646 NtUserSetKeyboardState(LPBYTE pKeyState)
2647 {
2648    UINT i;
2649    BOOL bRet = TRUE;
2650    PTHREADINFO pti;
2651    PUSER_MESSAGE_QUEUE MessageQueue;
2652 
2653    UserEnterExclusive();
2654 
2655    pti = PsGetCurrentThreadWin32Thread();
2656    MessageQueue = pti->MessageQueue;
2657 
2658    _SEH2_TRY
2659    {
2660        ProbeForRead(pKeyState, 256 * sizeof(BYTE), 1);
2661        for (i = 0; i < 256; ++i)
2662        {
2663             SET_KEY_DOWN(MessageQueue->afKeyState, i, pKeyState[i] & KS_DOWN_BIT);
2664             SET_KEY_LOCKED(MessageQueue->afKeyState, i, pKeyState[i] & KS_LOCK_BIT);
2665        }
2666    }
2667    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2668    {
2669        SetLastNtError(_SEH2_GetExceptionCode());
2670        bRet = FALSE;
2671    }
2672    _SEH2_END;
2673 
2674    UserLeave();
2675 
2676    return bRet;
2677 }
2678 
2679 /* EOF */
2680