[Win32SS|UxTheme]
[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 // 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 if ( pti->nCntsQBits[QSRosMouseButton] != 0 ||
2017 pti->nCntsQBits[QSRosMouseMove] != 0 ||
2018 pti->nCntsQBits[QSRosKey] != 0 ||
2019 pti->nCntsQBits[QSRosSendMessage] != 0 ||
2020 pti->nCntsQBits[QSRosPostMessage] != 0 )
2021 {
2022 TRACE("No time to wait!\n");
2023 return ret;
2024 }
2025
2026 UserLeaveCo();
2027
2028 ZwYieldExecution(); // Let someone else run!
2029
2030 ret = KeWaitForSingleObject( pti->pEventQueueServer,
2031 UserRequest,
2032 UserMode,
2033 FALSE,
2034 NULL );
2035 UserEnterCo();
2036 if ( ret == STATUS_USER_APC )
2037 {
2038 TRACE("MWFNW User APC\n");
2039 co_IntDeliverUserAPC();
2040 }
2041 return ret;
2042 }
2043
2044 BOOL FASTCALL
2045 MsqIsHung(PTHREADINFO pti)
2046 {
2047 LARGE_INTEGER LargeTickCount;
2048
2049 KeQueryTickCount(&LargeTickCount);
2050 return ((LargeTickCount.u.LowPart - pti->timeLast) > MSQ_HUNG);
2051 }
2052
2053 VOID
2054 CALLBACK
2055 HungAppSysTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
2056 {
2057 DoTheScreenSaver();
2058 TRACE("HungAppSysTimerProc\n");
2059 // Process list of windows that are hung and waiting.
2060 }
2061
2062 BOOLEAN FASTCALL
2063 MsqInitializeMessageQueue(PTHREADINFO pti, PUSER_MESSAGE_QUEUE MessageQueue)
2064 {
2065 MessageQueue->CaretInfo = (PTHRDCARETINFO)(MessageQueue + 1);
2066 InitializeListHead(&MessageQueue->HardwareMessagesListHead); // Keep here!
2067 MessageQueue->spwndFocus = NULL;
2068 MessageQueue->iCursorLevel = 0;
2069 MessageQueue->CursorObject = SYSTEMCUR(WAIT); // See test_initial_cursor.
2070 if (MessageQueue->CursorObject)
2071 {
2072 TRACE("Default cursor hcur %p\n",UserHMGetHandle(MessageQueue->CursorObject));
2073 UserReferenceObject(MessageQueue->CursorObject);
2074 }
2075 RtlCopyMemory(MessageQueue->afKeyState, gafAsyncKeyState, sizeof(gafAsyncKeyState));
2076 MessageQueue->ptiMouse = pti;
2077 MessageQueue->ptiKeyboard = pti;
2078 MessageQueue->cThreads++;
2079
2080 return TRUE;
2081 }
2082
2083 VOID FASTCALL
2084 MsqCleanupThreadMsgs(PTHREADINFO pti)
2085 {
2086 PLIST_ENTRY CurrentEntry;
2087 PUSER_MESSAGE CurrentMessage;
2088 PUSER_SENT_MESSAGE CurrentSentMessage;
2089
2090 /* cleanup posted messages */
2091 while (!IsListEmpty(&pti->PostedMessagesListHead))
2092 {
2093 CurrentEntry = RemoveHeadList(&pti->PostedMessagesListHead);
2094 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, ListEntry);
2095 if (CurrentMessage->dwQEvent)
2096 {
2097 if (CurrentMessage->dwQEvent == POSTEVENT_NWE)
2098 {
2099 ExFreePoolWithTag( (PVOID)CurrentMessage->ExtraInfo, TAG_HOOK);
2100 }
2101 }
2102 MsqDestroyMessage(CurrentMessage);
2103 }
2104
2105 /* remove the messages that have not yet been dispatched */
2106 while (!IsListEmpty(&pti->SentMessagesListHead))
2107 {
2108 CurrentEntry = RemoveHeadList(&pti->SentMessagesListHead);
2109 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE, ListEntry);
2110
2111 TRACE("Notify the sender and remove a message from the queue that had not been dispatched\n");
2112 /* Only if the message has a sender was the message in the DispatchingList */
2113 if ((CurrentSentMessage->ptiSender) &&
2114 (!IsListEmpty(&CurrentSentMessage->DispatchingListEntry)))
2115 {
2116 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
2117 InitializeListHead(&CurrentSentMessage->DispatchingListEntry);
2118 }
2119
2120 /* wake the sender's thread */
2121 if (CurrentSentMessage->CompletionEvent != NULL)
2122 {
2123 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
2124 }
2125
2126 if (CurrentSentMessage->HasPackedLParam)
2127 {
2128 if (CurrentSentMessage->Msg.lParam)
2129 ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
2130 }
2131
2132 /* free the message */
2133 ExFreePool(CurrentSentMessage);
2134 }
2135
2136 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
2137 ExitThread() was called in a SendMessage() umode callback */
2138 while (!IsListEmpty(&pti->LocalDispatchingMessagesHead))
2139 {
2140 CurrentEntry = RemoveHeadList(&pti->LocalDispatchingMessagesHead);
2141 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE, ListEntry);
2142
2143 /* remove the message from the dispatching list */
2144 if (!IsListEmpty(&CurrentSentMessage->DispatchingListEntry))
2145 {
2146 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
2147 InitializeListHead(&CurrentSentMessage->DispatchingListEntry);
2148 }
2149
2150 TRACE("Notify the sender, the thread has been terminated while dispatching a message!\n");
2151
2152 /* wake the sender's thread */
2153 if (CurrentSentMessage->CompletionEvent != NULL)
2154 {
2155 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
2156 }
2157
2158 if (CurrentSentMessage->HasPackedLParam)
2159 {
2160 if (CurrentSentMessage->Msg.lParam)
2161 {
2162 _PRAGMA_WARNING_SUPPRESS(__WARNING_USING_UNINIT_VAR);
2163 ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
2164 }
2165 }
2166
2167 /* free the message */
2168 ExFreePool(CurrentSentMessage);
2169 }
2170
2171 /* tell other threads not to bother returning any info to us */
2172 while (! IsListEmpty(&pti->DispatchingMessagesHead))
2173 {
2174 CurrentEntry = RemoveHeadList(&pti->DispatchingMessagesHead);
2175 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE, DispatchingListEntry);
2176 InitializeListHead(&CurrentSentMessage->DispatchingListEntry);
2177 CurrentSentMessage->CompletionEvent = NULL;
2178 CurrentSentMessage->Result = NULL;
2179
2180 /* do NOT dereference our message queue as it might get attempted to be
2181 locked later */
2182 }
2183
2184 // Clear it all out.
2185 if (pti->pcti)
2186 {
2187 pti->pcti->fsWakeBits = 0;
2188 pti->pcti->fsChangeBits = 0;
2189 }
2190
2191 pti->nCntsQBits[QSRosKey] = 0;
2192 pti->nCntsQBits[QSRosMouseMove] = 0;
2193 pti->nCntsQBits[QSRosMouseButton] = 0;
2194 pti->nCntsQBits[QSRosPostMessage] = 0;
2195 pti->nCntsQBits[QSRosSendMessage] = 0;
2196 pti->nCntsQBits[QSRosHotKey] = 0;
2197 pti->nCntsQBits[QSRosEvent] = 0;
2198 }
2199
2200 VOID FASTCALL
2201 MsqCleanupMessageQueue(PTHREADINFO pti)
2202 {
2203 PUSER_MESSAGE_QUEUE MessageQueue;
2204
2205 MessageQueue = pti->MessageQueue;
2206 MessageQueue->cThreads--;
2207
2208 if (MessageQueue->cThreads)
2209 {
2210 if (MessageQueue->ptiSysLock == pti) MessageQueue->ptiSysLock = NULL;
2211 }
2212
2213 if (MessageQueue->CursorObject)
2214 {
2215 PCURICON_OBJECT pCursor = MessageQueue->CursorObject;
2216
2217 /* Change to another cursor if we going to dereference current one
2218 Note: we can't use UserSetCursor because it uses current thread
2219 message queue instead of queue given for cleanup */
2220 if (IntGetSysCursorInfo()->CurrentCursorObject == pCursor)
2221 {
2222 HDC hdcScreen;
2223
2224 /* Get the screen DC */
2225 hdcScreen = IntGetScreenDC();
2226 if (hdcScreen)
2227 GreMovePointer(hdcScreen, -1, -1);
2228 IntGetSysCursorInfo()->CurrentCursorObject = NULL;
2229 }
2230
2231 TRACE("DereferenceObject pCursor\n");
2232 UserDereferenceObject(pCursor);
2233 }
2234
2235 if (gpqForeground == MessageQueue)
2236 {
2237 IntSetFocusMessageQueue(NULL);
2238 }
2239 if (gpqForegroundPrev == MessageQueue)
2240 {
2241 gpqForegroundPrev = NULL;
2242 }
2243 if (gpqCursor == MessageQueue)
2244 {
2245 gpqCursor = NULL;
2246 }
2247 }
2248
2249 PUSER_MESSAGE_QUEUE FASTCALL
2250 MsqCreateMessageQueue(PTHREADINFO pti)
2251 {
2252 PUSER_MESSAGE_QUEUE MessageQueue;
2253
2254 MessageQueue = (PUSER_MESSAGE_QUEUE)ExAllocatePoolWithTag(NonPagedPool,
2255 sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO),
2256 USERTAG_Q);
2257
2258 if (!MessageQueue)
2259 {
2260 return NULL;
2261 }
2262
2263 RtlZeroMemory(MessageQueue, sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO));
2264 /* hold at least one reference until it'll be destroyed */
2265 IntReferenceMessageQueue(MessageQueue);
2266 /* initialize the queue */
2267 if (!MsqInitializeMessageQueue(pti, MessageQueue))
2268 {
2269 IntDereferenceMessageQueue(MessageQueue);
2270 return NULL;
2271 }
2272
2273 return MessageQueue;
2274 }
2275
2276 VOID FASTCALL
2277 MsqDestroyMessageQueue(_In_ PTHREADINFO pti)
2278 {
2279 PDESKTOP desk;
2280 PUSER_MESSAGE_QUEUE MessageQueue = pti->MessageQueue;
2281
2282 NT_ASSERT(MessageQueue != NULL);
2283 MessageQueue->QF_flags |= QF_INDESTROY;
2284
2285 /* remove the message queue from any desktops */
2286 if ((desk = InterlockedExchangePointer((PVOID*)&MessageQueue->Desktop, 0)))
2287 {
2288 (void)InterlockedExchangePointer((PVOID*)&desk->ActiveMessageQueue, 0);
2289 IntDereferenceMessageQueue(MessageQueue);
2290 }
2291
2292 /* clean it up */
2293 MsqCleanupMessageQueue(pti);
2294
2295 /* decrease the reference counter, if it hits zero, the queue will be freed */
2296 _PRAGMA_WARNING_SUPPRESS(__WARNING_USING_UNINIT_VAR);
2297 IntDereferenceMessageQueue(MessageQueue);
2298 }
2299
2300 LPARAM FASTCALL
2301 MsqSetMessageExtraInfo(LPARAM lParam)
2302 {
2303 LPARAM Ret;
2304 PTHREADINFO pti;
2305 PUSER_MESSAGE_QUEUE MessageQueue;
2306
2307 pti = PsGetCurrentThreadWin32Thread();
2308 MessageQueue = pti->MessageQueue;
2309 if(!MessageQueue)
2310 {
2311 return 0;
2312 }
2313
2314 Ret = MessageQueue->ExtraInfo;
2315 MessageQueue->ExtraInfo = lParam;
2316
2317 return Ret;
2318 }
2319
2320 LPARAM FASTCALL
2321 MsqGetMessageExtraInfo(VOID)
2322 {
2323 PTHREADINFO pti;
2324 PUSER_MESSAGE_QUEUE MessageQueue;
2325
2326 pti = PsGetCurrentThreadWin32Thread();
2327 MessageQueue = pti->MessageQueue;
2328 if(!MessageQueue)
2329 {
2330 return 0;
2331 }
2332
2333 return MessageQueue->ExtraInfo;
2334 }
2335
2336 // ReplyMessage is called by the thread receiving the window message.
2337 BOOL FASTCALL
2338 co_MsqReplyMessage( LRESULT lResult )
2339 {
2340 PUSER_SENT_MESSAGE Message;
2341 PTHREADINFO pti;
2342
2343 pti = PsGetCurrentThreadWin32Thread();
2344 Message = pti->pusmCurrent;
2345
2346 if (!Message) return FALSE;
2347
2348 if (Message->QS_Flags & QS_SMRESULT) return FALSE;
2349
2350 // SendMessageXxx || Callback msg and not a notify msg
2351 if (Message->ptiSender || Message->CompletionCallback)
2352 {
2353 Message->lResult = lResult;
2354 Message->QS_Flags |= QS_SMRESULT;
2355 // See co_MsqDispatchOneSentMessage, change bits already accounted for and cleared and this msg is going away..
2356 }
2357 return TRUE;
2358 }
2359
2360 HWND FASTCALL
2361 MsqSetStateWindow(PTHREADINFO pti, ULONG Type, HWND hWnd)
2362 {
2363 HWND Prev;
2364 PUSER_MESSAGE_QUEUE MessageQueue;
2365
2366 MessageQueue = pti->MessageQueue;
2367
2368 switch(Type)
2369 {
2370 case MSQ_STATE_CAPTURE:
2371 Prev = MessageQueue->spwndCapture ? UserHMGetHandle(MessageQueue->spwndCapture) : 0;
2372 MessageQueue->spwndCapture = ValidateHwndNoErr(hWnd);
2373 return Prev;
2374 case MSQ_STATE_ACTIVE:
2375 Prev = MessageQueue->spwndActive ? UserHMGetHandle(MessageQueue->spwndActive) : 0;
2376 MessageQueue->spwndActive = ValidateHwndNoErr(hWnd);
2377 return Prev;
2378 case MSQ_STATE_FOCUS:
2379 Prev = MessageQueue->spwndFocus ? UserHMGetHandle(MessageQueue->spwndFocus) : 0;
2380 MessageQueue->spwndFocus = ValidateHwndNoErr(hWnd);
2381 return Prev;
2382 case MSQ_STATE_MENUOWNER:
2383 Prev = MessageQueue->MenuOwner;
2384 MessageQueue->MenuOwner = hWnd;
2385 return Prev;
2386 case MSQ_STATE_MOVESIZE:
2387 Prev = MessageQueue->MoveSize;
2388 MessageQueue->MoveSize = hWnd;
2389 return Prev;
2390 case MSQ_STATE_CARET:
2391 ASSERT(MessageQueue->CaretInfo);
2392 Prev = MessageQueue->CaretInfo->hWnd;
2393 MessageQueue->CaretInfo->hWnd = hWnd;
2394 return Prev;
2395 }
2396
2397 return NULL;
2398 }
2399
2400 SHORT
2401 APIENTRY
2402 NtUserGetKeyState(INT key)
2403 {
2404 DWORD Ret;
2405
2406 UserEnterShared();
2407
2408 Ret = UserGetKeyState(key);
2409
2410 UserLeave();
2411
2412 return (SHORT)Ret;
2413 }
2414
2415
2416 DWORD
2417 APIENTRY
2418 NtUserGetKeyboardState(LPBYTE lpKeyState)
2419 {
2420 DWORD i, ret = TRUE;
2421 PTHREADINFO pti;
2422 PUSER_MESSAGE_QUEUE MessageQueue;
2423
2424 UserEnterShared();
2425
2426 pti = PsGetCurrentThreadWin32Thread();
2427 MessageQueue = pti->MessageQueue;
2428
2429 _SEH2_TRY
2430 {
2431 /* Probe and copy key state to an array */
2432 ProbeForWrite(lpKeyState, 256 * sizeof(BYTE), 1);
2433 for (i = 0; i < 256; ++i)
2434 {
2435 lpKeyState[i] = 0;
2436 if (IS_KEY_DOWN(MessageQueue->afKeyState, i))
2437 lpKeyState[i] |= KS_DOWN_BIT;
2438 if (IS_KEY_LOCKED(MessageQueue->afKeyState, i))
2439 lpKeyState[i] |= KS_LOCK_BIT;
2440 }
2441 }
2442 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2443 {
2444 SetLastNtError(_SEH2_GetExceptionCode());
2445 ret = FALSE;
2446 }
2447 _SEH2_END;
2448
2449 UserLeave();
2450
2451 return ret;
2452 }
2453
2454 BOOL
2455 APIENTRY
2456 NtUserSetKeyboardState(LPBYTE pKeyState)
2457 {
2458 UINT i;
2459 BOOL bRet = TRUE;
2460 PTHREADINFO pti;
2461 PUSER_MESSAGE_QUEUE MessageQueue;
2462
2463 UserEnterExclusive();
2464
2465 pti = PsGetCurrentThreadWin32Thread();
2466 MessageQueue = pti->MessageQueue;
2467
2468 _SEH2_TRY
2469 {
2470 ProbeForRead(pKeyState, 256 * sizeof(BYTE), 1);
2471 for (i = 0; i < 256; ++i)
2472 {
2473 SET_KEY_DOWN(MessageQueue->afKeyState, i, pKeyState[i] & KS_DOWN_BIT);
2474 SET_KEY_LOCKED(MessageQueue->afKeyState, i, pKeyState[i] & KS_LOCK_BIT);
2475 }
2476 }
2477 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2478 {
2479 SetLastNtError(_SEH2_GetExceptionCode());
2480 bRet = FALSE;
2481 }
2482 _SEH2_END;
2483
2484 UserLeave();
2485
2486 return bRet;
2487 }
2488
2489 /* EOF */