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