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