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
->rpdesk
== 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
->rpdesk
== 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
->pti
->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
->pti
->MessageQueue
);
422 InsertTailList(&CaptureWindow
->pti
->MessageQueue
->HardwareMessagesListHead
,
423 &Message
->ListEntry
);
424 if(Message
->Msg
.message
== WM_MOUSEMOVE
)
426 if(CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
)
428 /* remove the old WM_MOUSEMOVE message, we're processing a more recent
430 RemoveEntryList(&CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
->ListEntry
);
431 ExFreePool(CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
);
433 /* save the pointer to the WM_MOUSEMOVE message in the new queue */
434 CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
= Message
;
436 CaptureWindow
->pti
->MessageQueue
->QueueBits
|= QS_MOUSEMOVE
;
437 CaptureWindow
->pti
->MessageQueue
->ChangedBits
|= QS_MOUSEMOVE
;
438 if (CaptureWindow
->pti
->MessageQueue
->WakeMask
& QS_MOUSEMOVE
)
439 KeSetEvent(CaptureWindow
->pti
->MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
443 CaptureWindow
->pti
->MessageQueue
->QueueBits
|= QS_MOUSEBUTTON
;
444 CaptureWindow
->pti
->MessageQueue
->ChangedBits
|= QS_MOUSEBUTTON
;
445 if (CaptureWindow
->pti
->MessageQueue
->WakeMask
& QS_MOUSEBUTTON
)
446 KeSetEvent(CaptureWindow
->pti
->MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
448 IntUnLockHardwareMessageQueue(CaptureWindow
->pti
->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
&& PtrToInt(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
->pti
->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
->pti
->MessageQueue
->HardwareMessagesListHead
,
473 &Message
->ListEntry
);
476 if (Message
->Msg
.message
== WM_MOUSEMOVE
)
478 if(CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
&&
479 (CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
!= Message
))
481 /* delete the old message */
482 RemoveEntryList(&CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
->ListEntry
);
483 ExFreePool(CaptureWindow
->pti
->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
->pti
->MessageQueue
->MouseMoveMsg
= Message
;
496 IntUnLockHardwareMessageQueue(CaptureWindow
->pti
->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
->pti
->MessageQueue
);
520 if(CaptureWindow
->pti
->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
->pti
->MessageQueue
->MouseMoveMsg
->ListEntry
);
525 ExFreePool(CaptureWindow
->pti
->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
->pti
->MessageQueue
->MouseMoveMsg
= NULL
;
530 IntUnLockHardwareMessageQueue(CaptureWindow
->pti
->MessageQueue
);
532 else if (CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
== Message
)
534 CaptureWindow
->pti
->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
->pti
->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
->pti
->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
,
1374 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
1375 ListHead
= &MessageQueue
->PostedMessagesListHead
;
1376 while (CurrentEntry
!= ListHead
)
1378 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1381 PtrToInt(Window
) == 1 ||
1382 Window
->hSelf
== CurrentMessage
->Msg
.hwnd
) &&
1383 ( (MsgFilterLow
== 0 && MsgFilterHigh
== 0) ||
1384 ( MsgFilterLow
<= CurrentMessage
->Msg
.message
&&
1385 MsgFilterHigh
>= CurrentMessage
->Msg
.message
) ) )
1389 RemoveEntryList(&CurrentMessage
->ListEntry
);
1392 *Message
= CurrentMessage
;
1395 CurrentEntry
= CurrentEntry
->Flink
;
1402 co_MsqWaitForNewMessages(PUSER_MESSAGE_QUEUE MessageQueue
, PWINDOW_OBJECT WndFilter
,
1403 UINT MsgFilterMin
, UINT MsgFilterMax
)
1405 PVOID WaitObjects
[2] = {MessageQueue
->NewMessages
, &HardwareMessageEvent
};
1406 LARGE_INTEGER TimerExpiry
;
1407 PLARGE_INTEGER Timeout
;
1410 if (MsqGetFirstTimerExpiry(MessageQueue
, WndFilter
, MsgFilterMin
, MsgFilterMax
, &TimerExpiry
))
1412 Timeout
= &TimerExpiry
;
1419 IdlePing(); // Going to wait so send Idle ping.
1423 ret
= KeWaitForMultipleObjects(2,
1438 MsqIsHung(PUSER_MESSAGE_QUEUE MessageQueue
)
1440 LARGE_INTEGER LargeTickCount
;
1442 KeQueryTickCount(&LargeTickCount
);
1443 return ((LargeTickCount
.u
.LowPart
- MessageQueue
->LastMsgRead
) > MSQ_HUNG
);
1447 MsqInitializeMessageQueue(struct _ETHREAD
*Thread
, PUSER_MESSAGE_QUEUE MessageQueue
)
1449 LARGE_INTEGER LargeTickCount
;
1452 MessageQueue
->Thread
= Thread
;
1453 MessageQueue
->CaretInfo
= (PTHRDCARETINFO
)(MessageQueue
+ 1);
1454 InitializeListHead(&MessageQueue
->PostedMessagesListHead
);
1455 InitializeListHead(&MessageQueue
->SentMessagesListHead
);
1456 InitializeListHead(&MessageQueue
->HardwareMessagesListHead
);
1457 InitializeListHead(&MessageQueue
->TimerListHead
);
1458 InitializeListHead(&MessageQueue
->DispatchingMessagesHead
);
1459 InitializeListHead(&MessageQueue
->LocalDispatchingMessagesHead
);
1460 KeInitializeMutex(&MessageQueue
->HardwareLock
, 0);
1461 MessageQueue
->QuitPosted
= FALSE
;
1462 MessageQueue
->QuitExitCode
= 0;
1463 KeQueryTickCount(&LargeTickCount
);
1464 MessageQueue
->LastMsgRead
= LargeTickCount
.u
.LowPart
;
1465 MessageQueue
->FocusWindow
= NULL
;
1466 MessageQueue
->PaintCount
= 0;
1467 // HACK!!!!!!! Newbies that wrote this should hold your head down in shame! (jt)
1468 MessageQueue
->WakeMask
= ~0;
1469 MessageQueue
->NewMessagesHandle
= NULL
;
1471 Status
= ZwCreateEvent(&MessageQueue
->NewMessagesHandle
, EVENT_ALL_ACCESS
,
1472 NULL
, SynchronizationEvent
, FALSE
);
1473 if (!NT_SUCCESS(Status
))
1478 Status
= ObReferenceObjectByHandle(MessageQueue
->NewMessagesHandle
, 0,
1479 ExEventObjectType
, KernelMode
,
1480 (PVOID
*)&MessageQueue
->NewMessages
, NULL
);
1481 if (!NT_SUCCESS(Status
))
1483 ZwClose(MessageQueue
->NewMessagesHandle
);
1484 MessageQueue
->NewMessagesHandle
= NULL
;
1492 MsqCleanupMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue
)
1494 PLIST_ENTRY CurrentEntry
;
1495 PUSER_MESSAGE CurrentMessage
;
1496 PTIMER_ENTRY CurrentTimer
;
1497 PUSER_SENT_MESSAGE CurrentSentMessage
;
1499 /* cleanup posted messages */
1500 while (!IsListEmpty(&MessageQueue
->PostedMessagesListHead
))
1502 CurrentEntry
= RemoveHeadList(&MessageQueue
->PostedMessagesListHead
);
1503 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1505 MsqDestroyMessage(CurrentMessage
);
1508 /* remove the messages that have not yet been dispatched */
1509 while (!IsListEmpty(&MessageQueue
->SentMessagesListHead
))
1511 CurrentEntry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
1512 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1515 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1517 /* remove the message from the dispatching list */
1518 if(CurrentSentMessage
->DispatchingListEntry
.Flink
!= NULL
)
1520 RemoveEntryList(&CurrentSentMessage
->DispatchingListEntry
);
1523 /* wake the sender's thread */
1524 if (CurrentSentMessage
->CompletionEvent
!= NULL
)
1526 KeSetEvent(CurrentSentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1529 /* dereference our and the sender's message queue */
1530 IntDereferenceMessageQueue(MessageQueue
);
1531 IntDereferenceMessageQueue(CurrentSentMessage
->SenderQueue
);
1533 /* free the message */
1534 ExFreePool(CurrentSentMessage
);
1537 /* cleanup timers */
1538 while (! IsListEmpty(&MessageQueue
->TimerListHead
))
1540 CurrentEntry
= RemoveHeadList(&MessageQueue
->TimerListHead
);
1541 CurrentTimer
= CONTAINING_RECORD(CurrentEntry
, TIMER_ENTRY
, ListEntry
);
1542 ExFreeToPagedLookasideList(&TimerLookasideList
, CurrentTimer
);
1545 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
1546 ExitThread() was called in a SendMessage() umode callback */
1547 while (!IsListEmpty(&MessageQueue
->LocalDispatchingMessagesHead
))
1549 CurrentEntry
= RemoveHeadList(&MessageQueue
->LocalDispatchingMessagesHead
);
1550 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1553 /* remove the message from the dispatching list */
1554 if(CurrentSentMessage
->DispatchingListEntry
.Flink
!= NULL
)
1556 RemoveEntryList(&CurrentSentMessage
->DispatchingListEntry
);
1559 DPRINT("Notify the sender, the thread has been terminated while dispatching a message!\n");
1561 /* wake the sender's thread */
1562 if (CurrentSentMessage
->CompletionEvent
!= NULL
)
1564 KeSetEvent(CurrentSentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1567 /* dereference our and the sender's message queue */
1568 IntDereferenceMessageQueue(MessageQueue
);
1569 IntDereferenceMessageQueue(CurrentSentMessage
->SenderQueue
);
1571 /* free the message */
1572 ExFreePool(CurrentSentMessage
);
1575 /* tell other threads not to bother returning any info to us */
1576 while (! IsListEmpty(&MessageQueue
->DispatchingMessagesHead
))
1578 CurrentEntry
= RemoveHeadList(&MessageQueue
->DispatchingMessagesHead
);
1579 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1580 DispatchingListEntry
);
1581 CurrentSentMessage
->CompletionEvent
= NULL
;
1582 CurrentSentMessage
->Result
= NULL
;
1584 /* do NOT dereference our message queue as it might get attempted to be
1590 PUSER_MESSAGE_QUEUE FASTCALL
1591 MsqCreateMessageQueue(struct _ETHREAD
*Thread
)
1593 PUSER_MESSAGE_QUEUE MessageQueue
;
1595 MessageQueue
= (PUSER_MESSAGE_QUEUE
)ExAllocatePoolWithTag(NonPagedPool
,
1596 sizeof(USER_MESSAGE_QUEUE
) + sizeof(THRDCARETINFO
),
1604 RtlZeroMemory(MessageQueue
, sizeof(USER_MESSAGE_QUEUE
) + sizeof(THRDCARETINFO
));
1605 /* hold at least one reference until it'll be destroyed */
1606 IntReferenceMessageQueue(MessageQueue
);
1607 /* initialize the queue */
1608 if (!MsqInitializeMessageQueue(Thread
, MessageQueue
))
1610 IntDereferenceMessageQueue(MessageQueue
);
1614 return MessageQueue
;
1618 MsqDestroyMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue
)
1622 /* remove the message queue from any desktops */
1623 if ((desk
= InterlockedExchangePointer((PVOID
*)&MessageQueue
->Desktop
, 0)))
1625 (void)InterlockedExchangePointer((PVOID
*)&desk
->ActiveMessageQueue
, 0);
1626 IntDereferenceMessageQueue(MessageQueue
);
1630 MsqCleanupMessageQueue(MessageQueue
);
1632 /* decrease the reference counter, if it hits zero, the queue will be freed */
1633 IntDereferenceMessageQueue(MessageQueue
);
1637 MsqGetHooks(PUSER_MESSAGE_QUEUE Queue
)
1639 return Queue
->Hooks
;
1643 MsqSetHooks(PUSER_MESSAGE_QUEUE Queue
, PHOOKTABLE Hooks
)
1645 Queue
->Hooks
= Hooks
;
1649 MsqSetMessageExtraInfo(LPARAM lParam
)
1653 PUSER_MESSAGE_QUEUE MessageQueue
;
1655 pti
= PsGetCurrentThreadWin32Thread();
1656 MessageQueue
= pti
->MessageQueue
;
1662 Ret
= MessageQueue
->ExtraInfo
;
1663 MessageQueue
->ExtraInfo
= lParam
;
1669 MsqGetMessageExtraInfo(VOID
)
1672 PUSER_MESSAGE_QUEUE MessageQueue
;
1674 pti
= PsGetCurrentThreadWin32Thread();
1675 MessageQueue
= pti
->MessageQueue
;
1681 return MessageQueue
->ExtraInfo
;
1685 MsqSetStateWindow(PUSER_MESSAGE_QUEUE MessageQueue
, ULONG Type
, HWND hWnd
)
1691 case MSQ_STATE_CAPTURE
:
1692 Prev
= MessageQueue
->CaptureWindow
;
1693 MessageQueue
->CaptureWindow
= hWnd
;
1695 case MSQ_STATE_ACTIVE
:
1696 Prev
= MessageQueue
->ActiveWindow
;
1697 MessageQueue
->ActiveWindow
= hWnd
;
1699 case MSQ_STATE_FOCUS
:
1700 Prev
= MessageQueue
->FocusWindow
;
1701 MessageQueue
->FocusWindow
= hWnd
;
1703 case MSQ_STATE_MENUOWNER
:
1704 Prev
= MessageQueue
->MenuOwner
;
1705 MessageQueue
->MenuOwner
= hWnd
;
1707 case MSQ_STATE_MOVESIZE
:
1708 Prev
= MessageQueue
->MoveSize
;
1709 MessageQueue
->MoveSize
= hWnd
;
1711 case MSQ_STATE_CARET
:
1712 ASSERT(MessageQueue
->CaretInfo
);
1713 Prev
= MessageQueue
->CaretInfo
->hWnd
;
1714 MessageQueue
->CaretInfo
->hWnd
= hWnd
;
1722 static VOID FASTCALL
1723 DumpTimerList(PUSER_MESSAGE_QUEUE MessageQueue
)
1725 PLIST_ENTRY Current
;
1728 Current
= MessageQueue
->TimerListHead
.Flink
;
1729 if (Current
== &MessageQueue
->TimerListHead
)
1731 DPRINT("timer list is empty for queue %p\n", MessageQueue
);
1733 while (Current
!= &MessageQueue
->TimerListHead
)
1735 Timer
= CONTAINING_RECORD(Current
, TIMER_ENTRY
, ListEntry
);
1736 DPRINT("queue %p timer %p expiry %I64d wnd %x id %p period %u timerproc %p msg %u\n",
1737 MessageQueue
, Timer
, Timer
->ExpiryTime
.QuadPart
, Timer
->Wnd
, Timer
->IDEvent
,
1738 Timer
->Period
, Timer
->TimerFunc
, Timer
->Msg
);
1739 Current
= Current
->Flink
;
1742 #endif /* ! defined(NDEBUG) */
1744 /* Must have the message queue locked while calling this */
1745 static VOID FASTCALL
1746 InsertTimer(PUSER_MESSAGE_QUEUE MessageQueue
, PTIMER_ENTRY NewTimer
)
1748 PLIST_ENTRY Current
;
1750 Current
= MessageQueue
->TimerListHead
.Flink
;
1751 while (Current
!= &MessageQueue
->TimerListHead
)
1753 if (NewTimer
->ExpiryTime
.QuadPart
<
1754 CONTAINING_RECORD(Current
, TIMER_ENTRY
, ListEntry
)->ExpiryTime
.QuadPart
)
1758 Current
= Current
->Flink
;
1761 InsertTailList(Current
, &NewTimer
->ListEntry
);
1764 /* Must have the message queue locked while calling this */
1765 static PTIMER_ENTRY FASTCALL
1766 RemoveTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
, UINT_PTR IDEvent
, UINT Msg
)
1769 PLIST_ENTRY EnumEntry
;
1771 /* Remove timer if already in the queue */
1772 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1773 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1775 Timer
= CONTAINING_RECORD(EnumEntry
, TIMER_ENTRY
, ListEntry
);
1776 EnumEntry
= EnumEntry
->Flink
;
1778 if (Timer
->Wnd
== Wnd
&&
1779 Timer
->IDEvent
== IDEvent
&&
1782 RemoveEntryList(&Timer
->ListEntry
);
1791 MsqSetTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
,
1792 UINT_PTR IDEvent
, UINT Period
, TIMERPROC TimerFunc
,
1796 LARGE_INTEGER CurrentTime
;
1798 DPRINT("MsqSetTimer queue %p wnd %x id %p period %u timerproc %p msg %d\n",
1799 MessageQueue
, Wnd
, IDEvent
, Period
, TimerFunc
, Msg
);
1801 Timer
= RemoveTimer(MessageQueue
, Wnd
, IDEvent
, Msg
);
1804 Timer
= ExAllocateFromPagedLookasideList(&TimerLookasideList
);
1807 DPRINT1("Failed to allocate timer entry\n");
1810 DPRINT("Allocated new timer entry %p\n", Timer
);
1812 Timer
->IDEvent
= IDEvent
;
1817 DPRINT("Updating existing timer entry %p\n", Timer
);
1820 KeQuerySystemTime(&CurrentTime
);
1821 Timer
->ExpiryTime
.QuadPart
= CurrentTime
.QuadPart
+
1822 (ULONGLONG
) Period
* (ULONGLONG
) 10000;
1823 Timer
->Period
= Period
;
1824 Timer
->TimerFunc
= TimerFunc
;
1825 DPRINT("Insert timer now %I64d expiry %I64d\n", CurrentTime
.QuadPart
,
1826 Timer
->ExpiryTime
.QuadPart
);
1828 InsertTimer(MessageQueue
, Timer
);
1832 DumpTimerList(MessageQueue
);
1833 #endif /* ! defined(NDEBUG) */
1839 MsqKillTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
,
1840 UINT_PTR IDEvent
, UINT Msg
)
1844 DPRINT("MsqKillTimer queue %p wnd %x id %p msg %d\n",
1845 MessageQueue
, Wnd
, IDEvent
, Msg
);
1847 Timer
= RemoveTimer(MessageQueue
, Wnd
, IDEvent
, Msg
);
1851 DPRINT("Failed to remove timer from list, not found\n");
1855 ExFreeToPagedLookasideList(&TimerLookasideList
, Timer
);
1859 DumpTimerList(MessageQueue
);
1860 #endif /* ! defined(NDEBUG) */
1862 return NULL
!= Timer
;
1866 MsqGetTimerMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
1867 PWINDOW_OBJECT WindowFilter
, UINT MsgFilterMin
, UINT MsgFilterMax
,
1868 MSG
*Msg
, BOOLEAN Restart
)
1871 LARGE_INTEGER CurrentTime
;
1872 LARGE_INTEGER LargeTickCount
;
1873 PLIST_ENTRY EnumEntry
;
1876 DPRINT("MsqGetTimerMessage queue %p msg %p restart %s\n",
1877 MessageQueue
, Msg
, Restart
? "TRUE" : "FALSE");
1879 KeQuerySystemTime(&CurrentTime
);
1880 DPRINT("Current time %I64d\n", CurrentTime
.QuadPart
);
1881 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1883 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1885 Timer
= CONTAINING_RECORD(MessageQueue
->TimerListHead
.Flink
,
1886 TIMER_ENTRY
, ListEntry
);
1887 DPRINT("Checking timer %p wnd %x expiry %I64d\n", Timer
, Timer
->Wnd
,
1888 Timer
->ExpiryTime
.QuadPart
);
1889 EnumEntry
= EnumEntry
->Flink
;
1890 if ((NULL
== WindowFilter
|| Timer
->Wnd
== WindowFilter
->hSelf
) &&
1891 ((MsgFilterMin
== 0 && MsgFilterMax
== 0) ||
1892 (MsgFilterMin
<= Timer
->Msg
&&
1893 Timer
->Msg
<= MsgFilterMax
)))
1895 if (Timer
->ExpiryTime
.QuadPart
<= CurrentTime
.QuadPart
)
1897 DPRINT("Timer is expired\n");
1903 DPRINT("No need to check later timers\n");
1909 DPRINT("timer %p (wnd %x msg %d) failed filter wnd %x msgmin %d msgmax %d\n",
1910 Timer
, Timer
->Wnd
, Timer
->Msg
, WindowFilter
->hSelf
, MsgFilterMin
, MsgFilterMax
);
1916 DPRINT("No timer pending\n");
1920 Msg
->hwnd
= Timer
->Wnd
;
1921 Msg
->message
= Timer
->Msg
;
1922 Msg
->wParam
= (WPARAM
) Timer
->IDEvent
;
1923 Msg
->lParam
= (LPARAM
) Timer
->TimerFunc
;
1924 KeQueryTickCount(&LargeTickCount
);
1925 Msg
->time
= MsqCalculateMessageTime(&LargeTickCount
);
1926 Msg
->pt
= gpsi
->ptCursor
;
1930 RemoveEntryList(&Timer
->ListEntry
);
1931 Timer
->ExpiryTime
.QuadPart
= CurrentTime
.QuadPart
+
1932 (ULONGLONG
) Timer
->Period
* (ULONGLONG
) 10000;
1933 DPRINT("Restarting timer %p expires %I64d\n", Timer
, Timer
->ExpiryTime
.QuadPart
);
1934 InsertTimer(MessageQueue
, Timer
);
1938 DumpTimerList(MessageQueue
);
1939 #endif /* ! defined(NDEBUG) */
1943 DPRINT("Created message wnd %x msg %d wParam %u lParam %u\n", Msg
->hwnd
, Msg
->message
,
1944 Msg
->wParam
, Msg
->lParam
);
1950 MsqRemoveTimersWindow(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
)
1953 PLIST_ENTRY EnumEntry
;
1955 DPRINT("MsqRemoveTimersWindow queue %p wnd %x\n", MessageQueue
, Wnd
);
1957 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1958 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1960 Timer
= CONTAINING_RECORD(EnumEntry
, TIMER_ENTRY
, ListEntry
);
1961 EnumEntry
= EnumEntry
->Flink
;
1962 if (Timer
->Wnd
== Wnd
)
1964 DPRINT("Removing timer %p because its window is going away\n", Timer
);
1965 RemoveEntryList(&Timer
->ListEntry
);
1966 ExFreeToPagedLookasideList(&TimerLookasideList
, Timer
);
1971 DumpTimerList(MessageQueue
);
1972 #endif /* ! defined(NDEBUG) */
1977 MsqGetFirstTimerExpiry(PUSER_MESSAGE_QUEUE MessageQueue
,
1978 PWINDOW_OBJECT WndFilter
, UINT MsgFilterMin
, UINT MsgFilterMax
,
1979 PLARGE_INTEGER FirstTimerExpiry
)
1982 PLIST_ENTRY EnumEntry
;
1984 DPRINT("MsqGetFirstTimerExpiry queue %p wndfilter %x msgfiltermin %d msgfiltermax %d expiry %p\n",
1985 MessageQueue
, WndFilter
, MsgFilterMin
, MsgFilterMax
, FirstTimerExpiry
);
1987 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1988 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1990 Timer
= CONTAINING_RECORD(MessageQueue
->TimerListHead
.Flink
,
1991 TIMER_ENTRY
, ListEntry
);
1992 EnumEntry
= EnumEntry
->Flink
;
1993 if ((NULL
== WndFilter
|| PtrToInt(WndFilter
) == 1 || Timer
->Wnd
== WndFilter
->hSelf
) &&
1994 ((MsgFilterMin
== 0 && MsgFilterMax
== 0) ||
1995 (MsgFilterMin
<= Timer
->Msg
&&
1996 Timer
->Msg
<= MsgFilterMax
)))
1998 *FirstTimerExpiry
= Timer
->ExpiryTime
;
1999 DPRINT("First timer expires %I64d\n", Timer
->ExpiryTime
);