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