[Win32k]
[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,
1032 UINT Msg,
1033 WPARAM wParam,
1034 LPARAM lParam,
1035 UINT uTimeout,
1036 BOOL Block,
1037 INT HookMessage,
1038 ULONG_PTR *uResult)
1039 {
1040 PTHREADINFO pti;
1041 PUSER_SENT_MESSAGE Message;
1042 KEVENT CompletionEvent;
1043 NTSTATUS WaitStatus;
1044 LARGE_INTEGER Timeout;
1045 PLIST_ENTRY Entry;
1046 PWND pWnd;
1047 LRESULT Result = 0; //// Result could be trashed. ////
1048
1049 pti = PsGetCurrentThreadWin32Thread();
1050 ASSERT(pti != ptirec);
1051 ASSERT(ptirec->pcti); // Send must have a client side to receive it!!!!
1052
1053 /* Don't send from or to a dying thread */
1054 if (pti->TIF_flags & TIF_INCLEANUP || ptirec->TIF_flags & TIF_INCLEANUP)
1055 {
1056 // Unless we are dying and need to tell our parents.
1057 if (pti->TIF_flags & TIF_INCLEANUP && !(ptirec->TIF_flags & TIF_INCLEANUP))
1058 {
1059 // Parent notify is the big one. Fire and forget!
1060 TRACE("Send message from dying thread %d\n",Msg);
1061 co_MsqSendMessageAsync(ptirec, Wnd, Msg, wParam, lParam, NULL, 0, FALSE, HookMessage);
1062 }
1063 if (uResult) *uResult = -1;
1064 TRACE("MsqSM: Msg %d Current pti %lu or Rec pti %lu\n", Msg, pti->TIF_flags & TIF_INCLEANUP, ptirec->TIF_flags & TIF_INCLEANUP);
1065 return STATUS_UNSUCCESSFUL;
1066 }
1067
1068 if ( HookMessage == MSQ_NORMAL )
1069 {
1070 pWnd = ValidateHwndNoErr(Wnd);
1071
1072 // These can not cross International Border lines!
1073 if ( pti->ppi != ptirec->ppi && pWnd )
1074 {
1075 switch(Msg)
1076 {
1077 // Handle the special case when working with password transfers across bordering processes.
1078 case EM_GETLINE:
1079 case EM_SETPASSWORDCHAR:
1080 case WM_GETTEXT:
1081 // Look for edit controls setup for passwords.
1082 if ( gpsi->atomSysClass[ICLS_EDIT] == pWnd->pcls->atomClassName && // Use atomNVClassName.
1083 pWnd->style & ES_PASSWORD )
1084 {
1085 if (uResult) *uResult = -1;
1086 ERR("Running across the border without a passport!\n");
1087 EngSetLastError(ERROR_ACCESS_DENIED);
1088 return STATUS_UNSUCCESSFUL;
1089 }
1090 break;
1091 case WM_NOTIFY:
1092 if (uResult) *uResult = -1;
1093 ERR("Running across the border without a passport!\n");
1094 return STATUS_UNSUCCESSFUL;
1095 }
1096 }
1097
1098 // These can not cross State lines!
1099 if ( Msg == WM_CREATE || Msg == WM_NCCREATE )
1100 {
1101 if (uResult) *uResult = -1;
1102 ERR("Can not tell the other State we have Create!\n");
1103 return STATUS_UNSUCCESSFUL;
1104 }
1105 }
1106
1107 if(!(Message = ExAllocatePoolWithTag(PagedPool, sizeof(USER_SENT_MESSAGE), TAG_USRMSG)))
1108 {
1109 ERR("MsqSendMessage(): Not enough memory to allocate a message");
1110 return STATUS_INSUFFICIENT_RESOURCES;
1111 }
1112
1113 KeInitializeEvent(&CompletionEvent, NotificationEvent, FALSE);
1114
1115 Timeout.QuadPart = Int32x32To64(-10000,uTimeout); // Pass SMTO test with a TO of 0x80000000.
1116 TRACE("Timeout val %lld\n",Timeout.QuadPart)
1117
1118 /* FIXME: Increase reference counter of sender's message queue here */
1119
1120 Message->Msg.hwnd = Wnd;
1121 Message->Msg.message = Msg;
1122 Message->Msg.wParam = wParam;
1123 Message->Msg.lParam = lParam;
1124 Message->CompletionEvent = &CompletionEvent;
1125 Message->Result = &Result;
1126 Message->lResult = 0;
1127 Message->QS_Flags = 0;
1128 Message->ptiReceiver = ptirec;
1129 Message->ptiSender = pti;
1130 Message->ptiCallBackSender = NULL;
1131 Message->CompletionCallback = NULL;
1132 Message->CompletionCallbackContext = 0;
1133 Message->HookMessage = HookMessage;
1134 Message->HasPackedLParam = FALSE;
1135
1136 /* Add it to the list of pending messages */
1137 InsertTailList(&pti->DispatchingMessagesHead, &Message->DispatchingListEntry);
1138
1139 /* Queue it in the destination's message queue */
1140 InsertTailList(&ptirec->SentMessagesListHead, &Message->ListEntry);
1141
1142 Message->QS_Flags = QS_SENDMESSAGE;
1143 MsqWakeQueue(ptirec, QS_SENDMESSAGE, TRUE);
1144
1145 /* We can't access the Message anymore since it could have already been deleted! */
1146
1147 if (Block)
1148 {
1149 PVOID WaitObjects[2];
1150
1151 WaitObjects[0] = &CompletionEvent; // Wait 0
1152 WaitObjects[1] = ptirec->pEThread; // Wait 1
1153
1154 UserLeaveCo();
1155
1156 WaitStatus = KeWaitForMultipleObjects(2, WaitObjects, WaitAny, UserRequest,
1157 UserMode, FALSE, (uTimeout ? &Timeout : NULL), NULL);
1158
1159 UserEnterCo();
1160
1161 if (WaitStatus == STATUS_TIMEOUT || WaitStatus == STATUS_USER_APC)
1162 {
1163 /* Look up if the message has not yet dispatched, if so
1164 make sure it can't pass a result and it must not set the completion event anymore */
1165 Entry = ptirec->SentMessagesListHead.Flink;
1166 while (Entry != &ptirec->SentMessagesListHead)
1167 {
1168 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry)
1169 == Message)
1170 {
1171 /* We can access Message here, it's secure because the message queue is locked
1172 and the message is still hasn't been dispatched */
1173 Message->CompletionEvent = NULL;
1174 Message->Result = NULL;
1175 break;
1176 }
1177 Entry = Entry->Flink;
1178 }
1179
1180 /* Remove from the local dispatching list so the other thread knows,
1181 it can't pass a result and it must not set the completion event anymore */
1182 Entry = pti->DispatchingMessagesHead.Flink;
1183 while (Entry != &pti->DispatchingMessagesHead)
1184 {
1185 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, DispatchingListEntry)
1186 == Message)
1187 {
1188 /* We can access Message here, it's secure because the sender's message is locked
1189 and the message has definitely not yet been destroyed, otherwise it would
1190 have been removed from this list by the dispatching routine right after
1191 dispatching the message */
1192 Message->CompletionEvent = NULL;
1193 Message->Result = NULL;
1194 RemoveEntryList(&Message->DispatchingListEntry);
1195 Message->DispatchingListEntry.Flink = NULL;
1196 break;
1197 }
1198 Entry = Entry->Flink;
1199 }
1200
1201 TRACE("MsqSendMessage (blocked) timed out 1 Status %p\n",WaitStatus);
1202
1203 }
1204 // Receiving thread passed on and left us hanging with issues still pending.
1205 if ( WaitStatus == STATUS_WAIT_1 )
1206 {
1207 ERR("Bk Receiving Thread woken up dead!\n");
1208 Entry = pti->DispatchingMessagesHead.Flink;
1209 while (Entry != &pti->DispatchingMessagesHead)
1210 {
1211 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, DispatchingListEntry)
1212 == Message)
1213 {
1214 Message->CompletionEvent = NULL;
1215 Message->Result = NULL;
1216 RemoveEntryList(&Message->DispatchingListEntry);
1217 Message->DispatchingListEntry.Flink = NULL;
1218 break;
1219 }
1220 Entry = Entry->Flink;
1221 }
1222 }
1223 while (co_MsqDispatchOneSentMessage(pti))
1224 ;
1225 }
1226 else
1227 {
1228 PVOID WaitObjects[3];
1229
1230 WaitObjects[0] = &CompletionEvent; // Wait 0
1231 WaitObjects[1] = pti->pEventQueueServer; // Wait 1
1232 WaitObjects[2] = ptirec->pEThread; // Wait 2
1233
1234 do
1235 {
1236 UserLeaveCo();
1237
1238 WaitStatus = KeWaitForMultipleObjects(3, WaitObjects, WaitAny, UserRequest,
1239 UserMode, FALSE, (uTimeout ? &Timeout : NULL), NULL);
1240
1241 UserEnterCo();
1242
1243 if (WaitStatus == STATUS_TIMEOUT || WaitStatus == STATUS_USER_APC)
1244 {
1245 /* Look up if the message has not yet been dispatched, if so
1246 make sure it can't pass a result and it must not set the completion event anymore */
1247 Entry = ptirec->SentMessagesListHead.Flink;
1248 while (Entry != &ptirec->SentMessagesListHead)
1249 {
1250 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry)
1251 == Message)
1252 {
1253 /* We can access Message here, it's secure because the message queue is locked
1254 and the message is still hasn't been dispatched */
1255 Message->CompletionEvent = NULL;
1256 Message->Result = NULL;
1257 break;
1258 }
1259 Entry = Entry->Flink;
1260 }
1261
1262 /* Remove from the local dispatching list so the other thread knows,
1263 it can't pass a result and it must not set the completion event anymore */
1264 Entry = pti->DispatchingMessagesHead.Flink;
1265 while (Entry != &pti->DispatchingMessagesHead)
1266 {
1267 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, DispatchingListEntry)
1268 == Message)
1269 {
1270 /* We can access Message here, it's secure because the sender's message is locked
1271 and the message has definitely not yet been destroyed, otherwise it would
1272 have been removed from this list by the dispatching routine right after
1273 dispatching the message */
1274 Message->CompletionEvent = NULL;
1275 Message->Result = NULL;
1276 RemoveEntryList(&Message->DispatchingListEntry);
1277 Message->DispatchingListEntry.Flink = NULL;
1278 break;
1279 }
1280 Entry = Entry->Flink;
1281 }
1282
1283 TRACE("MsqSendMessage timed out 2 Status %p\n",WaitStatus);
1284
1285 break;
1286 }
1287 // Receiving thread passed on and left us hanging with issues still pending.
1288 if ( WaitStatus == STATUS_WAIT_2 )
1289 {
1290 ERR("NB Receiving Thread woken up dead!\n");
1291 Entry = pti->DispatchingMessagesHead.Flink;
1292 while (Entry != &pti->DispatchingMessagesHead)
1293 {
1294 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, DispatchingListEntry)
1295 == Message)
1296 {
1297 Message->CompletionEvent = NULL;
1298 Message->Result = NULL;
1299 RemoveEntryList(&Message->DispatchingListEntry);
1300 Message->DispatchingListEntry.Flink = NULL;
1301 break;
1302 }
1303 Entry = Entry->Flink;
1304 }
1305 }
1306 while (co_MsqDispatchOneSentMessage(pti))
1307 ;
1308 }
1309 while (NT_SUCCESS(WaitStatus) && WaitStatus == STATUS_WAIT_1);
1310 }
1311
1312 if ( WaitStatus == STATUS_USER_APC )
1313 {
1314 // The current thread is dying!
1315 TRACE("User APC\n");
1316 co_IntDeliverUserAPC();
1317 ERR("User APC Returned\n"); // Should not see this message.
1318 }
1319
1320 if ( WaitStatus != STATUS_TIMEOUT )
1321 {
1322 if (uResult)
1323 {
1324 *uResult = (STATUS_WAIT_0 == WaitStatus ? Result : -1);
1325 }
1326 }
1327
1328 return WaitStatus;
1329 }
1330
1331 VOID FASTCALL
1332 MsqPostMessage(PTHREADINFO pti,
1333 MSG* Msg,
1334 BOOLEAN HardwareMessage,
1335 DWORD MessageBits,
1336 DWORD dwQEvent,
1337 LONG_PTR ExtraInfo)
1338 {
1339 PUSER_MESSAGE Message;
1340 PUSER_MESSAGE_QUEUE MessageQueue;
1341
1342 if ( pti->TIF_flags & TIF_INCLEANUP || pti->MessageQueue->QF_flags & QF_INDESTROY )
1343 {
1344 ERR("Post Msg; Thread or Q is Dead!\n");
1345 return;
1346 }
1347
1348 if(!(Message = MsqCreateMessage(Msg)))
1349 {
1350 return;
1351 }
1352
1353 MessageQueue = pti->MessageQueue;
1354
1355 if (dwQEvent)
1356 {
1357 ERR("Post Msg; System Qeued Event Message!\n");
1358 InsertHeadList(&pti->PostedMessagesListHead, &Message->ListEntry);
1359 }
1360 else if (!HardwareMessage)
1361 {
1362 InsertTailList(&pti->PostedMessagesListHead, &Message->ListEntry);
1363 }
1364 else
1365 {
1366 InsertTailList(&MessageQueue->HardwareMessagesListHead, &Message->ListEntry);
1367 }
1368
1369 if (Msg->message == WM_HOTKEY) MessageBits |= QS_HOTKEY; // Justin Case, just set it.
1370 Message->dwQEvent = dwQEvent;
1371 Message->ExtraInfo = ExtraInfo;
1372 Message->QS_Flags = MessageBits;
1373 Message->pti = pti;
1374 MsqWakeQueue(pti, MessageBits, TRUE);
1375 }
1376
1377 VOID FASTCALL
1378 MsqPostQuitMessage(PTHREADINFO pti, ULONG ExitCode)
1379 {
1380 pti->QuitPosted = TRUE;
1381 pti->exitCode = ExitCode;
1382 MsqWakeQueue(pti, QS_POSTMESSAGE|QS_ALLPOSTMESSAGE, TRUE);
1383 }
1384
1385 /***********************************************************************
1386 * MsqSendParentNotify
1387 *
1388 * Send a WM_PARENTNOTIFY to all ancestors of the given window, unless
1389 * the window has the WS_EX_NOPARENTNOTIFY style.
1390 */
1391 static void MsqSendParentNotify( PWND pwnd, WORD event, WORD idChild, POINT pt )
1392 {
1393 PWND pwndDesktop = UserGetDesktopWindow();
1394
1395 /* pt has to be in the client coordinates of the parent window */
1396 pt.x += pwndDesktop->rcClient.left - pwnd->rcClient.left;
1397 pt.y += pwndDesktop->rcClient.top - pwnd->rcClient.top;
1398
1399 for (;;)
1400 {
1401 PWND pwndParent;
1402
1403 if (!(pwnd->style & WS_CHILD)) break;
1404 if (pwnd->ExStyle & WS_EX_NOPARENTNOTIFY) break;
1405 if (!(pwndParent = IntGetParent(pwnd))) break;
1406 if (pwndParent == pwndDesktop) break;
1407 pt.x += pwnd->rcClient.left - pwndParent->rcClient.left;
1408 pt.y += pwnd->rcClient.top - pwndParent->rcClient.top;
1409
1410 pwnd = pwndParent;
1411 co_IntSendMessage( UserHMGetHandle(pwnd), WM_PARENTNOTIFY,
1412 MAKEWPARAM( event, idChild ), MAKELPARAM( pt.x, pt.y ) );
1413 }
1414 }
1415
1416 VOID
1417 FASTCALL
1418 IntTrackMouseMove(PWND pwndTrack, PDESKTOP pDesk, PMSG msg, USHORT hittest)
1419 {
1420 // PWND pwndTrack = IntChildrenWindowFromPoint(pwndMsg, msg->pt.x, msg->pt.y);
1421 // hittest = (USHORT)GetNCHitEx(pwndTrack, msg->pt); /// @todo WTF is this???
1422
1423 if ( pDesk->spwndTrack != pwndTrack || // Change with tracking window or
1424 msg->message != WM_MOUSEMOVE || // Mouse click changes or
1425 pDesk->htEx != hittest) // Change in current hit test states.
1426 {
1427 TRACE("ITMM: Track Mouse Move!\n");
1428
1429 /* Handle only the changing window track and mouse move across a border. */
1430 if ( pDesk->spwndTrack != pwndTrack ||
1431 (pDesk->htEx == HTCLIENT) ^ (hittest == HTCLIENT) )
1432 {
1433 TRACE("ITMM: Another Wnd %d or Across Border %d\n",
1434 pDesk->spwndTrack != pwndTrack,(pDesk->htEx == HTCLIENT) ^ (hittest == HTCLIENT));
1435
1436 if ( pDesk->dwDTFlags & DF_TME_LEAVE )
1437 UserPostMessage( UserHMGetHandle(pDesk->spwndTrack),
1438 (pDesk->htEx != HTCLIENT) ? WM_NCMOUSELEAVE : WM_MOUSELEAVE,
1439 0, 0);
1440
1441 if ( pDesk->dwDTFlags & DF_TME_HOVER )
1442 IntKillTimer(pDesk->spwndTrack, ID_EVENT_SYSTIMER_MOUSEHOVER, TRUE);
1443
1444 /* Clear the flags to sign a change. */
1445 pDesk->dwDTFlags &= ~(DF_TME_LEAVE|DF_TME_HOVER);
1446 }
1447 /* Set the Track window and hit test. */
1448 pDesk->spwndTrack = pwndTrack;
1449 pDesk->htEx = hittest;
1450 }
1451
1452 /* Reset, Same Track window, Hover set and Mouse Clicks or Clobbered Hover box. */
1453 if ( pDesk->spwndTrack == pwndTrack &&
1454 ( msg->message != WM_MOUSEMOVE || !RECTL_bPointInRect(&pDesk->rcMouseHover, msg->pt.x, msg->pt.y)) &&
1455 pDesk->dwDTFlags & DF_TME_HOVER )
1456 {
1457 TRACE("ITMM: Reset Hover points!\n");
1458 // Restart timer for the hover period.
1459 IntSetTimer(pDesk->spwndTrack, ID_EVENT_SYSTIMER_MOUSEHOVER, pDesk->dwMouseHoverTime, SystemTimerProc, TMRF_SYSTEM);
1460 // Reset desktop mouse hover from the system default hover rectangle.
1461 RECTL_vSetRect(&pDesk->rcMouseHover,
1462 msg->pt.x - gspv.iMouseHoverWidth / 2,
1463 msg->pt.y - gspv.iMouseHoverHeight / 2,
1464 msg->pt.x + gspv.iMouseHoverWidth / 2,
1465 msg->pt.y + gspv.iMouseHoverHeight / 2);
1466 }
1467 }
1468
1469 PWND FASTCALL
1470 co_IntFindChildWindowToOwner(PWND Root, PWND Owner)
1471 {
1472 PWND Ret;
1473 PWND Child, OwnerWnd;
1474
1475 for(Child = Root->spwndChild; Child; Child = Child->spwndNext)
1476 {
1477 OwnerWnd = Child->spwndOwner;
1478 if(!OwnerWnd)
1479 continue;
1480
1481 if(OwnerWnd == Owner)
1482 {
1483 Ret = Child;
1484 return Ret;
1485 }
1486 }
1487 return NULL;
1488 }
1489
1490 BOOL co_IntProcessMouseMessage(MSG* msg, BOOL* RemoveMessages, UINT first, UINT last)
1491 {
1492 MSG clk_msg;
1493 POINT pt;
1494 UINT message;
1495 USHORT hittest;
1496 EVENTMSG event;
1497 MOUSEHOOKSTRUCT hook;
1498 BOOL eatMsg = FALSE;
1499
1500 PWND pwndMsg, pwndDesktop, pwndPopUP;
1501 PUSER_MESSAGE_QUEUE MessageQueue;
1502 PTHREADINFO pti;
1503 PSYSTEM_CURSORINFO CurInfo;
1504 PDESKTOP pDesk;
1505 DECLARE_RETURN(BOOL);
1506
1507 pti = PsGetCurrentThreadWin32Thread();
1508 pwndDesktop = UserGetDesktopWindow();
1509 MessageQueue = pti->MessageQueue;
1510 CurInfo = IntGetSysCursorInfo();
1511 pwndPopUP = pwndMsg = ValidateHwndNoErr(msg->hwnd);
1512 clk_msg = MessageQueue->msgDblClk;
1513 pDesk = pwndDesktop->head.rpdesk;
1514
1515 /* find the window to dispatch this mouse message to */
1516 if (MessageQueue->spwndCapture)
1517 {
1518 hittest = HTCLIENT;
1519 pwndMsg = MessageQueue->spwndCapture;
1520 if (pwndMsg) UserReferenceObject(pwndMsg);
1521 }
1522 else
1523 {
1524 /*
1525 Start with null window. See wine win.c:test_mouse_input:WM_COMMAND tests.
1526 */
1527 pwndMsg = co_WinPosWindowFromPoint( NULL, &msg->pt, &hittest, FALSE);
1528 //
1529 // CORE-6129, Override if a diabled window with a visible popup was selected.
1530 //
1531 if (pwndPopUP && pwndPopUP->style & WS_DISABLED)
1532 {
1533 TRACE("window disabled\n");
1534 pwndPopUP = co_IntFindChildWindowToOwner(UserGetDesktopWindow(), pwndPopUP);
1535 if ( pwndPopUP &&
1536 pwndPopUP->style & WS_POPUP &&
1537 pwndPopUP->style & WS_VISIBLE &&
1538 (pwndPopUP->head.pti->MessageQueue != gpqForeground ||
1539 pwndPopUP->head.pti->MessageQueue->spwndActive != pwndPopUP) &&
1540 //pwndPopUP != pwndPopUP->head.rpdesk->pDeskInfo->spwndShell needs testing.
1541 pwndPopUP != ValidateHwndNoErr(InputWindowStation->ShellWindow) )
1542 {
1543 TRACE("Found Popup!\n");
1544 UserDereferenceObject(pwndMsg);
1545 pwndMsg = pwndPopUP;
1546 UserReferenceObject(pwndMsg);
1547 }
1548 }
1549 }
1550
1551 TRACE("Got mouse message for %p, hittest: 0x%x\n", msg->hwnd, hittest);
1552
1553 // Null window or not the same "Hardware" message queue.
1554 if (pwndMsg == NULL || pwndMsg->head.pti->MessageQueue != pti->MessageQueue)
1555 {
1556 /* Remove and ignore the message */
1557 *RemoveMessages = TRUE;
1558 RETURN(FALSE);
1559 }
1560
1561 if ( MessageQueue == gpqCursor ) // Cursor must use the same Queue!
1562 {
1563 IntTrackMouseMove(pwndMsg, pDesk, msg, hittest);
1564 }
1565 else
1566 {
1567 ERR("Not the same cursor!\n");
1568 }
1569
1570 msg->hwnd = UserHMGetHandle(pwndMsg);
1571
1572 pt = msg->pt;
1573 message = msg->message;
1574 /* Note: windows has no concept of a non-client wheel message */
1575 if (message != WM_MOUSEWHEEL)
1576 {
1577 if (hittest != HTCLIENT)
1578 {
1579 message += WM_NCMOUSEMOVE - WM_MOUSEMOVE;
1580 msg->wParam = hittest; // Caution! This might break wParam check in DblClk.
1581 }
1582 else
1583 {
1584 /* coordinates don't get translated while tracking a menu */
1585 /* FIXME: should differentiate popups and top-level menus */
1586 if (!(MessageQueue->MenuOwner))
1587 {
1588 pt.x += pwndDesktop->rcClient.left - pwndMsg->rcClient.left;
1589 pt.y += pwndDesktop->rcClient.top - pwndMsg->rcClient.top;
1590 }
1591 }
1592 }
1593 msg->lParam = MAKELONG( pt.x, pt.y );
1594
1595 /* translate double clicks */
1596
1597 if ((msg->message == WM_LBUTTONDOWN) ||
1598 (msg->message == WM_RBUTTONDOWN) ||
1599 (msg->message == WM_MBUTTONDOWN) ||
1600 (msg->message == WM_XBUTTONDOWN))
1601 {
1602 BOOL update = *RemoveMessages;
1603
1604 /* translate double clicks -
1605 * note that ...MOUSEMOVEs can slip in between
1606 * ...BUTTONDOWN and ...BUTTONDBLCLK messages */
1607
1608 if ((MessageQueue->MenuOwner || MessageQueue->MoveSize) ||
1609 hittest != HTCLIENT ||
1610 (pwndMsg->pcls->style & CS_DBLCLKS))
1611 {
1612 if ((msg->message == clk_msg.message) &&
1613 (msg->hwnd == clk_msg.hwnd) &&
1614 // Only worry about XButton wParam.
1615 (msg->message != WM_XBUTTONDOWN || GET_XBUTTON_WPARAM(msg->wParam) == GET_XBUTTON_WPARAM(clk_msg.wParam)) &&
1616 ((msg->time - clk_msg.time) < (ULONG)gspv.iDblClickTime) &&
1617 (abs(msg->pt.x - clk_msg.pt.x) < UserGetSystemMetrics(SM_CXDOUBLECLK)/2) &&
1618 (abs(msg->pt.y - clk_msg.pt.y) < UserGetSystemMetrics(SM_CYDOUBLECLK)/2))
1619 {
1620 message += (WM_LBUTTONDBLCLK - WM_LBUTTONDOWN);
1621 if (update)
1622 {
1623 MessageQueue->msgDblClk.message = 0; /* clear the double click conditions */
1624 update = FALSE;
1625 }
1626 }
1627 }
1628
1629 if (!((first == 0 && last == 0) || (message >= first || message <= last)))
1630 {
1631 TRACE("Message out of range!!!\n");
1632 RETURN(FALSE);
1633 }
1634
1635 /* update static double click conditions */
1636 if (update) MessageQueue->msgDblClk = *msg;
1637 }
1638 else
1639 {
1640 if (!((first == 0 && last == 0) || (message >= first || message <= last)))
1641 {
1642 TRACE("Message out of range!!!\n");
1643 RETURN(FALSE);
1644 }
1645
1646 // Update mouse move down keys.
1647 if (message == WM_MOUSEMOVE)
1648 {
1649 msg->wParam = MsqGetDownKeyState(MessageQueue);
1650 }
1651 }
1652
1653 if(gspv.bMouseClickLock)
1654 {
1655 BOOL IsClkLck = FALSE;
1656
1657 if(msg->message == WM_LBUTTONUP)
1658 {
1659 IsClkLck = ((msg->time - CurInfo->ClickLockTime) >= gspv.dwMouseClickLockTime);
1660 if (IsClkLck && (!CurInfo->ClickLockActive))
1661 {
1662 CurInfo->ClickLockActive = TRUE;
1663 }
1664 }
1665 else if (msg->message == WM_LBUTTONDOWN)
1666 {
1667 if (CurInfo->ClickLockActive)
1668 {
1669 IsClkLck = TRUE;
1670 CurInfo->ClickLockActive = FALSE;
1671 }
1672
1673 CurInfo->ClickLockTime = msg->time;
1674 }
1675
1676 if(IsClkLck)
1677 {
1678 /* Remove and ignore the message */
1679 *RemoveMessages = TRUE;
1680 TRACE("Remove and ignore the message\n");
1681 RETURN(FALSE);
1682 }
1683 }
1684
1685 /* message is accepted now (but may still get dropped) */
1686
1687 event.message = msg->message;
1688 event.time = msg->time;
1689 event.hwnd = msg->hwnd;
1690 event.paramL = msg->pt.x;
1691 event.paramH = msg->pt.y;
1692 co_HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&event );
1693
1694 hook.pt = msg->pt;
1695 hook.hwnd = msg->hwnd;
1696 hook.wHitTestCode = hittest;
1697 hook.dwExtraInfo = 0 /* extra_info */ ;
1698 if (co_HOOK_CallHooks( WH_MOUSE, *RemoveMessages ? HC_ACTION : HC_NOREMOVE,
1699 message, (LPARAM)&hook ))
1700 {
1701 hook.pt = msg->pt;
1702 hook.hwnd = msg->hwnd;
1703 hook.wHitTestCode = hittest;
1704 hook.dwExtraInfo = 0 /* extra_info */ ;
1705 co_HOOK_CallHooks( WH_CBT, HCBT_CLICKSKIPPED, message, (LPARAM)&hook );
1706
1707 ERR("WH_MOUSE dropped mouse message!\n");
1708
1709 /* Remove and skip message */
1710 *RemoveMessages = TRUE;
1711 RETURN(FALSE);
1712 }
1713
1714 if ((hittest == HTERROR) || (hittest == HTNOWHERE))
1715 {
1716 co_IntSendMessage( msg->hwnd, WM_SETCURSOR, (WPARAM)msg->hwnd,
1717 MAKELONG( hittest, msg->message ));
1718
1719 /* Remove and skip message */
1720 *RemoveMessages = TRUE;
1721 RETURN(FALSE);
1722 }
1723
1724 if ((*RemoveMessages == FALSE) || MessageQueue->spwndCapture)
1725 {
1726 /* Accept the message */
1727 msg->message = message;
1728 RETURN(TRUE);
1729 }
1730
1731 if ((msg->message == WM_LBUTTONDOWN) ||
1732 (msg->message == WM_RBUTTONDOWN) ||
1733 (msg->message == WM_MBUTTONDOWN) ||
1734 (msg->message == WM_XBUTTONDOWN))
1735 {
1736 /* Send the WM_PARENTNOTIFY,
1737 * note that even for double/nonclient clicks
1738 * notification message is still WM_L/M/RBUTTONDOWN.
1739 */
1740 MsqSendParentNotify(pwndMsg, msg->message, 0, msg->pt );
1741
1742 /* Activate the window if needed */
1743
1744 if (pwndMsg != MessageQueue->spwndActive)
1745 {
1746 PWND pwndTop = pwndMsg;
1747 pwndTop = IntGetNonChildAncestor(pwndTop);
1748
1749 if (pwndTop && pwndTop != pwndDesktop)
1750 {
1751 LONG ret = co_IntSendMessage( msg->hwnd,
1752 WM_MOUSEACTIVATE,
1753 (WPARAM)UserHMGetHandle(pwndTop),
1754 MAKELONG( hittest, msg->message));
1755 switch(ret)
1756 {
1757 case MA_NOACTIVATEANDEAT:
1758 eatMsg = TRUE;
1759 /* fall through */
1760 case MA_NOACTIVATE:
1761 break;
1762 case MA_ACTIVATEANDEAT:
1763 eatMsg = TRUE;
1764 /* fall through */
1765 case MA_ACTIVATE:
1766 case 0:
1767 if (!co_IntMouseActivateWindow( pwndTop )) eatMsg = TRUE;
1768 break;
1769 default:
1770 ERR( "unknown WM_MOUSEACTIVATE code %d\n", ret );
1771 break;
1772 }
1773 }
1774 }
1775 }
1776
1777 /* send the WM_SETCURSOR message */
1778
1779 /* Windows sends the normal mouse message as the message parameter
1780 in the WM_SETCURSOR message even if it's non-client mouse message */
1781 co_IntSendMessage( msg->hwnd, WM_SETCURSOR, (WPARAM)msg->hwnd, MAKELONG( hittest, msg->message ));
1782
1783 msg->message = message;
1784 RETURN(!eatMsg);
1785
1786 CLEANUP:
1787 if(pwndMsg)
1788 UserDereferenceObject(pwndMsg);
1789
1790 END_CLEANUP;
1791 }
1792
1793 BOOL co_IntProcessKeyboardMessage(MSG* Msg, BOOL* RemoveMessages)
1794 {
1795 EVENTMSG Event;
1796 USER_REFERENCE_ENTRY Ref;
1797 PWND pWnd;
1798 UINT ImmRet;
1799 BOOL Ret = TRUE;
1800 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
1801
1802 if (Msg->message == VK_PACKET)
1803 {
1804 pti->wchInjected = HIWORD(Msg->wParam);
1805 }
1806
1807 if (Msg->message == WM_KEYDOWN || Msg->message == WM_SYSKEYDOWN ||
1808 Msg->message == WM_KEYUP || Msg->message == WM_SYSKEYUP)
1809 {
1810 switch (Msg->wParam)
1811 {
1812 case VK_LSHIFT: case VK_RSHIFT:
1813 Msg->wParam = VK_SHIFT;
1814 break;
1815 case VK_LCONTROL: case VK_RCONTROL:
1816 Msg->wParam = VK_CONTROL;
1817 break;
1818 case VK_LMENU: case VK_RMENU:
1819 Msg->wParam = VK_MENU;
1820 break;
1821 }
1822 }
1823
1824 pWnd = ValidateHwndNoErr(Msg->hwnd);
1825 if (pWnd) UserRefObjectCo(pWnd, &Ref);
1826
1827 Event.message = Msg->message;
1828 Event.hwnd = Msg->hwnd;
1829 Event.time = Msg->time;
1830 Event.paramL = (Msg->wParam & 0xFF) | (HIWORD(Msg->lParam) << 8);
1831 Event.paramH = Msg->lParam & 0x7FFF;
1832 if (HIWORD(Msg->lParam) & 0x0100) Event.paramH |= 0x8000;
1833 co_HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&Event);
1834
1835 if (*RemoveMessages)
1836 {
1837 if ((Msg->message == WM_KEYDOWN) &&
1838 (Msg->hwnd != IntGetDesktopWindow()))
1839 {
1840 /* Handle F1 key by sending out WM_HELP message */
1841 if (Msg->wParam == VK_F1)
1842 {
1843 UserPostMessage( Msg->hwnd, WM_KEYF1, 0, 0 );
1844 }
1845 else if (Msg->wParam >= VK_BROWSER_BACK &&
1846 Msg->wParam <= VK_LAUNCH_APP2)
1847 {
1848 /* FIXME: Process keystate */
1849 co_IntSendMessage(Msg->hwnd, WM_APPCOMMAND, (WPARAM)Msg->hwnd, MAKELPARAM(0, (FAPPCOMMAND_KEY | (Msg->wParam - VK_BROWSER_BACK + 1))));
1850 }
1851 }
1852 else if (Msg->message == WM_KEYUP)
1853 {
1854 /* Handle VK_APPS key by posting a WM_CONTEXTMENU message */
1855 if (Msg->wParam == VK_APPS && pti->MessageQueue->MenuOwner == NULL)
1856 UserPostMessage( Msg->hwnd, WM_CONTEXTMENU, (WPARAM)Msg->hwnd, -1 );
1857 }
1858 }
1859
1860 if (co_HOOK_CallHooks( WH_KEYBOARD,
1861 *RemoveMessages ? HC_ACTION : HC_NOREMOVE,
1862 LOWORD(Msg->wParam),
1863 Msg->lParam))
1864 {
1865 /* skip this message */
1866 co_HOOK_CallHooks( WH_CBT,
1867 HCBT_KEYSKIPPED,
1868 LOWORD(Msg->wParam),
1869 Msg->lParam );
1870
1871 ERR("KeyboardMessage WH_KEYBOARD Call Hook return!\n");
1872
1873 *RemoveMessages = TRUE;
1874
1875 Ret = FALSE;
1876 }
1877
1878 if ( pWnd && Ret && *RemoveMessages && Msg->message == WM_KEYDOWN && !(pti->TIF_flags & TIF_DISABLEIME))
1879 {
1880 if ( (ImmRet = IntImmProcessKey(pti->MessageQueue, pWnd, Msg->message, Msg->wParam, Msg->lParam)) )
1881 {
1882 if ( ImmRet & (IPHK_HOTKEY|IPHK_SKIPTHISKEY) )
1883 {
1884 ImmRet = 0;
1885 }
1886 if ( ImmRet & IPHK_PROCESSBYIME )
1887 {
1888 Msg->wParam = VK_PROCESSKEY;
1889 }
1890 }
1891 }
1892
1893 if (pWnd) UserDerefObjectCo(pWnd);
1894 return Ret;
1895 }
1896
1897 BOOL co_IntProcessHardwareMessage(MSG* Msg, BOOL* RemoveMessages, UINT first, UINT last)
1898 {
1899 if ( IS_MOUSE_MESSAGE(Msg->message))
1900 {
1901 return co_IntProcessMouseMessage(Msg, RemoveMessages, first, last);
1902 }
1903 else if ( IS_KBD_MESSAGE(Msg->message))
1904 {
1905 return co_IntProcessKeyboardMessage(Msg, RemoveMessages);
1906 }
1907
1908 return TRUE;
1909 }
1910
1911 /* check whether a message filter contains at least one potential hardware message */
1912 static INT FASTCALL
1913 filter_contains_hw_range( UINT first, UINT last )
1914 {
1915 /* hardware message ranges are (in numerical order):
1916 * WM_NCMOUSEFIRST .. WM_NCMOUSELAST
1917 * WM_KEYFIRST .. WM_KEYLAST
1918 * WM_MOUSEFIRST .. WM_MOUSELAST
1919 */
1920 if (!last) --last;
1921 if (last < WM_NCMOUSEFIRST) return 0;
1922 if (first > WM_NCMOUSELAST && last < WM_KEYFIRST) return 0;
1923 if (first > WM_KEYLAST && last < WM_MOUSEFIRST) return 0;
1924 if (first > WM_MOUSELAST) return 0;
1925 return 1;
1926 }
1927
1928 BOOL APIENTRY
1929 co_MsqPeekHardwareMessage(IN PTHREADINFO pti,
1930 IN BOOL Remove,
1931 IN PWND Window,
1932 IN UINT MsgFilterLow,
1933 IN UINT MsgFilterHigh,
1934 IN UINT QSflags,
1935 OUT MSG* pMsg)
1936 {
1937 BOOL AcceptMessage;
1938 PUSER_MESSAGE CurrentMessage;
1939 PLIST_ENTRY ListHead;
1940 MSG msg;
1941 ULONG_PTR idSave;
1942 DWORD QS_Flags;
1943 BOOL Ret = FALSE;
1944 PUSER_MESSAGE_QUEUE MessageQueue = pti->MessageQueue;
1945
1946 if (!filter_contains_hw_range( MsgFilterLow, MsgFilterHigh )) return FALSE;
1947
1948 ListHead = MessageQueue->HardwareMessagesListHead.Flink;
1949
1950 if (IsListEmpty(ListHead)) return FALSE;
1951
1952 if (!MessageQueue->ptiSysLock)
1953 {
1954 MessageQueue->ptiSysLock = pti;
1955 pti->pcti->CTI_flags |= CTI_THREADSYSLOCK;
1956 }
1957
1958 if (MessageQueue->ptiSysLock != pti)
1959 {
1960 ERR("MsqPeekHardwareMessage: Thread Q is locked to another pti!\n");
1961 return FALSE;
1962 }
1963
1964 while (ListHead != &MessageQueue->HardwareMessagesListHead)
1965 {
1966 CurrentMessage = CONTAINING_RECORD(ListHead, USER_MESSAGE, ListEntry);
1967 ListHead = ListHead->Flink;
1968
1969 if (MessageQueue->idSysPeek == (ULONG_PTR)CurrentMessage)
1970 {
1971 TRACE("Skip this message due to it is in play!\n");
1972 continue;
1973 }
1974 /*
1975 MSDN:
1976 1: any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL.
1977 2: retrieves only messages on the current thread's message queue whose hwnd value is NULL.
1978 3: handle to the window whose messages are to be retrieved.
1979 */
1980 if ( ( !Window || // 1
1981 ( Window == PWND_BOTTOM && CurrentMessage->Msg.hwnd == NULL ) || // 2
1982 ( Window != PWND_BOTTOM && Window->head.h == CurrentMessage->Msg.hwnd ) || // 3
1983 ( CurrentMessage->Msg.message == WM_MOUSEMOVE ) ) && // Null window for mouse moves.
1984 ( ( ( MsgFilterLow == 0 && MsgFilterHigh == 0 ) && CurrentMessage->QS_Flags & QSflags ) ||
1985 ( MsgFilterLow <= CurrentMessage->Msg.message && MsgFilterHigh >= CurrentMessage->Msg.message ) ) )
1986 {
1987 idSave = MessageQueue->idSysPeek;
1988 MessageQueue->idSysPeek = (ULONG_PTR)CurrentMessage;
1989
1990 msg = CurrentMessage->Msg;
1991 QS_Flags = CurrentMessage->QS_Flags;
1992
1993 UpdateKeyStateFromMsg(MessageQueue, &msg);
1994 AcceptMessage = co_IntProcessHardwareMessage(&msg, &Remove, MsgFilterLow, MsgFilterHigh);
1995
1996 if (Remove)
1997 {
1998 if (CurrentMessage->pti != NULL)
1999 {
2000 RemoveEntryList(&CurrentMessage->ListEntry);
2001 MsqDestroyMessage(CurrentMessage);
2002 }
2003 ClearMsgBitsMask(pti, QS_Flags);
2004 }
2005
2006 MessageQueue->idSysPeek = idSave;
2007
2008 if (AcceptMessage)
2009 {
2010 *pMsg = msg;
2011 Ret = TRUE;
2012 break;
2013 }
2014 }
2015 }
2016
2017 MessageQueue->ptiSysLock = NULL;
2018 pti->pcti->CTI_flags &= ~CTI_THREADSYSLOCK;
2019 return Ret;
2020 }
2021
2022 BOOLEAN APIENTRY
2023 MsqPeekMessage(IN PTHREADINFO pti,
2024 IN BOOLEAN Remove,
2025 IN PWND Window,
2026 IN UINT MsgFilterLow,
2027 IN UINT MsgFilterHigh,
2028 IN UINT QSflags,
2029 OUT LONG_PTR *ExtraInfo,
2030 OUT PMSG Message)
2031 {
2032 PUSER_MESSAGE CurrentMessage;
2033 PLIST_ENTRY ListHead;
2034 DWORD QS_Flags;
2035 BOOL Ret = FALSE;
2036
2037 ListHead = pti->PostedMessagesListHead.Flink;
2038
2039 if (IsListEmpty(ListHead)) return FALSE;
2040
2041 while(ListHead != &pti->PostedMessagesListHead)
2042 {
2043 CurrentMessage = CONTAINING_RECORD(ListHead, USER_MESSAGE, ListEntry);
2044 ListHead = ListHead->Flink;
2045 /*
2046 MSDN:
2047 1: any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL.
2048 2: retrieves only messages on the current thread's message queue whose hwnd value is NULL.
2049 3: handle to the window whose messages are to be retrieved.
2050 */
2051 if ( ( !Window || // 1
2052 ( Window == PWND_BOTTOM && CurrentMessage->Msg.hwnd == NULL ) || // 2
2053 ( Window != PWND_BOTTOM && Window->head.h == CurrentMessage->Msg.hwnd ) ) && // 3
2054 ( ( ( MsgFilterLow == 0 && MsgFilterHigh == 0 ) && CurrentMessage->QS_Flags & QSflags ) ||
2055 ( MsgFilterLow <= CurrentMessage->Msg.message && MsgFilterHigh >= CurrentMessage->Msg.message ) ) )
2056 {
2057 *Message = CurrentMessage->Msg;
2058 *ExtraInfo = CurrentMessage->ExtraInfo;
2059 QS_Flags = CurrentMessage->QS_Flags;
2060
2061 if (Remove)
2062 {
2063 if (CurrentMessage->pti != NULL)
2064 {
2065 RemoveEntryList(&CurrentMessage->ListEntry);
2066 MsqDestroyMessage(CurrentMessage);
2067 }
2068 ClearMsgBitsMask(pti, QS_Flags);
2069 }
2070 Ret = TRUE;
2071 break;
2072 }
2073 }
2074
2075 return Ret;
2076 }
2077
2078 NTSTATUS FASTCALL
2079 co_MsqWaitForNewMessages(PTHREADINFO pti, PWND WndFilter,
2080 UINT MsgFilterMin, UINT MsgFilterMax)
2081 {
2082 NTSTATUS ret;
2083 UserLeaveCo();
2084 ret = KeWaitForSingleObject( pti->pEventQueueServer,
2085 UserRequest,
2086 UserMode,
2087 FALSE,
2088 NULL );
2089 UserEnterCo();
2090 if ( ret == STATUS_USER_APC )
2091 {
2092 TRACE("MWFNW User APC\n");
2093 co_IntDeliverUserAPC();
2094 }
2095 return ret;
2096 }
2097
2098 BOOL FASTCALL
2099 MsqIsHung(PTHREADINFO pti)
2100 {
2101 LARGE_INTEGER LargeTickCount;
2102
2103 KeQueryTickCount(&LargeTickCount);
2104 return ((LargeTickCount.u.LowPart - pti->timeLast) > MSQ_HUNG);
2105 }
2106
2107 VOID
2108 CALLBACK
2109 HungAppSysTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
2110 {
2111 DoTheScreenSaver();
2112 TRACE("HungAppSysTimerProc\n");
2113 // Process list of windows that are hung and waiting.
2114 }
2115
2116 BOOLEAN FASTCALL
2117 MsqInitializeMessageQueue(PTHREADINFO pti, PUSER_MESSAGE_QUEUE MessageQueue)
2118 {
2119 MessageQueue->CaretInfo = (PTHRDCARETINFO)(MessageQueue + 1);
2120 InitializeListHead(&MessageQueue->HardwareMessagesListHead); // Keep here!
2121 MessageQueue->spwndFocus = NULL;
2122 MessageQueue->iCursorLevel = 0;
2123 MessageQueue->CursorObject = NULL;
2124 RtlCopyMemory(MessageQueue->afKeyState, gafAsyncKeyState, sizeof(gafAsyncKeyState));
2125 MessageQueue->ptiMouse = pti;
2126 MessageQueue->ptiKeyboard = pti;
2127 MessageQueue->cThreads++;
2128
2129 return TRUE;
2130 }
2131
2132 VOID FASTCALL
2133 MsqCleanupThreadMsgs(PTHREADINFO pti)
2134 {
2135 PLIST_ENTRY CurrentEntry;
2136 PUSER_MESSAGE CurrentMessage;
2137 PUSER_SENT_MESSAGE CurrentSentMessage;
2138
2139 /* cleanup posted messages */
2140 while (!IsListEmpty(&pti->PostedMessagesListHead))
2141 {
2142 CurrentEntry = RemoveHeadList(&pti->PostedMessagesListHead);
2143 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
2144 ListEntry);
2145 MsqDestroyMessage(CurrentMessage);
2146 }
2147
2148 /* remove the messages that have not yet been dispatched */
2149 while (!IsListEmpty(&pti->SentMessagesListHead))
2150 {
2151 CurrentEntry = RemoveHeadList(&pti->SentMessagesListHead);
2152 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
2153 ListEntry);
2154
2155 TRACE("Notify the sender and remove a message from the queue that had not been dispatched\n");
2156 /* Only if the message has a sender was the message in the DispatchingList */
2157 if ((CurrentSentMessage->ptiSender)
2158 && (CurrentSentMessage->DispatchingListEntry.Flink != NULL))
2159 {
2160 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
2161 }
2162
2163 /* wake the sender's thread */
2164 if (CurrentSentMessage->CompletionEvent != NULL)
2165 {
2166 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
2167 }
2168
2169 if (CurrentSentMessage->HasPackedLParam)
2170 {
2171 if (CurrentSentMessage->Msg.lParam)
2172 ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
2173 }
2174
2175 /* free the message */
2176 ExFreePool(CurrentSentMessage);
2177 }
2178
2179 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
2180 ExitThread() was called in a SendMessage() umode callback */
2181 while (!IsListEmpty(&pti->LocalDispatchingMessagesHead))
2182 {
2183 CurrentEntry = RemoveHeadList(&pti->LocalDispatchingMessagesHead);
2184 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
2185 ListEntry);
2186
2187 /* remove the message from the dispatching list */
2188 if(CurrentSentMessage->DispatchingListEntry.Flink != NULL)
2189 {
2190 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
2191 }
2192
2193 TRACE("Notify the sender, the thread has been terminated while dispatching a message!\n");
2194
2195 /* wake the sender's thread */
2196 if (CurrentSentMessage->CompletionEvent != NULL)
2197 {
2198 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
2199 }
2200
2201 if (CurrentSentMessage->HasPackedLParam)
2202 {
2203 if (CurrentSentMessage->Msg.lParam)
2204 ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
2205 }
2206
2207 /* free the message */
2208 ExFreePool(CurrentSentMessage);
2209 }
2210
2211 /* tell other threads not to bother returning any info to us */
2212 while (! IsListEmpty(&pti->DispatchingMessagesHead))
2213 {
2214 CurrentEntry = RemoveHeadList(&pti->DispatchingMessagesHead);
2215 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
2216 DispatchingListEntry);
2217 CurrentSentMessage->CompletionEvent = NULL;
2218 CurrentSentMessage->Result = NULL;
2219
2220 /* do NOT dereference our message queue as it might get attempted to be
2221 locked later */
2222 }
2223
2224 // Clear it all out.
2225 if (pti->pcti)
2226 {
2227 pti->pcti->fsWakeBits = 0;
2228 pti->pcti->fsChangeBits = 0;
2229 }
2230
2231 pti->nCntsQBits[QSRosKey] = 0;
2232 pti->nCntsQBits[QSRosMouseMove] = 0;
2233 pti->nCntsQBits[QSRosMouseButton] = 0;
2234 pti->nCntsQBits[QSRosPostMessage] = 0;
2235 pti->nCntsQBits[QSRosSendMessage] = 0;
2236 pti->nCntsQBits[QSRosHotKey] = 0;
2237 pti->nCntsQBits[QSRosEvent] = 0;
2238 }
2239
2240 VOID FASTCALL
2241 MsqCleanupMessageQueue(PTHREADINFO pti)
2242 {
2243 PUSER_MESSAGE_QUEUE MessageQueue;
2244
2245 MessageQueue = pti->MessageQueue;
2246 MessageQueue->cThreads--;
2247
2248 if (MessageQueue->cThreads)
2249 {
2250 if (MessageQueue->ptiSysLock == pti) MessageQueue->ptiSysLock = NULL;
2251 }
2252
2253 if (MessageQueue->CursorObject)
2254 {
2255 PCURICON_OBJECT pCursor = MessageQueue->CursorObject;
2256
2257 /* Change to another cursor if we going to dereference current one
2258 Note: we can't use UserSetCursor because it uses current thread
2259 message queue instead of queue given for cleanup */
2260 if (IntGetSysCursorInfo()->CurrentCursorObject == pCursor)
2261 {
2262 HDC hdcScreen;
2263
2264 /* Get the screen DC */
2265 hdcScreen = IntGetScreenDC();
2266 if (hdcScreen)
2267 GreMovePointer(hdcScreen, -1, -1);
2268 IntGetSysCursorInfo()->CurrentCursorObject = NULL;
2269 }
2270
2271 TRACE("DereferenceObject pCursor\n");
2272 UserDereferenceObject(pCursor);
2273 }
2274
2275 if (gpqForeground == MessageQueue)
2276 {
2277 IntSetFocusMessageQueue(NULL);
2278 }
2279 if (gpqForegroundPrev == MessageQueue)
2280 {
2281 gpqForegroundPrev = NULL;
2282 }
2283 if (gpqCursor == MessageQueue)
2284 {
2285 gpqCursor = NULL;
2286 }
2287 }
2288
2289 PUSER_MESSAGE_QUEUE FASTCALL
2290 MsqCreateMessageQueue(PTHREADINFO pti)
2291 {
2292 PUSER_MESSAGE_QUEUE MessageQueue;
2293
2294 MessageQueue = (PUSER_MESSAGE_QUEUE)ExAllocatePoolWithTag(NonPagedPool,
2295 sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO),
2296 USERTAG_Q);
2297
2298 if (!MessageQueue)
2299 {
2300 return NULL;
2301 }
2302
2303 RtlZeroMemory(MessageQueue, sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO));
2304 /* hold at least one reference until it'll be destroyed */
2305 IntReferenceMessageQueue(MessageQueue);
2306 /* initialize the queue */
2307 if (!MsqInitializeMessageQueue(pti, MessageQueue))
2308 {
2309 IntDereferenceMessageQueue(MessageQueue);
2310 return NULL;
2311 }
2312
2313 return MessageQueue;
2314 }
2315
2316 VOID FASTCALL
2317 MsqDestroyMessageQueue(PTHREADINFO pti)
2318 {
2319 PDESKTOP desk;
2320 PUSER_MESSAGE_QUEUE MessageQueue = pti->MessageQueue;
2321
2322 MessageQueue->QF_flags |= QF_INDESTROY;
2323
2324 /* remove the message queue from any desktops */
2325 if ((desk = InterlockedExchangePointer((PVOID*)&MessageQueue->Desktop, 0)))
2326 {
2327 (void)InterlockedExchangePointer((PVOID*)&desk->ActiveMessageQueue, 0);
2328 IntDereferenceMessageQueue(MessageQueue);
2329 }
2330
2331 /* clean it up */
2332 MsqCleanupMessageQueue(pti);
2333
2334 /* decrease the reference counter, if it hits zero, the queue will be freed */
2335 IntDereferenceMessageQueue(MessageQueue);
2336 }
2337
2338 LPARAM FASTCALL
2339 MsqSetMessageExtraInfo(LPARAM lParam)
2340 {
2341 LPARAM Ret;
2342 PTHREADINFO pti;
2343 PUSER_MESSAGE_QUEUE MessageQueue;
2344
2345 pti = PsGetCurrentThreadWin32Thread();
2346 MessageQueue = pti->MessageQueue;
2347 if(!MessageQueue)
2348 {
2349 return 0;
2350 }
2351
2352 Ret = MessageQueue->ExtraInfo;
2353 MessageQueue->ExtraInfo = lParam;
2354
2355 return Ret;
2356 }
2357
2358 LPARAM FASTCALL
2359 MsqGetMessageExtraInfo(VOID)
2360 {
2361 PTHREADINFO pti;
2362 PUSER_MESSAGE_QUEUE MessageQueue;
2363
2364 pti = PsGetCurrentThreadWin32Thread();
2365 MessageQueue = pti->MessageQueue;
2366 if(!MessageQueue)
2367 {
2368 return 0;
2369 }
2370
2371 return MessageQueue->ExtraInfo;
2372 }
2373
2374 // ReplyMessage is called by the thread receiving the window message.
2375 BOOL FASTCALL
2376 co_MsqReplyMessage( LRESULT lResult )
2377 {
2378 PUSER_SENT_MESSAGE Message;
2379 PTHREADINFO pti;
2380
2381 pti = PsGetCurrentThreadWin32Thread();
2382 Message = pti->pusmCurrent;
2383
2384 if (!Message) return FALSE;
2385
2386 if (Message->QS_Flags & QS_SMRESULT) return FALSE;
2387
2388 // SendMessageXxx || Callback msg and not a notify msg
2389 if (Message->ptiSender || Message->CompletionCallback)
2390 {
2391 Message->lResult = lResult;
2392 Message->QS_Flags |= QS_SMRESULT;
2393 // See co_MsqDispatchOneSentMessage, change bits already accounted for and cleared and this msg is going away..
2394 }
2395 return TRUE;
2396 }
2397
2398 HWND FASTCALL
2399 MsqSetStateWindow(PTHREADINFO pti, ULONG Type, HWND hWnd)
2400 {
2401 HWND Prev;
2402 PUSER_MESSAGE_QUEUE MessageQueue;
2403
2404 MessageQueue = pti->MessageQueue;
2405
2406 switch(Type)
2407 {
2408 case MSQ_STATE_CAPTURE:
2409 Prev = MessageQueue->spwndCapture ? UserHMGetHandle(MessageQueue->spwndCapture) : 0;
2410 MessageQueue->spwndCapture = ValidateHwndNoErr(hWnd);
2411 return Prev;
2412 case MSQ_STATE_ACTIVE:
2413 Prev = MessageQueue->spwndActive ? UserHMGetHandle(MessageQueue->spwndActive) : 0;
2414 MessageQueue->spwndActive = ValidateHwndNoErr(hWnd);
2415 return Prev;
2416 case MSQ_STATE_FOCUS:
2417 Prev = MessageQueue->spwndFocus ? UserHMGetHandle(MessageQueue->spwndFocus) : 0;
2418 MessageQueue->spwndFocus = ValidateHwndNoErr(hWnd);
2419 return Prev;
2420 case MSQ_STATE_MENUOWNER:
2421 Prev = MessageQueue->MenuOwner;
2422 MessageQueue->MenuOwner = hWnd;
2423 return Prev;
2424 case MSQ_STATE_MOVESIZE:
2425 Prev = MessageQueue->MoveSize;
2426 MessageQueue->MoveSize = hWnd;
2427 return Prev;
2428 case MSQ_STATE_CARET:
2429 ASSERT(MessageQueue->CaretInfo);
2430 Prev = MessageQueue->CaretInfo->hWnd;
2431 MessageQueue->CaretInfo->hWnd = hWnd;
2432 return Prev;
2433 }
2434
2435 return NULL;
2436 }
2437
2438 SHORT
2439 APIENTRY
2440 NtUserGetKeyState(INT key)
2441 {
2442 DWORD Ret;
2443
2444 UserEnterShared();
2445
2446 Ret = UserGetKeyState(key);
2447
2448 UserLeave();
2449
2450 return (SHORT)Ret;
2451 }
2452
2453
2454 DWORD
2455 APIENTRY
2456 NtUserGetKeyboardState(LPBYTE lpKeyState)
2457 {
2458 DWORD i, ret = TRUE;
2459 PTHREADINFO pti;
2460 PUSER_MESSAGE_QUEUE MessageQueue;
2461
2462 UserEnterShared();
2463
2464 pti = PsGetCurrentThreadWin32Thread();
2465 MessageQueue = pti->MessageQueue;
2466
2467 _SEH2_TRY
2468 {
2469 /* Probe and copy key state to an array */
2470 ProbeForWrite(lpKeyState, 256 * sizeof(BYTE), 1);
2471 for (i = 0; i < 256; ++i)
2472 {
2473 lpKeyState[i] = 0;
2474 if (IS_KEY_DOWN(MessageQueue->afKeyState, i))
2475 lpKeyState[i] |= KS_DOWN_BIT;
2476 if (IS_KEY_LOCKED(MessageQueue->afKeyState, i))
2477 lpKeyState[i] |= KS_LOCK_BIT;
2478 }
2479 }
2480 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2481 {
2482 SetLastNtError(_SEH2_GetExceptionCode());
2483 ret = FALSE;
2484 }
2485 _SEH2_END;
2486
2487 UserLeave();
2488
2489 return ret;
2490 }
2491
2492 BOOL
2493 APIENTRY
2494 NtUserSetKeyboardState(LPBYTE pKeyState)
2495 {
2496 UINT i;
2497 BOOL bRet = TRUE;
2498 PTHREADINFO pti;
2499 PUSER_MESSAGE_QUEUE MessageQueue;
2500
2501 UserEnterExclusive();
2502
2503 pti = PsGetCurrentThreadWin32Thread();
2504 MessageQueue = pti->MessageQueue;
2505
2506 _SEH2_TRY
2507 {
2508 ProbeForRead(pKeyState, 256 * sizeof(BYTE), 1);
2509 for (i = 0; i < 256; ++i)
2510 {
2511 SET_KEY_DOWN(MessageQueue->afKeyState, i, pKeyState[i] & KS_DOWN_BIT);
2512 SET_KEY_LOCKED(MessageQueue->afKeyState, i, pKeyState[i] & KS_LOCK_BIT);
2513 }
2514 }
2515 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2516 {
2517 SetLastNtError(_SEH2_GetExceptionCode());
2518 bRet = FALSE;
2519 }
2520 _SEH2_END;
2521
2522 UserLeave();
2523
2524 return bRet;
2525 }
2526
2527 /* EOF */