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