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