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