2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
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.
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.
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.
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)
26 * 06-06-2001 CSH Created
29 /* INCLUDES ******************************************************************/
36 /* GLOBALS *******************************************************************/
38 #define SYSTEM_MESSAGE_QUEUE_SIZE (256)
40 static MSG SystemMessageQueue
[SYSTEM_MESSAGE_QUEUE_SIZE
];
41 static ULONG SystemMessageQueueHead
= 0;
42 static ULONG SystemMessageQueueTail
= 0;
43 static ULONG SystemMessageQueueCount
= 0;
44 static KSPIN_LOCK SystemMessageQueueLock
;
46 static ULONG
volatile HardwareMessageQueueStamp
= 0;
47 static LIST_ENTRY HardwareMessageQueueHead
;
48 static KMUTANT HardwareMessageQueueLock
;
50 static KEVENT HardwareMessageEvent
;
52 static PAGED_LOOKASIDE_LIST MessageLookasideList
;
53 static PAGED_LOOKASIDE_LIST TimerLookasideList
;
55 #define IntLockSystemMessageQueue(OldIrql) \
56 KeAcquireSpinLock(&SystemMessageQueueLock, &OldIrql)
58 #define IntUnLockSystemMessageQueue(OldIrql) \
59 KeReleaseSpinLock(&SystemMessageQueueLock, OldIrql)
61 #define IntUnLockSystemHardwareMessageQueueLock(Wait) \
62 KeReleaseMutant(&HardwareMessageQueueLock, IO_NO_INCREMENT, FALSE, Wait)
64 /* FUNCTIONS *****************************************************************/
67 // Wakeup any thread/process waiting on idle input.
73 PWINDOW_OBJECT Window
;
74 PPROCESSINFO W32d
= PsGetCurrentProcessWin32Process();
76 hWnd
= UserGetForegroundWindow();
78 Window
= UserGetWindowObject(hWnd
);
80 if (Window
&& Window
->pti
)
82 if (Window
->pti
->fsHooks
& HOOKID_TO_FLAG(WH_FOREGROUNDIDLE
))
84 co_HOOK_CallHooks(WH_FOREGROUNDIDLE
,HC_ACTION
,0,0);
88 if (W32d
&& W32d
->InputIdleEvent
)
89 KePulseEvent( W32d
->InputIdleEvent
, EVENT_INCREMENT
, TRUE
);
93 IntMsqSetWakeMask(DWORD WakeMask
)
95 PTHREADINFO Win32Thread
;
96 PUSER_MESSAGE_QUEUE MessageQueue
;
97 HANDLE MessageEventHandle
;
99 Win32Thread
= PsGetCurrentThreadWin32Thread();
100 if (Win32Thread
== NULL
|| Win32Thread
->MessageQueue
== NULL
)
103 MessageQueue
= Win32Thread
->MessageQueue
;
104 MessageQueue
->WakeMask
= WakeMask
;
105 MessageEventHandle
= MessageQueue
->NewMessagesHandle
;
107 return MessageEventHandle
;
111 IntMsqClearWakeMask(VOID
)
113 PTHREADINFO Win32Thread
;
114 PUSER_MESSAGE_QUEUE MessageQueue
;
116 Win32Thread
= PsGetCurrentThreadWin32Thread();
117 if (Win32Thread
== NULL
|| Win32Thread
->MessageQueue
== NULL
)
120 MessageQueue
= Win32Thread
->MessageQueue
;
121 // HACK!!!!!!! Newbies that wrote this should hold your head down in shame! (jt)
122 MessageQueue
->WakeMask
= ~0;
128 MsqIncPaintCountQueue(PUSER_MESSAGE_QUEUE Queue
)
131 Queue
->QueueBits
|= QS_PAINT
;
132 Queue
->ChangedBits
|= QS_PAINT
;
133 if (Queue
->WakeMask
& QS_PAINT
)
134 KeSetEvent(Queue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
138 MsqDecPaintCountQueue(PUSER_MESSAGE_QUEUE Queue
)
145 MsqInitializeImpl(VOID
)
147 /*CurrentFocusMessageQueue = NULL;*/
148 InitializeListHead(&HardwareMessageQueueHead
);
149 KeInitializeEvent(&HardwareMessageEvent
, NotificationEvent
, 0);
150 KeInitializeSpinLock(&SystemMessageQueueLock
);
151 KeInitializeMutant(&HardwareMessageQueueLock
, 0);
153 ExInitializePagedLookasideList(&MessageLookasideList
,
157 sizeof(USER_MESSAGE
),
160 ExInitializePagedLookasideList(&TimerLookasideList
,
168 return(STATUS_SUCCESS
);
172 MsqInsertSystemMessage(MSG
* Msg
)
174 LARGE_INTEGER LargeTickCount
;
179 IntLockSystemMessageQueue(OldIrql
);
182 * Bail out if the queue is full. FIXME: We should handle this case
186 if (SystemMessageQueueCount
== SYSTEM_MESSAGE_QUEUE_SIZE
)
188 IntUnLockSystemMessageQueue(OldIrql
);
192 KeQueryTickCount(&LargeTickCount
);
193 Msg
->time
= MsqCalculateMessageTime(&LargeTickCount
);
195 Event
.message
= Msg
->message
;
196 Event
.time
= Msg
->time
;
197 Event
.hwnd
= Msg
->hwnd
;
198 Event
.paramL
= Msg
->pt
.x
;
199 Event
.paramH
= Msg
->pt
.y
;
200 co_HOOK_CallHooks( WH_JOURNALRECORD
, HC_ACTION
, 0, (LPARAM
)&Event
);
203 * If we got WM_MOUSEMOVE and there are already messages in the
204 * system message queue, check if the last message is mouse move
205 * and if it is then just overwrite it.
208 if (Msg
->message
== WM_MOUSEMOVE
&& SystemMessageQueueCount
)
210 if (SystemMessageQueueTail
== 0)
211 Prev
= SYSTEM_MESSAGE_QUEUE_SIZE
- 1;
213 Prev
= SystemMessageQueueTail
- 1;
214 if (SystemMessageQueue
[Prev
].message
== WM_MOUSEMOVE
)
216 SystemMessageQueueTail
= Prev
;
217 SystemMessageQueueCount
--;
222 * Actually insert the message into the system message queue.
225 SystemMessageQueue
[SystemMessageQueueTail
] = *Msg
;
226 SystemMessageQueueTail
=
227 (SystemMessageQueueTail
+ 1) % SYSTEM_MESSAGE_QUEUE_SIZE
;
228 SystemMessageQueueCount
++;
230 IntUnLockSystemMessageQueue(OldIrql
);
232 KeSetEvent(&HardwareMessageEvent
, IO_NO_INCREMENT
, FALSE
);
236 MsqIsClkLck(LPMSG Msg
, BOOL Remove
)
239 PSYSTEM_CURSORINFO CurInfo
;
242 pti
= PsGetCurrentThreadWin32Thread();
243 if (pti
->Desktop
== NULL
)
248 CurInfo
= IntGetSysCursorInfo();
250 switch (Msg
->message
)
253 Res
= ((Msg
->time
- CurInfo
->ClickLockTime
) >= gspv
.dwMouseClickLockTime
);
254 if (Res
&& (!CurInfo
->ClickLockActive
))
256 CurInfo
->ClickLockActive
= TRUE
;
260 if (CurInfo
->ClickLockActive
)
263 CurInfo
->ClickLockActive
= FALSE
;
264 CurInfo
->ClickLockTime
= 0;
268 CurInfo
->ClickLockTime
= Msg
->time
;
276 MsqIsDblClk(LPMSG Msg
, BOOL Remove
)
279 PSYSTEM_CURSORINFO CurInfo
;
283 pti
= PsGetCurrentThreadWin32Thread();
284 if (pti
->Desktop
== NULL
)
289 CurInfo
= IntGetSysCursorInfo();
290 Res
= (Msg
->hwnd
== (HWND
)CurInfo
->LastClkWnd
) &&
291 ((Msg
->time
- CurInfo
->LastBtnDown
) < gspv
.iDblClickTime
);
295 dX
= CurInfo
->LastBtnDownX
- Msg
->pt
.x
;
296 dY
= CurInfo
->LastBtnDownY
- Msg
->pt
.y
;
302 Res
= (dX
<= gspv
.iDblClickWidth
) &&
303 (dY
<= gspv
.iDblClickHeight
);
307 if(CurInfo
->ButtonsDown
)
308 Res
= (CurInfo
->ButtonsDown
== Msg
->message
);
314 CurInfo
->LastBtnDownX
= Msg
->pt
.x
;
315 CurInfo
->LastBtnDownY
= Msg
->pt
.y
;
316 CurInfo
->ButtonsDown
= Msg
->message
;
319 CurInfo
->LastBtnDown
= 0;
320 CurInfo
->LastClkWnd
= NULL
;
324 CurInfo
->LastClkWnd
= (HANDLE
)Msg
->hwnd
;
325 CurInfo
->LastBtnDown
= Msg
->time
;
333 co_MsqTranslateMouseMessage(PUSER_MESSAGE_QUEUE MessageQueue
, PWINDOW_OBJECT Window
, UINT FilterLow
, UINT FilterHigh
,
334 PUSER_MESSAGE Message
, BOOL Remove
, PBOOL Freed
,
335 PWINDOW_OBJECT ScopeWin
, PPOINT ScreenPoint
, BOOL FromGlobalQueue
, PLIST_ENTRY
*Next
)
337 USHORT Msg
= Message
->Msg
.message
;
338 PWINDOW_OBJECT CaptureWindow
= NULL
;
341 ASSERT_REFS_CO(ScopeWin
);
344 co_WinPosWindowFromPoint can return a Window, and in that case
345 that window has a ref that we need to deref. Thats why we add "dummy"
346 refs in all other cases.
349 hCaptureWin
= IntGetCaptureWindow();
350 if (hCaptureWin
== NULL
)
352 if (Msg
== WM_MOUSEWHEEL
)
354 CaptureWindow
= UserGetWindowObject(IntGetFocusWindow());
355 if (CaptureWindow
) UserReferenceObject(CaptureWindow
);
359 co_WinPosWindowFromPoint(ScopeWin
, NULL
, &Message
->Msg
.pt
, &CaptureWindow
);
360 if(CaptureWindow
== NULL
)
362 CaptureWindow
= ScopeWin
;
363 if (CaptureWindow
) UserReferenceObject(CaptureWindow
);
367 /* this is the one case where we dont add a ref, since the returned
368 window is already referenced */
374 /* FIXME - window messages should go to the right window if no buttons are
376 CaptureWindow
= UserGetWindowObject(hCaptureWin
);
377 if (CaptureWindow
) UserReferenceObject(CaptureWindow
);
382 if (CaptureWindow
== NULL
)
386 RemoveEntryList(&Message
->ListEntry
);
387 if(MessageQueue
->MouseMoveMsg
== Message
)
389 MessageQueue
->MouseMoveMsg
= NULL
;
392 // when FromGlobalQueue is true, the caller has already removed the Message
398 if (CaptureWindow
->MessageQueue
!= MessageQueue
)
400 if (! FromGlobalQueue
)
402 DPRINT("Moving msg between private queues\n");
403 /* This message is already queued in a private queue, but we need
404 * to move it to a different queue, perhaps because a new window
405 * was created which now covers the screen area previously taken
406 * by another window. To move it, we need to take it out of the
407 * old queue. Note that we're already holding the lock mutexes of the
409 RemoveEntryList(&Message
->ListEntry
);
411 /* remove the pointer for the current WM_MOUSEMOVE message in case we
413 if(MessageQueue
->MouseMoveMsg
== Message
)
415 MessageQueue
->MouseMoveMsg
= NULL
;
419 /* lock the destination message queue, so we don't get in trouble with other
420 threads, messing with it at the same time */
421 IntLockHardwareMessageQueue(CaptureWindow
->MessageQueue
);
422 InsertTailList(&CaptureWindow
->MessageQueue
->HardwareMessagesListHead
,
423 &Message
->ListEntry
);
424 if(Message
->Msg
.message
== WM_MOUSEMOVE
)
426 if(CaptureWindow
->MessageQueue
->MouseMoveMsg
)
428 /* remove the old WM_MOUSEMOVE message, we're processing a more recent
430 RemoveEntryList(&CaptureWindow
->MessageQueue
->MouseMoveMsg
->ListEntry
);
431 ExFreePool(CaptureWindow
->MessageQueue
->MouseMoveMsg
);
433 /* save the pointer to the WM_MOUSEMOVE message in the new queue */
434 CaptureWindow
->MessageQueue
->MouseMoveMsg
= Message
;
436 CaptureWindow
->MessageQueue
->QueueBits
|= QS_MOUSEMOVE
;
437 CaptureWindow
->MessageQueue
->ChangedBits
|= QS_MOUSEMOVE
;
438 if (CaptureWindow
->MessageQueue
->WakeMask
& QS_MOUSEMOVE
)
439 KeSetEvent(CaptureWindow
->MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
443 CaptureWindow
->MessageQueue
->QueueBits
|= QS_MOUSEBUTTON
;
444 CaptureWindow
->MessageQueue
->ChangedBits
|= QS_MOUSEBUTTON
;
445 if (CaptureWindow
->MessageQueue
->WakeMask
& QS_MOUSEBUTTON
)
446 KeSetEvent(CaptureWindow
->MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
448 IntUnLockHardwareMessageQueue(CaptureWindow
->MessageQueue
);
451 UserDereferenceObject(CaptureWindow
);
455 /* From here on, we're in the same message queue as the caller! */
457 *ScreenPoint
= Message
->Msg
.pt
;
459 if((Window
!= NULL
&& (int)Window
!= 1 && CaptureWindow
->hSelf
!= Window
->hSelf
) ||
460 ((FilterLow
!= 0 || FilterHigh
!= 0) && (Msg
< FilterLow
|| Msg
> FilterHigh
)))
462 /* Reject the message because it doesn't match the filter */
466 /* Lock the message queue so no other thread can mess with it.
467 Our own message queue is not locked while fetching from the global
468 queue, so we have to make sure nothing interferes! */
469 IntLockHardwareMessageQueue(CaptureWindow
->MessageQueue
);
470 /* if we're from the global queue, we need to add our message to our
471 private queue so we don't loose it! */
472 InsertTailList(&CaptureWindow
->MessageQueue
->HardwareMessagesListHead
,
473 &Message
->ListEntry
);
476 if (Message
->Msg
.message
== WM_MOUSEMOVE
)
478 if(CaptureWindow
->MessageQueue
->MouseMoveMsg
&&
479 (CaptureWindow
->MessageQueue
->MouseMoveMsg
!= Message
))
481 /* delete the old message */
482 RemoveEntryList(&CaptureWindow
->MessageQueue
->MouseMoveMsg
->ListEntry
);
483 ExFreePool(CaptureWindow
->MessageQueue
->MouseMoveMsg
);
484 if (!FromGlobalQueue
)
486 // We might have deleted the next one in our queue, so fix next
487 *Next
= Message
->ListEntry
.Flink
;
490 /* always save a pointer to this WM_MOUSEMOVE message here because we're
491 sure that the message is in the private queue */
492 CaptureWindow
->MessageQueue
->MouseMoveMsg
= Message
;
496 IntUnLockHardwareMessageQueue(CaptureWindow
->MessageQueue
);
499 UserDereferenceObject(CaptureWindow
);
504 /* FIXME - only assign if removing? */
505 Message
->Msg
.hwnd
= CaptureWindow
->hSelf
;
506 Message
->Msg
.message
= Msg
;
507 Message
->Msg
.lParam
= MAKELONG(Message
->Msg
.pt
.x
, Message
->Msg
.pt
.y
);
509 /* remove the reference to the current WM_(NC)MOUSEMOVE message, if this message
511 if (Message
->Msg
.message
== WM_MOUSEMOVE
||
512 Message
->Msg
.message
== WM_NCMOUSEMOVE
)
516 /* Lock the message queue so no other thread can mess with it.
517 Our own message queue is not locked while fetching from the global
518 queue, so we have to make sure nothing interferes! */
519 IntLockHardwareMessageQueue(CaptureWindow
->MessageQueue
);
520 if(CaptureWindow
->MessageQueue
->MouseMoveMsg
)
522 /* delete the WM_(NC)MOUSEMOVE message in the private queue, we're dealing
523 with one that's been sent later */
524 RemoveEntryList(&CaptureWindow
->MessageQueue
->MouseMoveMsg
->ListEntry
);
525 ExFreePool(CaptureWindow
->MessageQueue
->MouseMoveMsg
);
526 /* our message is not in the private queue so we can remove the pointer
527 instead of setting it to the current message we're processing */
528 CaptureWindow
->MessageQueue
->MouseMoveMsg
= NULL
;
530 IntUnLockHardwareMessageQueue(CaptureWindow
->MessageQueue
);
532 else if (CaptureWindow
->MessageQueue
->MouseMoveMsg
== Message
)
534 CaptureWindow
->MessageQueue
->MouseMoveMsg
= NULL
;
538 UserDereferenceObject(CaptureWindow
);
544 co_MsqPeekHardwareMessage(PUSER_MESSAGE_QUEUE MessageQueue
, PWINDOW_OBJECT Window
,
545 UINT FilterLow
, UINT FilterHigh
, BOOL Remove
,
546 PUSER_MESSAGE
* Message
)
551 PLIST_ENTRY CurrentEntry
;
552 PWINDOW_OBJECT DesktopWindow
= NULL
;
553 PVOID WaitObjects
[2];
555 DECLARE_RETURN(BOOL
);
556 USER_REFERENCE_ENTRY Ref
;
557 PDESKTOPINFO Desk
= NULL
;
559 WaitObjects
[1] = MessageQueue
->NewMessages
;
560 WaitObjects
[0] = &HardwareMessageQueueLock
;
567 WaitStatus
= KeWaitForMultipleObjects(2, WaitObjects
, WaitAny
, UserRequest
,
568 UserMode
, FALSE
, NULL
, NULL
);
572 while (NT_SUCCESS(WaitStatus
) && STATUS_WAIT_0
!= WaitStatus
);
574 DesktopWindow
= UserGetWindowObject(IntGetDesktopWindow());
578 UserRefObjectCo(DesktopWindow
, &Ref
);//can DesktopWindow be NULL?
579 Desk
= DesktopWindow
->pti
->pDeskInfo
;
582 /* Process messages in the message queue itself. */
583 IntLockHardwareMessageQueue(MessageQueue
);
584 CurrentEntry
= MessageQueue
->HardwareMessagesListHead
.Flink
;
585 while (CurrentEntry
!= &MessageQueue
->HardwareMessagesListHead
)
587 PUSER_MESSAGE Current
=
588 CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
, ListEntry
);
589 CurrentEntry
= CurrentEntry
->Flink
;
590 if (Current
->Msg
.message
>= WM_MOUSEFIRST
&&
591 Current
->Msg
.message
<= WM_MOUSELAST
)
595 Accept
= co_MsqTranslateMouseMessage(MessageQueue
, Window
, FilterLow
, FilterHigh
,
596 Current
, Remove
, &Freed
,
597 DesktopWindow
, &ScreenPoint
, FALSE
, &CurrentEntry
);
602 RemoveEntryList(&Current
->ListEntry
);
604 IntUnLockHardwareMessageQueue(MessageQueue
);
605 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
609 Desk
->LastInputWasKbd
= FALSE
;
616 IntUnLockHardwareMessageQueue(MessageQueue
);
618 /* Now try the global queue. */
620 /* Transfer all messages from the DPC accessible queue to the main queue. */
621 IntLockSystemMessageQueue(OldIrql
);
622 while (SystemMessageQueueCount
> 0)
624 PUSER_MESSAGE UserMsg
;
628 ASSERT(SystemMessageQueueHead
< SYSTEM_MESSAGE_QUEUE_SIZE
);
629 Msg
= SystemMessageQueue
[SystemMessageQueueHead
];
630 SystemMessageQueueHead
=
631 (SystemMessageQueueHead
+ 1) % SYSTEM_MESSAGE_QUEUE_SIZE
;
632 SystemMessageQueueCount
--;
633 IntUnLockSystemMessageQueue(OldIrql
);
634 if (WM_MOUSEFIRST
<= Msg
.message
&& Msg
.message
<= WM_MOUSELAST
)
636 MSLLHOOKSTRUCT MouseHookData
;
638 MouseHookData
.pt
.x
= LOWORD(Msg
.lParam
);
639 MouseHookData
.pt
.y
= HIWORD(Msg
.lParam
);
643 MouseHookData
.mouseData
= MAKELONG(0, GET_WHEEL_DELTA_WPARAM(Msg
.wParam
));
647 case WM_XBUTTONDBLCLK
:
648 case WM_NCXBUTTONDOWN
:
650 case WM_NCXBUTTONDBLCLK
:
651 MouseHookData
.mouseData
= MAKELONG(0, HIWORD(Msg
.wParam
));
654 MouseHookData
.mouseData
= 0;
657 MouseHookData
.flags
= 0;
658 MouseHookData
.time
= Msg
.time
;
659 MouseHookData
.dwExtraInfo
= 0;
660 ProcessMessage
= (0 == co_HOOK_CallHooks(WH_MOUSE_LL
, HC_ACTION
,
661 Msg
.message
, (LPARAM
) &MouseHookData
));
665 ProcessMessage
= TRUE
;
669 UserMsg
= ExAllocateFromPagedLookasideList(&MessageLookasideList
);
670 /* What to do if out of memory? For now we just panic a bit in debug */
672 UserMsg
->FreeLParam
= FALSE
;
674 InsertTailList(&HardwareMessageQueueHead
, &UserMsg
->ListEntry
);
676 IntLockSystemMessageQueue(OldIrql
);
678 HardwareMessageQueueStamp
++;
679 IntUnLockSystemMessageQueue(OldIrql
);
681 /* Process messages in the queue until we find one to return. */
682 CurrentEntry
= HardwareMessageQueueHead
.Flink
;
683 while (CurrentEntry
!= &HardwareMessageQueueHead
)
685 PUSER_MESSAGE Current
=
686 CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
, ListEntry
);
687 CurrentEntry
= CurrentEntry
->Flink
;
688 RemoveEntryList(&Current
->ListEntry
);
689 HardwareMessageQueueStamp
++;
690 if (Current
->Msg
.message
>= WM_MOUSEFIRST
&&
691 Current
->Msg
.message
<= WM_MOUSELAST
)
693 const ULONG ActiveStamp
= HardwareMessageQueueStamp
;
694 /* Translate the message. */
695 Accept
= co_MsqTranslateMouseMessage(MessageQueue
, Window
, FilterLow
, FilterHigh
,
696 Current
, Remove
, &Freed
,
697 DesktopWindow
, &ScreenPoint
, TRUE
, NULL
);
700 /* Check for no more messages in the system queue. */
701 IntLockSystemMessageQueue(OldIrql
);
702 if (SystemMessageQueueCount
== 0 &&
703 IsListEmpty(&HardwareMessageQueueHead
))
705 KeClearEvent(&HardwareMessageEvent
);
707 IntUnLockSystemMessageQueue(OldIrql
);
710 If we aren't removing the message then add it to the private
715 IntLockHardwareMessageQueue(MessageQueue
);
716 if(Current
->Msg
.message
== WM_MOUSEMOVE
)
718 if(MessageQueue
->MouseMoveMsg
)
720 RemoveEntryList(&MessageQueue
->MouseMoveMsg
->ListEntry
);
721 ExFreePool(MessageQueue
->MouseMoveMsg
);
723 MessageQueue
->MouseMoveMsg
= Current
;
725 InsertTailList(&MessageQueue
->HardwareMessagesListHead
,
726 &Current
->ListEntry
);
727 IntUnLockHardwareMessageQueue(MessageQueue
);
729 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
734 /* If the contents of the queue changed then restart processing. */
735 if (HardwareMessageQueueStamp
!= ActiveStamp
)
737 CurrentEntry
= HardwareMessageQueueHead
.Flink
;
743 /* Check if the system message queue is now empty. */
744 IntLockSystemMessageQueue(OldIrql
);
745 if (SystemMessageQueueCount
== 0 && IsListEmpty(&HardwareMessageQueueHead
))
747 KeClearEvent(&HardwareMessageEvent
);
749 IntUnLockSystemMessageQueue(OldIrql
);
750 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
755 if (DesktopWindow
) UserDerefObjectCo(DesktopWindow
);
761 // Note: Only called from input.c.
764 co_MsqPostKeyboardMessage(UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
766 PUSER_MESSAGE_QUEUE FocusMessageQueue
;
768 LARGE_INTEGER LargeTickCount
;
769 KBDLLHOOKSTRUCT KbdHookData
;
771 BOOLEAN Entered
= FALSE
;
773 DPRINT("MsqPostKeyboardMessage(uMsg 0x%x, wParam 0x%x, lParam 0x%x)\n",
774 uMsg
, wParam
, lParam
);
776 // Condition may arise when calling MsqPostMessage and waiting for an event.
777 if (!UserIsEntered())
779 // Fixme: Not sure ATM if this thread is locked.
780 UserEnterExclusive();
784 FocusMessageQueue
= IntGetFocusMessageQueue();
788 if (FocusMessageQueue
&& (FocusMessageQueue
->FocusWindow
!= (HWND
)0))
789 Msg
.hwnd
= FocusMessageQueue
->FocusWindow
;
795 KeQueryTickCount(&LargeTickCount
);
796 Msg
.time
= MsqCalculateMessageTime(&LargeTickCount
);
798 Event
.message
= Msg
.message
;
799 Event
.hwnd
= Msg
.hwnd
;
800 Event
.time
= Msg
.time
;
801 Event
.paramL
= (Msg
.wParam
& 0xFF) | (HIWORD(Msg
.lParam
) << 8);
802 Event
.paramH
= Msg
.lParam
& 0x7FFF;
803 if (HIWORD(Msg
.lParam
) & 0x0100) Event
.paramH
|= 0x8000;
804 co_HOOK_CallHooks( WH_JOURNALRECORD
, HC_ACTION
, 0, (LPARAM
)&Event
);
806 /* We can't get the Msg.pt point here since we don't know thread
807 (and thus the window station) the message will end up in yet. */
809 KbdHookData
.vkCode
= Msg
.wParam
;
810 KbdHookData
.scanCode
= (Msg
.lParam
>> 16) & 0xff;
811 KbdHookData
.flags
= (0 == (Msg
.lParam
& 0x01000000) ? 0 : LLKHF_EXTENDED
) |
812 (0 == (Msg
.lParam
& 0x20000000) ? 0 : LLKHF_ALTDOWN
) |
813 (0 == (Msg
.lParam
& 0x80000000) ? 0 : LLKHF_UP
);
814 KbdHookData
.time
= Msg
.time
;
815 KbdHookData
.dwExtraInfo
= 0;
816 if (co_HOOK_CallHooks(WH_KEYBOARD_LL
, HC_ACTION
, Msg
.message
, (LPARAM
) &KbdHookData
))
818 DPRINT("Kbd msg %d wParam %d lParam 0x%08x dropped by WH_KEYBOARD_LL hook\n",
819 Msg
.message
, Msg
.wParam
, Msg
.lParam
);
820 if (Entered
) UserLeave();
824 if (FocusMessageQueue
== NULL
)
826 DPRINT("No focus message queue\n");
827 if (Entered
) UserLeave();
831 if (FocusMessageQueue
->FocusWindow
!= (HWND
)0)
833 Msg
.hwnd
= FocusMessageQueue
->FocusWindow
;
834 DPRINT("Msg.hwnd = %x\n", Msg
.hwnd
);
836 FocusMessageQueue
->Desktop
->pDeskInfo
->LastInputWasKbd
= TRUE
;
838 Msg
.pt
= gpsi
->ptCursor
;
839 MsqPostMessage(FocusMessageQueue
, &Msg
, FALSE
, QS_KEY
);
843 DPRINT("Invalid focus window handle\n");
846 if (Entered
) UserLeave();
851 MsqPostHotKeyMessage(PVOID Thread
, HWND hWnd
, WPARAM wParam
, LPARAM lParam
)
853 PWINDOW_OBJECT Window
;
854 PTHREADINFO Win32Thread
;
856 LARGE_INTEGER LargeTickCount
;
859 Status
= ObReferenceObjectByPointer (Thread
,
863 if (!NT_SUCCESS(Status
))
866 Win32Thread
= ((PETHREAD
)Thread
)->Tcb
.Win32Thread
;
867 if (Win32Thread
== NULL
|| Win32Thread
->MessageQueue
== NULL
)
869 ObDereferenceObject ((PETHREAD
)Thread
);
873 Window
= IntGetWindowObject(hWnd
);
876 ObDereferenceObject ((PETHREAD
)Thread
);
881 Mesg
.message
= WM_HOTKEY
;
882 Mesg
.wParam
= wParam
;
883 Mesg
.lParam
= lParam
;
884 KeQueryTickCount(&LargeTickCount
);
885 Mesg
.time
= MsqCalculateMessageTime(&LargeTickCount
);
886 Mesg
.pt
= gpsi
->ptCursor
;
887 MsqPostMessage(Window
->MessageQueue
, &Mesg
, FALSE
, QS_HOTKEY
);
888 UserDereferenceObject(Window
);
889 ObDereferenceObject (Thread
);
891 // InsertHeadList(&pThread->MessageQueue->PostedMessagesListHead,
892 // &Message->ListEntry);
893 // KeSetEvent(pThread->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
896 PUSER_MESSAGE FASTCALL
897 MsqCreateMessage(LPMSG Msg
, BOOLEAN FreeLParam
)
899 PUSER_MESSAGE Message
;
901 Message
= ExAllocateFromPagedLookasideList(&MessageLookasideList
);
907 Message
->FreeLParam
= FreeLParam
;
908 RtlMoveMemory(&Message
->Msg
, Msg
, sizeof(MSG
));
914 MsqDestroyMessage(PUSER_MESSAGE Message
)
916 ExFreeToPagedLookasideList(&MessageLookasideList
, Message
);
920 co_MsqDispatchSentNotifyMessages(PUSER_MESSAGE_QUEUE MessageQueue
)
922 PLIST_ENTRY ListEntry
;
923 PUSER_SENT_MESSAGE_NOTIFY Message
;
925 while (!IsListEmpty(&MessageQueue
->SentMessagesListHead
))
927 ListEntry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
928 Message
= CONTAINING_RECORD(ListEntry
, USER_SENT_MESSAGE_NOTIFY
,
931 co_IntCallSentMessageCallback(Message
->CompletionCallback
,
934 Message
->CompletionCallbackContext
,
942 MsqPeekSentMessages(PUSER_MESSAGE_QUEUE MessageQueue
)
944 return(!IsListEmpty(&MessageQueue
->SentMessagesListHead
));
948 co_MsqDispatchOneSentMessage(PUSER_MESSAGE_QUEUE MessageQueue
)
950 PUSER_SENT_MESSAGE Message
;
954 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage
;
956 if (IsListEmpty(&MessageQueue
->SentMessagesListHead
))
961 /* remove it from the list of pending messages */
962 Entry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
963 Message
= CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
);
965 /* insert it to the list of messages that are currently dispatched by this
967 InsertTailList(&MessageQueue
->LocalDispatchingMessagesHead
,
968 &Message
->ListEntry
);
970 if (Message
->HookMessage
== MSQ_ISHOOK
)
972 Result
= co_HOOK_CallHooks(Message
->Msg
.message
,
973 (INT
)(INT_PTR
)Message
->Msg
.hwnd
,
975 Message
->Msg
.lParam
);
977 else if (Message
->HookMessage
== MSQ_ISEVENT
)
979 Result
= co_EVENT_CallEvents( Message
->Msg
.message
,
982 Message
->Msg
.lParam
);
986 /* Call the window procedure. */
987 Result
= co_IntSendMessage(Message
->Msg
.hwnd
,
988 Message
->Msg
.message
,
990 Message
->Msg
.lParam
);
993 /* remove the message from the local dispatching list, because it doesn't need
994 to be cleaned up on thread termination anymore */
995 RemoveEntryList(&Message
->ListEntry
);
997 /* remove the message from the dispatching list, so lock the sender's message queue */
998 SenderReturned
= (Message
->DispatchingListEntry
.Flink
== NULL
);
1001 /* only remove it from the dispatching list if not already removed by a timeout */
1002 RemoveEntryList(&Message
->DispatchingListEntry
);
1004 /* still keep the sender's message queue locked, so the sender can't exit the
1005 MsqSendMessage() function (if timed out) */
1007 /* Let the sender know the result. */
1008 if (Message
->Result
!= NULL
)
1010 *Message
->Result
= Result
;
1013 /* Notify the sender. */
1014 if (Message
->CompletionEvent
!= NULL
)
1016 KeSetEvent(Message
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1019 /* Notify the sender if they specified a callback. */
1020 if (!SenderReturned
&& Message
->CompletionCallback
!= NULL
)
1022 if(!(NotifyMessage
= ExAllocatePoolWithTag(NonPagedPool
,
1023 sizeof(USER_SENT_MESSAGE_NOTIFY
), TAG_USRMSG
)))
1025 DPRINT1("MsqDispatchOneSentMessage(): Not enough memory to create a callback notify message\n");
1028 NotifyMessage
->CompletionCallback
=
1029 Message
->CompletionCallback
;
1030 NotifyMessage
->CompletionCallbackContext
=
1031 Message
->CompletionCallbackContext
;
1032 NotifyMessage
->Result
= Result
;
1033 NotifyMessage
->hWnd
= Message
->Msg
.hwnd
;
1034 NotifyMessage
->Msg
= Message
->Msg
.message
;
1035 MsqSendNotifyMessage(Message
->SenderQueue
, NotifyMessage
);
1040 /* dereference both sender and our queue */
1041 IntDereferenceMessageQueue(MessageQueue
);
1042 IntDereferenceMessageQueue(Message
->SenderQueue
);
1044 /* free the message */
1045 ExFreePool(Message
);
1050 MsqRemoveWindowMessagesFromQueue(PVOID pWindow
)
1052 PUSER_SENT_MESSAGE SentMessage
;
1053 PUSER_MESSAGE PostedMessage
;
1054 PUSER_MESSAGE_QUEUE MessageQueue
;
1055 PLIST_ENTRY CurrentEntry
, ListHead
;
1056 PWINDOW_OBJECT Window
= pWindow
;
1060 MessageQueue
= Window
->MessageQueue
;
1061 ASSERT(MessageQueue
);
1063 /* remove the posted messages for this window */
1064 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
1065 ListHead
= &MessageQueue
->PostedMessagesListHead
;
1066 while (CurrentEntry
!= ListHead
)
1068 PostedMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1070 if (PostedMessage
->Msg
.hwnd
== Window
->hSelf
)
1072 RemoveEntryList(&PostedMessage
->ListEntry
);
1073 MsqDestroyMessage(PostedMessage
);
1074 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
1078 CurrentEntry
= CurrentEntry
->Flink
;
1082 /* remove the sent messages for this window */
1083 CurrentEntry
= MessageQueue
->SentMessagesListHead
.Flink
;
1084 ListHead
= &MessageQueue
->SentMessagesListHead
;
1085 while (CurrentEntry
!= ListHead
)
1087 SentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1089 if(SentMessage
->Msg
.hwnd
== Window
->hSelf
)
1091 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1093 RemoveEntryList(&SentMessage
->ListEntry
);
1095 /* remove the message from the dispatching list */
1096 if(SentMessage
->DispatchingListEntry
.Flink
!= NULL
)
1098 RemoveEntryList(&SentMessage
->DispatchingListEntry
);
1101 /* wake the sender's thread */
1102 if (SentMessage
->CompletionEvent
!= NULL
)
1104 KeSetEvent(SentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1107 /* dereference our and the sender's message queue */
1108 IntDereferenceMessageQueue(MessageQueue
);
1109 IntDereferenceMessageQueue(SentMessage
->SenderQueue
);
1111 /* free the message */
1112 ExFreePool(SentMessage
);
1114 CurrentEntry
= MessageQueue
->SentMessagesListHead
.Flink
;
1118 CurrentEntry
= CurrentEntry
->Flink
;
1124 MsqSendNotifyMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
1125 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage
)
1127 InsertTailList(&MessageQueue
->NotifyMessagesListHead
,
1128 &NotifyMessage
->ListEntry
);
1129 MessageQueue
->QueueBits
|= QS_SENDMESSAGE
;
1130 MessageQueue
->ChangedBits
|= QS_SENDMESSAGE
;
1131 if (MessageQueue
->WakeMask
& QS_SENDMESSAGE
)
1132 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1136 co_MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
1137 HWND Wnd
, UINT Msg
, WPARAM wParam
, LPARAM lParam
,
1138 UINT uTimeout
, BOOL Block
, INT HookMessage
,
1142 PUSER_SENT_MESSAGE Message
;
1143 KEVENT CompletionEvent
;
1144 NTSTATUS WaitStatus
;
1146 PUSER_MESSAGE_QUEUE ThreadQueue
;
1147 LARGE_INTEGER Timeout
;
1150 if(!(Message
= ExAllocatePoolWithTag(PagedPool
, sizeof(USER_SENT_MESSAGE
), TAG_USRMSG
)))
1152 DPRINT1("MsqSendMessage(): Not enough memory to allocate a message");
1153 return STATUS_INSUFFICIENT_RESOURCES
;
1156 KeInitializeEvent(&CompletionEvent
, NotificationEvent
, FALSE
);
1158 pti
= PsGetCurrentThreadWin32Thread();
1159 ThreadQueue
= pti
->MessageQueue
;
1160 ASSERT(ThreadQueue
!= MessageQueue
);
1162 Timeout
.QuadPart
= (LONGLONG
) uTimeout
* (LONGLONG
) -10000;
1164 /* FIXME - increase reference counter of sender's message queue here */
1167 Message
->Msg
.hwnd
= Wnd
;
1168 Message
->Msg
.message
= Msg
;
1169 Message
->Msg
.wParam
= wParam
;
1170 Message
->Msg
.lParam
= lParam
;
1171 Message
->CompletionEvent
= &CompletionEvent
;
1172 Message
->Result
= &Result
;
1173 Message
->SenderQueue
= ThreadQueue
;
1174 IntReferenceMessageQueue(ThreadQueue
);
1175 Message
->CompletionCallback
= NULL
;
1176 Message
->HookMessage
= HookMessage
;
1178 IntReferenceMessageQueue(MessageQueue
);
1180 /* add it to the list of pending messages */
1181 InsertTailList(&ThreadQueue
->DispatchingMessagesHead
, &Message
->DispatchingListEntry
);
1183 /* queue it in the destination's message queue */
1184 InsertTailList(&MessageQueue
->SentMessagesListHead
, &Message
->ListEntry
);
1186 MessageQueue
->QueueBits
|= QS_SENDMESSAGE
;
1187 MessageQueue
->ChangedBits
|= QS_SENDMESSAGE
;
1188 if (MessageQueue
->WakeMask
& QS_SENDMESSAGE
)
1189 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1191 /* we can't access the Message anymore since it could have already been deleted! */
1199 /* don't process messages sent to the thread */
1200 WaitStatus
= KeWaitForSingleObject(&CompletionEvent
, UserRequest
, UserMode
,
1201 FALSE
, (uTimeout
? &Timeout
: NULL
));
1205 if(WaitStatus
== STATUS_TIMEOUT
)
1207 /* look up if the message has not yet dispatched, if so
1208 make sure it can't pass a result and it must not set the completion event anymore */
1209 Entry
= MessageQueue
->SentMessagesListHead
.Flink
;
1210 while (Entry
!= &MessageQueue
->SentMessagesListHead
)
1212 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
)
1215 /* we can access Message here, it's secure because the message queue is locked
1216 and the message is still hasn't been dispatched */
1217 Message
->CompletionEvent
= NULL
;
1218 Message
->Result
= NULL
;
1221 Entry
= Entry
->Flink
;
1224 /* remove from the local dispatching list so the other thread knows,
1225 it can't pass a result and it must not set the completion event anymore */
1226 Entry
= ThreadQueue
->DispatchingMessagesHead
.Flink
;
1227 while (Entry
!= &ThreadQueue
->DispatchingMessagesHead
)
1229 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, DispatchingListEntry
)
1232 /* we can access Message here, it's secure because the sender's message is locked
1233 and the message has definitely not yet been destroyed, otherwise it would
1234 have been removed from this list by the dispatching routine right after
1235 dispatching the message */
1236 Message
->CompletionEvent
= NULL
;
1237 Message
->Result
= NULL
;
1238 RemoveEntryList(&Message
->DispatchingListEntry
);
1239 Message
->DispatchingListEntry
.Flink
= NULL
;
1242 Entry
= Entry
->Flink
;
1245 DPRINT("MsqSendMessage (blocked) timed out\n");
1247 while (co_MsqDispatchOneSentMessage(ThreadQueue
))
1252 PVOID WaitObjects
[2];
1254 WaitObjects
[0] = &CompletionEvent
;
1255 WaitObjects
[1] = ThreadQueue
->NewMessages
;
1262 WaitStatus
= KeWaitForMultipleObjects(2, WaitObjects
, WaitAny
, UserRequest
,
1263 UserMode
, FALSE
, (uTimeout
? &Timeout
: NULL
), NULL
);
1267 if(WaitStatus
== STATUS_TIMEOUT
)
1269 /* look up if the message has not yet been dispatched, if so
1270 make sure it can't pass a result and it must not set the completion event anymore */
1271 Entry
= MessageQueue
->SentMessagesListHead
.Flink
;
1272 while (Entry
!= &MessageQueue
->SentMessagesListHead
)
1274 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
)
1277 /* we can access Message here, it's secure because the message queue is locked
1278 and the message is still hasn't been dispatched */
1279 Message
->CompletionEvent
= NULL
;
1280 Message
->Result
= NULL
;
1283 Entry
= Entry
->Flink
;
1286 /* remove from the local dispatching list so the other thread knows,
1287 it can't pass a result and it must not set the completion event anymore */
1288 Entry
= ThreadQueue
->DispatchingMessagesHead
.Flink
;
1289 while (Entry
!= &ThreadQueue
->DispatchingMessagesHead
)
1291 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, DispatchingListEntry
)
1294 /* we can access Message here, it's secure because the sender's message is locked
1295 and the message has definitely not yet been destroyed, otherwise it would
1296 have been removed from this list by the dispatching routine right after
1297 dispatching the message */
1298 Message
->CompletionEvent
= NULL
;
1299 Message
->Result
= NULL
;
1300 RemoveEntryList(&Message
->DispatchingListEntry
);
1301 Message
->DispatchingListEntry
.Flink
= NULL
;
1304 Entry
= Entry
->Flink
;
1307 DPRINT("MsqSendMessage timed out\n");
1310 while (co_MsqDispatchOneSentMessage(ThreadQueue
))
1313 while (NT_SUCCESS(WaitStatus
) && STATUS_WAIT_0
!= WaitStatus
);
1316 if(WaitStatus
!= STATUS_TIMEOUT
)
1317 *uResult
= (STATUS_WAIT_0
== WaitStatus
? Result
: -1);
1323 MsqPostMessage(PUSER_MESSAGE_QUEUE MessageQueue
, MSG
* Msg
, BOOLEAN FreeLParam
,
1326 PUSER_MESSAGE Message
;
1328 if(!(Message
= MsqCreateMessage(Msg
, FreeLParam
)))
1332 InsertTailList(&MessageQueue
->PostedMessagesListHead
,
1333 &Message
->ListEntry
);
1334 MessageQueue
->QueueBits
|= MessageBits
;
1335 MessageQueue
->ChangedBits
|= MessageBits
;
1336 if (MessageQueue
->WakeMask
& MessageBits
)
1337 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1341 MsqPostQuitMessage(PUSER_MESSAGE_QUEUE MessageQueue
, ULONG ExitCode
)
1343 MessageQueue
->QuitPosted
= TRUE
;
1344 MessageQueue
->QuitExitCode
= ExitCode
;
1345 MessageQueue
->QueueBits
|= QS_POSTMESSAGE
;
1346 MessageQueue
->ChangedBits
|= QS_POSTMESSAGE
;
1347 if (MessageQueue
->WakeMask
& QS_POSTMESSAGE
)
1348 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1352 co_MsqFindMessage(IN PUSER_MESSAGE_QUEUE MessageQueue
,
1353 IN BOOLEAN Hardware
,
1355 IN PWINDOW_OBJECT Window
,
1356 IN UINT MsgFilterLow
,
1357 IN UINT MsgFilterHigh
,
1358 OUT PUSER_MESSAGE
* Message
)
1360 PLIST_ENTRY CurrentEntry
;
1361 PUSER_MESSAGE CurrentMessage
;
1362 PLIST_ENTRY ListHead
;
1366 return(co_MsqPeekHardwareMessage(MessageQueue
, Window
,
1367 MsgFilterLow
, MsgFilterHigh
,
1371 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
1372 ListHead
= &MessageQueue
->PostedMessagesListHead
;
1373 while (CurrentEntry
!= ListHead
)
1375 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1377 if ((!Window
|| (int)Window
== 1 || Window
->hSelf
== CurrentMessage
->Msg
.hwnd
) &&
1378 ((MsgFilterLow
== 0 && MsgFilterHigh
== 0) ||
1379 (MsgFilterLow
<= CurrentMessage
->Msg
.message
&&
1380 MsgFilterHigh
>= CurrentMessage
->Msg
.message
)))
1384 RemoveEntryList(&CurrentMessage
->ListEntry
);
1387 *Message
= CurrentMessage
;
1390 CurrentEntry
= CurrentEntry
->Flink
;
1397 co_MsqWaitForNewMessages(PUSER_MESSAGE_QUEUE MessageQueue
, PWINDOW_OBJECT WndFilter
,
1398 UINT MsgFilterMin
, UINT MsgFilterMax
)
1400 PVOID WaitObjects
[2] = {MessageQueue
->NewMessages
, &HardwareMessageEvent
};
1401 LARGE_INTEGER TimerExpiry
;
1402 PLARGE_INTEGER Timeout
;
1405 if (MsqGetFirstTimerExpiry(MessageQueue
, WndFilter
, MsgFilterMin
, MsgFilterMax
, &TimerExpiry
))
1407 Timeout
= &TimerExpiry
;
1414 IdlePing(); // Going to wait so send Idle ping.
1418 ret
= KeWaitForMultipleObjects(2,
1433 MsqIsHung(PUSER_MESSAGE_QUEUE MessageQueue
)
1435 LARGE_INTEGER LargeTickCount
;
1437 KeQueryTickCount(&LargeTickCount
);
1438 return ((LargeTickCount
.u
.LowPart
- MessageQueue
->LastMsgRead
) > MSQ_HUNG
);
1442 MsqInitializeMessageQueue(struct _ETHREAD
*Thread
, PUSER_MESSAGE_QUEUE MessageQueue
)
1444 LARGE_INTEGER LargeTickCount
;
1447 MessageQueue
->Thread
= Thread
;
1448 MessageQueue
->CaretInfo
= (PTHRDCARETINFO
)(MessageQueue
+ 1);
1449 InitializeListHead(&MessageQueue
->PostedMessagesListHead
);
1450 InitializeListHead(&MessageQueue
->SentMessagesListHead
);
1451 InitializeListHead(&MessageQueue
->HardwareMessagesListHead
);
1452 InitializeListHead(&MessageQueue
->TimerListHead
);
1453 InitializeListHead(&MessageQueue
->DispatchingMessagesHead
);
1454 InitializeListHead(&MessageQueue
->LocalDispatchingMessagesHead
);
1455 KeInitializeMutex(&MessageQueue
->HardwareLock
, 0);
1456 MessageQueue
->QuitPosted
= FALSE
;
1457 MessageQueue
->QuitExitCode
= 0;
1458 KeQueryTickCount(&LargeTickCount
);
1459 MessageQueue
->LastMsgRead
= LargeTickCount
.u
.LowPart
;
1460 MessageQueue
->FocusWindow
= NULL
;
1461 MessageQueue
->PaintCount
= 0;
1462 // HACK!!!!!!! Newbies that wrote this should hold your head down in shame! (jt)
1463 MessageQueue
->WakeMask
= ~0;
1464 MessageQueue
->NewMessagesHandle
= NULL
;
1466 Status
= ZwCreateEvent(&MessageQueue
->NewMessagesHandle
, EVENT_ALL_ACCESS
,
1467 NULL
, SynchronizationEvent
, FALSE
);
1468 if (!NT_SUCCESS(Status
))
1473 Status
= ObReferenceObjectByHandle(MessageQueue
->NewMessagesHandle
, 0,
1474 ExEventObjectType
, KernelMode
,
1475 (PVOID
*)&MessageQueue
->NewMessages
, NULL
);
1476 if (!NT_SUCCESS(Status
))
1478 ZwClose(MessageQueue
->NewMessagesHandle
);
1479 MessageQueue
->NewMessagesHandle
= NULL
;
1487 MsqCleanupMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue
)
1489 PLIST_ENTRY CurrentEntry
;
1490 PUSER_MESSAGE CurrentMessage
;
1491 PTIMER_ENTRY CurrentTimer
;
1492 PUSER_SENT_MESSAGE CurrentSentMessage
;
1494 /* cleanup posted messages */
1495 while (!IsListEmpty(&MessageQueue
->PostedMessagesListHead
))
1497 CurrentEntry
= RemoveHeadList(&MessageQueue
->PostedMessagesListHead
);
1498 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1500 MsqDestroyMessage(CurrentMessage
);
1503 /* remove the messages that have not yet been dispatched */
1504 while (!IsListEmpty(&MessageQueue
->SentMessagesListHead
))
1506 CurrentEntry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
1507 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1510 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1512 /* remove the message from the dispatching list */
1513 if(CurrentSentMessage
->DispatchingListEntry
.Flink
!= NULL
)
1515 RemoveEntryList(&CurrentSentMessage
->DispatchingListEntry
);
1518 /* wake the sender's thread */
1519 if (CurrentSentMessage
->CompletionEvent
!= NULL
)
1521 KeSetEvent(CurrentSentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1524 /* dereference our and the sender's message queue */
1525 IntDereferenceMessageQueue(MessageQueue
);
1526 IntDereferenceMessageQueue(CurrentSentMessage
->SenderQueue
);
1528 /* free the message */
1529 ExFreePool(CurrentSentMessage
);
1532 /* cleanup timers */
1533 while (! IsListEmpty(&MessageQueue
->TimerListHead
))
1535 CurrentEntry
= RemoveHeadList(&MessageQueue
->TimerListHead
);
1536 CurrentTimer
= CONTAINING_RECORD(CurrentEntry
, TIMER_ENTRY
, ListEntry
);
1537 ExFreeToPagedLookasideList(&TimerLookasideList
, CurrentTimer
);
1540 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
1541 ExitThread() was called in a SendMessage() umode callback */
1542 while (!IsListEmpty(&MessageQueue
->LocalDispatchingMessagesHead
))
1544 CurrentEntry
= RemoveHeadList(&MessageQueue
->LocalDispatchingMessagesHead
);
1545 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1548 /* remove the message from the dispatching list */
1549 if(CurrentSentMessage
->DispatchingListEntry
.Flink
!= NULL
)
1551 RemoveEntryList(&CurrentSentMessage
->DispatchingListEntry
);
1554 DPRINT("Notify the sender, the thread has been terminated while dispatching a message!\n");
1556 /* wake the sender's thread */
1557 if (CurrentSentMessage
->CompletionEvent
!= NULL
)
1559 KeSetEvent(CurrentSentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1562 /* dereference our and the sender's message queue */
1563 IntDereferenceMessageQueue(MessageQueue
);
1564 IntDereferenceMessageQueue(CurrentSentMessage
->SenderQueue
);
1566 /* free the message */
1567 ExFreePool(CurrentSentMessage
);
1570 /* tell other threads not to bother returning any info to us */
1571 while (! IsListEmpty(&MessageQueue
->DispatchingMessagesHead
))
1573 CurrentEntry
= RemoveHeadList(&MessageQueue
->DispatchingMessagesHead
);
1574 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1575 DispatchingListEntry
);
1576 CurrentSentMessage
->CompletionEvent
= NULL
;
1577 CurrentSentMessage
->Result
= NULL
;
1579 /* do NOT dereference our message queue as it might get attempted to be
1585 PUSER_MESSAGE_QUEUE FASTCALL
1586 MsqCreateMessageQueue(struct _ETHREAD
*Thread
)
1588 PUSER_MESSAGE_QUEUE MessageQueue
;
1590 MessageQueue
= (PUSER_MESSAGE_QUEUE
)ExAllocatePoolWithTag(NonPagedPool
,
1591 sizeof(USER_MESSAGE_QUEUE
) + sizeof(THRDCARETINFO
),
1599 RtlZeroMemory(MessageQueue
, sizeof(USER_MESSAGE_QUEUE
) + sizeof(THRDCARETINFO
));
1600 /* hold at least one reference until it'll be destroyed */
1601 IntReferenceMessageQueue(MessageQueue
);
1602 /* initialize the queue */
1603 if (!MsqInitializeMessageQueue(Thread
, MessageQueue
))
1605 IntDereferenceMessageQueue(MessageQueue
);
1609 return MessageQueue
;
1613 MsqDestroyMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue
)
1617 /* remove the message queue from any desktops */
1618 if ((desk
= InterlockedExchangePointer((PVOID
*)&MessageQueue
->Desktop
, 0)))
1620 (void)InterlockedExchangePointer((PVOID
*)&desk
->ActiveMessageQueue
, 0);
1621 IntDereferenceMessageQueue(MessageQueue
);
1625 MsqCleanupMessageQueue(MessageQueue
);
1627 /* decrease the reference counter, if it hits zero, the queue will be freed */
1628 IntDereferenceMessageQueue(MessageQueue
);
1632 MsqGetHooks(PUSER_MESSAGE_QUEUE Queue
)
1634 return Queue
->Hooks
;
1638 MsqSetHooks(PUSER_MESSAGE_QUEUE Queue
, PHOOKTABLE Hooks
)
1640 Queue
->Hooks
= Hooks
;
1644 MsqSetMessageExtraInfo(LPARAM lParam
)
1648 PUSER_MESSAGE_QUEUE MessageQueue
;
1650 pti
= PsGetCurrentThreadWin32Thread();
1651 MessageQueue
= pti
->MessageQueue
;
1657 Ret
= MessageQueue
->ExtraInfo
;
1658 MessageQueue
->ExtraInfo
= lParam
;
1664 MsqGetMessageExtraInfo(VOID
)
1667 PUSER_MESSAGE_QUEUE MessageQueue
;
1669 pti
= PsGetCurrentThreadWin32Thread();
1670 MessageQueue
= pti
->MessageQueue
;
1676 return MessageQueue
->ExtraInfo
;
1680 MsqSetStateWindow(PUSER_MESSAGE_QUEUE MessageQueue
, ULONG Type
, HWND hWnd
)
1686 case MSQ_STATE_CAPTURE
:
1687 Prev
= MessageQueue
->CaptureWindow
;
1688 MessageQueue
->CaptureWindow
= hWnd
;
1690 case MSQ_STATE_ACTIVE
:
1691 Prev
= MessageQueue
->ActiveWindow
;
1692 MessageQueue
->ActiveWindow
= hWnd
;
1694 case MSQ_STATE_FOCUS
:
1695 Prev
= MessageQueue
->FocusWindow
;
1696 MessageQueue
->FocusWindow
= hWnd
;
1698 case MSQ_STATE_MENUOWNER
:
1699 Prev
= MessageQueue
->MenuOwner
;
1700 MessageQueue
->MenuOwner
= hWnd
;
1702 case MSQ_STATE_MOVESIZE
:
1703 Prev
= MessageQueue
->MoveSize
;
1704 MessageQueue
->MoveSize
= hWnd
;
1706 case MSQ_STATE_CARET
:
1707 ASSERT(MessageQueue
->CaretInfo
);
1708 Prev
= MessageQueue
->CaretInfo
->hWnd
;
1709 MessageQueue
->CaretInfo
->hWnd
= hWnd
;
1717 static VOID FASTCALL
1718 DumpTimerList(PUSER_MESSAGE_QUEUE MessageQueue
)
1720 PLIST_ENTRY Current
;
1723 Current
= MessageQueue
->TimerListHead
.Flink
;
1724 if (Current
== &MessageQueue
->TimerListHead
)
1726 DPRINT("timer list is empty for queue %p\n", MessageQueue
);
1728 while (Current
!= &MessageQueue
->TimerListHead
)
1730 Timer
= CONTAINING_RECORD(Current
, TIMER_ENTRY
, ListEntry
);
1731 DPRINT("queue %p timer %p expiry %I64d wnd %x id %p period %u timerproc %p msg %u\n",
1732 MessageQueue
, Timer
, Timer
->ExpiryTime
.QuadPart
, Timer
->Wnd
, Timer
->IDEvent
,
1733 Timer
->Period
, Timer
->TimerFunc
, Timer
->Msg
);
1734 Current
= Current
->Flink
;
1737 #endif /* ! defined(NDEBUG) */
1739 /* Must have the message queue locked while calling this */
1740 static VOID FASTCALL
1741 InsertTimer(PUSER_MESSAGE_QUEUE MessageQueue
, PTIMER_ENTRY NewTimer
)
1743 PLIST_ENTRY Current
;
1745 Current
= MessageQueue
->TimerListHead
.Flink
;
1746 while (Current
!= &MessageQueue
->TimerListHead
)
1748 if (NewTimer
->ExpiryTime
.QuadPart
<
1749 CONTAINING_RECORD(Current
, TIMER_ENTRY
, ListEntry
)->ExpiryTime
.QuadPart
)
1753 Current
= Current
->Flink
;
1756 InsertTailList(Current
, &NewTimer
->ListEntry
);
1759 /* Must have the message queue locked while calling this */
1760 static PTIMER_ENTRY FASTCALL
1761 RemoveTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
, UINT_PTR IDEvent
, UINT Msg
)
1764 PLIST_ENTRY EnumEntry
;
1766 /* Remove timer if already in the queue */
1767 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1768 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1770 Timer
= CONTAINING_RECORD(EnumEntry
, TIMER_ENTRY
, ListEntry
);
1771 EnumEntry
= EnumEntry
->Flink
;
1773 if (Timer
->Wnd
== Wnd
&&
1774 Timer
->IDEvent
== IDEvent
&&
1777 RemoveEntryList(&Timer
->ListEntry
);
1786 MsqSetTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
,
1787 UINT_PTR IDEvent
, UINT Period
, TIMERPROC TimerFunc
,
1791 LARGE_INTEGER CurrentTime
;
1793 DPRINT("MsqSetTimer queue %p wnd %x id %p period %u timerproc %p msg %d\n",
1794 MessageQueue
, Wnd
, IDEvent
, Period
, TimerFunc
, Msg
);
1796 Timer
= RemoveTimer(MessageQueue
, Wnd
, IDEvent
, Msg
);
1799 Timer
= ExAllocateFromPagedLookasideList(&TimerLookasideList
);
1802 DPRINT1("Failed to allocate timer entry\n");
1805 DPRINT("Allocated new timer entry %p\n", Timer
);
1807 Timer
->IDEvent
= IDEvent
;
1812 DPRINT("Updating existing timer entry %p\n", Timer
);
1815 KeQuerySystemTime(&CurrentTime
);
1816 Timer
->ExpiryTime
.QuadPart
= CurrentTime
.QuadPart
+
1817 (ULONGLONG
) Period
* (ULONGLONG
) 10000;
1818 Timer
->Period
= Period
;
1819 Timer
->TimerFunc
= TimerFunc
;
1820 DPRINT("Insert timer now %I64d expiry %I64d\n", CurrentTime
.QuadPart
,
1821 Timer
->ExpiryTime
.QuadPart
);
1823 InsertTimer(MessageQueue
, Timer
);
1827 DumpTimerList(MessageQueue
);
1828 #endif /* ! defined(NDEBUG) */
1834 MsqKillTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
,
1835 UINT_PTR IDEvent
, UINT Msg
)
1839 DPRINT("MsqKillTimer queue %p wnd %x id %p msg %d\n",
1840 MessageQueue
, Wnd
, IDEvent
, Msg
);
1842 Timer
= RemoveTimer(MessageQueue
, Wnd
, IDEvent
, Msg
);
1846 DPRINT("Failed to remove timer from list, not found\n");
1850 ExFreeToPagedLookasideList(&TimerLookasideList
, Timer
);
1854 DumpTimerList(MessageQueue
);
1855 #endif /* ! defined(NDEBUG) */
1857 return NULL
!= Timer
;
1861 MsqGetTimerMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
1862 PWINDOW_OBJECT WindowFilter
, UINT MsgFilterMin
, UINT MsgFilterMax
,
1863 MSG
*Msg
, BOOLEAN Restart
)
1866 LARGE_INTEGER CurrentTime
;
1867 LARGE_INTEGER LargeTickCount
;
1868 PLIST_ENTRY EnumEntry
;
1871 DPRINT("MsqGetTimerMessage queue %p msg %p restart %s\n",
1872 MessageQueue
, Msg
, Restart
? "TRUE" : "FALSE");
1874 KeQuerySystemTime(&CurrentTime
);
1875 DPRINT("Current time %I64d\n", CurrentTime
.QuadPart
);
1876 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1878 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1880 Timer
= CONTAINING_RECORD(MessageQueue
->TimerListHead
.Flink
,
1881 TIMER_ENTRY
, ListEntry
);
1882 DPRINT("Checking timer %p wnd %x expiry %I64d\n", Timer
, Timer
->Wnd
,
1883 Timer
->ExpiryTime
.QuadPart
);
1884 EnumEntry
= EnumEntry
->Flink
;
1885 if ((NULL
== WindowFilter
|| Timer
->Wnd
== WindowFilter
->hSelf
) &&
1886 ((MsgFilterMin
== 0 && MsgFilterMax
== 0) ||
1887 (MsgFilterMin
<= Timer
->Msg
&&
1888 Timer
->Msg
<= MsgFilterMax
)))
1890 if (Timer
->ExpiryTime
.QuadPart
<= CurrentTime
.QuadPart
)
1892 DPRINT("Timer is expired\n");
1898 DPRINT("No need to check later timers\n");
1904 DPRINT("timer %p (wnd %x msg %d) failed filter wnd %x msgmin %d msgmax %d\n",
1905 Timer
, Timer
->Wnd
, Timer
->Msg
, WindowFilter
->hSelf
, MsgFilterMin
, MsgFilterMax
);
1911 DPRINT("No timer pending\n");
1915 Msg
->hwnd
= Timer
->Wnd
;
1916 Msg
->message
= Timer
->Msg
;
1917 Msg
->wParam
= (WPARAM
) Timer
->IDEvent
;
1918 Msg
->lParam
= (LPARAM
) Timer
->TimerFunc
;
1919 KeQueryTickCount(&LargeTickCount
);
1920 Msg
->time
= MsqCalculateMessageTime(&LargeTickCount
);
1921 Msg
->pt
= gpsi
->ptCursor
;
1925 RemoveEntryList(&Timer
->ListEntry
);
1926 Timer
->ExpiryTime
.QuadPart
= CurrentTime
.QuadPart
+
1927 (ULONGLONG
) Timer
->Period
* (ULONGLONG
) 10000;
1928 DPRINT("Restarting timer %p expires %I64d\n", Timer
, Timer
->ExpiryTime
.QuadPart
);
1929 InsertTimer(MessageQueue
, Timer
);
1933 DumpTimerList(MessageQueue
);
1934 #endif /* ! defined(NDEBUG) */
1938 DPRINT("Created message wnd %x msg %d wParam %u lParam %u\n", Msg
->hwnd
, Msg
->message
,
1939 Msg
->wParam
, Msg
->lParam
);
1945 MsqRemoveTimersWindow(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
)
1948 PLIST_ENTRY EnumEntry
;
1950 DPRINT("MsqRemoveTimersWindow queue %p wnd %x\n", MessageQueue
, Wnd
);
1952 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1953 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1955 Timer
= CONTAINING_RECORD(EnumEntry
, TIMER_ENTRY
, ListEntry
);
1956 EnumEntry
= EnumEntry
->Flink
;
1957 if (Timer
->Wnd
== Wnd
)
1959 DPRINT("Removing timer %p because its window is going away\n", Timer
);
1960 RemoveEntryList(&Timer
->ListEntry
);
1961 ExFreeToPagedLookasideList(&TimerLookasideList
, Timer
);
1966 DumpTimerList(MessageQueue
);
1967 #endif /* ! defined(NDEBUG) */
1972 MsqGetFirstTimerExpiry(PUSER_MESSAGE_QUEUE MessageQueue
,
1973 PWINDOW_OBJECT WndFilter
, UINT MsgFilterMin
, UINT MsgFilterMax
,
1974 PLARGE_INTEGER FirstTimerExpiry
)
1977 PLIST_ENTRY EnumEntry
;
1979 DPRINT("MsqGetFirstTimerExpiry queue %p wndfilter %x msgfiltermin %d msgfiltermax %d expiry %p\n",
1980 MessageQueue
, WndFilter
, MsgFilterMin
, MsgFilterMax
, FirstTimerExpiry
);
1982 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1983 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1985 Timer
= CONTAINING_RECORD(MessageQueue
->TimerListHead
.Flink
,
1986 TIMER_ENTRY
, ListEntry
);
1987 EnumEntry
= EnumEntry
->Flink
;
1988 if ((NULL
== WndFilter
|| (int)WndFilter
== 1 || Timer
->Wnd
== WndFilter
->hSelf
) &&
1989 ((MsgFilterMin
== 0 && MsgFilterMax
== 0) ||
1990 (MsgFilterMin
<= Timer
->Msg
&&
1991 Timer
->Msg
<= MsgFilterMax
)))
1993 *FirstTimerExpiry
= Timer
->ExpiryTime
;
1994 DPRINT("First timer expires %I64d\n", Timer
->ExpiryTime
);