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