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