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