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