[Win32SS]
[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)) != (WS_EX_LAYERED|WS_EX_TRANSPARENT) &&
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 // Check to see if this is attached.
643 if ( pti != MessageQueue->ptiMouse &&
644 MessageQueue->cThreads > 1 )
645 {
646 // Set the send pti to the message queue mouse pti.
647 pti = MessageQueue->ptiMouse;
648 }
649
650 if (Msg->message == WM_MOUSEMOVE)
651 {
652 /* Check if cursor should be visible */
653 if(hdcScreen &&
654 MessageQueue->CursorObject &&
655 MessageQueue->iCursorLevel >= 0)
656 {
657 /* Check if shape has changed */
658 if(CurInfo->CurrentCursorObject != MessageQueue->CursorObject)
659 {
660 /* Call GDI to set the new screen cursor */
661 GreSetPointerShape(hdcScreen,
662 MessageQueue->CursorObject->hbmAlpha ?
663 NULL : MessageQueue->CursorObject->hbmMask,
664 MessageQueue->CursorObject->hbmAlpha ?
665 MessageQueue->CursorObject->hbmAlpha : MessageQueue->CursorObject->hbmColor,
666 MessageQueue->CursorObject->xHotspot,
667 MessageQueue->CursorObject->yHotspot,
668 gpsi->ptCursor.x,
669 gpsi->ptCursor.y,
670 MessageQueue->CursorObject->hbmAlpha ? SPS_ALPHA : 0);
671
672 } else
673 GreMovePointer(hdcScreen, Msg->pt.x, Msg->pt.y);
674 }
675 /* Check if we have to hide cursor */
676 else if (CurInfo->ShowingCursor >= 0)
677 GreMovePointer(hdcScreen, -1, -1);
678
679 /* Update global cursor info */
680 CurInfo->ShowingCursor = MessageQueue->iCursorLevel;
681 CurInfo->CurrentCursorObject = MessageQueue->CursorObject;
682 gpqCursor = MessageQueue;
683
684 /* Mouse move is a special case */
685 MessageQueue->QF_flags |= QF_MOUSEMOVED;
686 gdwMouseMoveExtraInfo = dwExtraInfo;
687 gdwMouseMoveTimeStamp = Msg->time;
688 MsqWakeQueue(pti, QS_MOUSEMOVE, TRUE);
689 }
690 else
691 {
692 if (!IntGetCaptureWindow())
693 {
694 // ERR("ptiLastInput is set\n");
695 // ptiLastInput = pti; // Once this is set during Reboot or Shutdown, this prevents the exit window having foreground.
696 // Find all the Move Mouse calls and fix mouse set active focus issues......
697 }
698
699 // Post mouse move before posting mouse buttons, keep it in sync.
700 if (pti->MessageQueue->QF_flags & QF_MOUSEMOVED)
701 {
702 IntCoalesceMouseMove(pti);
703 }
704
705 TRACE("Posting mouse message to hwnd=%p!\n", UserHMGetHandle(pwnd));
706 MsqPostMessage(pti, Msg, TRUE, QS_MOUSEBUTTON, 0, dwExtraInfo);
707 }
708 }
709 else if (hdcScreen)
710 {
711 /* always show cursor on background; FIXME: set default pointer */
712 GreMovePointer(hdcScreen, Msg->pt.x, Msg->pt.y);
713 CurInfo->ShowingCursor = 0;
714 }
715 }
716
717 PUSER_MESSAGE FASTCALL
718 MsqCreateMessage(LPMSG Msg)
719 {
720 PUSER_MESSAGE Message;
721
722 Message = ExAllocateFromPagedLookasideList(pgMessageLookasideList);
723 if (!Message)
724 {
725 return NULL;
726 }
727
728 RtlZeroMemory(Message, sizeof(*Message));
729 RtlMoveMemory(&Message->Msg, Msg, sizeof(MSG));
730
731 return Message;
732 }
733
734 VOID FASTCALL
735 MsqDestroyMessage(PUSER_MESSAGE Message)
736 {
737 if (Message->pti == NULL)
738 {
739 ERR("Double Free Message\n");
740 return;
741 }
742 Message->pti = NULL;
743 ExFreeToPagedLookasideList(pgMessageLookasideList, Message);
744 }
745
746 BOOLEAN FASTCALL
747 co_MsqDispatchOneSentMessage(
748 _In_ PTHREADINFO pti)
749 {
750 PUSER_SENT_MESSAGE SaveMsg, Message;
751 PLIST_ENTRY Entry;
752 BOOL Ret;
753 LRESULT Result = 0;
754
755 ASSERT(pti == PsGetCurrentThreadWin32Thread());
756
757 if (IsListEmpty(&pti->SentMessagesListHead))
758 {
759 return(FALSE);
760 }
761
762 /* remove it from the list of pending messages */
763 Entry = RemoveHeadList(&pti->SentMessagesListHead);
764 Message = CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry);
765
766 SaveMsg = pti->pusmCurrent;
767 pti->pusmCurrent = Message;
768
769 // Processing a message sent to it from another thread.
770 if ( ( Message->ptiSender && pti != Message->ptiSender) ||
771 ( Message->ptiCallBackSender && pti != Message->ptiCallBackSender ))
772 { // most likely, but, to be sure.
773 pti->pcti->CTI_flags |= CTI_INSENDMESSAGE; // Let the user know...
774 }
775
776 /* insert it to the list of messages that are currently dispatched by this
777 message queue */
778 InsertTailList(&pti->LocalDispatchingMessagesHead, &Message->ListEntry);
779
780 ClearMsgBitsMask(pti, Message->QS_Flags);
781
782 if (Message->HookMessage == MSQ_ISHOOK)
783 { // Direct Hook Call processor
784 Result = co_CallHook( Message->Msg.message, // HookId
785 (INT)(INT_PTR)Message->Msg.hwnd, // Code
786 Message->Msg.wParam,
787 Message->Msg.lParam);
788 }
789 else if(Message->HookMessage == MSQ_INJECTMODULE)
790 {
791 Result = IntLoadHookModule(Message->Msg.message,
792 (HHOOK)Message->Msg.lParam,
793 Message->Msg.wParam);
794 }
795 else if ((Message->CompletionCallback) &&
796 (Message->ptiCallBackSender == pti))
797 { /* Call the callback routine */
798 if (Message->QS_Flags & QS_SMRESULT)
799 {
800 co_IntCallSentMessageCallback(Message->CompletionCallback,
801 Message->Msg.hwnd,
802 Message->Msg.message,
803 Message->CompletionCallbackContext,
804 Message->lResult);
805 /* Set callback to NULL to prevent reentry */
806 Message->CompletionCallback = NULL;
807 }
808 else
809 {
810 /* The message has not been processed yet, reinsert it. */
811 RemoveEntryList(&Message->ListEntry);
812 InsertTailList(&Message->ptiCallBackSender->SentMessagesListHead, &Message->ListEntry);
813 TRACE("Callback Message not processed yet. Requeuing the message\n");
814 Ret = FALSE;
815 goto Exit;
816 }
817 }
818 else
819 { /* Call the window procedure. */
820 Result = co_IntSendMessage( Message->Msg.hwnd,
821 Message->Msg.message,
822 Message->Msg.wParam,
823 Message->Msg.lParam);
824 }
825
826 /* remove the message from the local dispatching list, because it doesn't need
827 to be cleaned up on thread termination anymore */
828 RemoveEntryList(&Message->ListEntry);
829
830 /* If the message is a callback, insert it in the callback senders MessageQueue */
831 if (Message->CompletionCallback)
832 {
833 if (Message->ptiCallBackSender)
834 {
835 Message->lResult = Result;
836 Message->QS_Flags |= QS_SMRESULT;
837
838 /* insert it in the callers message queue */
839 InsertTailList(&Message->ptiCallBackSender->SentMessagesListHead, &Message->ListEntry);
840 MsqWakeQueue(Message->ptiCallBackSender, QS_SENDMESSAGE, TRUE);
841 }
842 Ret = TRUE;
843 goto Exit;
844 }
845
846 /* remove the message from the dispatching list if needed, so lock the sender's message queue */
847 if (Message->ptiSender && !(Message->ptiSender->TIF_flags & TIF_INCLEANUP))
848 {
849 if (!IsListEmpty(&Message->DispatchingListEntry))
850 {
851 /* only remove it from the dispatching list if not already removed by a timeout */
852 RemoveEntryList(&Message->DispatchingListEntry);
853 InitializeListHead(&Message->DispatchingListEntry);
854 }
855 }
856
857 /* still keep the sender's message queue locked, so the sender can't exit the
858 MsqSendMessage() function (if timed out) */
859 if (Message->QS_Flags & QS_SMRESULT)
860 {
861 Result = Message->lResult;
862 }
863
864 /* Let the sender know the result. */
865 if (Message->Result != NULL)
866 {
867 *Message->Result = Result;
868 }
869
870 if (Message->HasPackedLParam)
871 {
872 if (Message->Msg.lParam)
873 ExFreePool((PVOID)Message->Msg.lParam);
874 }
875
876 /* Notify the sender. */
877 if (Message->CompletionEvent != NULL)
878 {
879 KeSetEvent(Message->CompletionEvent, IO_NO_INCREMENT, FALSE);
880 }
881
882 /* free the message */
883 ExFreePoolWithTag(Message, TAG_USRMSG);
884 Ret = TRUE;
885 Exit:
886 /* do not hangup on the user if this is reentering */
887 if (!SaveMsg) pti->pcti->CTI_flags &= ~CTI_INSENDMESSAGE;
888 pti->pusmCurrent = SaveMsg;
889
890 return Ret;
891 }
892
893 VOID APIENTRY
894 MsqRemoveWindowMessagesFromQueue(PWND Window)
895 {
896 PTHREADINFO pti;
897 PUSER_SENT_MESSAGE SentMessage;
898 PUSER_MESSAGE PostedMessage;
899 PLIST_ENTRY CurrentEntry, ListHead;
900
901 ASSERT(Window);
902
903 pti = Window->head.pti;
904
905 /* remove the posted messages for this window */
906 CurrentEntry = pti->PostedMessagesListHead.Flink;
907 ListHead = &pti->PostedMessagesListHead;
908 while (CurrentEntry != ListHead)
909 {
910 PostedMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
911 ListEntry);
912 if (PostedMessage->Msg.hwnd == Window->head.h)
913 {
914 if (PostedMessage->Msg.message == WM_QUIT && pti->QuitPosted == 0)
915 {
916 pti->QuitPosted = 1;
917 pti->exitCode = PostedMessage->Msg.wParam;
918 }
919 RemoveEntryList(&PostedMessage->ListEntry);
920 ClearMsgBitsMask(pti, PostedMessage->QS_Flags);
921 MsqDestroyMessage(PostedMessage);
922 CurrentEntry = pti->PostedMessagesListHead.Flink;
923 }
924 else
925 {
926 CurrentEntry = CurrentEntry->Flink;
927 }
928 }
929
930 /* remove the sent messages for this window */
931 CurrentEntry = pti->SentMessagesListHead.Flink;
932 ListHead = &pti->SentMessagesListHead;
933 while (CurrentEntry != ListHead)
934 {
935 SentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE, ListEntry);
936
937 if(SentMessage->Msg.hwnd == Window->head.h)
938 {
939 TRACE("Notify the sender and remove a message from the queue that had not been dispatched\n");
940
941 RemoveEntryList(&SentMessage->ListEntry);
942 ClearMsgBitsMask(pti, SentMessage->QS_Flags);
943
944 /* Only if the message has a sender was the queue referenced */
945 if ((SentMessage->ptiSender) &&
946 (!IsListEmpty(&SentMessage->DispatchingListEntry)))
947 {
948 RemoveEntryList(&SentMessage->DispatchingListEntry);
949 InitializeListHead(&SentMessage->DispatchingListEntry);
950 }
951
952 /* wake the sender's thread */
953 if (SentMessage->CompletionEvent != NULL)
954 {
955 KeSetEvent(SentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
956 }
957
958 if (SentMessage->HasPackedLParam)
959 {
960 if (SentMessage->Msg.lParam)
961 ExFreePool((PVOID)SentMessage->Msg.lParam);
962 }
963
964 /* free the message */
965 ExFreePoolWithTag(SentMessage, TAG_USRMSG);
966
967 CurrentEntry = pti->SentMessagesListHead.Flink;
968 }
969 else
970 {
971 CurrentEntry = CurrentEntry->Flink;
972 }
973 }
974 }
975
976 BOOL FASTCALL
977 co_MsqSendMessageAsync(PTHREADINFO ptiReceiver,
978 HWND hwnd,
979 UINT Msg,
980 WPARAM wParam,
981 LPARAM lParam,
982 SENDASYNCPROC CompletionCallback,
983 ULONG_PTR CompletionCallbackContext,
984 BOOL HasPackedLParam,
985 INT HookMessage)
986 {
987
988 PTHREADINFO ptiSender;
989 PUSER_SENT_MESSAGE Message;
990
991 if(!(Message = ExAllocatePoolWithTag(NonPagedPool, sizeof(USER_SENT_MESSAGE), TAG_USRMSG)))
992 {
993 ERR("MsqSendMessage(): Not enough memory to allocate a message");
994 return FALSE;
995 }
996
997 ptiSender = PsGetCurrentThreadWin32Thread();
998
999 Message->Msg.hwnd = hwnd;
1000 Message->Msg.message = Msg;
1001 Message->Msg.wParam = wParam;
1002 Message->Msg.lParam = lParam;
1003 Message->CompletionEvent = NULL;
1004 Message->Result = 0;
1005 Message->lResult = 0;
1006 Message->ptiReceiver = ptiReceiver;
1007 Message->ptiSender = NULL;
1008 Message->ptiCallBackSender = ptiSender;
1009 InitializeListHead(&Message->DispatchingListEntry);
1010 Message->CompletionCallback = CompletionCallback;
1011 Message->CompletionCallbackContext = CompletionCallbackContext;
1012 Message->HookMessage = HookMessage;
1013 Message->HasPackedLParam = HasPackedLParam;
1014 Message->QS_Flags = QS_SENDMESSAGE;
1015
1016 InsertTailList(&ptiReceiver->SentMessagesListHead, &Message->ListEntry);
1017 MsqWakeQueue(ptiReceiver, QS_SENDMESSAGE, TRUE);
1018
1019 return TRUE;
1020 }
1021
1022 NTSTATUS FASTCALL
1023 co_MsqSendMessage(PTHREADINFO ptirec,
1024 HWND Wnd,
1025 UINT Msg,
1026 WPARAM wParam,
1027 LPARAM lParam,
1028 UINT uTimeout,
1029 BOOL Block,
1030 INT HookMessage,
1031 ULONG_PTR *uResult)
1032 {
1033 PTHREADINFO pti;
1034 PUSER_SENT_MESSAGE Message;
1035 KEVENT CompletionEvent;
1036 NTSTATUS WaitStatus;
1037 LARGE_INTEGER Timeout;
1038 PLIST_ENTRY Entry;
1039 PWND pWnd;
1040 LRESULT Result = 0; //// Result could be trashed. ////
1041
1042 pti = PsGetCurrentThreadWin32Thread();
1043 ASSERT(pti != ptirec);
1044 ASSERT(ptirec->pcti); // Send must have a client side to receive it!!!!
1045
1046 /* Don't send from or to a dying thread */
1047 if (pti->TIF_flags & TIF_INCLEANUP || ptirec->TIF_flags & TIF_INCLEANUP)
1048 {
1049 // Unless we are dying and need to tell our parents.
1050 if (pti->TIF_flags & TIF_INCLEANUP && !(ptirec->TIF_flags & TIF_INCLEANUP))
1051 {
1052 // Parent notify is the big one. Fire and forget!
1053 TRACE("Send message from dying thread %u\n", Msg);
1054 co_MsqSendMessageAsync(ptirec, Wnd, Msg, wParam, lParam, NULL, 0, FALSE, HookMessage);
1055 }
1056 if (uResult) *uResult = -1;
1057 TRACE("MsqSM: Msg %u Current pti %lu or Rec pti %lu\n", Msg, pti->TIF_flags & TIF_INCLEANUP, ptirec->TIF_flags & TIF_INCLEANUP);
1058 return STATUS_UNSUCCESSFUL;
1059 }
1060
1061 // Should we do the same for No Wait?
1062 if ( HookMessage == MSQ_NORMAL )
1063 {
1064 pWnd = ValidateHwndNoErr(Wnd);
1065
1066 // These can not cross International Border lines!
1067 if ( pti->ppi != ptirec->ppi && pWnd )
1068 {
1069 switch(Msg)
1070 {
1071 // Handle the special case when working with password transfers across bordering processes.
1072 case EM_GETLINE:
1073 case EM_SETPASSWORDCHAR:
1074 case WM_GETTEXT:
1075 // Look for edit controls setup for passwords.
1076 if ( gpsi->atomSysClass[ICLS_EDIT] == pWnd->pcls->atomClassName && // Use atomNVClassName.
1077 pWnd->style & ES_PASSWORD )
1078 {
1079 if (uResult) *uResult = -1;
1080 ERR("Running across the border without a passport!\n");
1081 EngSetLastError(ERROR_ACCESS_DENIED);
1082 return STATUS_UNSUCCESSFUL;
1083 }
1084 break;
1085 case WM_NOTIFY:
1086 if (uResult) *uResult = -1;
1087 ERR("Running across the border without a passport!\n");
1088 return STATUS_UNSUCCESSFUL;
1089 }
1090 }
1091
1092 // These can not cross State lines!
1093 if ( Msg == WM_CREATE || Msg == WM_NCCREATE )
1094 {
1095 if (uResult) *uResult = -1;
1096 ERR("Can not tell the other State we have Create!\n");
1097 return STATUS_UNSUCCESSFUL;
1098 }
1099 }
1100
1101 if(!(Message = ExAllocatePoolWithTag(PagedPool, sizeof(USER_SENT_MESSAGE), TAG_USRMSG)))
1102 {
1103 ERR("MsqSendMessage(): Not enough memory to allocate a message\n");
1104 return STATUS_INSUFFICIENT_RESOURCES;
1105 }
1106
1107 KeInitializeEvent(&CompletionEvent, NotificationEvent, FALSE);
1108
1109 Timeout.QuadPart = Int32x32To64(-10000,uTimeout); // Pass SMTO test with a TO of 0x80000000.
1110 TRACE("Timeout val %lld\n",Timeout.QuadPart)
1111
1112 /* FIXME: Increase reference counter of sender's message queue here */
1113
1114 Message->Msg.hwnd = Wnd;
1115 Message->Msg.message = Msg;
1116 Message->Msg.wParam = wParam;
1117 Message->Msg.lParam = lParam;
1118 Message->CompletionEvent = &CompletionEvent;
1119 Message->Result = &Result;
1120 Message->lResult = 0;
1121 Message->QS_Flags = 0;
1122 Message->ptiReceiver = ptirec;
1123 Message->ptiSender = pti;
1124 Message->ptiCallBackSender = NULL;
1125 Message->CompletionCallback = NULL;
1126 Message->CompletionCallbackContext = 0;
1127 Message->HookMessage = HookMessage;
1128 Message->HasPackedLParam = FALSE;
1129
1130 /* Add it to the list of pending messages */
1131 InsertTailList(&pti->DispatchingMessagesHead, &Message->DispatchingListEntry);
1132
1133 /* Queue it in the destination's message queue */
1134 InsertTailList(&ptirec->SentMessagesListHead, &Message->ListEntry);
1135
1136 Message->QS_Flags = QS_SENDMESSAGE;
1137 MsqWakeQueue(ptirec, QS_SENDMESSAGE, TRUE);
1138
1139 /* We can't access the Message anymore since it could have already been deleted! */
1140
1141 if (Block)
1142 {
1143 PVOID WaitObjects[2];
1144
1145 WaitObjects[0] = &CompletionEvent; // Wait 0
1146 WaitObjects[1] = ptirec->pEThread; // Wait 1
1147
1148 UserLeaveCo();
1149
1150 WaitStatus = KeWaitForMultipleObjects(2, WaitObjects, WaitAny, UserRequest,
1151 UserMode, FALSE, (uTimeout ? &Timeout : NULL), NULL);
1152
1153 UserEnterCo();
1154
1155 if (WaitStatus == STATUS_TIMEOUT)
1156 {
1157 /* Look up if the message has not yet dispatched, if so
1158 make sure it can't pass a result and it must not set the completion event anymore */
1159 Entry = ptirec->SentMessagesListHead.Flink;
1160 while (Entry != &ptirec->SentMessagesListHead)
1161 {
1162 if (CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry) == Message)
1163 {
1164 /* We can access Message here, it's secure because the message queue is locked
1165 and the message is still hasn't been dispatched */
1166 Message->CompletionEvent = NULL;
1167 Message->Result = NULL;
1168 RemoveEntryList(&Message->ListEntry);
1169 RemoveEntryList(&Message->DispatchingListEntry);
1170 ClearMsgBitsMask(ptirec, Message->QS_Flags);
1171 ExFreePoolWithTag(Message, TAG_USRMSG);
1172 break;
1173 }
1174 Entry = Entry->Flink;
1175 }
1176
1177 TRACE("MsqSendMessage (blocked) timed out 1 Status %lx\n", WaitStatus);
1178 }
1179 // Receiving thread passed on and left us hanging with issues still pending.
1180 else if (WaitStatus == STATUS_WAIT_1)
1181 {
1182 ERR("Bk Receiving Thread woken up dead!\n");
1183 Entry = pti->DispatchingMessagesHead.Flink;
1184 while (Entry != &pti->DispatchingMessagesHead)
1185 {
1186 if (CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, DispatchingListEntry) == Message)
1187 {
1188 Message->CompletionEvent = NULL;
1189 Message->Result = NULL;
1190 RemoveEntryList(&Message->DispatchingListEntry);
1191 InitializeListHead(&Message->DispatchingListEntry);
1192 break;
1193 }
1194 Entry = Entry->Flink;
1195 }
1196 }
1197 while (co_MsqDispatchOneSentMessage(pti))
1198 ;
1199 }
1200 else
1201 {
1202 PVOID WaitObjects[3];
1203
1204 WaitObjects[0] = &CompletionEvent; // Wait 0
1205 WaitObjects[1] = pti->pEventQueueServer; // Wait 1
1206 WaitObjects[2] = ptirec->pEThread; // Wait 2
1207
1208 do
1209 {
1210 UserLeaveCo();
1211
1212 WaitStatus = KeWaitForMultipleObjects(3, WaitObjects, WaitAny, UserRequest,
1213 UserMode, FALSE, (uTimeout ? &Timeout : NULL), NULL);
1214
1215 UserEnterCo();
1216
1217 if (WaitStatus == STATUS_TIMEOUT)
1218 {
1219 /* Look up if the message has not yet been dispatched, if so
1220 make sure it can't pass a result and it must not set the completion event anymore */
1221 Entry = ptirec->SentMessagesListHead.Flink;
1222 while (Entry != &ptirec->SentMessagesListHead)
1223 {
1224 if (CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry) == Message)
1225 {
1226 /* We can access Message here, it's secure because the message queue is locked
1227 and the message is still hasn't been dispatched */
1228 Message->CompletionEvent = NULL;
1229 Message->Result = NULL;
1230 RemoveEntryList(&Message->ListEntry);
1231 RemoveEntryList(&Message->DispatchingListEntry);
1232 InitializeListHead(&Message->DispatchingListEntry);
1233 ClearMsgBitsMask(ptirec, Message->QS_Flags);
1234 ExFreePoolWithTag(Message, TAG_USRMSG);
1235 break;
1236 }
1237 Entry = Entry->Flink;
1238 }
1239
1240 TRACE("MsqSendMessage timed out 2 Status %lx\n", WaitStatus);
1241
1242 break;
1243 }
1244 // Receiving thread passed on and left us hanging with issues still pending.
1245 else if (WaitStatus == STATUS_WAIT_2)
1246 {
1247 ERR("NB Receiving Thread woken up dead!\n");
1248 Entry = pti->DispatchingMessagesHead.Flink;
1249 while (Entry != &pti->DispatchingMessagesHead)
1250 {
1251 if (CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, DispatchingListEntry) == Message)
1252 {
1253 Message->CompletionEvent = NULL;
1254 Message->Result = NULL;
1255 RemoveEntryList(&Message->DispatchingListEntry);
1256 InitializeListHead(&Message->DispatchingListEntry);
1257 break;
1258 }
1259 Entry = Entry->Flink;
1260 }
1261 break;
1262 }
1263
1264 if (WaitStatus == STATUS_USER_APC) break;
1265
1266 while (co_MsqDispatchOneSentMessage(pti))
1267 ;
1268 } while (WaitStatus == STATUS_WAIT_1);
1269 }
1270
1271 if (WaitStatus == STATUS_USER_APC)
1272 {
1273 // The current thread is dying!
1274 TRACE("User APC\n");
1275 co_IntDeliverUserAPC();
1276 ERR("User APC Returned\n"); // Should not see this message.
1277 }
1278
1279 if (WaitStatus != STATUS_TIMEOUT)
1280 {
1281 if (uResult)
1282 {
1283 *uResult = (STATUS_WAIT_0 == WaitStatus ? Result : -1);
1284 }
1285 }
1286
1287 return WaitStatus;
1288 }
1289
1290 VOID FASTCALL
1291 MsqPostMessage(PTHREADINFO pti,
1292 MSG* Msg,
1293 BOOLEAN HardwareMessage,
1294 DWORD MessageBits,
1295 DWORD dwQEvent,
1296 LONG_PTR ExtraInfo)
1297 {
1298 PUSER_MESSAGE Message;
1299 PUSER_MESSAGE_QUEUE MessageQueue;
1300
1301 if ( pti->TIF_flags & TIF_INCLEANUP || pti->MessageQueue->QF_flags & QF_INDESTROY )
1302 {
1303 ERR("Post Msg; Thread or Q is Dead!\n");
1304 return;
1305 }
1306
1307 if(!(Message = MsqCreateMessage(Msg)))
1308 {
1309 return;
1310 }
1311
1312 MessageQueue = pti->MessageQueue;
1313
1314 if (!HardwareMessage)
1315 {
1316 InsertTailList(&pti->PostedMessagesListHead, &Message->ListEntry);
1317 }
1318 else
1319 {
1320 InsertTailList(&MessageQueue->HardwareMessagesListHead, &Message->ListEntry);
1321 }
1322
1323 if (Msg->message == WM_HOTKEY) MessageBits |= QS_HOTKEY; // Justin Case, just set it.
1324 Message->dwQEvent = dwQEvent;
1325 Message->ExtraInfo = ExtraInfo;
1326 Message->QS_Flags = MessageBits;
1327 Message->pti = pti;
1328 MsqWakeQueue(pti, MessageBits, TRUE);
1329 }
1330
1331 VOID FASTCALL
1332 MsqPostQuitMessage(PTHREADINFO pti, ULONG ExitCode)
1333 {
1334 pti->QuitPosted = TRUE;
1335 pti->exitCode = ExitCode;
1336 MsqWakeQueue(pti, QS_POSTMESSAGE|QS_ALLPOSTMESSAGE, TRUE);
1337 }
1338
1339 /***********************************************************************
1340 * MsqSendParentNotify
1341 *
1342 * Send a WM_PARENTNOTIFY to all ancestors of the given window, unless
1343 * the window has the WS_EX_NOPARENTNOTIFY style.
1344 */
1345 static void MsqSendParentNotify( PWND pwnd, WORD event, WORD idChild, POINT pt )
1346 {
1347 PWND pwndDesktop = UserGetDesktopWindow();
1348
1349 /* pt has to be in the client coordinates of the parent window */
1350 pt.x += pwndDesktop->rcClient.left - pwnd->rcClient.left;
1351 pt.y += pwndDesktop->rcClient.top - pwnd->rcClient.top;
1352
1353 for (;;)
1354 {
1355 PWND pwndParent;
1356
1357 if (!(pwnd->style & WS_CHILD)) break;
1358 if (pwnd->ExStyle & WS_EX_NOPARENTNOTIFY) break;
1359 if (!(pwndParent = IntGetParent(pwnd))) break;
1360 if (pwndParent == pwndDesktop) break;
1361 pt.x += pwnd->rcClient.left - pwndParent->rcClient.left;
1362 pt.y += pwnd->rcClient.top - pwndParent->rcClient.top;
1363
1364 pwnd = pwndParent;
1365 co_IntSendMessage( UserHMGetHandle(pwnd), WM_PARENTNOTIFY,
1366 MAKEWPARAM( event, idChild ), MAKELPARAM( pt.x, pt.y ) );
1367 }
1368 }
1369
1370 VOID
1371 FASTCALL
1372 IntTrackMouseMove(PWND pwndTrack, PDESKTOP pDesk, PMSG msg, USHORT hittest)
1373 {
1374 // PWND pwndTrack = IntChildrenWindowFromPoint(pwndMsg, msg->pt.x, msg->pt.y);
1375 // hittest = (USHORT)GetNCHitEx(pwndTrack, msg->pt); /// @todo WTF is this???
1376
1377 if ( pDesk->spwndTrack != pwndTrack || // Change with tracking window or
1378 msg->message != WM_MOUSEMOVE || // Mouse click changes or
1379 pDesk->htEx != hittest) // Change in current hit test states.
1380 {
1381 TRACE("ITMM: Track Mouse Move!\n");
1382
1383 /* Handle only the changing window track and mouse move across a border. */
1384 if ( pDesk->spwndTrack != pwndTrack ||
1385 (pDesk->htEx == HTCLIENT) ^ (hittest == HTCLIENT) )
1386 {
1387 TRACE("ITMM: Another Wnd %d or Across Border %d\n",
1388 pDesk->spwndTrack != pwndTrack,(pDesk->htEx == HTCLIENT) ^ (hittest == HTCLIENT));
1389
1390 if ( pDesk->dwDTFlags & DF_TME_LEAVE )
1391 UserPostMessage( UserHMGetHandle(pDesk->spwndTrack),
1392 (pDesk->htEx != HTCLIENT) ? WM_NCMOUSELEAVE : WM_MOUSELEAVE,
1393 0, 0);
1394
1395 if ( pDesk->dwDTFlags & DF_TME_HOVER )
1396 IntKillTimer(pDesk->spwndTrack, ID_EVENT_SYSTIMER_MOUSEHOVER, TRUE);
1397
1398 /* Clear the flags to sign a change. */
1399 pDesk->dwDTFlags &= ~(DF_TME_LEAVE|DF_TME_HOVER);
1400 }
1401 /* Set the Track window and hit test. */
1402 pDesk->spwndTrack = pwndTrack;
1403 pDesk->htEx = hittest;
1404 }
1405
1406 /* Reset, Same Track window, Hover set and Mouse Clicks or Clobbered Hover box. */
1407 if ( pDesk->spwndTrack == pwndTrack &&
1408 ( msg->message != WM_MOUSEMOVE || !RECTL_bPointInRect(&pDesk->rcMouseHover, msg->pt.x, msg->pt.y)) &&
1409 pDesk->dwDTFlags & DF_TME_HOVER )
1410 {
1411 TRACE("ITMM: Reset Hover points!\n");
1412 // Restart timer for the hover period.
1413 IntSetTimer(pDesk->spwndTrack, ID_EVENT_SYSTIMER_MOUSEHOVER, pDesk->dwMouseHoverTime, SystemTimerProc, TMRF_SYSTEM);
1414 // Reset desktop mouse hover from the system default hover rectangle.
1415 RECTL_vSetRect(&pDesk->rcMouseHover,
1416 msg->pt.x - gspv.iMouseHoverWidth / 2,
1417 msg->pt.y - gspv.iMouseHoverHeight / 2,
1418 msg->pt.x + gspv.iMouseHoverWidth / 2,
1419 msg->pt.y + gspv.iMouseHoverHeight / 2);
1420 }
1421 }
1422
1423 BOOL co_IntProcessMouseMessage(MSG* msg, BOOL* RemoveMessages, BOOL* NotForUs, UINT first, UINT last)
1424 {
1425 MSG clk_msg;
1426 POINT pt;
1427 UINT message;
1428 USHORT hittest;
1429 EVENTMSG event;
1430 MOUSEHOOKSTRUCT hook;
1431 BOOL eatMsg = FALSE;
1432
1433 PWND pwndMsg, pwndDesktop;
1434 PUSER_MESSAGE_QUEUE MessageQueue;
1435 PTHREADINFO pti;
1436 PSYSTEM_CURSORINFO CurInfo;
1437 PDESKTOP pDesk;
1438
1439 pti = PsGetCurrentThreadWin32Thread();
1440 pwndDesktop = UserGetDesktopWindow();
1441 MessageQueue = pti->MessageQueue;
1442 CurInfo = IntGetSysCursorInfo();
1443 pwndMsg = ValidateHwndNoErr(msg->hwnd);
1444 clk_msg = MessageQueue->msgDblClk;
1445 pDesk = pwndDesktop->head.rpdesk;
1446
1447 /* find the window to dispatch this mouse message to */
1448 if (MessageQueue->spwndCapture)
1449 {
1450 hittest = HTCLIENT;
1451 pwndMsg = MessageQueue->spwndCapture;
1452 }
1453 else
1454 {
1455 /*
1456 Start with null window. See wine win.c:test_mouse_input:WM_COMMAND tests.
1457 */
1458 pwndMsg = co_WinPosWindowFromPoint( NULL, &msg->pt, &hittest, FALSE);
1459 }
1460
1461 TRACE("Got mouse message for %p, hittest: 0x%x\n", msg->hwnd, hittest);
1462
1463 // Null window or not the same "Hardware" message queue.
1464 if (pwndMsg == NULL || pwndMsg->head.pti->MessageQueue != MessageQueue)
1465 {
1466 // Crossing a boundary, so set cursor. See default message queue cursor.
1467 UserSetCursor(SYSTEMCUR(ARROW), FALSE);
1468 /* Remove and ignore the message */
1469 *RemoveMessages = TRUE;
1470 return FALSE;
1471 }
1472
1473 // Check to see if this is attached,
1474 if ( pwndMsg->head.pti != pti && // window thread is not current,
1475 MessageQueue->cThreads > 1 ) // and is attached...
1476 {
1477 // This is not for us and we should leave so the other thread can check for messages!!!
1478 *NotForUs = TRUE;
1479 *RemoveMessages = TRUE;
1480 return FALSE;
1481 }
1482
1483 if ( MessageQueue == gpqCursor ) // Cursor must use the same Queue!
1484 {
1485 IntTrackMouseMove(pwndMsg, pDesk, msg, hittest);
1486 }
1487 else
1488 {
1489 ERR("Not the same cursor!\n");
1490 }
1491
1492 msg->hwnd = UserHMGetHandle(pwndMsg);
1493
1494 pt = msg->pt;
1495 message = msg->message;
1496
1497 /* Note: windows has no concept of a non-client wheel message */
1498 if (message != WM_MOUSEWHEEL)
1499 {
1500 if (hittest != HTCLIENT)
1501 {
1502 message += WM_NCMOUSEMOVE - WM_MOUSEMOVE;
1503 msg->wParam = hittest; // Caution! This might break wParam check in DblClk.
1504 }
1505 else
1506 {
1507 /* coordinates don't get translated while tracking a menu */
1508 /* FIXME: should differentiate popups and top-level menus */
1509 if (!(MessageQueue->MenuOwner))
1510 {
1511 pt.x += pwndDesktop->rcClient.left - pwndMsg->rcClient.left;
1512 pt.y += pwndDesktop->rcClient.top - pwndMsg->rcClient.top;
1513 }
1514 }
1515 }
1516 msg->lParam = MAKELONG( pt.x, pt.y );
1517
1518 /* translate double clicks */
1519
1520 if ((msg->message == WM_LBUTTONDOWN) ||
1521 (msg->message == WM_RBUTTONDOWN) ||
1522 (msg->message == WM_MBUTTONDOWN) ||
1523 (msg->message == WM_XBUTTONDOWN))
1524 {
1525 BOOL update = *RemoveMessages;
1526
1527 /* translate double clicks -
1528 * note that ...MOUSEMOVEs can slip in between
1529 * ...BUTTONDOWN and ...BUTTONDBLCLK messages */
1530
1531 if ((MessageQueue->MenuOwner || MessageQueue->MoveSize) ||
1532 hittest != HTCLIENT ||
1533 (pwndMsg->pcls->style & CS_DBLCLKS))
1534 {
1535 if ((msg->message == clk_msg.message) &&
1536 (msg->hwnd == clk_msg.hwnd) &&
1537 // Only worry about XButton wParam.
1538 (msg->message != WM_XBUTTONDOWN || GET_XBUTTON_WPARAM(msg->wParam) == GET_XBUTTON_WPARAM(clk_msg.wParam)) &&
1539 ((msg->time - clk_msg.time) < (ULONG)gspv.iDblClickTime) &&
1540 (abs(msg->pt.x - clk_msg.pt.x) < UserGetSystemMetrics(SM_CXDOUBLECLK)/2) &&
1541 (abs(msg->pt.y - clk_msg.pt.y) < UserGetSystemMetrics(SM_CYDOUBLECLK)/2))
1542 {
1543 message += (WM_LBUTTONDBLCLK - WM_LBUTTONDOWN);
1544 if (update)
1545 {
1546 MessageQueue->msgDblClk.message = 0; /* clear the double click conditions */
1547 update = FALSE;
1548 }
1549 }
1550 }
1551
1552 if (!((first == 0 && last == 0) || (message >= first || message <= last)))
1553 {
1554 TRACE("Message out of range!!!\n");
1555 return FALSE;
1556 }
1557
1558 /* update static double click conditions */
1559 if (update) MessageQueue->msgDblClk = *msg;
1560 }
1561 else
1562 {
1563 if (!((first == 0 && last == 0) || (message >= first || message <= last)))
1564 {
1565 TRACE("Message out of range!!!\n");
1566 return FALSE;
1567 }
1568
1569 // Update mouse move down keys.
1570 if (message == WM_MOUSEMOVE)
1571 {
1572 msg->wParam = MsqGetDownKeyState(MessageQueue);
1573 }
1574 }
1575
1576 if (gspv.bMouseClickLock)
1577 {
1578 BOOL IsClkLck = FALSE;
1579
1580 if(msg->message == WM_LBUTTONUP)
1581 {
1582 IsClkLck = ((msg->time - CurInfo->ClickLockTime) >= gspv.dwMouseClickLockTime);
1583 if (IsClkLck && (!CurInfo->ClickLockActive))
1584 {
1585 CurInfo->ClickLockActive = TRUE;
1586 }
1587 }
1588 else if (msg->message == WM_LBUTTONDOWN)
1589 {
1590 if (CurInfo->ClickLockActive)
1591 {
1592 IsClkLck = TRUE;
1593 CurInfo->ClickLockActive = FALSE;
1594 }
1595
1596 CurInfo->ClickLockTime = msg->time;
1597 }
1598
1599 if(IsClkLck)
1600 {
1601 /* Remove and ignore the message */
1602 *RemoveMessages = TRUE;
1603 TRACE("Remove and ignore the message\n");
1604 return FALSE;
1605 }
1606 }
1607
1608 /* message is accepted now (but still get dropped) */
1609
1610 event.message = msg->message;
1611 event.time = msg->time;
1612 event.hwnd = msg->hwnd;
1613 event.paramL = msg->pt.x;
1614 event.paramH = msg->pt.y;
1615 co_HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&event );
1616
1617 hook.pt = msg->pt;
1618 hook.hwnd = msg->hwnd;
1619 hook.wHitTestCode = hittest;
1620 hook.dwExtraInfo = 0 /* extra_info */ ;
1621 if (co_HOOK_CallHooks( WH_MOUSE, *RemoveMessages ? HC_ACTION : HC_NOREMOVE,
1622 message, (LPARAM)&hook ))
1623 {
1624 hook.pt = msg->pt;
1625 hook.hwnd = msg->hwnd;
1626 hook.wHitTestCode = hittest;
1627 hook.dwExtraInfo = 0 /* extra_info */ ;
1628 co_HOOK_CallHooks( WH_CBT, HCBT_CLICKSKIPPED, message, (LPARAM)&hook );
1629
1630 ERR("WH_MOUSE dropped mouse message!\n");
1631
1632 /* Remove and skip message */
1633 *RemoveMessages = TRUE;
1634 return FALSE;
1635 }
1636
1637 if ((hittest == (USHORT)HTERROR) || (hittest == (USHORT)HTNOWHERE))
1638 {
1639 co_IntSendMessage( msg->hwnd, WM_SETCURSOR, (WPARAM)msg->hwnd, MAKELONG( hittest, msg->message ));
1640
1641 /* Remove and skip message */
1642 *RemoveMessages = TRUE;
1643 return FALSE;
1644 }
1645
1646 if ((*RemoveMessages == FALSE) || MessageQueue->spwndCapture)
1647 {
1648 /* Accept the message */
1649 msg->message = message;
1650 return TRUE;
1651 }
1652
1653 if ((msg->message == WM_LBUTTONDOWN) ||
1654 (msg->message == WM_RBUTTONDOWN) ||
1655 (msg->message == WM_MBUTTONDOWN) ||
1656 (msg->message == WM_XBUTTONDOWN))
1657 {
1658 /* Send the WM_PARENTNOTIFY,
1659 * note that even for double/nonclient clicks
1660 * notification message is still WM_L/M/RBUTTONDOWN.
1661 */
1662 MsqSendParentNotify(pwndMsg, msg->message, 0, msg->pt );
1663
1664 /* Activate the window if needed */
1665
1666 if (pwndMsg != MessageQueue->spwndActive)
1667 {
1668 PWND pwndTop = pwndMsg;
1669 pwndTop = IntGetNonChildAncestor(pwndTop);
1670
1671 TRACE("Mouse pti %p pwndMsg pti %p pwndTop pti %p\n",MessageQueue->ptiMouse,pwndMsg->head.pti,pwndTop->head.pti);
1672
1673 if (pwndTop && pwndTop != pwndDesktop)
1674 {
1675 LONG ret = co_IntSendMessage( msg->hwnd,
1676 WM_MOUSEACTIVATE,
1677 (WPARAM)UserHMGetHandle(pwndTop),
1678 MAKELONG( hittest, msg->message));
1679 switch(ret)
1680 {
1681 case MA_NOACTIVATEANDEAT:
1682 eatMsg = TRUE;
1683 /* fall through */
1684 case MA_NOACTIVATE:
1685 break;
1686 case MA_ACTIVATEANDEAT:
1687 eatMsg = TRUE;
1688 /* fall through */
1689 case MA_ACTIVATE:
1690 case 0:
1691 if (!co_IntMouseActivateWindow( pwndTop )) eatMsg = TRUE;
1692 break;
1693 default:
1694 ERR( "unknown WM_MOUSEACTIVATE code %d\n", ret );
1695 break;
1696 }
1697 }
1698 }
1699 }
1700
1701 /* send the WM_SETCURSOR message */
1702
1703 /* Windows sends the normal mouse message as the message parameter
1704 in the WM_SETCURSOR message even if it's non-client mouse message */
1705 co_IntSendMessage( msg->hwnd, WM_SETCURSOR, (WPARAM)msg->hwnd, MAKELONG( hittest, msg->message ));
1706
1707 msg->message = message;
1708 return !eatMsg;
1709 }
1710
1711 BOOL co_IntProcessKeyboardMessage(MSG* Msg, BOOL* RemoveMessages)
1712 {
1713 EVENTMSG Event;
1714 USER_REFERENCE_ENTRY Ref;
1715 PWND pWnd;
1716 UINT ImmRet;
1717 BOOL Ret = TRUE;
1718 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
1719
1720 if (Msg->message == VK_PACKET)
1721 {
1722 pti->wchInjected = HIWORD(Msg->wParam);
1723 }
1724
1725 if (Msg->message == WM_KEYDOWN || Msg->message == WM_SYSKEYDOWN ||
1726 Msg->message == WM_KEYUP || Msg->message == WM_SYSKEYUP)
1727 {
1728 switch (Msg->wParam)
1729 {
1730 case VK_LSHIFT: case VK_RSHIFT:
1731 Msg->wParam = VK_SHIFT;
1732 break;
1733 case VK_LCONTROL: case VK_RCONTROL:
1734 Msg->wParam = VK_CONTROL;
1735 break;
1736 case VK_LMENU: case VK_RMENU:
1737 Msg->wParam = VK_MENU;
1738 break;
1739 }
1740 }
1741
1742 pWnd = ValidateHwndNoErr(Msg->hwnd);
1743 if (pWnd) UserRefObjectCo(pWnd, &Ref);
1744
1745 Event.message = Msg->message;
1746 Event.hwnd = Msg->hwnd;
1747 Event.time = Msg->time;
1748 Event.paramL = (Msg->wParam & 0xFF) | (HIWORD(Msg->lParam) << 8);
1749 Event.paramH = Msg->lParam & 0x7FFF;
1750 if (HIWORD(Msg->lParam) & 0x0100) Event.paramH |= 0x8000;
1751 co_HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&Event);
1752
1753 if (*RemoveMessages)
1754 {
1755 if ((Msg->message == WM_KEYDOWN) &&
1756 (Msg->hwnd != IntGetDesktopWindow()))
1757 {
1758 /* Handle F1 key by sending out WM_HELP message */
1759 if (Msg->wParam == VK_F1)
1760 {
1761 UserPostMessage( Msg->hwnd, WM_KEYF1, 0, 0 );
1762 }
1763 else if (Msg->wParam >= VK_BROWSER_BACK &&
1764 Msg->wParam <= VK_LAUNCH_APP2)
1765 {
1766 /* FIXME: Process keystate */
1767 co_IntSendMessage(Msg->hwnd, WM_APPCOMMAND, (WPARAM)Msg->hwnd, MAKELPARAM(0, (FAPPCOMMAND_KEY | (Msg->wParam - VK_BROWSER_BACK + 1))));
1768 }
1769 }
1770 else if (Msg->message == WM_KEYUP)
1771 {
1772 /* Handle VK_APPS key by posting a WM_CONTEXTMENU message */
1773 if (Msg->wParam == VK_APPS && pti->MessageQueue->MenuOwner == NULL)
1774 UserPostMessage( Msg->hwnd, WM_CONTEXTMENU, (WPARAM)Msg->hwnd, -1 );
1775 }
1776 }
1777
1778 if (co_HOOK_CallHooks( WH_KEYBOARD,
1779 *RemoveMessages ? HC_ACTION : HC_NOREMOVE,
1780 LOWORD(Msg->wParam),
1781 Msg->lParam))
1782 {
1783 /* skip this message */
1784 co_HOOK_CallHooks( WH_CBT,
1785 HCBT_KEYSKIPPED,
1786 LOWORD(Msg->wParam),
1787 Msg->lParam );
1788
1789 ERR("KeyboardMessage WH_KEYBOARD Call Hook return!\n");
1790
1791 *RemoveMessages = TRUE;
1792
1793 Ret = FALSE;
1794 }
1795
1796 if ( pWnd && Ret && *RemoveMessages && Msg->message == WM_KEYDOWN && !(pti->TIF_flags & TIF_DISABLEIME))
1797 {
1798 if ( (ImmRet = IntImmProcessKey(pti->MessageQueue, pWnd, Msg->message, Msg->wParam, Msg->lParam)) )
1799 {
1800 if ( ImmRet & (IPHK_HOTKEY|IPHK_SKIPTHISKEY) )
1801 {
1802 ImmRet = 0;
1803 }
1804 if ( ImmRet & IPHK_PROCESSBYIME )
1805 {
1806 Msg->wParam = VK_PROCESSKEY;
1807 }
1808 }
1809 }
1810
1811 if (pWnd) UserDerefObjectCo(pWnd);
1812 return Ret;
1813 }
1814
1815 BOOL co_IntProcessHardwareMessage(MSG* Msg, BOOL* RemoveMessages, BOOL* NotForUs, UINT first, UINT last)
1816 {
1817 if ( IS_MOUSE_MESSAGE(Msg->message))
1818 {
1819 return co_IntProcessMouseMessage(Msg, RemoveMessages, NotForUs, first, last);
1820 }
1821 else if ( IS_KBD_MESSAGE(Msg->message))
1822 {
1823 return co_IntProcessKeyboardMessage(Msg, RemoveMessages);
1824 }
1825
1826 return TRUE;
1827 }
1828
1829 /* check whether a message filter contains at least one potential hardware message */
1830 static INT FASTCALL
1831 filter_contains_hw_range( UINT first, UINT last )
1832 {
1833 /* hardware message ranges are (in numerical order):
1834 * WM_NCMOUSEFIRST .. WM_NCMOUSELAST
1835 * WM_KEYFIRST .. WM_KEYLAST
1836 * WM_MOUSEFIRST .. WM_MOUSELAST
1837 */
1838 if (!last) --last;
1839 if (last < WM_NCMOUSEFIRST) return 0;
1840 if (first > WM_NCMOUSELAST && last < WM_KEYFIRST) return 0;
1841 if (first > WM_KEYLAST && last < WM_MOUSEFIRST) return 0;
1842 if (first > WM_MOUSELAST) return 0;
1843 return 1;
1844 }
1845
1846 BOOL APIENTRY
1847 co_MsqPeekHardwareMessage(IN PTHREADINFO pti,
1848 IN BOOL Remove,
1849 IN PWND Window,
1850 IN UINT MsgFilterLow,
1851 IN UINT MsgFilterHigh,
1852 IN UINT QSflags,
1853 OUT MSG* pMsg)
1854 {
1855 BOOL AcceptMessage, NotForUs;
1856 PUSER_MESSAGE CurrentMessage;
1857 PLIST_ENTRY ListHead;
1858 MSG msg;
1859 ULONG_PTR idSave;
1860 DWORD QS_Flags;
1861 BOOL Ret = FALSE;
1862 PUSER_MESSAGE_QUEUE MessageQueue = pti->MessageQueue;
1863
1864 if (!filter_contains_hw_range( MsgFilterLow, MsgFilterHigh )) return FALSE;
1865
1866 ListHead = MessageQueue->HardwareMessagesListHead.Flink;
1867
1868 if (IsListEmpty(ListHead)) return FALSE;
1869
1870 if (!MessageQueue->ptiSysLock)
1871 {
1872 MessageQueue->ptiSysLock = pti;
1873 pti->pcti->CTI_flags |= CTI_THREADSYSLOCK;
1874 }
1875
1876 if (MessageQueue->ptiSysLock != pti)
1877 {
1878 ERR("Thread Q is locked to ptiSysLock 0x%p pti 0x%p\n",MessageQueue->ptiSysLock,pti);
1879 return FALSE;
1880 }
1881
1882 while (ListHead != &MessageQueue->HardwareMessagesListHead)
1883 {
1884 CurrentMessage = CONTAINING_RECORD(ListHead, USER_MESSAGE, ListEntry);
1885 ListHead = ListHead->Flink;
1886
1887 if (MessageQueue->idSysPeek == (ULONG_PTR)CurrentMessage)
1888 {
1889 TRACE("Skip this message due to it is in play!\n");
1890 continue;
1891 }
1892 /*
1893 MSDN:
1894 1: any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL.
1895 2: retrieves only messages on the current thread's message queue whose hwnd value is NULL.
1896 3: handle to the window whose messages are to be retrieved.
1897 */
1898 if ( ( !Window || // 1
1899 ( Window == PWND_BOTTOM && CurrentMessage->Msg.hwnd == NULL ) || // 2
1900 ( Window != PWND_BOTTOM && Window->head.h == CurrentMessage->Msg.hwnd ) || // 3
1901 ( CurrentMessage->Msg.message == WM_MOUSEMOVE ) ) && // Null window for mouse moves.
1902 ( ( ( MsgFilterLow == 0 && MsgFilterHigh == 0 ) && CurrentMessage->QS_Flags & QSflags ) ||
1903 ( MsgFilterLow <= CurrentMessage->Msg.message && MsgFilterHigh >= CurrentMessage->Msg.message ) ) )
1904 {
1905 idSave = MessageQueue->idSysPeek;
1906 MessageQueue->idSysPeek = (ULONG_PTR)CurrentMessage;
1907
1908 msg = CurrentMessage->Msg;
1909 QS_Flags = CurrentMessage->QS_Flags;
1910
1911 NotForUs = FALSE;
1912
1913 UpdateKeyStateFromMsg(MessageQueue, &msg);
1914 AcceptMessage = co_IntProcessHardwareMessage(&msg, &Remove, &NotForUs, MsgFilterLow, MsgFilterHigh);
1915
1916 if (Remove)
1917 {
1918 if (CurrentMessage->pti != NULL)
1919 {
1920 RemoveEntryList(&CurrentMessage->ListEntry);
1921 MsqDestroyMessage(CurrentMessage);
1922 }
1923 ClearMsgBitsMask(pti, QS_Flags);
1924 }
1925
1926 MessageQueue->idSysPeek = idSave;
1927
1928 if (NotForUs)
1929 {
1930 Ret = FALSE;
1931 break;
1932 }
1933
1934 if (AcceptMessage)
1935 {
1936 *pMsg = msg;
1937 Ret = TRUE;
1938 break;
1939 }
1940 }
1941 }
1942
1943 MessageQueue->ptiSysLock = NULL;
1944 pti->pcti->CTI_flags &= ~CTI_THREADSYSLOCK;
1945 return Ret;
1946 }
1947
1948 BOOLEAN APIENTRY
1949 MsqPeekMessage(IN PTHREADINFO pti,
1950 IN BOOLEAN Remove,
1951 IN PWND Window,
1952 IN UINT MsgFilterLow,
1953 IN UINT MsgFilterHigh,
1954 IN UINT QSflags,
1955 OUT LONG_PTR *ExtraInfo,
1956 OUT PMSG Message)
1957 {
1958 PUSER_MESSAGE CurrentMessage;
1959 PLIST_ENTRY ListHead;
1960 DWORD QS_Flags;
1961 BOOL Ret = FALSE;
1962
1963 ListHead = pti->PostedMessagesListHead.Flink;
1964
1965 if (IsListEmpty(ListHead)) return FALSE;
1966
1967 while(ListHead != &pti->PostedMessagesListHead)
1968 {
1969 CurrentMessage = CONTAINING_RECORD(ListHead, USER_MESSAGE, ListEntry);
1970 ListHead = ListHead->Flink;
1971 /*
1972 MSDN:
1973 1: any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL.
1974 2: retrieves only messages on the current thread's message queue whose hwnd value is NULL.
1975 3: handle to the window whose messages are to be retrieved.
1976 */
1977 if ( ( !Window || // 1
1978 ( Window == PWND_BOTTOM && CurrentMessage->Msg.hwnd == NULL ) || // 2
1979 ( Window != PWND_BOTTOM && Window->head.h == CurrentMessage->Msg.hwnd ) ) && // 3
1980 ( ( ( MsgFilterLow == 0 && MsgFilterHigh == 0 ) && CurrentMessage->QS_Flags & QSflags ) ||
1981 ( MsgFilterLow <= CurrentMessage->Msg.message && MsgFilterHigh >= CurrentMessage->Msg.message ) ) )
1982 {
1983 *Message = CurrentMessage->Msg;
1984 *ExtraInfo = CurrentMessage->ExtraInfo;
1985 QS_Flags = CurrentMessage->QS_Flags;
1986
1987 if (Remove)
1988 {
1989 if (CurrentMessage->pti != NULL)
1990 {
1991 RemoveEntryList(&CurrentMessage->ListEntry);
1992 MsqDestroyMessage(CurrentMessage);
1993 }
1994 ClearMsgBitsMask(pti, QS_Flags);
1995 }
1996 Ret = TRUE;
1997 break;
1998 }
1999 }
2000
2001 return Ret;
2002 }
2003
2004 NTSTATUS FASTCALL
2005 co_MsqWaitForNewMessages(PTHREADINFO pti, PWND WndFilter,
2006 UINT MsgFilterMin, UINT MsgFilterMax)
2007 {
2008 NTSTATUS ret = STATUS_SUCCESS;
2009
2010 // Post mouse moves before waiting for messages.
2011 if (pti->MessageQueue->QF_flags & QF_MOUSEMOVED)
2012 {
2013 IntCoalesceMouseMove(pti);
2014 }
2015
2016 UserLeaveCo();
2017
2018 ZwYieldExecution(); // Let someone else run!
2019
2020 ret = KeWaitForSingleObject( pti->pEventQueueServer,
2021 UserRequest,
2022 UserMode,
2023 FALSE,
2024 NULL );
2025 UserEnterCo();
2026 if ( ret == STATUS_USER_APC )
2027 {
2028 TRACE("MWFNW User APC\n");
2029 co_IntDeliverUserAPC();
2030 }
2031 return ret;
2032 }
2033
2034 BOOL FASTCALL
2035 MsqIsHung(PTHREADINFO pti)
2036 {
2037 LARGE_INTEGER LargeTickCount;
2038
2039 KeQueryTickCount(&LargeTickCount);
2040 return ((LargeTickCount.u.LowPart - pti->timeLast) > MSQ_HUNG);
2041 }
2042
2043 VOID
2044 CALLBACK
2045 HungAppSysTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
2046 {
2047 DoTheScreenSaver();
2048 TRACE("HungAppSysTimerProc\n");
2049 // Process list of windows that are hung and waiting.
2050 }
2051
2052 BOOLEAN FASTCALL
2053 MsqInitializeMessageQueue(PTHREADINFO pti, PUSER_MESSAGE_QUEUE MessageQueue)
2054 {
2055 MessageQueue->CaretInfo = (PTHRDCARETINFO)(MessageQueue + 1);
2056 InitializeListHead(&MessageQueue->HardwareMessagesListHead); // Keep here!
2057 MessageQueue->spwndFocus = NULL;
2058 MessageQueue->iCursorLevel = 0;
2059 MessageQueue->CursorObject = SYSTEMCUR(WAIT); // See test_initial_cursor.
2060 if (MessageQueue->CursorObject)
2061 {
2062 TRACE("Default cursor hcur %p\n",UserHMGetHandle(MessageQueue->CursorObject));
2063 UserReferenceObject(MessageQueue->CursorObject);
2064 }
2065 RtlCopyMemory(MessageQueue->afKeyState, gafAsyncKeyState, sizeof(gafAsyncKeyState));
2066 MessageQueue->ptiMouse = pti;
2067 MessageQueue->ptiKeyboard = pti;
2068 MessageQueue->cThreads++;
2069
2070 return TRUE;
2071 }
2072
2073 VOID FASTCALL
2074 MsqCleanupThreadMsgs(PTHREADINFO pti)
2075 {
2076 PLIST_ENTRY CurrentEntry;
2077 PUSER_MESSAGE CurrentMessage;
2078 PUSER_SENT_MESSAGE CurrentSentMessage;
2079
2080 /* cleanup posted messages */
2081 while (!IsListEmpty(&pti->PostedMessagesListHead))
2082 {
2083 CurrentEntry = RemoveHeadList(&pti->PostedMessagesListHead);
2084 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, ListEntry);
2085 if (CurrentMessage->dwQEvent)
2086 {
2087 if (CurrentMessage->dwQEvent == POSTEVENT_NWE)
2088 {
2089 ExFreePoolWithTag( (PVOID)CurrentMessage->ExtraInfo, TAG_HOOK);
2090 }
2091 }
2092 MsqDestroyMessage(CurrentMessage);
2093 }
2094
2095 /* remove the messages that have not yet been dispatched */
2096 while (!IsListEmpty(&pti->SentMessagesListHead))
2097 {
2098 CurrentEntry = RemoveHeadList(&pti->SentMessagesListHead);
2099 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE, ListEntry);
2100
2101 TRACE("Notify the sender and remove a message from the queue that had not been dispatched\n");
2102 /* Only if the message has a sender was the message in the DispatchingList */
2103 if ((CurrentSentMessage->ptiSender) &&
2104 (!IsListEmpty(&CurrentSentMessage->DispatchingListEntry)))
2105 {
2106 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
2107 InitializeListHead(&CurrentSentMessage->DispatchingListEntry);
2108 }
2109
2110 /* wake the sender's thread */
2111 if (CurrentSentMessage->CompletionEvent != NULL)
2112 {
2113 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
2114 }
2115
2116 if (CurrentSentMessage->HasPackedLParam)
2117 {
2118 if (CurrentSentMessage->Msg.lParam)
2119 ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
2120 }
2121
2122 /* free the message */
2123 ExFreePool(CurrentSentMessage);
2124 }
2125
2126 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
2127 ExitThread() was called in a SendMessage() umode callback */
2128 while (!IsListEmpty(&pti->LocalDispatchingMessagesHead))
2129 {
2130 CurrentEntry = RemoveHeadList(&pti->LocalDispatchingMessagesHead);
2131 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE, ListEntry);
2132
2133 /* remove the message from the dispatching list */
2134 if (!IsListEmpty(&CurrentSentMessage->DispatchingListEntry))
2135 {
2136 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
2137 InitializeListHead(&CurrentSentMessage->DispatchingListEntry);
2138 }
2139
2140 TRACE("Notify the sender, the thread has been terminated while dispatching a message!\n");
2141
2142 /* wake the sender's thread */
2143 if (CurrentSentMessage->CompletionEvent != NULL)
2144 {
2145 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
2146 }
2147
2148 if (CurrentSentMessage->HasPackedLParam)
2149 {
2150 if (CurrentSentMessage->Msg.lParam)
2151 {
2152 _PRAGMA_WARNING_SUPPRESS(__WARNING_USING_UNINIT_VAR);
2153 ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
2154 }
2155 }
2156
2157 /* free the message */
2158 ExFreePool(CurrentSentMessage);
2159 }
2160
2161 /* tell other threads not to bother returning any info to us */
2162 while (! IsListEmpty(&pti->DispatchingMessagesHead))
2163 {
2164 CurrentEntry = RemoveHeadList(&pti->DispatchingMessagesHead);
2165 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE, DispatchingListEntry);
2166 InitializeListHead(&CurrentSentMessage->DispatchingListEntry);
2167 CurrentSentMessage->CompletionEvent = NULL;
2168 CurrentSentMessage->Result = NULL;
2169
2170 /* do NOT dereference our message queue as it might get attempted to be
2171 locked later */
2172 }
2173
2174 // Clear it all out.
2175 if (pti->pcti)
2176 {
2177 pti->pcti->fsWakeBits = 0;
2178 pti->pcti->fsChangeBits = 0;
2179 }
2180
2181 pti->nCntsQBits[QSRosKey] = 0;
2182 pti->nCntsQBits[QSRosMouseMove] = 0;
2183 pti->nCntsQBits[QSRosMouseButton] = 0;
2184 pti->nCntsQBits[QSRosPostMessage] = 0;
2185 pti->nCntsQBits[QSRosSendMessage] = 0;
2186 pti->nCntsQBits[QSRosHotKey] = 0;
2187 pti->nCntsQBits[QSRosEvent] = 0;
2188 }
2189
2190 VOID FASTCALL
2191 MsqCleanupMessageQueue(PTHREADINFO pti)
2192 {
2193 PUSER_MESSAGE_QUEUE MessageQueue;
2194
2195 MessageQueue = pti->MessageQueue;
2196 MessageQueue->cThreads--;
2197
2198 if (MessageQueue->cThreads)
2199 {
2200 if (MessageQueue->ptiSysLock == pti) MessageQueue->ptiSysLock = NULL;
2201 }
2202
2203 if (MessageQueue->CursorObject)
2204 {
2205 PCURICON_OBJECT pCursor = MessageQueue->CursorObject;
2206
2207 /* Change to another cursor if we going to dereference current one
2208 Note: we can't use UserSetCursor because it uses current thread
2209 message queue instead of queue given for cleanup */
2210 if (IntGetSysCursorInfo()->CurrentCursorObject == pCursor)
2211 {
2212 HDC hdcScreen;
2213
2214 /* Get the screen DC */
2215 hdcScreen = IntGetScreenDC();
2216 if (hdcScreen)
2217 GreMovePointer(hdcScreen, -1, -1);
2218 IntGetSysCursorInfo()->CurrentCursorObject = NULL;
2219 }
2220
2221 TRACE("DereferenceObject pCursor\n");
2222 UserDereferenceObject(pCursor);
2223 }
2224
2225 if (gpqForeground == MessageQueue)
2226 {
2227 IntSetFocusMessageQueue(NULL);
2228 }
2229 if (gpqForegroundPrev == MessageQueue)
2230 {
2231 gpqForegroundPrev = NULL;
2232 }
2233 if (gpqCursor == MessageQueue)
2234 {
2235 gpqCursor = NULL;
2236 }
2237 }
2238
2239 PUSER_MESSAGE_QUEUE FASTCALL
2240 MsqCreateMessageQueue(PTHREADINFO pti)
2241 {
2242 PUSER_MESSAGE_QUEUE MessageQueue;
2243
2244 MessageQueue = (PUSER_MESSAGE_QUEUE)ExAllocatePoolWithTag(NonPagedPool,
2245 sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO),
2246 USERTAG_Q);
2247
2248 if (!MessageQueue)
2249 {
2250 return NULL;
2251 }
2252
2253 RtlZeroMemory(MessageQueue, sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO));
2254 /* hold at least one reference until it'll be destroyed */
2255 IntReferenceMessageQueue(MessageQueue);
2256 /* initialize the queue */
2257 if (!MsqInitializeMessageQueue(pti, MessageQueue))
2258 {
2259 IntDereferenceMessageQueue(MessageQueue);
2260 return NULL;
2261 }
2262
2263 return MessageQueue;
2264 }
2265
2266 VOID FASTCALL
2267 MsqDestroyMessageQueue(_In_ PTHREADINFO pti)
2268 {
2269 PDESKTOP desk;
2270 PUSER_MESSAGE_QUEUE MessageQueue = pti->MessageQueue;
2271
2272 NT_ASSERT(MessageQueue != NULL);
2273 MessageQueue->QF_flags |= QF_INDESTROY;
2274
2275 /* remove the message queue from any desktops */
2276 if ((desk = InterlockedExchangePointer((PVOID*)&MessageQueue->Desktop, 0)))
2277 {
2278 (void)InterlockedExchangePointer((PVOID*)&desk->ActiveMessageQueue, 0);
2279 IntDereferenceMessageQueue(MessageQueue);
2280 }
2281
2282 /* clean it up */
2283 MsqCleanupMessageQueue(pti);
2284
2285 /* decrease the reference counter, if it hits zero, the queue will be freed */
2286 _PRAGMA_WARNING_SUPPRESS(__WARNING_USING_UNINIT_VAR);
2287 IntDereferenceMessageQueue(MessageQueue);
2288 }
2289
2290 LPARAM FASTCALL
2291 MsqSetMessageExtraInfo(LPARAM lParam)
2292 {
2293 LPARAM Ret;
2294 PTHREADINFO pti;
2295 PUSER_MESSAGE_QUEUE MessageQueue;
2296
2297 pti = PsGetCurrentThreadWin32Thread();
2298 MessageQueue = pti->MessageQueue;
2299 if(!MessageQueue)
2300 {
2301 return 0;
2302 }
2303
2304 Ret = MessageQueue->ExtraInfo;
2305 MessageQueue->ExtraInfo = lParam;
2306
2307 return Ret;
2308 }
2309
2310 LPARAM FASTCALL
2311 MsqGetMessageExtraInfo(VOID)
2312 {
2313 PTHREADINFO pti;
2314 PUSER_MESSAGE_QUEUE MessageQueue;
2315
2316 pti = PsGetCurrentThreadWin32Thread();
2317 MessageQueue = pti->MessageQueue;
2318 if(!MessageQueue)
2319 {
2320 return 0;
2321 }
2322
2323 return MessageQueue->ExtraInfo;
2324 }
2325
2326 // ReplyMessage is called by the thread receiving the window message.
2327 BOOL FASTCALL
2328 co_MsqReplyMessage( LRESULT lResult )
2329 {
2330 PUSER_SENT_MESSAGE Message;
2331 PTHREADINFO pti;
2332
2333 pti = PsGetCurrentThreadWin32Thread();
2334 Message = pti->pusmCurrent;
2335
2336 if (!Message) return FALSE;
2337
2338 if (Message->QS_Flags & QS_SMRESULT) return FALSE;
2339
2340 // SendMessageXxx || Callback msg and not a notify msg
2341 if (Message->ptiSender || Message->CompletionCallback)
2342 {
2343 Message->lResult = lResult;
2344 Message->QS_Flags |= QS_SMRESULT;
2345 // See co_MsqDispatchOneSentMessage, change bits already accounted for and cleared and this msg is going away..
2346 }
2347 return TRUE;
2348 }
2349
2350 HWND FASTCALL
2351 MsqSetStateWindow(PTHREADINFO pti, ULONG Type, HWND hWnd)
2352 {
2353 HWND Prev;
2354 PUSER_MESSAGE_QUEUE MessageQueue;
2355
2356 MessageQueue = pti->MessageQueue;
2357
2358 switch(Type)
2359 {
2360 case MSQ_STATE_CAPTURE:
2361 Prev = MessageQueue->spwndCapture ? UserHMGetHandle(MessageQueue->spwndCapture) : 0;
2362 MessageQueue->spwndCapture = ValidateHwndNoErr(hWnd);
2363 return Prev;
2364 case MSQ_STATE_ACTIVE:
2365 Prev = MessageQueue->spwndActive ? UserHMGetHandle(MessageQueue->spwndActive) : 0;
2366 MessageQueue->spwndActive = ValidateHwndNoErr(hWnd);
2367 return Prev;
2368 case MSQ_STATE_FOCUS:
2369 Prev = MessageQueue->spwndFocus ? UserHMGetHandle(MessageQueue->spwndFocus) : 0;
2370 MessageQueue->spwndFocus = ValidateHwndNoErr(hWnd);
2371 return Prev;
2372 case MSQ_STATE_MENUOWNER:
2373 Prev = MessageQueue->MenuOwner;
2374 MessageQueue->MenuOwner = hWnd;
2375 return Prev;
2376 case MSQ_STATE_MOVESIZE:
2377 Prev = MessageQueue->MoveSize;
2378 MessageQueue->MoveSize = hWnd;
2379 return Prev;
2380 case MSQ_STATE_CARET:
2381 ASSERT(MessageQueue->CaretInfo);
2382 Prev = MessageQueue->CaretInfo->hWnd;
2383 MessageQueue->CaretInfo->hWnd = hWnd;
2384 return Prev;
2385 }
2386
2387 return NULL;
2388 }
2389
2390 SHORT
2391 APIENTRY
2392 NtUserGetKeyState(INT key)
2393 {
2394 DWORD Ret;
2395
2396 UserEnterShared();
2397
2398 Ret = UserGetKeyState(key);
2399
2400 UserLeave();
2401
2402 return (SHORT)Ret;
2403 }
2404
2405
2406 DWORD
2407 APIENTRY
2408 NtUserGetKeyboardState(LPBYTE lpKeyState)
2409 {
2410 DWORD i, ret = TRUE;
2411 PTHREADINFO pti;
2412 PUSER_MESSAGE_QUEUE MessageQueue;
2413
2414 UserEnterShared();
2415
2416 pti = PsGetCurrentThreadWin32Thread();
2417 MessageQueue = pti->MessageQueue;
2418
2419 _SEH2_TRY
2420 {
2421 /* Probe and copy key state to an array */
2422 ProbeForWrite(lpKeyState, 256 * sizeof(BYTE), 1);
2423 for (i = 0; i < 256; ++i)
2424 {
2425 lpKeyState[i] = 0;
2426 if (IS_KEY_DOWN(MessageQueue->afKeyState, i))
2427 lpKeyState[i] |= KS_DOWN_BIT;
2428 if (IS_KEY_LOCKED(MessageQueue->afKeyState, i))
2429 lpKeyState[i] |= KS_LOCK_BIT;
2430 }
2431 }
2432 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2433 {
2434 SetLastNtError(_SEH2_GetExceptionCode());
2435 ret = FALSE;
2436 }
2437 _SEH2_END;
2438
2439 UserLeave();
2440
2441 return ret;
2442 }
2443
2444 BOOL
2445 APIENTRY
2446 NtUserSetKeyboardState(LPBYTE pKeyState)
2447 {
2448 UINT i;
2449 BOOL bRet = TRUE;
2450 PTHREADINFO pti;
2451 PUSER_MESSAGE_QUEUE MessageQueue;
2452
2453 UserEnterExclusive();
2454
2455 pti = PsGetCurrentThreadWin32Thread();
2456 MessageQueue = pti->MessageQueue;
2457
2458 _SEH2_TRY
2459 {
2460 ProbeForRead(pKeyState, 256 * sizeof(BYTE), 1);
2461 for (i = 0; i < 256; ++i)
2462 {
2463 SET_KEY_DOWN(MessageQueue->afKeyState, i, pKeyState[i] & KS_DOWN_BIT);
2464 SET_KEY_LOCKED(MessageQueue->afKeyState, i, pKeyState[i] & KS_LOCK_BIT);
2465 }
2466 }
2467 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2468 {
2469 SetLastNtError(_SEH2_GetExceptionCode());
2470 bRet = FALSE;
2471 }
2472 _SEH2_END;
2473
2474 UserLeave();
2475
2476 return bRet;
2477 }
2478
2479 /* EOF */