232fe21e8c3740b2a5f6b2024334a5d1c9d057f5
[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 #define NDEBUG
18 #include <debug.h>
19
20 /* GLOBALS *******************************************************************/
21
22 static PAGED_LOOKASIDE_LIST MessageLookasideList;
23
24 /* FUNCTIONS *****************************************************************/
25
26 INIT_FUNCTION
27 NTSTATUS
28 NTAPI
29 MsqInitializeImpl(VOID)
30 {
31 ExInitializePagedLookasideList(&MessageLookasideList,
32 NULL,
33 NULL,
34 0,
35 sizeof(USER_MESSAGE),
36 TAG_USRMSG,
37 256);
38
39 return(STATUS_SUCCESS);
40 }
41
42 HANDLE FASTCALL
43 IntMsqSetWakeMask(DWORD WakeMask)
44 {
45 PTHREADINFO Win32Thread;
46 PUSER_MESSAGE_QUEUE MessageQueue;
47 HANDLE MessageEventHandle;
48 DWORD dwFlags = HIWORD(WakeMask);
49
50 Win32Thread = PsGetCurrentThreadWin32Thread();
51 if (Win32Thread == NULL || Win32Thread->MessageQueue == NULL)
52 return 0;
53
54 MessageQueue = Win32Thread->MessageQueue;
55 // Win32Thread->pEventQueueServer; IntMsqSetWakeMask returns Win32Thread->hEventQueueClient
56 MessageEventHandle = MessageQueue->NewMessagesHandle;
57
58 if (Win32Thread->pcti)
59 {
60 if ( (Win32Thread->pcti->fsChangeBits & LOWORD(WakeMask)) ||
61 ( (dwFlags & MWMO_INPUTAVAILABLE) && (Win32Thread->pcti->fsWakeBits & LOWORD(WakeMask)) ) )
62 {
63 DPRINT1("Chg 0x%x Wake 0x%x Mask 0x%x\n",Win32Thread->pcti->fsChangeBits, Win32Thread->pcti->fsWakeBits, WakeMask);
64 KeSetEvent(MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE); // Wake it up!
65 return MessageEventHandle;
66 }
67 }
68
69 IdlePing();
70
71 return MessageEventHandle;
72 }
73
74 BOOL FASTCALL
75 IntMsqClearWakeMask(VOID)
76 {
77 PTHREADINFO Win32Thread;
78
79 Win32Thread = PsGetCurrentThreadWin32Thread();
80 if (Win32Thread == NULL || Win32Thread->MessageQueue == NULL)
81 return FALSE;
82 // Very hacky, but that is what they do.
83 Win32Thread->pcti->fsWakeBits = 0;
84
85 IdlePong();
86
87 return TRUE;
88 }
89
90 /*
91 Due to the uncertainty of knowing what was set in our multilevel message queue,
92 and even if the bits are all cleared. The same as cTimers/cPaintsReady.
93 I think this is the best solution... (jt) */
94 VOID FASTCALL
95 MsqWakeQueue(PUSER_MESSAGE_QUEUE Queue, DWORD MessageBits, BOOL KeyEvent)
96 {
97 PTHREADINFO pti;
98
99 pti = Queue->Thread->Tcb.Win32Thread;
100 pti->pcti->fsWakeBits |= MessageBits;
101 pti->pcti->fsChangeBits |= MessageBits;
102
103 // Start bit accounting to help clear the main set of bits.
104 if (MessageBits & QS_KEY) Queue->nCntsQBits[QSRosKey]++;
105 if (MessageBits & QS_MOUSEMOVE) Queue->nCntsQBits[QSRosMouseMove]++;
106 if (MessageBits & QS_MOUSEBUTTON) Queue->nCntsQBits[QSRosMouseButton]++;
107 if (MessageBits & QS_POSTMESSAGE) Queue->nCntsQBits[QSRosPostMessage]++;
108 if (MessageBits & QS_SENDMESSAGE) Queue->nCntsQBits[QSRosSendMessage]++;
109 if (MessageBits & QS_HOTKEY) Queue->nCntsQBits[QSRosHotKey]++;
110
111 if (KeyEvent)
112 KeSetEvent(Queue->NewMessages, IO_NO_INCREMENT, FALSE);
113 }
114
115 VOID FASTCALL
116 ClearMsgBitsMask(PUSER_MESSAGE_QUEUE Queue, UINT MessageBits)
117 {
118 PTHREADINFO pti;
119 UINT ClrMask = 0;
120
121 pti = Queue->Thread->Tcb.Win32Thread;
122
123 if (MessageBits & QS_KEY)
124 {
125 if (--Queue->nCntsQBits[QSRosKey] == 0) ClrMask |= QS_KEY;
126 }
127 if (MessageBits & QS_MOUSEMOVE) // ReactOS hard coded.
128 { // Account for tracking mouse moves..
129 if (--Queue->nCntsQBits[QSRosMouseMove] == 0) ClrMask |= QS_MOUSEMOVE;
130 // Handle mouse move bits here.
131 if (Queue->MouseMoved) ClrMask |= QS_MOUSEMOVE;
132 }
133 if (MessageBits & QS_MOUSEBUTTON)
134 {
135 if (--Queue->nCntsQBits[QSRosMouseButton] == 0) ClrMask |= QS_MOUSEBUTTON;
136 }
137 if (MessageBits & QS_POSTMESSAGE)
138 {
139 if (--Queue->nCntsQBits[QSRosPostMessage] == 0) ClrMask |= QS_POSTMESSAGE;
140 }
141 if (MessageBits & QS_TIMER) // ReactOS hard coded.
142 { // Handle timer bits here.
143 if ( pti->cTimersReady )
144 {
145 if (--pti->cTimersReady == 0) ClrMask |= QS_TIMER;
146 }
147 }
148 if (MessageBits & QS_PAINT) // ReactOS hard coded.
149 { // Handle paint bits here.
150 if ( pti->cPaintsReady )
151 {
152 if (--pti->cPaintsReady == 0) ClrMask |= QS_PAINT;
153 }
154 }
155 if (MessageBits & QS_SENDMESSAGE)
156 {
157 if (--Queue->nCntsQBits[QSRosSendMessage] == 0) ClrMask |= QS_SENDMESSAGE;
158 }
159 if (MessageBits & QS_HOTKEY)
160 {
161 if (--Queue->nCntsQBits[QSRosHotKey] == 0) ClrMask |= QS_HOTKEY;
162 }
163
164 pti->pcti->fsWakeBits &= ~ClrMask;
165 pti->pcti->fsChangeBits &= ~ClrMask;
166 }
167
168 VOID FASTCALL
169 MsqIncPaintCountQueue(PUSER_MESSAGE_QUEUE Queue)
170 {
171 PTHREADINFO pti;
172 pti = Queue->Thread->Tcb.Win32Thread;
173 pti->cPaintsReady++;
174 MsqWakeQueue(Queue, QS_PAINT, TRUE);
175 }
176
177 VOID FASTCALL
178 MsqDecPaintCountQueue(PUSER_MESSAGE_QUEUE Queue)
179 {
180 ClearMsgBitsMask(Queue, QS_PAINT);
181 }
182
183 VOID FASTCALL
184 MsqPostMouseMove(PUSER_MESSAGE_QUEUE MessageQueue, MSG* Msg)
185 {
186 MessageQueue->MouseMoveMsg = *Msg;
187 MessageQueue->MouseMoved = TRUE;
188 MsqWakeQueue(MessageQueue, QS_MOUSEMOVE, TRUE);
189 }
190
191 VOID FASTCALL
192 co_MsqInsertMouseMessage(MSG* Msg)
193 {
194 LARGE_INTEGER LargeTickCount;
195 MSLLHOOKSTRUCT MouseHookData;
196 PWND pwnd, pwndDesktop;
197
198 KeQueryTickCount(&LargeTickCount);
199 Msg->time = MsqCalculateMessageTime(&LargeTickCount);
200
201 MouseHookData.pt.x = LOWORD(Msg->lParam);
202 MouseHookData.pt.y = HIWORD(Msg->lParam);
203 switch(Msg->message)
204 {
205 case WM_MOUSEWHEEL:
206 MouseHookData.mouseData = MAKELONG(0, GET_WHEEL_DELTA_WPARAM(Msg->wParam));
207 break;
208 case WM_XBUTTONDOWN:
209 case WM_XBUTTONUP:
210 case WM_XBUTTONDBLCLK:
211 case WM_NCXBUTTONDOWN:
212 case WM_NCXBUTTONUP:
213 case WM_NCXBUTTONDBLCLK:
214 MouseHookData.mouseData = MAKELONG(0, HIWORD(Msg->wParam));
215 break;
216 default:
217 MouseHookData.mouseData = 0;
218 break;
219 }
220
221 MouseHookData.flags = 0;
222 MouseHookData.time = Msg->time;
223 MouseHookData.dwExtraInfo = 0;
224
225 /* If the hook procedure returned non zero, dont send the message */
226 if (co_HOOK_CallHooks(WH_MOUSE_LL, HC_ACTION, Msg->message, (LPARAM) &MouseHookData))
227 return;
228
229 /* Get the desktop window */
230 pwndDesktop = UserGetDesktopWindow();
231 if(!pwndDesktop)
232 return;
233
234 /* Check if the mouse is captured */
235 Msg->hwnd = IntGetCaptureWindow();
236 if(Msg->hwnd != NULL)
237 {
238 pwnd = UserGetWindowObject(Msg->hwnd);
239 }
240 else
241 {
242 /* Loop all top level windows to find which one should receive input */
243 for( pwnd = pwndDesktop->spwndChild;
244 pwnd != NULL;
245 pwnd = pwnd->spwndNext )
246 {
247 if((pwnd->style & WS_VISIBLE) &&
248 IntPtInWindow(pwnd, Msg->pt.x, Msg->pt.y))
249 {
250 Msg->hwnd = pwnd->head.h;
251 break;
252 }
253 }
254 }
255
256 /* Check if we found a window */
257 if(Msg->hwnd != NULL && pwnd != NULL)
258 {
259 if(Msg->message == WM_MOUSEMOVE)
260 {
261 /* Mouse move is a special case*/
262 MsqPostMouseMove(pwnd->head.pti->MessageQueue, Msg);
263 }
264 else
265 {
266 DPRINT("Posting mouse message to hwnd=0x%x!\n", UserHMGetHandle(pwnd));
267 MsqPostMessage(pwnd->head.pti->MessageQueue, Msg, TRUE, QS_MOUSEBUTTON);
268 }
269 }
270 }
271
272 //
273 // Note: Only called from input.c.
274 //
275 VOID FASTCALL
276 co_MsqPostKeyboardMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
277 {
278 PUSER_MESSAGE_QUEUE FocusMessageQueue;
279 MSG Msg;
280 LARGE_INTEGER LargeTickCount;
281 KBDLLHOOKSTRUCT KbdHookData;
282 BOOLEAN Entered = FALSE;
283
284 DPRINT("MsqPostKeyboardMessage(uMsg 0x%x, wParam 0x%x, lParam 0x%x)\n",
285 uMsg, wParam, lParam);
286
287 // Condition may arise when calling MsqPostMessage and waiting for an event.
288 if (!UserIsEntered())
289 {
290 // Fixme: Not sure ATM if this thread is locked.
291 UserEnterExclusive();
292 Entered = TRUE;
293 }
294
295 FocusMessageQueue = IntGetFocusMessageQueue();
296
297 Msg.hwnd = 0;
298
299 if (FocusMessageQueue && (FocusMessageQueue->FocusWindow != (HWND)0))
300 Msg.hwnd = FocusMessageQueue->FocusWindow;
301
302 Msg.message = uMsg;
303 Msg.wParam = wParam;
304 Msg.lParam = lParam;
305
306 KeQueryTickCount(&LargeTickCount);
307 Msg.time = MsqCalculateMessageTime(&LargeTickCount);
308
309 /* We can't get the Msg.pt point here since we don't know thread
310 (and thus the window station) the message will end up in yet. */
311
312 KbdHookData.vkCode = Msg.wParam;
313 KbdHookData.scanCode = (Msg.lParam >> 16) & 0xff;
314 KbdHookData.flags = (0 == (Msg.lParam & 0x01000000) ? 0 : LLKHF_EXTENDED) |
315 (0 == (Msg.lParam & 0x20000000) ? 0 : LLKHF_ALTDOWN) |
316 (0 == (Msg.lParam & 0x80000000) ? 0 : LLKHF_UP);
317 KbdHookData.time = Msg.time;
318 KbdHookData.dwExtraInfo = 0;
319 if (co_HOOK_CallHooks(WH_KEYBOARD_LL, HC_ACTION, Msg.message, (LPARAM) &KbdHookData))
320 {
321 DPRINT1("Kbd msg %d wParam %d lParam 0x%08x dropped by WH_KEYBOARD_LL hook\n",
322 Msg.message, Msg.wParam, Msg.lParam);
323 if (Entered) UserLeave();
324 return;
325 }
326
327 if (FocusMessageQueue == NULL)
328 {
329 DPRINT("No focus message queue\n");
330 if (Entered) UserLeave();
331 return;
332 }
333
334 if (FocusMessageQueue->FocusWindow != (HWND)0)
335 {
336 Msg.hwnd = FocusMessageQueue->FocusWindow;
337 DPRINT("Msg.hwnd = %x\n", Msg.hwnd);
338
339 FocusMessageQueue->Desktop->pDeskInfo->LastInputWasKbd = TRUE;
340
341 Msg.pt = gpsi->ptCursor;
342 MsqPostMessage(FocusMessageQueue, &Msg, TRUE, QS_KEY);
343 }
344 else
345 {
346 DPRINT("Invalid focus window handle\n");
347 }
348
349 if (Entered) UserLeave();
350 return;
351 }
352
353 VOID FASTCALL
354 MsqPostHotKeyMessage(PVOID Thread, HWND hWnd, WPARAM wParam, LPARAM lParam)
355 {
356 PWND Window;
357 PTHREADINFO Win32Thread;
358 MSG Mesg;
359 LARGE_INTEGER LargeTickCount;
360 NTSTATUS Status;
361
362 Status = ObReferenceObjectByPointer (Thread,
363 THREAD_ALL_ACCESS,
364 PsThreadType,
365 KernelMode);
366 if (!NT_SUCCESS(Status))
367 return;
368
369 Win32Thread = ((PETHREAD)Thread)->Tcb.Win32Thread;
370 if (Win32Thread == NULL || Win32Thread->MessageQueue == NULL)
371 {
372 ObDereferenceObject ((PETHREAD)Thread);
373 return;
374 }
375
376 Window = IntGetWindowObject(hWnd);
377 if (!Window)
378 {
379 ObDereferenceObject ((PETHREAD)Thread);
380 return;
381 }
382
383 Mesg.hwnd = hWnd;
384 Mesg.message = WM_HOTKEY;
385 Mesg.wParam = wParam;
386 Mesg.lParam = lParam;
387 KeQueryTickCount(&LargeTickCount);
388 Mesg.time = MsqCalculateMessageTime(&LargeTickCount);
389 Mesg.pt = gpsi->ptCursor;
390 MsqPostMessage(Window->head.pti->MessageQueue, &Mesg, FALSE, QS_HOTKEY);
391 UserDereferenceObject(Window);
392 ObDereferenceObject (Thread);
393
394 }
395
396 PUSER_MESSAGE FASTCALL
397 MsqCreateMessage(LPMSG Msg)
398 {
399 PUSER_MESSAGE Message;
400
401 Message = ExAllocateFromPagedLookasideList(&MessageLookasideList);
402 if (!Message)
403 {
404 return NULL;
405 }
406
407 RtlMoveMemory(&Message->Msg, Msg, sizeof(MSG));
408
409 return Message;
410 }
411
412 VOID FASTCALL
413 MsqDestroyMessage(PUSER_MESSAGE Message)
414 {
415 ExFreeToPagedLookasideList(&MessageLookasideList, Message);
416 }
417
418 BOOLEAN FASTCALL
419 co_MsqDispatchOneSentMessage(PUSER_MESSAGE_QUEUE MessageQueue)
420 {
421 PUSER_SENT_MESSAGE SaveMsg, Message;
422 PLIST_ENTRY Entry;
423 LRESULT Result;
424 PTHREADINFO pti;
425
426 if (IsListEmpty(&MessageQueue->SentMessagesListHead))
427 {
428 return(FALSE);
429 }
430
431 /* remove it from the list of pending messages */
432 Entry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
433 Message = CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry);
434
435 pti = MessageQueue->Thread->Tcb.Win32Thread;
436
437 SaveMsg = pti->pusmCurrent;
438 pti->pusmCurrent = Message;
439
440 // Processing a message sent to it from another thread.
441 if ( ( Message->SenderQueue && MessageQueue != Message->SenderQueue) ||
442 ( Message->CallBackSenderQueue && MessageQueue != Message->CallBackSenderQueue ))
443 { // most likely, but, to be sure.
444 pti->pcti->CTI_flags |= CTI_INSENDMESSAGE; // Let the user know...
445 }
446
447 /* insert it to the list of messages that are currently dispatched by this
448 message queue */
449 InsertTailList(&MessageQueue->LocalDispatchingMessagesHead,
450 &Message->ListEntry);
451
452 ClearMsgBitsMask(MessageQueue, Message->QS_Flags);
453
454 if (Message->HookMessage == MSQ_ISHOOK)
455 { // Direct Hook Call processor
456 Result = co_CallHook( Message->Msg.message, // HookId
457 (INT)(INT_PTR)Message->Msg.hwnd, // Code
458 Message->Msg.wParam,
459 Message->Msg.lParam);
460 }
461 else if (Message->HookMessage == MSQ_ISEVENT)
462 { // Direct Event Call processor
463 Result = co_EVENT_CallEvents( Message->Msg.message,
464 Message->Msg.hwnd,
465 Message->Msg.wParam,
466 Message->Msg.lParam);
467 }
468 else
469 { /* Call the window procedure. */
470 Result = co_IntSendMessage( Message->Msg.hwnd,
471 Message->Msg.message,
472 Message->Msg.wParam,
473 Message->Msg.lParam);
474 }
475
476 /* remove the message from the local dispatching list, because it doesn't need
477 to be cleaned up on thread termination anymore */
478 RemoveEntryList(&Message->ListEntry);
479
480 /* remove the message from the dispatching list if needed, so lock the sender's message queue */
481 if (!(Message->HookMessage & MSQ_SENTNOWAIT))
482 {
483 if (Message->DispatchingListEntry.Flink != NULL)
484 {
485 /* only remove it from the dispatching list if not already removed by a timeout */
486 RemoveEntryList(&Message->DispatchingListEntry);
487 }
488 }
489 /* still keep the sender's message queue locked, so the sender can't exit the
490 MsqSendMessage() function (if timed out) */
491
492 if (Message->QS_Flags & QS_SMRESULT)
493 {
494 Result = Message->lResult;
495 }
496
497 /* Let the sender know the result. */
498 if (Message->Result != NULL)
499 {
500 *Message->Result = Result;
501 }
502
503 if (Message->HasPackedLParam == TRUE)
504 {
505 if (Message->Msg.lParam)
506 ExFreePool((PVOID)Message->Msg.lParam);
507 }
508
509 /* Notify the sender. */
510 if (Message->CompletionEvent != NULL)
511 {
512 KeSetEvent(Message->CompletionEvent, IO_NO_INCREMENT, FALSE);
513 }
514
515 /* Call the callback if the message was sent with SendMessageCallback */
516 if (Message->CompletionCallback != NULL)
517 {
518 co_IntCallSentMessageCallback(Message->CompletionCallback,
519 Message->Msg.hwnd,
520 Message->Msg.message,
521 Message->CompletionCallbackContext,
522 Result);
523 }
524
525 /* Only if it is not a no wait message */
526 if (!(Message->HookMessage & MSQ_SENTNOWAIT))
527 {
528 IntDereferenceMessageQueue(Message->SenderQueue);
529 IntDereferenceMessageQueue(MessageQueue);
530 }
531
532 /* free the message */
533 ExFreePoolWithTag(Message, TAG_USRMSG);
534
535 /* do not hangup on the user if this is reentering */
536 if (!SaveMsg) pti->pcti->CTI_flags &= ~CTI_INSENDMESSAGE;
537 pti->pusmCurrent = SaveMsg;
538
539 return(TRUE);
540 }
541
542 VOID APIENTRY
543 MsqRemoveWindowMessagesFromQueue(PVOID pWindow)
544 {
545 PUSER_SENT_MESSAGE SentMessage;
546 PUSER_MESSAGE PostedMessage;
547 PUSER_MESSAGE_QUEUE MessageQueue;
548 PLIST_ENTRY CurrentEntry, ListHead;
549 PWND Window = pWindow;
550
551 ASSERT(Window);
552
553 MessageQueue = Window->head.pti->MessageQueue;
554 ASSERT(MessageQueue);
555
556 /* remove the posted messages for this window */
557 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
558 ListHead = &MessageQueue->PostedMessagesListHead;
559 while (CurrentEntry != ListHead)
560 {
561 PostedMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
562 ListEntry);
563 if (PostedMessage->Msg.hwnd == Window->head.h)
564 {
565 RemoveEntryList(&PostedMessage->ListEntry);
566 ClearMsgBitsMask(MessageQueue, PostedMessage->QS_Flags);
567 MsqDestroyMessage(PostedMessage);
568 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
569 }
570 else
571 {
572 CurrentEntry = CurrentEntry->Flink;
573 }
574 }
575
576 /* remove the sent messages for this window */
577 CurrentEntry = MessageQueue->SentMessagesListHead.Flink;
578 ListHead = &MessageQueue->SentMessagesListHead;
579 while (CurrentEntry != ListHead)
580 {
581 SentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
582 ListEntry);
583 if(SentMessage->Msg.hwnd == Window->head.h)
584 {
585 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
586
587 RemoveEntryList(&SentMessage->ListEntry);
588 ClearMsgBitsMask(MessageQueue, SentMessage->QS_Flags);
589
590 /* remove the message from the dispatching list if neede */
591 if ((!(SentMessage->HookMessage & MSQ_SENTNOWAIT))
592 && (SentMessage->DispatchingListEntry.Flink != NULL))
593 {
594 RemoveEntryList(&SentMessage->DispatchingListEntry);
595 }
596
597 /* wake the sender's thread */
598 if (SentMessage->CompletionEvent != NULL)
599 {
600 KeSetEvent(SentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
601 }
602
603 if (SentMessage->HasPackedLParam == TRUE)
604 {
605 if (SentMessage->Msg.lParam)
606 ExFreePool((PVOID)SentMessage->Msg.lParam);
607 }
608
609 /* Only if it is not a no wait message */
610 if (!(SentMessage->HookMessage & MSQ_SENTNOWAIT))
611 {
612 /* dereference our and the sender's message queue */
613 IntDereferenceMessageQueue(MessageQueue);
614 IntDereferenceMessageQueue(SentMessage->SenderQueue);
615 }
616
617 /* free the message */
618 ExFreePoolWithTag(SentMessage, TAG_USRMSG);
619
620 CurrentEntry = MessageQueue->SentMessagesListHead.Flink;
621 }
622 else
623 {
624 CurrentEntry = CurrentEntry->Flink;
625 }
626 }
627 }
628
629 NTSTATUS FASTCALL
630 co_MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue,
631 HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam,
632 UINT uTimeout, BOOL Block, INT HookMessage,
633 ULONG_PTR *uResult)
634 {
635 PTHREADINFO pti;
636 PUSER_SENT_MESSAGE Message;
637 KEVENT CompletionEvent;
638 NTSTATUS WaitStatus;
639 PUSER_MESSAGE_QUEUE ThreadQueue;
640 LARGE_INTEGER Timeout;
641 PLIST_ENTRY Entry;
642 LRESULT Result = 0; //// Result could be trashed. ////
643
644 if(!(Message = ExAllocatePoolWithTag(PagedPool, sizeof(USER_SENT_MESSAGE), TAG_USRMSG)))
645 {
646 DPRINT1("MsqSendMessage(): Not enough memory to allocate a message");
647 return STATUS_INSUFFICIENT_RESOURCES;
648 }
649
650 KeInitializeEvent(&CompletionEvent, NotificationEvent, FALSE);
651
652 pti = PsGetCurrentThreadWin32Thread();
653 ThreadQueue = pti->MessageQueue;
654 ASSERT(ThreadQueue != MessageQueue);
655
656 Timeout.QuadPart = (LONGLONG) uTimeout * (LONGLONG) -10000;
657
658 /* FIXME - increase reference counter of sender's message queue here */
659
660 Message->Msg.hwnd = Wnd;
661 Message->Msg.message = Msg;
662 Message->Msg.wParam = wParam;
663 Message->Msg.lParam = lParam;
664 Message->CompletionEvent = &CompletionEvent;
665 Message->Result = &Result;
666 Message->lResult = 0;
667 Message->QS_Flags = 0;
668 Message->SenderQueue = ThreadQueue;
669 Message->CallBackSenderQueue = NULL;
670 IntReferenceMessageQueue(ThreadQueue);
671 Message->CompletionCallback = NULL;
672 Message->CompletionCallbackContext = 0;
673 Message->HookMessage = HookMessage;
674 Message->HasPackedLParam = FALSE;
675
676 IntReferenceMessageQueue(MessageQueue);
677
678 /* add it to the list of pending messages */
679 InsertTailList(&ThreadQueue->DispatchingMessagesHead, &Message->DispatchingListEntry);
680
681 /* queue it in the destination's message queue */
682 InsertTailList(&MessageQueue->SentMessagesListHead, &Message->ListEntry);
683
684 Message->QS_Flags = QS_SENDMESSAGE;
685 MsqWakeQueue(MessageQueue, QS_SENDMESSAGE, TRUE);
686
687 /* we can't access the Message anymore since it could have already been deleted! */
688
689 if(Block)
690 {
691 UserLeaveCo();
692
693 /* don't process messages sent to the thread */
694 WaitStatus = KeWaitForSingleObject(&CompletionEvent, UserRequest, UserMode,
695 FALSE, (uTimeout ? &Timeout : NULL));
696
697 UserEnterCo();
698
699 if(WaitStatus == STATUS_TIMEOUT)
700 {
701 /* look up if the message has not yet dispatched, if so
702 make sure it can't pass a result and it must not set the completion event anymore */
703 Entry = MessageQueue->SentMessagesListHead.Flink;
704 while (Entry != &MessageQueue->SentMessagesListHead)
705 {
706 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry)
707 == Message)
708 {
709 /* we can access Message here, it's secure because the message queue is locked
710 and the message is still hasn't been dispatched */
711 Message->CompletionEvent = NULL;
712 Message->Result = NULL;
713 break;
714 }
715 Entry = Entry->Flink;
716 }
717
718 /* remove from the local dispatching list so the other thread knows,
719 it can't pass a result and it must not set the completion event anymore */
720 Entry = ThreadQueue->DispatchingMessagesHead.Flink;
721 while (Entry != &ThreadQueue->DispatchingMessagesHead)
722 {
723 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, DispatchingListEntry)
724 == Message)
725 {
726 /* we can access Message here, it's secure because the sender's message is locked
727 and the message has definitely not yet been destroyed, otherwise it would
728 have been removed from this list by the dispatching routine right after
729 dispatching the message */
730 Message->CompletionEvent = NULL;
731 Message->Result = NULL;
732 RemoveEntryList(&Message->DispatchingListEntry);
733 Message->DispatchingListEntry.Flink = NULL;
734 break;
735 }
736 Entry = Entry->Flink;
737 }
738
739 DPRINT("MsqSendMessage (blocked) timed out\n");
740 }
741 while (co_MsqDispatchOneSentMessage(ThreadQueue))
742 ;
743 }
744 else
745 {
746 PVOID WaitObjects[2];
747
748 WaitObjects[0] = &CompletionEvent;
749 WaitObjects[1] = ThreadQueue->NewMessages;
750 do
751 {
752 UserLeaveCo();
753
754 WaitStatus = KeWaitForMultipleObjects(2, WaitObjects, WaitAny, UserRequest,
755 UserMode, FALSE, (uTimeout ? &Timeout : NULL), NULL);
756
757 UserEnterCo();
758
759 if(WaitStatus == STATUS_TIMEOUT)
760 {
761 /* look up if the message has not yet been dispatched, if so
762 make sure it can't pass a result and it must not set the completion event anymore */
763 Entry = MessageQueue->SentMessagesListHead.Flink;
764 while (Entry != &MessageQueue->SentMessagesListHead)
765 {
766 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry)
767 == Message)
768 {
769 /* we can access Message here, it's secure because the message queue is locked
770 and the message is still hasn't been dispatched */
771 Message->CompletionEvent = NULL;
772 Message->Result = NULL;
773 break;
774 }
775 Entry = Entry->Flink;
776 }
777
778 /* remove from the local dispatching list so the other thread knows,
779 it can't pass a result and it must not set the completion event anymore */
780 Entry = ThreadQueue->DispatchingMessagesHead.Flink;
781 while (Entry != &ThreadQueue->DispatchingMessagesHead)
782 {
783 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, DispatchingListEntry)
784 == Message)
785 {
786 /* we can access Message here, it's secure because the sender's message is locked
787 and the message has definitely not yet been destroyed, otherwise it would
788 have been removed from this list by the dispatching routine right after
789 dispatching the message */
790 Message->CompletionEvent = NULL;
791 Message->Result = NULL;
792 RemoveEntryList(&Message->DispatchingListEntry);
793 Message->DispatchingListEntry.Flink = NULL;
794 break;
795 }
796 Entry = Entry->Flink;
797 }
798
799 DPRINT("MsqSendMessage timed out\n");
800 break;
801 }
802 while (co_MsqDispatchOneSentMessage(ThreadQueue))
803 ;
804 }
805 while (NT_SUCCESS(WaitStatus) && STATUS_WAIT_0 != WaitStatus);
806 }
807
808 if(WaitStatus != STATUS_TIMEOUT)
809 *uResult = (STATUS_WAIT_0 == WaitStatus ? Result : -1);
810
811 return WaitStatus;
812 }
813
814 VOID FASTCALL
815 MsqPostMessage(PUSER_MESSAGE_QUEUE MessageQueue, MSG* Msg, BOOLEAN HardwareMessage,
816 DWORD MessageBits)
817 {
818 PUSER_MESSAGE Message;
819
820 if(!(Message = MsqCreateMessage(Msg)))
821 {
822 return;
823 }
824
825 if(!HardwareMessage)
826 {
827 InsertTailList(&MessageQueue->PostedMessagesListHead,
828 &Message->ListEntry);
829 }
830 else
831 {
832 InsertTailList(&MessageQueue->HardwareMessagesListHead,
833 &Message->ListEntry);
834 }
835
836 Message->QS_Flags = MessageBits;
837 MsqWakeQueue(MessageQueue, MessageBits, (MessageBits & QS_TIMER ? FALSE : TRUE));
838 }
839
840 VOID FASTCALL
841 MsqPostQuitMessage(PUSER_MESSAGE_QUEUE MessageQueue, ULONG ExitCode)
842 {
843 MessageQueue->QuitPosted = TRUE;
844 MessageQueue->QuitExitCode = ExitCode;
845 MsqWakeQueue(MessageQueue, QS_POSTMESSAGE|QS_ALLPOSTMESSAGE, TRUE);
846 }
847
848 /***********************************************************************
849 * MsqSendParentNotify
850 *
851 * Send a WM_PARENTNOTIFY to all ancestors of the given window, unless
852 * the window has the WS_EX_NOPARENTNOTIFY style.
853 */
854 static void MsqSendParentNotify( PWND pwnd, WORD event, WORD idChild, POINT pt )
855 {
856 PWND pwndDesktop = UserGetWindowObject(IntGetDesktopWindow());
857
858 /* pt has to be in the client coordinates of the parent window */
859 pt.x += pwndDesktop->rcClient.left - pwnd->rcClient.left;
860 pt.y += pwndDesktop->rcClient.top - pwnd->rcClient.top;
861
862 for (;;)
863 {
864 PWND pwndParent;
865
866 if (!(pwnd->style & WS_CHILD)) break;
867 if (pwnd->ExStyle & WS_EX_NOPARENTNOTIFY) break;
868 if (!(pwndParent = IntGetParent(pwnd))) break;
869 if (pwndParent == pwndDesktop) break;
870 pt.x += pwnd->rcClient.left - pwndParent->rcClient.left;
871 pt.y += pwnd->rcClient.top - pwndParent->rcClient.top;
872
873 pwnd = pwndParent;
874 co_IntSendMessage( UserHMGetHandle(pwnd), WM_PARENTNOTIFY,
875 MAKEWPARAM( event, idChild ), MAKELPARAM( pt.x, pt.y ) );
876 }
877 }
878
879 BOOL co_IntProcessMouseMessage(MSG* msg, BOOL* RemoveMessages, UINT first, UINT last)
880 {
881 MSG clk_msg;
882 POINT pt;
883 UINT message;
884 USHORT hittest;
885 EVENTMSG event;
886 MOUSEHOOKSTRUCT hook;
887 BOOL eatMsg;
888
889 PWND pwndMsg, pwndDesktop;
890 PUSER_MESSAGE_QUEUE MessageQueue;
891 PTHREADINFO pti;
892 PSYSTEM_CURSORINFO CurInfo;
893 DECLARE_RETURN(BOOL);
894
895 pti = PsGetCurrentThreadWin32Thread();
896 pwndDesktop = UserGetDesktopWindow();
897 MessageQueue = pti->MessageQueue;
898 CurInfo = IntGetSysCursorInfo();
899 pwndMsg = UserGetWindowObject(msg->hwnd);
900 clk_msg = MessageQueue->msgDblClk;
901
902 /* find the window to dispatch this mouse message to */
903 if (MessageQueue->CaptureWindow)
904 {
905 hittest = HTCLIENT;
906 pwndMsg = IntGetWindowObject(MessageQueue->CaptureWindow);
907 }
908 else
909 {
910 pwndMsg = co_WinPosWindowFromPoint(pwndMsg, &msg->pt, &hittest);
911 }
912
913 DPRINT("Got mouse message for 0x%x, hittest: 0x%x\n", msg->hwnd, hittest );
914
915 if (pwndMsg == NULL || pwndMsg->head.pti != pti)
916 {
917 /* Remove and ignore the message */
918 *RemoveMessages = TRUE;
919 RETURN(FALSE);
920 }
921
922 msg->hwnd = UserHMGetHandle(pwndMsg);
923
924 #if 0
925 if (!check_hwnd_filter( msg, hwnd_filter )) RETURN(FALSE);
926 #endif
927
928 pt = msg->pt;
929 message = msg->message;
930 /* Note: windows has no concept of a non-client wheel message */
931 if (message != WM_MOUSEWHEEL)
932 {
933 if (hittest != HTCLIENT)
934 {
935 message += WM_NCMOUSEMOVE - WM_MOUSEMOVE;
936 msg->wParam = hittest;
937 }
938 else
939 {
940 /* coordinates don't get translated while tracking a menu */
941 /* FIXME: should differentiate popups and top-level menus */
942 if (!(MessageQueue->MenuOwner))
943 {
944 pt.x += pwndDesktop->rcClient.left - pwndMsg->rcClient.left;
945 pt.y += pwndDesktop->rcClient.top - pwndMsg->rcClient.top;
946 }
947 }
948 }
949 msg->lParam = MAKELONG( pt.x, pt.y );
950
951 /* translate double clicks */
952
953 if ((msg->message == WM_LBUTTONDOWN) ||
954 (msg->message == WM_RBUTTONDOWN) ||
955 (msg->message == WM_MBUTTONDOWN) ||
956 (msg->message == WM_XBUTTONDOWN))
957 {
958 BOOL update = *RemoveMessages;
959
960 /* translate double clicks -
961 * note that ...MOUSEMOVEs can slip in between
962 * ...BUTTONDOWN and ...BUTTONDBLCLK messages */
963
964 if ((MessageQueue->MenuOwner || MessageQueue->MoveSize) ||
965 hittest != HTCLIENT ||
966 (pwndMsg->pcls->style & CS_DBLCLKS))
967 {
968 if ((msg->message == clk_msg.message) &&
969 (msg->hwnd == clk_msg.hwnd) &&
970 (msg->wParam == clk_msg.wParam) &&
971 (msg->time - clk_msg.time < gspv.iDblClickTime) &&
972 (abs(msg->pt.x - clk_msg.pt.x) < UserGetSystemMetrics(SM_CXDOUBLECLK)/2) &&
973 (abs(msg->pt.y - clk_msg.pt.y) < UserGetSystemMetrics(SM_CYDOUBLECLK)/2))
974 {
975 message += (WM_LBUTTONDBLCLK - WM_LBUTTONDOWN);
976 if (update)
977 {
978 MessageQueue->msgDblClk.message = 0; /* clear the double click conditions */
979 update = FALSE;
980 }
981 }
982 }
983
984 if (!((first == 0 && last == 0) || (message >= first || message <= last)))
985 {
986 DPRINT("Message out of range!!!\n");
987 RETURN(FALSE);
988 }
989
990 /* update static double click conditions */
991 if (update) MessageQueue->msgDblClk = *msg;
992 }
993 else
994 {
995 if (!((first == 0 && last == 0) || (message >= first || message <= last)))
996 {
997 DPRINT("Message out of range!!!\n");
998 RETURN(FALSE);
999 }
1000 }
1001
1002 if(gspv.bMouseClickLock)
1003 {
1004 BOOL IsClkLck = FALSE;
1005
1006 if(msg->message == WM_LBUTTONUP)
1007 {
1008 IsClkLck = ((msg->time - CurInfo->ClickLockTime) >= gspv.dwMouseClickLockTime);
1009 if (IsClkLck && (!CurInfo->ClickLockActive))
1010 {
1011 CurInfo->ClickLockActive = TRUE;
1012 }
1013 }
1014 else if (msg->message == WM_LBUTTONDOWN)
1015 {
1016 if (CurInfo->ClickLockActive)
1017 {
1018 IsClkLck = TRUE;
1019 CurInfo->ClickLockActive = FALSE;
1020 }
1021
1022 CurInfo->ClickLockTime = msg->time;
1023 }
1024
1025 if(IsClkLck)
1026 {
1027 /* Remove and ignore the message */
1028 *RemoveMessages = TRUE;
1029 RETURN(FALSE);
1030 }
1031 }
1032
1033 /* message is accepted now (but may still get dropped) */
1034
1035 pti->rpdesk->htEx = hittest; /* Now set the capture hit. */
1036
1037 event.message = msg->message;
1038 event.time = msg->time;
1039 event.hwnd = msg->hwnd;
1040 event.paramL = msg->pt.x;
1041 event.paramH = msg->pt.y;
1042 co_HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&event );
1043
1044 hook.pt = msg->pt;
1045 hook.hwnd = msg->hwnd;
1046 hook.wHitTestCode = hittest;
1047 hook.dwExtraInfo = 0/*extra_info*/;
1048 if (co_HOOK_CallHooks( WH_MOUSE, *RemoveMessages ? HC_ACTION : HC_NOREMOVE,
1049 message, (LPARAM)&hook ))
1050 {
1051 hook.pt = msg->pt;
1052 hook.hwnd = msg->hwnd;
1053 hook.wHitTestCode = hittest;
1054 hook.dwExtraInfo = 0/*extra_info*/;
1055 co_HOOK_CallHooks( WH_CBT, HCBT_CLICKSKIPPED, message, (LPARAM)&hook );
1056
1057 DPRINT1("WH_MOUSE dorpped mouse message!\n");
1058
1059 /* Remove and skip message */
1060 *RemoveMessages = TRUE;
1061 RETURN(FALSE);
1062 }
1063
1064 if ((hittest == HTERROR) || (hittest == HTNOWHERE))
1065 {
1066 co_IntSendMessage( msg->hwnd, WM_SETCURSOR, (WPARAM)msg->hwnd,
1067 MAKELONG( hittest, msg->message ));
1068
1069 /* Remove and skip message */
1070 *RemoveMessages = TRUE;
1071 RETURN(FALSE);
1072 }
1073
1074 if ((*RemoveMessages == FALSE) || MessageQueue->CaptureWindow)
1075 {
1076 /* Accept the message */
1077 msg->message = message;
1078 RETURN(TRUE);
1079 }
1080
1081 eatMsg = FALSE;
1082
1083 if ((msg->message == WM_LBUTTONDOWN) ||
1084 (msg->message == WM_RBUTTONDOWN) ||
1085 (msg->message == WM_MBUTTONDOWN) ||
1086 (msg->message == WM_XBUTTONDOWN))
1087 {
1088 /* Send the WM_PARENTNOTIFY,
1089 * note that even for double/nonclient clicks
1090 * notification message is still WM_L/M/RBUTTONDOWN.
1091 */
1092 MsqSendParentNotify(pwndMsg, msg->message, 0, msg->pt );
1093
1094 /* Activate the window if needed */
1095
1096 if (msg->hwnd != MessageQueue->ActiveWindow)
1097 {
1098 PWND pwndTop = pwndMsg;
1099 while (pwndTop)
1100 {
1101 if ((pwndTop->style & (WS_POPUP|WS_CHILD)) != WS_CHILD) break;
1102 pwndTop = IntGetParent( pwndTop );
1103 }
1104
1105 if (pwndTop && pwndTop != pwndDesktop)
1106 {
1107 LONG ret = co_IntSendMessage( msg->hwnd,
1108 WM_MOUSEACTIVATE,
1109 (WPARAM)UserHMGetHandle(pwndTop),
1110 MAKELONG( hittest, msg->message));
1111 switch(ret)
1112 {
1113 case MA_NOACTIVATEANDEAT:
1114 eatMsg = TRUE;
1115 /* fall through */
1116 case MA_NOACTIVATE:
1117 break;
1118 case MA_ACTIVATEANDEAT:
1119 eatMsg = TRUE;
1120 /* fall through */
1121 case MA_ACTIVATE:
1122 case 0:
1123 if(!co_IntMouseActivateWindow(pwndMsg)) eatMsg = TRUE;
1124 break;
1125 default:
1126 DPRINT1( "unknown WM_MOUSEACTIVATE code %d\n", ret );
1127 break;
1128 }
1129 }
1130 }
1131 }
1132
1133 /* send the WM_SETCURSOR message */
1134
1135 /* Windows sends the normal mouse message as the message parameter
1136 in the WM_SETCURSOR message even if it's non-client mouse message */
1137 co_IntSendMessage( msg->hwnd, WM_SETCURSOR, (WPARAM)msg->hwnd, MAKELONG( hittest, msg->message ));
1138
1139 msg->message = message;
1140 RETURN(!eatMsg);
1141
1142 CLEANUP:
1143 if(pwndMsg)
1144 UserDereferenceObject(pwndMsg);
1145
1146 END_CLEANUP;
1147 }
1148
1149 BOOL co_IntProcessKeyboardMessage(MSG* Msg, BOOL* RemoveMessages)
1150 {
1151 EVENTMSG Event;
1152
1153 Event.message = Msg->message;
1154 Event.hwnd = Msg->hwnd;
1155 Event.time = Msg->time;
1156 Event.paramL = (Msg->wParam & 0xFF) | (HIWORD(Msg->lParam) << 8);
1157 Event.paramH = Msg->lParam & 0x7FFF;
1158 if (HIWORD(Msg->lParam) & 0x0100) Event.paramH |= 0x8000;
1159 co_HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&Event);
1160
1161 if (co_HOOK_CallHooks( WH_KEYBOARD,
1162 *RemoveMessages ? HC_ACTION : HC_NOREMOVE,
1163 LOWORD(Msg->wParam),
1164 Msg->lParam))
1165 {
1166 /* skip this message */
1167 co_HOOK_CallHooks( WH_CBT,
1168 HCBT_KEYSKIPPED,
1169 LOWORD(Msg->wParam),
1170 Msg->lParam );
1171 DPRINT1("KeyboardMessage WH_CBT Call Hook return!\n");
1172 return FALSE;
1173 }
1174 return TRUE;
1175 }
1176
1177 BOOL co_IntProcessHardwareMessage(MSG* Msg, BOOL* RemoveMessages, UINT first, UINT last)
1178 {
1179 if ( IS_MOUSE_MESSAGE(Msg->message))
1180 {
1181 return co_IntProcessMouseMessage(Msg, RemoveMessages, first, last);
1182 }
1183 else if ( IS_KBD_MESSAGE(Msg->message))
1184 {
1185 return co_IntProcessKeyboardMessage(Msg, RemoveMessages);
1186 }
1187
1188 return TRUE;
1189 }
1190
1191 BOOL APIENTRY
1192 co_MsqPeekMouseMove(IN PUSER_MESSAGE_QUEUE MessageQueue,
1193 IN BOOL Remove,
1194 IN PWND Window,
1195 IN UINT MsgFilterLow,
1196 IN UINT MsgFilterHigh,
1197 OUT MSG* pMsg)
1198 {
1199 BOOL AcceptMessage;
1200 MSG msg;
1201
1202 if(!(MessageQueue->MouseMoved))
1203 return FALSE;
1204
1205 msg = MessageQueue->MouseMoveMsg;
1206
1207 AcceptMessage = co_IntProcessMouseMessage(&msg, &Remove, MsgFilterLow, MsgFilterHigh);
1208
1209 if(AcceptMessage)
1210 *pMsg = msg;
1211
1212 if(Remove)
1213 {
1214 ClearMsgBitsMask(MessageQueue, QS_MOUSEMOVE);
1215 MessageQueue->MouseMoved = FALSE;
1216 }
1217
1218 return AcceptMessage;
1219 }
1220
1221 /* check whether a message filter contains at least one potential hardware message */
1222 static INT FASTCALL
1223 filter_contains_hw_range( UINT first, UINT last )
1224 {
1225 /* hardware message ranges are (in numerical order):
1226 * WM_NCMOUSEFIRST .. WM_NCMOUSELAST
1227 * WM_KEYFIRST .. WM_KEYLAST
1228 * WM_MOUSEFIRST .. WM_MOUSELAST
1229 */
1230 if (!last) --last;
1231 if (last < WM_NCMOUSEFIRST) return 0;
1232 if (first > WM_NCMOUSELAST && last < WM_KEYFIRST) return 0;
1233 if (first > WM_KEYLAST && last < WM_MOUSEFIRST) return 0;
1234 if (first > WM_MOUSELAST) return 0;
1235 return 1;
1236 }
1237
1238 BOOL APIENTRY
1239 co_MsqPeekHardwareMessage(IN PUSER_MESSAGE_QUEUE MessageQueue,
1240 IN BOOL Remove,
1241 IN PWND Window,
1242 IN UINT MsgFilterLow,
1243 IN UINT MsgFilterHigh,
1244 IN UINT QSflags,
1245 OUT MSG* pMsg)
1246 {
1247
1248 BOOL AcceptMessage;
1249 PUSER_MESSAGE CurrentMessage;
1250 PLIST_ENTRY ListHead, CurrentEntry = NULL;
1251 MSG msg;
1252
1253 if (!filter_contains_hw_range( MsgFilterLow, MsgFilterHigh )) return FALSE;
1254
1255 ListHead = &MessageQueue->HardwareMessagesListHead;
1256 CurrentEntry = ListHead->Flink;
1257
1258 if (IsListEmpty(CurrentEntry)) return FALSE;
1259
1260 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1261 ListEntry);
1262 do
1263 {
1264 if (IsListEmpty(CurrentEntry)) break;
1265 if (!CurrentMessage) break;
1266 CurrentEntry = CurrentMessage->ListEntry.Flink;
1267
1268 if ( (( MsgFilterLow == 0 && MsgFilterHigh == 0 ) && (CurrentMessage->QS_Flags & QSflags)) ||
1269 ( MsgFilterLow <= CurrentMessage->Msg.message && MsgFilterHigh >= CurrentMessage->Msg.message ) )
1270 {
1271 msg = CurrentMessage->Msg;
1272
1273 AcceptMessage = co_IntProcessHardwareMessage(&msg, &Remove, MsgFilterLow, MsgFilterHigh);
1274
1275 if (Remove)
1276 {
1277 RemoveEntryList(&CurrentMessage->ListEntry);
1278 ClearMsgBitsMask(MessageQueue, CurrentMessage->QS_Flags);
1279 MsqDestroyMessage(CurrentMessage);
1280 }
1281
1282 if (AcceptMessage)
1283 {
1284 *pMsg = msg;
1285 return TRUE;
1286 }
1287 }
1288 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1289 ListEntry);
1290 }
1291 while(CurrentEntry != ListHead);
1292
1293 return FALSE;
1294 }
1295
1296 BOOLEAN APIENTRY
1297 MsqPeekMessage(IN PUSER_MESSAGE_QUEUE MessageQueue,
1298 IN BOOLEAN Remove,
1299 IN PWND Window,
1300 IN UINT MsgFilterLow,
1301 IN UINT MsgFilterHigh,
1302 IN UINT QSflags,
1303 OUT PMSG Message)
1304 {
1305 PLIST_ENTRY CurrentEntry;
1306 PUSER_MESSAGE CurrentMessage;
1307 PLIST_ENTRY ListHead;
1308
1309 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
1310 ListHead = &MessageQueue->PostedMessagesListHead;
1311
1312 if (IsListEmpty(CurrentEntry)) return FALSE;
1313
1314 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1315 ListEntry);
1316 do
1317 {
1318 if (IsListEmpty(CurrentEntry)) break;
1319 if (!CurrentMessage) break;
1320 CurrentEntry = CurrentEntry->Flink;
1321
1322 if ( ( !Window || Window == HWND_BOTTOM || Window->head.h == CurrentMessage->Msg.hwnd ) &&
1323 ( ( ( MsgFilterLow == 0 && MsgFilterHigh == 0 ) && CurrentMessage->QS_Flags & QSflags ) ||
1324 ( MsgFilterLow <= CurrentMessage->Msg.message && MsgFilterHigh >= CurrentMessage->Msg.message ) ) )
1325 {
1326 *Message = CurrentMessage->Msg;
1327
1328 if (Remove)
1329 {
1330 RemoveEntryList(&CurrentMessage->ListEntry);
1331 ClearMsgBitsMask(MessageQueue, CurrentMessage->QS_Flags);
1332 MsqDestroyMessage(CurrentMessage);
1333 }
1334 return(TRUE);
1335 }
1336 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1337 ListEntry);
1338 }
1339 while (CurrentEntry != ListHead);
1340
1341 return(FALSE);
1342 }
1343
1344 NTSTATUS FASTCALL
1345 co_MsqWaitForNewMessages(PUSER_MESSAGE_QUEUE MessageQueue, PWND WndFilter,
1346 UINT MsgFilterMin, UINT MsgFilterMax)
1347 {
1348 NTSTATUS ret;
1349
1350 UserLeaveCo();
1351 ret = KeWaitForSingleObject(MessageQueue->NewMessages,
1352 Executive,
1353 UserMode,
1354 FALSE,
1355 NULL);
1356 UserEnterCo();
1357 return ret;
1358 }
1359
1360 BOOL FASTCALL
1361 MsqIsHung(PUSER_MESSAGE_QUEUE MessageQueue)
1362 {
1363 LARGE_INTEGER LargeTickCount;
1364
1365 KeQueryTickCount(&LargeTickCount);
1366 return ((LargeTickCount.u.LowPart - MessageQueue->LastMsgRead) > MSQ_HUNG);
1367 }
1368
1369 BOOLEAN FASTCALL
1370 MsqInitializeMessageQueue(struct _ETHREAD *Thread, PUSER_MESSAGE_QUEUE MessageQueue)
1371 {
1372 LARGE_INTEGER LargeTickCount;
1373 NTSTATUS Status;
1374
1375 MessageQueue->Thread = Thread;
1376 MessageQueue->CaretInfo = (PTHRDCARETINFO)(MessageQueue + 1);
1377 InitializeListHead(&MessageQueue->PostedMessagesListHead);
1378 InitializeListHead(&MessageQueue->SentMessagesListHead);
1379 InitializeListHead(&MessageQueue->HardwareMessagesListHead);
1380 InitializeListHead(&MessageQueue->DispatchingMessagesHead);
1381 InitializeListHead(&MessageQueue->LocalDispatchingMessagesHead);
1382 KeInitializeMutex(&MessageQueue->HardwareLock, 0);
1383 MessageQueue->QuitPosted = FALSE;
1384 MessageQueue->QuitExitCode = 0;
1385 KeQueryTickCount(&LargeTickCount);
1386 MessageQueue->LastMsgRead = LargeTickCount.u.LowPart;
1387 MessageQueue->FocusWindow = NULL;
1388 MessageQueue->NewMessagesHandle = NULL;
1389
1390 Status = ZwCreateEvent(&MessageQueue->NewMessagesHandle, EVENT_ALL_ACCESS,
1391 NULL, SynchronizationEvent, FALSE);
1392 if (!NT_SUCCESS(Status))
1393 {
1394 return FALSE;
1395 }
1396
1397 Status = ObReferenceObjectByHandle(MessageQueue->NewMessagesHandle, 0,
1398 ExEventObjectType, KernelMode,
1399 (PVOID*)&MessageQueue->NewMessages, NULL);
1400 if (!NT_SUCCESS(Status))
1401 {
1402 ZwClose(MessageQueue->NewMessagesHandle);
1403 MessageQueue->NewMessagesHandle = NULL;
1404 return FALSE;
1405 }
1406
1407 return TRUE;
1408 }
1409
1410 VOID FASTCALL
1411 MsqCleanupMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
1412 {
1413 PLIST_ENTRY CurrentEntry;
1414 PUSER_MESSAGE CurrentMessage;
1415 PUSER_SENT_MESSAGE CurrentSentMessage;
1416 PTHREADINFO pti;
1417
1418 pti = MessageQueue->Thread->Tcb.Win32Thread;
1419
1420
1421 /* cleanup posted messages */
1422 while (!IsListEmpty(&MessageQueue->PostedMessagesListHead))
1423 {
1424 CurrentEntry = RemoveHeadList(&MessageQueue->PostedMessagesListHead);
1425 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1426 ListEntry);
1427 MsqDestroyMessage(CurrentMessage);
1428 }
1429
1430 /* remove the messages that have not yet been dispatched */
1431 while (!IsListEmpty(&MessageQueue->SentMessagesListHead))
1432 {
1433 CurrentEntry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
1434 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1435 ListEntry);
1436
1437 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1438
1439 /* remove the message from the dispatching list if needed */
1440 if ((!(CurrentSentMessage->HookMessage & MSQ_SENTNOWAIT))
1441 && (CurrentSentMessage->DispatchingListEntry.Flink != NULL))
1442 {
1443 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
1444 }
1445
1446 /* wake the sender's thread */
1447 if (CurrentSentMessage->CompletionEvent != NULL)
1448 {
1449 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1450 }
1451
1452 if (CurrentSentMessage->HasPackedLParam == TRUE)
1453 {
1454 if (CurrentSentMessage->Msg.lParam)
1455 ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
1456 }
1457
1458 /* Only if it is not a no wait message */
1459 if (!(CurrentSentMessage->HookMessage & MSQ_SENTNOWAIT))
1460 {
1461 /* dereference our and the sender's message queue */
1462 IntDereferenceMessageQueue(MessageQueue);
1463 IntDereferenceMessageQueue(CurrentSentMessage->SenderQueue);
1464 }
1465
1466 /* free the message */
1467 ExFreePool(CurrentSentMessage);
1468 }
1469
1470 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
1471 ExitThread() was called in a SendMessage() umode callback */
1472 while (!IsListEmpty(&MessageQueue->LocalDispatchingMessagesHead))
1473 {
1474 CurrentEntry = RemoveHeadList(&MessageQueue->LocalDispatchingMessagesHead);
1475 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1476 ListEntry);
1477
1478 /* remove the message from the dispatching list */
1479 if(CurrentSentMessage->DispatchingListEntry.Flink != NULL)
1480 {
1481 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
1482 }
1483
1484 DPRINT("Notify the sender, the thread has been terminated while dispatching a message!\n");
1485
1486 /* wake the sender's thread */
1487 if (CurrentSentMessage->CompletionEvent != NULL)
1488 {
1489 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1490 }
1491
1492 if (CurrentSentMessage->HasPackedLParam == TRUE)
1493 {
1494 if (CurrentSentMessage->Msg.lParam)
1495 ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
1496 }
1497
1498 /* Only if it is not a no wait message */
1499 if (!(CurrentSentMessage->HookMessage & MSQ_SENTNOWAIT))
1500 {
1501 /* dereference our and the sender's message queue */
1502 IntDereferenceMessageQueue(MessageQueue);
1503 IntDereferenceMessageQueue(CurrentSentMessage->SenderQueue);
1504 }
1505
1506 /* free the message */
1507 ExFreePool(CurrentSentMessage);
1508 }
1509
1510 /* tell other threads not to bother returning any info to us */
1511 while (! IsListEmpty(&MessageQueue->DispatchingMessagesHead))
1512 {
1513 CurrentEntry = RemoveHeadList(&MessageQueue->DispatchingMessagesHead);
1514 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1515 DispatchingListEntry);
1516 CurrentSentMessage->CompletionEvent = NULL;
1517 CurrentSentMessage->Result = NULL;
1518
1519 /* do NOT dereference our message queue as it might get attempted to be
1520 locked later */
1521 }
1522
1523 // Clear it all out.
1524 pti->pcti->fsWakeBits = 0;
1525 pti->pcti->fsChangeBits = 0;
1526
1527 MessageQueue->nCntsQBits[QSRosKey] = 0;
1528 MessageQueue->nCntsQBits[QSRosMouseMove] = 0;
1529 MessageQueue->nCntsQBits[QSRosMouseButton] = 0;
1530 MessageQueue->nCntsQBits[QSRosPostMessage] = 0;
1531 MessageQueue->nCntsQBits[QSRosSendMessage] = 0;
1532 MessageQueue->nCntsQBits[QSRosHotKey] = 0;
1533 }
1534
1535 PUSER_MESSAGE_QUEUE FASTCALL
1536 MsqCreateMessageQueue(struct _ETHREAD *Thread)
1537 {
1538 PUSER_MESSAGE_QUEUE MessageQueue;
1539
1540 MessageQueue = (PUSER_MESSAGE_QUEUE)ExAllocatePoolWithTag(NonPagedPool,
1541 sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO),
1542 TAG_MSGQ);
1543
1544 if (!MessageQueue)
1545 {
1546 return NULL;
1547 }
1548
1549 RtlZeroMemory(MessageQueue, sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO));
1550 /* hold at least one reference until it'll be destroyed */
1551 IntReferenceMessageQueue(MessageQueue);
1552 /* initialize the queue */
1553 if (!MsqInitializeMessageQueue(Thread, MessageQueue))
1554 {
1555 IntDereferenceMessageQueue(MessageQueue);
1556 return NULL;
1557 }
1558
1559 return MessageQueue;
1560 }
1561
1562 VOID FASTCALL
1563 MsqDestroyMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
1564 {
1565 PDESKTOP desk;
1566
1567 /* remove the message queue from any desktops */
1568 if ((desk = InterlockedExchangePointer((PVOID*)&MessageQueue->Desktop, 0)))
1569 {
1570 (void)InterlockedExchangePointer((PVOID*)&desk->ActiveMessageQueue, 0);
1571 IntDereferenceMessageQueue(MessageQueue);
1572 }
1573
1574 /* clean it up */
1575 MsqCleanupMessageQueue(MessageQueue);
1576
1577 /* decrease the reference counter, if it hits zero, the queue will be freed */
1578 IntDereferenceMessageQueue(MessageQueue);
1579 }
1580
1581 LPARAM FASTCALL
1582 MsqSetMessageExtraInfo(LPARAM lParam)
1583 {
1584 LPARAM Ret;
1585 PTHREADINFO pti;
1586 PUSER_MESSAGE_QUEUE MessageQueue;
1587
1588 pti = PsGetCurrentThreadWin32Thread();
1589 MessageQueue = pti->MessageQueue;
1590 if(!MessageQueue)
1591 {
1592 return 0;
1593 }
1594
1595 Ret = MessageQueue->ExtraInfo;
1596 MessageQueue->ExtraInfo = lParam;
1597
1598 return Ret;
1599 }
1600
1601 LPARAM FASTCALL
1602 MsqGetMessageExtraInfo(VOID)
1603 {
1604 PTHREADINFO pti;
1605 PUSER_MESSAGE_QUEUE MessageQueue;
1606
1607 pti = PsGetCurrentThreadWin32Thread();
1608 MessageQueue = pti->MessageQueue;
1609 if(!MessageQueue)
1610 {
1611 return 0;
1612 }
1613
1614 return MessageQueue->ExtraInfo;
1615 }
1616
1617 // ReplyMessage is called by the thread receiving the window message.
1618 BOOL FASTCALL
1619 co_MsqReplyMessage( LRESULT lResult )
1620 {
1621 PUSER_SENT_MESSAGE Message;
1622 PTHREADINFO pti;
1623
1624 pti = PsGetCurrentThreadWin32Thread();
1625 Message = pti->pusmCurrent;
1626
1627 if (!Message) return FALSE;
1628
1629 if (Message->QS_Flags & QS_SMRESULT) return FALSE;
1630
1631 // SendMessageXxx || Callback msg and not a notify msg
1632 if (Message->SenderQueue || Message->CompletionCallback)
1633 {
1634 Message->lResult = lResult;
1635 Message->QS_Flags |= QS_SMRESULT;
1636 // See co_MsqDispatchOneSentMessage, change bits already accounted for and cleared and this msg is going away..
1637 }
1638 return TRUE;
1639 }
1640
1641 HWND FASTCALL
1642 MsqSetStateWindow(PUSER_MESSAGE_QUEUE MessageQueue, ULONG Type, HWND hWnd)
1643 {
1644 HWND Prev;
1645
1646 switch(Type)
1647 {
1648 case MSQ_STATE_CAPTURE:
1649 Prev = MessageQueue->CaptureWindow;
1650 MessageQueue->CaptureWindow = hWnd;
1651 return Prev;
1652 case MSQ_STATE_ACTIVE:
1653 Prev = MessageQueue->ActiveWindow;
1654 MessageQueue->ActiveWindow = hWnd;
1655 return Prev;
1656 case MSQ_STATE_FOCUS:
1657 Prev = MessageQueue->FocusWindow;
1658 MessageQueue->FocusWindow = hWnd;
1659 return Prev;
1660 case MSQ_STATE_MENUOWNER:
1661 Prev = MessageQueue->MenuOwner;
1662 MessageQueue->MenuOwner = hWnd;
1663 return Prev;
1664 case MSQ_STATE_MOVESIZE:
1665 Prev = MessageQueue->MoveSize;
1666 MessageQueue->MoveSize = hWnd;
1667 return Prev;
1668 case MSQ_STATE_CARET:
1669 ASSERT(MessageQueue->CaretInfo);
1670 Prev = MessageQueue->CaretInfo->hWnd;
1671 MessageQueue->CaretInfo->hWnd = hWnd;
1672 return Prev;
1673 }
1674
1675 return NULL;
1676 }
1677
1678 /* EOF */