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