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
->ti
)
82 if (Window
->ti
->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 PWINSTATION_OBJECT WinStaObject
;
240 PSYSTEM_CURSORINFO CurInfo
;
243 pti
= PsGetCurrentThreadWin32Thread();
244 if (pti
->Desktop
== NULL
)
249 WinStaObject
= pti
->Desktop
->WindowStation
;
251 CurInfo
= IntGetSysCursorInfo(WinStaObject
);
253 switch (Msg
->message
)
256 Res
= ((Msg
->time
- CurInfo
->ClickLockTime
) >= gspv
.dwMouseClickLockTime
);
257 if (Res
&& (!CurInfo
->ClickLockActive
))
259 CurInfo
->ClickLockActive
= TRUE
;
263 if (CurInfo
->ClickLockActive
)
266 CurInfo
->ClickLockActive
= FALSE
;
267 CurInfo
->ClickLockTime
= 0;
271 CurInfo
->ClickLockTime
= Msg
->time
;
279 MsqIsDblClk(LPMSG Msg
, BOOL Remove
)
282 PWINSTATION_OBJECT WinStaObject
;
283 PSYSTEM_CURSORINFO CurInfo
;
287 pti
= PsGetCurrentThreadWin32Thread();
288 if (pti
->Desktop
== NULL
)
293 WinStaObject
= pti
->Desktop
->WindowStation
;
295 CurInfo
= IntGetSysCursorInfo(WinStaObject
);
296 Res
= (Msg
->hwnd
== (HWND
)CurInfo
->LastClkWnd
) &&
297 ((Msg
->time
- CurInfo
->LastBtnDown
) < gspv
.iDblClickTime
);
301 dX
= CurInfo
->LastBtnDownX
- Msg
->pt
.x
;
302 dY
= CurInfo
->LastBtnDownY
- Msg
->pt
.y
;
308 Res
= (dX
<= gspv
.iDblClickWidth
) &&
309 (dY
<= gspv
.iDblClickHeight
);
313 if(CurInfo
->ButtonsDown
)
314 Res
= (CurInfo
->ButtonsDown
== Msg
->message
);
320 CurInfo
->LastBtnDownX
= Msg
->pt
.x
;
321 CurInfo
->LastBtnDownY
= Msg
->pt
.y
;
322 CurInfo
->ButtonsDown
= Msg
->message
;
325 CurInfo
->LastBtnDown
= 0;
326 CurInfo
->LastClkWnd
= NULL
;
330 CurInfo
->LastClkWnd
= (HANDLE
)Msg
->hwnd
;
331 CurInfo
->LastBtnDown
= Msg
->time
;
339 co_MsqTranslateMouseMessage(PUSER_MESSAGE_QUEUE MessageQueue
, PWINDOW_OBJECT Window
, UINT FilterLow
, UINT FilterHigh
,
340 PUSER_MESSAGE Message
, BOOL Remove
, PBOOL Freed
,
341 PWINDOW_OBJECT ScopeWin
, PPOINT ScreenPoint
, BOOL FromGlobalQueue
, PLIST_ENTRY
*Next
)
343 USHORT Msg
= Message
->Msg
.message
;
344 PWINDOW_OBJECT CaptureWindow
= NULL
;
347 ASSERT_REFS_CO(ScopeWin
);
350 co_WinPosWindowFromPoint can return a Window, and in that case
351 that window has a ref that we need to deref. Thats why we add "dummy"
352 refs in all other cases.
355 hCaptureWin
= IntGetCaptureWindow();
356 if (hCaptureWin
== NULL
)
358 if (Msg
== WM_MOUSEWHEEL
)
360 CaptureWindow
= UserGetWindowObject(IntGetFocusWindow());
361 if (CaptureWindow
) UserReferenceObject(CaptureWindow
);
365 co_WinPosWindowFromPoint(ScopeWin
, NULL
, &Message
->Msg
.pt
, &CaptureWindow
);
366 if(CaptureWindow
== NULL
)
368 CaptureWindow
= ScopeWin
;
369 if (CaptureWindow
) UserReferenceObject(CaptureWindow
);
373 /* this is the one case where we dont add a ref, since the returned
374 window is already referenced */
380 /* FIXME - window messages should go to the right window if no buttons are
382 CaptureWindow
= UserGetWindowObject(hCaptureWin
);
383 if (CaptureWindow
) UserReferenceObject(CaptureWindow
);
388 if (CaptureWindow
== NULL
)
392 RemoveEntryList(&Message
->ListEntry
);
393 if(MessageQueue
->MouseMoveMsg
== Message
)
395 MessageQueue
->MouseMoveMsg
= NULL
;
398 // when FromGlobalQueue is true, the caller has already removed the Message
404 if (CaptureWindow
->MessageQueue
!= MessageQueue
)
406 if (! FromGlobalQueue
)
408 DPRINT("Moving msg between private queues\n");
409 /* This message is already queued in a private queue, but we need
410 * to move it to a different queue, perhaps because a new window
411 * was created which now covers the screen area previously taken
412 * by another window. To move it, we need to take it out of the
413 * old queue. Note that we're already holding the lock mutexes of the
415 RemoveEntryList(&Message
->ListEntry
);
417 /* remove the pointer for the current WM_MOUSEMOVE message in case we
419 if(MessageQueue
->MouseMoveMsg
== Message
)
421 MessageQueue
->MouseMoveMsg
= NULL
;
425 /* lock the destination message queue, so we don't get in trouble with other
426 threads, messing with it at the same time */
427 IntLockHardwareMessageQueue(CaptureWindow
->MessageQueue
);
428 InsertTailList(&CaptureWindow
->MessageQueue
->HardwareMessagesListHead
,
429 &Message
->ListEntry
);
430 if(Message
->Msg
.message
== WM_MOUSEMOVE
)
432 if(CaptureWindow
->MessageQueue
->MouseMoveMsg
)
434 /* remove the old WM_MOUSEMOVE message, we're processing a more recent
436 RemoveEntryList(&CaptureWindow
->MessageQueue
->MouseMoveMsg
->ListEntry
);
437 ExFreePool(CaptureWindow
->MessageQueue
->MouseMoveMsg
);
439 /* save the pointer to the WM_MOUSEMOVE message in the new queue */
440 CaptureWindow
->MessageQueue
->MouseMoveMsg
= Message
;
442 CaptureWindow
->MessageQueue
->QueueBits
|= QS_MOUSEMOVE
;
443 CaptureWindow
->MessageQueue
->ChangedBits
|= QS_MOUSEMOVE
;
444 if (CaptureWindow
->MessageQueue
->WakeMask
& QS_MOUSEMOVE
)
445 KeSetEvent(CaptureWindow
->MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
449 CaptureWindow
->MessageQueue
->QueueBits
|= QS_MOUSEBUTTON
;
450 CaptureWindow
->MessageQueue
->ChangedBits
|= QS_MOUSEBUTTON
;
451 if (CaptureWindow
->MessageQueue
->WakeMask
& QS_MOUSEBUTTON
)
452 KeSetEvent(CaptureWindow
->MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
454 IntUnLockHardwareMessageQueue(CaptureWindow
->MessageQueue
);
457 UserDereferenceObject(CaptureWindow
);
461 /* From here on, we're in the same message queue as the caller! */
463 *ScreenPoint
= Message
->Msg
.pt
;
465 if((Window
!= NULL
&& PtrToInt(Window
) != 1 && CaptureWindow
->hSelf
!= Window
->hSelf
) ||
466 ((FilterLow
!= 0 || FilterHigh
!= 0) && (Msg
< FilterLow
|| Msg
> FilterHigh
)))
468 /* Reject the message because it doesn't match the filter */
472 /* Lock the message queue so no other thread can mess with it.
473 Our own message queue is not locked while fetching from the global
474 queue, so we have to make sure nothing interferes! */
475 IntLockHardwareMessageQueue(CaptureWindow
->MessageQueue
);
476 /* if we're from the global queue, we need to add our message to our
477 private queue so we don't loose it! */
478 InsertTailList(&CaptureWindow
->MessageQueue
->HardwareMessagesListHead
,
479 &Message
->ListEntry
);
482 if (Message
->Msg
.message
== WM_MOUSEMOVE
)
484 if(CaptureWindow
->MessageQueue
->MouseMoveMsg
&&
485 (CaptureWindow
->MessageQueue
->MouseMoveMsg
!= Message
))
487 /* delete the old message */
488 RemoveEntryList(&CaptureWindow
->MessageQueue
->MouseMoveMsg
->ListEntry
);
489 ExFreePool(CaptureWindow
->MessageQueue
->MouseMoveMsg
);
490 if (!FromGlobalQueue
)
492 // We might have deleted the next one in our queue, so fix next
493 *Next
= Message
->ListEntry
.Flink
;
496 /* always save a pointer to this WM_MOUSEMOVE message here because we're
497 sure that the message is in the private queue */
498 CaptureWindow
->MessageQueue
->MouseMoveMsg
= Message
;
502 IntUnLockHardwareMessageQueue(CaptureWindow
->MessageQueue
);
505 UserDereferenceObject(CaptureWindow
);
510 /* FIXME - only assign if removing? */
511 Message
->Msg
.hwnd
= CaptureWindow
->hSelf
;
512 Message
->Msg
.message
= Msg
;
513 Message
->Msg
.lParam
= MAKELONG(Message
->Msg
.pt
.x
, Message
->Msg
.pt
.y
);
515 /* remove the reference to the current WM_(NC)MOUSEMOVE message, if this message
517 if (Message
->Msg
.message
== WM_MOUSEMOVE
||
518 Message
->Msg
.message
== WM_NCMOUSEMOVE
)
522 /* Lock the message queue so no other thread can mess with it.
523 Our own message queue is not locked while fetching from the global
524 queue, so we have to make sure nothing interferes! */
525 IntLockHardwareMessageQueue(CaptureWindow
->MessageQueue
);
526 if(CaptureWindow
->MessageQueue
->MouseMoveMsg
)
528 /* delete the WM_(NC)MOUSEMOVE message in the private queue, we're dealing
529 with one that's been sent later */
530 RemoveEntryList(&CaptureWindow
->MessageQueue
->MouseMoveMsg
->ListEntry
);
531 ExFreePool(CaptureWindow
->MessageQueue
->MouseMoveMsg
);
532 /* our message is not in the private queue so we can remove the pointer
533 instead of setting it to the current message we're processing */
534 CaptureWindow
->MessageQueue
->MouseMoveMsg
= NULL
;
536 IntUnLockHardwareMessageQueue(CaptureWindow
->MessageQueue
);
538 else if (CaptureWindow
->MessageQueue
->MouseMoveMsg
== Message
)
540 CaptureWindow
->MessageQueue
->MouseMoveMsg
= NULL
;
544 UserDereferenceObject(CaptureWindow
);
550 co_MsqPeekHardwareMessage(PUSER_MESSAGE_QUEUE MessageQueue
, PWINDOW_OBJECT Window
,
551 UINT FilterLow
, UINT FilterHigh
, BOOL Remove
,
552 PUSER_MESSAGE
* Message
)
557 PLIST_ENTRY CurrentEntry
;
558 PWINDOW_OBJECT DesktopWindow
= NULL
;
559 PVOID WaitObjects
[2];
561 DECLARE_RETURN(BOOL
);
562 USER_REFERENCE_ENTRY Ref
;
563 PDESKTOPINFO Desk
= NULL
;
565 WaitObjects
[1] = MessageQueue
->NewMessages
;
566 WaitObjects
[0] = &HardwareMessageQueueLock
;
573 WaitStatus
= KeWaitForMultipleObjects(2, WaitObjects
, WaitAny
, UserRequest
,
574 UserMode
, FALSE
, NULL
, NULL
);
578 while (NT_SUCCESS(WaitStatus
) && STATUS_WAIT_0
!= WaitStatus
);
580 DesktopWindow
= UserGetWindowObject(IntGetDesktopWindow());
584 UserRefObjectCo(DesktopWindow
, &Ref
);//can DesktopWindow be NULL?
585 Desk
= DesktopWindow
->ti
->pDeskInfo
;
588 /* Process messages in the message queue itself. */
589 IntLockHardwareMessageQueue(MessageQueue
);
590 CurrentEntry
= MessageQueue
->HardwareMessagesListHead
.Flink
;
591 while (CurrentEntry
!= &MessageQueue
->HardwareMessagesListHead
)
593 PUSER_MESSAGE Current
=
594 CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
, ListEntry
);
595 CurrentEntry
= CurrentEntry
->Flink
;
596 if (Current
->Msg
.message
>= WM_MOUSEFIRST
&&
597 Current
->Msg
.message
<= WM_MOUSELAST
)
601 Accept
= co_MsqTranslateMouseMessage(MessageQueue
, Window
, FilterLow
, FilterHigh
,
602 Current
, Remove
, &Freed
,
603 DesktopWindow
, &ScreenPoint
, FALSE
, &CurrentEntry
);
608 RemoveEntryList(&Current
->ListEntry
);
610 IntUnLockHardwareMessageQueue(MessageQueue
);
611 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
615 Desk
->LastInputWasKbd
= FALSE
;
622 IntUnLockHardwareMessageQueue(MessageQueue
);
624 /* Now try the global queue. */
626 /* Transfer all messages from the DPC accessible queue to the main queue. */
627 IntLockSystemMessageQueue(OldIrql
);
628 while (SystemMessageQueueCount
> 0)
630 PUSER_MESSAGE UserMsg
;
634 ASSERT(SystemMessageQueueHead
< SYSTEM_MESSAGE_QUEUE_SIZE
);
635 Msg
= SystemMessageQueue
[SystemMessageQueueHead
];
636 SystemMessageQueueHead
=
637 (SystemMessageQueueHead
+ 1) % SYSTEM_MESSAGE_QUEUE_SIZE
;
638 SystemMessageQueueCount
--;
639 IntUnLockSystemMessageQueue(OldIrql
);
640 if (WM_MOUSEFIRST
<= Msg
.message
&& Msg
.message
<= WM_MOUSELAST
)
642 MSLLHOOKSTRUCT MouseHookData
;
644 MouseHookData
.pt
.x
= LOWORD(Msg
.lParam
);
645 MouseHookData
.pt
.y
= HIWORD(Msg
.lParam
);
649 MouseHookData
.mouseData
= MAKELONG(0, GET_WHEEL_DELTA_WPARAM(Msg
.wParam
));
653 case WM_XBUTTONDBLCLK
:
654 case WM_NCXBUTTONDOWN
:
656 case WM_NCXBUTTONDBLCLK
:
657 MouseHookData
.mouseData
= MAKELONG(0, HIWORD(Msg
.wParam
));
660 MouseHookData
.mouseData
= 0;
663 MouseHookData
.flags
= 0;
664 MouseHookData
.time
= Msg
.time
;
665 MouseHookData
.dwExtraInfo
= 0;
666 ProcessMessage
= (0 == co_HOOK_CallHooks(WH_MOUSE_LL
, HC_ACTION
,
667 Msg
.message
, (LPARAM
) &MouseHookData
));
671 ProcessMessage
= TRUE
;
675 UserMsg
= ExAllocateFromPagedLookasideList(&MessageLookasideList
);
676 /* What to do if out of memory? For now we just panic a bit in debug */
678 UserMsg
->FreeLParam
= FALSE
;
680 InsertTailList(&HardwareMessageQueueHead
, &UserMsg
->ListEntry
);
682 IntLockSystemMessageQueue(OldIrql
);
684 HardwareMessageQueueStamp
++;
685 IntUnLockSystemMessageQueue(OldIrql
);
687 /* Process messages in the queue until we find one to return. */
688 CurrentEntry
= HardwareMessageQueueHead
.Flink
;
689 while (CurrentEntry
!= &HardwareMessageQueueHead
)
691 PUSER_MESSAGE Current
=
692 CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
, ListEntry
);
693 CurrentEntry
= CurrentEntry
->Flink
;
694 RemoveEntryList(&Current
->ListEntry
);
695 HardwareMessageQueueStamp
++;
696 if (Current
->Msg
.message
>= WM_MOUSEFIRST
&&
697 Current
->Msg
.message
<= WM_MOUSELAST
)
699 const ULONG ActiveStamp
= HardwareMessageQueueStamp
;
700 /* Translate the message. */
701 Accept
= co_MsqTranslateMouseMessage(MessageQueue
, Window
, FilterLow
, FilterHigh
,
702 Current
, Remove
, &Freed
,
703 DesktopWindow
, &ScreenPoint
, TRUE
, NULL
);
706 /* Check for no more messages in the system queue. */
707 IntLockSystemMessageQueue(OldIrql
);
708 if (SystemMessageQueueCount
== 0 &&
709 IsListEmpty(&HardwareMessageQueueHead
))
711 KeClearEvent(&HardwareMessageEvent
);
713 IntUnLockSystemMessageQueue(OldIrql
);
716 If we aren't removing the message then add it to the private
721 IntLockHardwareMessageQueue(MessageQueue
);
722 if(Current
->Msg
.message
== WM_MOUSEMOVE
)
724 if(MessageQueue
->MouseMoveMsg
)
726 RemoveEntryList(&MessageQueue
->MouseMoveMsg
->ListEntry
);
727 ExFreePool(MessageQueue
->MouseMoveMsg
);
729 MessageQueue
->MouseMoveMsg
= Current
;
731 InsertTailList(&MessageQueue
->HardwareMessagesListHead
,
732 &Current
->ListEntry
);
733 IntUnLockHardwareMessageQueue(MessageQueue
);
735 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
740 /* If the contents of the queue changed then restart processing. */
741 if (HardwareMessageQueueStamp
!= ActiveStamp
)
743 CurrentEntry
= HardwareMessageQueueHead
.Flink
;
749 /* Check if the system message queue is now empty. */
750 IntLockSystemMessageQueue(OldIrql
);
751 if (SystemMessageQueueCount
== 0 && IsListEmpty(&HardwareMessageQueueHead
))
753 KeClearEvent(&HardwareMessageEvent
);
755 IntUnLockSystemMessageQueue(OldIrql
);
756 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
761 if (DesktopWindow
) UserDerefObjectCo(DesktopWindow
);
767 // Note: Only called from input.c.
770 co_MsqPostKeyboardMessage(UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
772 PUSER_MESSAGE_QUEUE FocusMessageQueue
;
774 LARGE_INTEGER LargeTickCount
;
775 KBDLLHOOKSTRUCT KbdHookData
;
777 BOOLEAN Entered
= FALSE
;
779 DPRINT("MsqPostKeyboardMessage(uMsg 0x%x, wParam 0x%x, lParam 0x%x)\n",
780 uMsg
, wParam
, lParam
);
782 // Condition may arise when calling MsqPostMessage and waiting for an event.
783 if (!UserIsEntered())
785 // Fixme: Not sure ATM if this thread is locked.
786 UserEnterExclusive();
790 FocusMessageQueue
= IntGetFocusMessageQueue();
794 if (FocusMessageQueue
&& (FocusMessageQueue
->FocusWindow
!= (HWND
)0))
795 Msg
.hwnd
= FocusMessageQueue
->FocusWindow
;
801 KeQueryTickCount(&LargeTickCount
);
802 Msg
.time
= MsqCalculateMessageTime(&LargeTickCount
);
804 Event
.message
= Msg
.message
;
805 Event
.hwnd
= Msg
.hwnd
;
806 Event
.time
= Msg
.time
;
807 Event
.paramL
= (Msg
.wParam
& 0xFF) | (HIWORD(Msg
.lParam
) << 8);
808 Event
.paramH
= Msg
.lParam
& 0x7FFF;
809 if (HIWORD(Msg
.lParam
) & 0x0100) Event
.paramH
|= 0x8000;
810 co_HOOK_CallHooks( WH_JOURNALRECORD
, HC_ACTION
, 0, (LPARAM
)&Event
);
812 /* We can't get the Msg.pt point here since we don't know thread
813 (and thus the window station) the message will end up in yet. */
815 KbdHookData
.vkCode
= Msg
.wParam
;
816 KbdHookData
.scanCode
= (Msg
.lParam
>> 16) & 0xff;
817 KbdHookData
.flags
= (0 == (Msg
.lParam
& 0x01000000) ? 0 : LLKHF_EXTENDED
) |
818 (0 == (Msg
.lParam
& 0x20000000) ? 0 : LLKHF_ALTDOWN
) |
819 (0 == (Msg
.lParam
& 0x80000000) ? 0 : LLKHF_UP
);
820 KbdHookData
.time
= Msg
.time
;
821 KbdHookData
.dwExtraInfo
= 0;
822 if (co_HOOK_CallHooks(WH_KEYBOARD_LL
, HC_ACTION
, Msg
.message
, (LPARAM
) &KbdHookData
))
824 DPRINT("Kbd msg %d wParam %d lParam 0x%08x dropped by WH_KEYBOARD_LL hook\n",
825 Msg
.message
, Msg
.wParam
, Msg
.lParam
);
826 if (Entered
) UserLeave();
830 if (FocusMessageQueue
== NULL
)
832 DPRINT("No focus message queue\n");
833 if (Entered
) UserLeave();
837 if (FocusMessageQueue
->FocusWindow
!= (HWND
)0)
839 Msg
.hwnd
= FocusMessageQueue
->FocusWindow
;
840 DPRINT("Msg.hwnd = %x\n", Msg
.hwnd
);
842 FocusMessageQueue
->Desktop
->DesktopInfo
->LastInputWasKbd
= TRUE
;
844 IntGetCursorLocation(FocusMessageQueue
->Desktop
->WindowStation
,
846 MsqPostMessage(FocusMessageQueue
, &Msg
, FALSE
, QS_KEY
);
850 DPRINT("Invalid focus window handle\n");
853 if (Entered
) UserLeave();
858 MsqPostHotKeyMessage(PVOID Thread
, HWND hWnd
, WPARAM wParam
, LPARAM lParam
)
860 PWINDOW_OBJECT Window
;
861 PTHREADINFO Win32Thread
;
862 PWINSTATION_OBJECT WinSta
;
864 LARGE_INTEGER LargeTickCount
;
867 Status
= ObReferenceObjectByPointer (Thread
,
871 if (!NT_SUCCESS(Status
))
874 Win32Thread
= ((PETHREAD
)Thread
)->Tcb
.Win32Thread
;
875 if (Win32Thread
== NULL
|| Win32Thread
->MessageQueue
== NULL
)
877 ObDereferenceObject ((PETHREAD
)Thread
);
881 WinSta
= Win32Thread
->Desktop
->WindowStation
;
882 Window
= IntGetWindowObject(hWnd
);
885 ObDereferenceObject ((PETHREAD
)Thread
);
890 Mesg
.message
= WM_HOTKEY
;
891 Mesg
.wParam
= wParam
;
892 Mesg
.lParam
= lParam
;
893 KeQueryTickCount(&LargeTickCount
);
894 Mesg
.time
= MsqCalculateMessageTime(&LargeTickCount
);
895 IntGetCursorLocation(WinSta
, &Mesg
.pt
);
896 MsqPostMessage(Window
->MessageQueue
, &Mesg
, FALSE
, QS_HOTKEY
);
897 UserDereferenceObject(Window
);
898 ObDereferenceObject (Thread
);
900 // InsertHeadList(&pThread->MessageQueue->PostedMessagesListHead,
901 // &Message->ListEntry);
902 // KeSetEvent(pThread->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
905 PUSER_MESSAGE FASTCALL
906 MsqCreateMessage(LPMSG Msg
, BOOLEAN FreeLParam
)
908 PUSER_MESSAGE Message
;
910 Message
= ExAllocateFromPagedLookasideList(&MessageLookasideList
);
916 Message
->FreeLParam
= FreeLParam
;
917 RtlMoveMemory(&Message
->Msg
, Msg
, sizeof(MSG
));
923 MsqDestroyMessage(PUSER_MESSAGE Message
)
925 ExFreeToPagedLookasideList(&MessageLookasideList
, Message
);
929 co_MsqDispatchSentNotifyMessages(PUSER_MESSAGE_QUEUE MessageQueue
)
931 PLIST_ENTRY ListEntry
;
932 PUSER_SENT_MESSAGE_NOTIFY Message
;
934 while (!IsListEmpty(&MessageQueue
->SentMessagesListHead
))
936 ListEntry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
937 Message
= CONTAINING_RECORD(ListEntry
, USER_SENT_MESSAGE_NOTIFY
,
940 co_IntCallSentMessageCallback(Message
->CompletionCallback
,
943 Message
->CompletionCallbackContext
,
951 MsqPeekSentMessages(PUSER_MESSAGE_QUEUE MessageQueue
)
953 return(!IsListEmpty(&MessageQueue
->SentMessagesListHead
));
957 co_MsqDispatchOneSentMessage(PUSER_MESSAGE_QUEUE MessageQueue
)
959 PUSER_SENT_MESSAGE Message
;
963 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage
;
965 if (IsListEmpty(&MessageQueue
->SentMessagesListHead
))
970 /* remove it from the list of pending messages */
971 Entry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
972 Message
= CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
);
974 /* insert it to the list of messages that are currently dispatched by this
976 InsertTailList(&MessageQueue
->LocalDispatchingMessagesHead
,
977 &Message
->ListEntry
);
979 if (Message
->HookMessage
== MSQ_ISHOOK
)
981 Result
= co_HOOK_CallHooks(Message
->Msg
.message
,
982 (INT
)(INT_PTR
)Message
->Msg
.hwnd
,
984 Message
->Msg
.lParam
);
986 else if (Message
->HookMessage
== MSQ_ISEVENT
)
988 Result
= co_EVENT_CallEvents( Message
->Msg
.message
,
991 Message
->Msg
.lParam
);
995 /* Call the window procedure. */
996 Result
= co_IntSendMessage(Message
->Msg
.hwnd
,
997 Message
->Msg
.message
,
999 Message
->Msg
.lParam
);
1002 /* remove the message from the local dispatching list, because it doesn't need
1003 to be cleaned up on thread termination anymore */
1004 RemoveEntryList(&Message
->ListEntry
);
1006 /* remove the message from the dispatching list, so lock the sender's message queue */
1007 SenderReturned
= (Message
->DispatchingListEntry
.Flink
== NULL
);
1010 /* only remove it from the dispatching list if not already removed by a timeout */
1011 RemoveEntryList(&Message
->DispatchingListEntry
);
1013 /* still keep the sender's message queue locked, so the sender can't exit the
1014 MsqSendMessage() function (if timed out) */
1016 /* Let the sender know the result. */
1017 if (Message
->Result
!= NULL
)
1019 *Message
->Result
= Result
;
1022 /* Notify the sender. */
1023 if (Message
->CompletionEvent
!= NULL
)
1025 KeSetEvent(Message
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1028 /* Notify the sender if they specified a callback. */
1029 if (!SenderReturned
&& Message
->CompletionCallback
!= NULL
)
1031 if(!(NotifyMessage
= ExAllocatePoolWithTag(NonPagedPool
,
1032 sizeof(USER_SENT_MESSAGE_NOTIFY
), TAG_USRMSG
)))
1034 DPRINT1("MsqDispatchOneSentMessage(): Not enough memory to create a callback notify message\n");
1037 NotifyMessage
->CompletionCallback
=
1038 Message
->CompletionCallback
;
1039 NotifyMessage
->CompletionCallbackContext
=
1040 Message
->CompletionCallbackContext
;
1041 NotifyMessage
->Result
= Result
;
1042 NotifyMessage
->hWnd
= Message
->Msg
.hwnd
;
1043 NotifyMessage
->Msg
= Message
->Msg
.message
;
1044 MsqSendNotifyMessage(Message
->SenderQueue
, NotifyMessage
);
1049 /* dereference both sender and our queue */
1050 IntDereferenceMessageQueue(MessageQueue
);
1051 IntDereferenceMessageQueue(Message
->SenderQueue
);
1053 /* free the message */
1054 ExFreePool(Message
);
1059 MsqRemoveWindowMessagesFromQueue(PVOID pWindow
)
1061 PUSER_SENT_MESSAGE SentMessage
;
1062 PUSER_MESSAGE PostedMessage
;
1063 PUSER_MESSAGE_QUEUE MessageQueue
;
1064 PLIST_ENTRY CurrentEntry
, ListHead
;
1065 PWINDOW_OBJECT Window
= pWindow
;
1069 MessageQueue
= Window
->MessageQueue
;
1070 ASSERT(MessageQueue
);
1072 /* remove the posted messages for this window */
1073 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
1074 ListHead
= &MessageQueue
->PostedMessagesListHead
;
1075 while (CurrentEntry
!= ListHead
)
1077 PostedMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1079 if (PostedMessage
->Msg
.hwnd
== Window
->hSelf
)
1081 RemoveEntryList(&PostedMessage
->ListEntry
);
1082 MsqDestroyMessage(PostedMessage
);
1083 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
1087 CurrentEntry
= CurrentEntry
->Flink
;
1091 /* remove the sent messages for this window */
1092 CurrentEntry
= MessageQueue
->SentMessagesListHead
.Flink
;
1093 ListHead
= &MessageQueue
->SentMessagesListHead
;
1094 while (CurrentEntry
!= ListHead
)
1096 SentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1098 if(SentMessage
->Msg
.hwnd
== Window
->hSelf
)
1100 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1102 RemoveEntryList(&SentMessage
->ListEntry
);
1104 /* remove the message from the dispatching list */
1105 if(SentMessage
->DispatchingListEntry
.Flink
!= NULL
)
1107 RemoveEntryList(&SentMessage
->DispatchingListEntry
);
1110 /* wake the sender's thread */
1111 if (SentMessage
->CompletionEvent
!= NULL
)
1113 KeSetEvent(SentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1116 /* dereference our and the sender's message queue */
1117 IntDereferenceMessageQueue(MessageQueue
);
1118 IntDereferenceMessageQueue(SentMessage
->SenderQueue
);
1120 /* free the message */
1121 ExFreePool(SentMessage
);
1123 CurrentEntry
= MessageQueue
->SentMessagesListHead
.Flink
;
1127 CurrentEntry
= CurrentEntry
->Flink
;
1133 MsqSendNotifyMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
1134 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage
)
1136 InsertTailList(&MessageQueue
->NotifyMessagesListHead
,
1137 &NotifyMessage
->ListEntry
);
1138 MessageQueue
->QueueBits
|= QS_SENDMESSAGE
;
1139 MessageQueue
->ChangedBits
|= QS_SENDMESSAGE
;
1140 if (MessageQueue
->WakeMask
& QS_SENDMESSAGE
)
1141 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1145 co_MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
1146 HWND Wnd
, UINT Msg
, WPARAM wParam
, LPARAM lParam
,
1147 UINT uTimeout
, BOOL Block
, INT HookMessage
,
1151 PUSER_SENT_MESSAGE Message
;
1152 KEVENT CompletionEvent
;
1153 NTSTATUS WaitStatus
;
1155 PUSER_MESSAGE_QUEUE ThreadQueue
;
1156 LARGE_INTEGER Timeout
;
1159 if(!(Message
= ExAllocatePoolWithTag(PagedPool
, sizeof(USER_SENT_MESSAGE
), TAG_USRMSG
)))
1161 DPRINT1("MsqSendMessage(): Not enough memory to allocate a message");
1162 return STATUS_INSUFFICIENT_RESOURCES
;
1165 KeInitializeEvent(&CompletionEvent
, NotificationEvent
, FALSE
);
1167 pti
= PsGetCurrentThreadWin32Thread();
1168 ThreadQueue
= pti
->MessageQueue
;
1169 ASSERT(ThreadQueue
!= MessageQueue
);
1171 Timeout
.QuadPart
= (LONGLONG
) uTimeout
* (LONGLONG
) -10000;
1173 /* FIXME - increase reference counter of sender's message queue here */
1176 Message
->Msg
.hwnd
= Wnd
;
1177 Message
->Msg
.message
= Msg
;
1178 Message
->Msg
.wParam
= wParam
;
1179 Message
->Msg
.lParam
= lParam
;
1180 Message
->CompletionEvent
= &CompletionEvent
;
1181 Message
->Result
= &Result
;
1182 Message
->SenderQueue
= ThreadQueue
;
1183 IntReferenceMessageQueue(ThreadQueue
);
1184 Message
->CompletionCallback
= NULL
;
1185 Message
->HookMessage
= HookMessage
;
1187 IntReferenceMessageQueue(MessageQueue
);
1189 /* add it to the list of pending messages */
1190 InsertTailList(&ThreadQueue
->DispatchingMessagesHead
, &Message
->DispatchingListEntry
);
1192 /* queue it in the destination's message queue */
1193 InsertTailList(&MessageQueue
->SentMessagesListHead
, &Message
->ListEntry
);
1195 MessageQueue
->QueueBits
|= QS_SENDMESSAGE
;
1196 MessageQueue
->ChangedBits
|= QS_SENDMESSAGE
;
1197 if (MessageQueue
->WakeMask
& QS_SENDMESSAGE
)
1198 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1200 /* we can't access the Message anymore since it could have already been deleted! */
1208 /* don't process messages sent to the thread */
1209 WaitStatus
= KeWaitForSingleObject(&CompletionEvent
, UserRequest
, UserMode
,
1210 FALSE
, (uTimeout
? &Timeout
: NULL
));
1214 if(WaitStatus
== STATUS_TIMEOUT
)
1216 /* look up if the message has not yet dispatched, if so
1217 make sure it can't pass a result and it must not set the completion event anymore */
1218 Entry
= MessageQueue
->SentMessagesListHead
.Flink
;
1219 while (Entry
!= &MessageQueue
->SentMessagesListHead
)
1221 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
)
1224 /* we can access Message here, it's secure because the message queue is locked
1225 and the message is still hasn't been dispatched */
1226 Message
->CompletionEvent
= NULL
;
1227 Message
->Result
= NULL
;
1230 Entry
= Entry
->Flink
;
1233 /* remove from the local dispatching list so the other thread knows,
1234 it can't pass a result and it must not set the completion event anymore */
1235 Entry
= ThreadQueue
->DispatchingMessagesHead
.Flink
;
1236 while (Entry
!= &ThreadQueue
->DispatchingMessagesHead
)
1238 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, DispatchingListEntry
)
1241 /* we can access Message here, it's secure because the sender's message is locked
1242 and the message has definitely not yet been destroyed, otherwise it would
1243 have been removed from this list by the dispatching routine right after
1244 dispatching the message */
1245 Message
->CompletionEvent
= NULL
;
1246 Message
->Result
= NULL
;
1247 RemoveEntryList(&Message
->DispatchingListEntry
);
1248 Message
->DispatchingListEntry
.Flink
= NULL
;
1251 Entry
= Entry
->Flink
;
1254 DPRINT("MsqSendMessage (blocked) timed out\n");
1256 while (co_MsqDispatchOneSentMessage(ThreadQueue
))
1261 PVOID WaitObjects
[2];
1263 WaitObjects
[0] = &CompletionEvent
;
1264 WaitObjects
[1] = ThreadQueue
->NewMessages
;
1271 WaitStatus
= KeWaitForMultipleObjects(2, WaitObjects
, WaitAny
, UserRequest
,
1272 UserMode
, FALSE
, (uTimeout
? &Timeout
: NULL
), NULL
);
1276 if(WaitStatus
== STATUS_TIMEOUT
)
1278 /* look up if the message has not yet been dispatched, if so
1279 make sure it can't pass a result and it must not set the completion event anymore */
1280 Entry
= MessageQueue
->SentMessagesListHead
.Flink
;
1281 while (Entry
!= &MessageQueue
->SentMessagesListHead
)
1283 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
)
1286 /* we can access Message here, it's secure because the message queue is locked
1287 and the message is still hasn't been dispatched */
1288 Message
->CompletionEvent
= NULL
;
1289 Message
->Result
= NULL
;
1292 Entry
= Entry
->Flink
;
1295 /* remove from the local dispatching list so the other thread knows,
1296 it can't pass a result and it must not set the completion event anymore */
1297 Entry
= ThreadQueue
->DispatchingMessagesHead
.Flink
;
1298 while (Entry
!= &ThreadQueue
->DispatchingMessagesHead
)
1300 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, DispatchingListEntry
)
1303 /* we can access Message here, it's secure because the sender's message is locked
1304 and the message has definitely not yet been destroyed, otherwise it would
1305 have been removed from this list by the dispatching routine right after
1306 dispatching the message */
1307 Message
->CompletionEvent
= NULL
;
1308 Message
->Result
= NULL
;
1309 RemoveEntryList(&Message
->DispatchingListEntry
);
1310 Message
->DispatchingListEntry
.Flink
= NULL
;
1313 Entry
= Entry
->Flink
;
1316 DPRINT("MsqSendMessage timed out\n");
1319 while (co_MsqDispatchOneSentMessage(ThreadQueue
))
1322 while (NT_SUCCESS(WaitStatus
) && STATUS_WAIT_0
!= WaitStatus
);
1325 if(WaitStatus
!= STATUS_TIMEOUT
)
1326 *uResult
= (STATUS_WAIT_0
== WaitStatus
? Result
: -1);
1332 MsqPostMessage(PUSER_MESSAGE_QUEUE MessageQueue
, MSG
* Msg
, BOOLEAN FreeLParam
,
1335 PUSER_MESSAGE Message
;
1337 if(!(Message
= MsqCreateMessage(Msg
, FreeLParam
)))
1341 InsertTailList(&MessageQueue
->PostedMessagesListHead
,
1342 &Message
->ListEntry
);
1343 MessageQueue
->QueueBits
|= MessageBits
;
1344 MessageQueue
->ChangedBits
|= MessageBits
;
1345 if (MessageQueue
->WakeMask
& MessageBits
)
1346 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1350 MsqPostQuitMessage(PUSER_MESSAGE_QUEUE MessageQueue
, ULONG ExitCode
)
1352 MessageQueue
->QuitPosted
= TRUE
;
1353 MessageQueue
->QuitExitCode
= ExitCode
;
1354 MessageQueue
->QueueBits
|= QS_POSTMESSAGE
;
1355 MessageQueue
->ChangedBits
|= QS_POSTMESSAGE
;
1356 if (MessageQueue
->WakeMask
& QS_POSTMESSAGE
)
1357 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1361 co_MsqFindMessage(IN PUSER_MESSAGE_QUEUE MessageQueue
,
1362 IN BOOLEAN Hardware
,
1364 IN PWINDOW_OBJECT Window
,
1365 IN UINT MsgFilterLow
,
1366 IN UINT MsgFilterHigh
,
1367 OUT PUSER_MESSAGE
* Message
)
1369 PLIST_ENTRY CurrentEntry
;
1370 PUSER_MESSAGE CurrentMessage
;
1371 PLIST_ENTRY ListHead
;
1375 return(co_MsqPeekHardwareMessage(MessageQueue
, Window
,
1376 MsgFilterLow
, MsgFilterHigh
,
1380 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
1381 ListHead
= &MessageQueue
->PostedMessagesListHead
;
1382 while (CurrentEntry
!= ListHead
)
1384 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1386 if ((!Window
|| PtrToInt(Window
) == 1 || Window
->hSelf
== CurrentMessage
->Msg
.hwnd
) &&
1387 ((MsgFilterLow
== 0 && MsgFilterHigh
== 0) ||
1388 (MsgFilterLow
<= CurrentMessage
->Msg
.message
&&
1389 MsgFilterHigh
>= CurrentMessage
->Msg
.message
)))
1393 RemoveEntryList(&CurrentMessage
->ListEntry
);
1396 *Message
= CurrentMessage
;
1399 CurrentEntry
= CurrentEntry
->Flink
;
1406 co_MsqWaitForNewMessages(PUSER_MESSAGE_QUEUE MessageQueue
, PWINDOW_OBJECT WndFilter
,
1407 UINT MsgFilterMin
, UINT MsgFilterMax
)
1409 PVOID WaitObjects
[2] = {MessageQueue
->NewMessages
, &HardwareMessageEvent
};
1410 LARGE_INTEGER TimerExpiry
;
1411 PLARGE_INTEGER Timeout
;
1414 if (MsqGetFirstTimerExpiry(MessageQueue
, WndFilter
, MsgFilterMin
, MsgFilterMax
, &TimerExpiry
))
1416 Timeout
= &TimerExpiry
;
1423 IdlePing(); // Going to wait so send Idle ping.
1427 ret
= KeWaitForMultipleObjects(2,
1442 MsqIsHung(PUSER_MESSAGE_QUEUE MessageQueue
)
1444 LARGE_INTEGER LargeTickCount
;
1446 KeQueryTickCount(&LargeTickCount
);
1447 return ((LargeTickCount
.u
.LowPart
- MessageQueue
->LastMsgRead
) > MSQ_HUNG
);
1451 MsqInitializeMessageQueue(struct _ETHREAD
*Thread
, PUSER_MESSAGE_QUEUE MessageQueue
)
1453 LARGE_INTEGER LargeTickCount
;
1456 MessageQueue
->Thread
= Thread
;
1457 MessageQueue
->CaretInfo
= (PTHRDCARETINFO
)(MessageQueue
+ 1);
1458 InitializeListHead(&MessageQueue
->PostedMessagesListHead
);
1459 InitializeListHead(&MessageQueue
->SentMessagesListHead
);
1460 InitializeListHead(&MessageQueue
->HardwareMessagesListHead
);
1461 InitializeListHead(&MessageQueue
->TimerListHead
);
1462 InitializeListHead(&MessageQueue
->DispatchingMessagesHead
);
1463 InitializeListHead(&MessageQueue
->LocalDispatchingMessagesHead
);
1464 KeInitializeMutex(&MessageQueue
->HardwareLock
, 0);
1465 MessageQueue
->QuitPosted
= FALSE
;
1466 MessageQueue
->QuitExitCode
= 0;
1467 KeQueryTickCount(&LargeTickCount
);
1468 MessageQueue
->LastMsgRead
= LargeTickCount
.u
.LowPart
;
1469 MessageQueue
->FocusWindow
= NULL
;
1470 MessageQueue
->PaintCount
= 0;
1471 // HACK!!!!!!! Newbies that wrote this should hold your head down in shame! (jt)
1472 MessageQueue
->WakeMask
= ~0;
1473 MessageQueue
->NewMessagesHandle
= NULL
;
1475 Status
= ZwCreateEvent(&MessageQueue
->NewMessagesHandle
, EVENT_ALL_ACCESS
,
1476 NULL
, SynchronizationEvent
, FALSE
);
1477 if (!NT_SUCCESS(Status
))
1482 Status
= ObReferenceObjectByHandle(MessageQueue
->NewMessagesHandle
, 0,
1483 ExEventObjectType
, KernelMode
,
1484 (PVOID
*)&MessageQueue
->NewMessages
, NULL
);
1485 if (!NT_SUCCESS(Status
))
1487 ZwClose(MessageQueue
->NewMessagesHandle
);
1488 MessageQueue
->NewMessagesHandle
= NULL
;
1496 MsqCleanupMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue
)
1498 PLIST_ENTRY CurrentEntry
;
1499 PUSER_MESSAGE CurrentMessage
;
1500 PTIMER_ENTRY CurrentTimer
;
1501 PUSER_SENT_MESSAGE CurrentSentMessage
;
1503 /* cleanup posted messages */
1504 while (!IsListEmpty(&MessageQueue
->PostedMessagesListHead
))
1506 CurrentEntry
= RemoveHeadList(&MessageQueue
->PostedMessagesListHead
);
1507 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1509 MsqDestroyMessage(CurrentMessage
);
1512 /* remove the messages that have not yet been dispatched */
1513 while (!IsListEmpty(&MessageQueue
->SentMessagesListHead
))
1515 CurrentEntry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
1516 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1519 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1521 /* remove the message from the dispatching list */
1522 if(CurrentSentMessage
->DispatchingListEntry
.Flink
!= NULL
)
1524 RemoveEntryList(&CurrentSentMessage
->DispatchingListEntry
);
1527 /* wake the sender's thread */
1528 if (CurrentSentMessage
->CompletionEvent
!= NULL
)
1530 KeSetEvent(CurrentSentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1533 /* dereference our and the sender's message queue */
1534 IntDereferenceMessageQueue(MessageQueue
);
1535 IntDereferenceMessageQueue(CurrentSentMessage
->SenderQueue
);
1537 /* free the message */
1538 ExFreePool(CurrentSentMessage
);
1541 /* cleanup timers */
1542 while (! IsListEmpty(&MessageQueue
->TimerListHead
))
1544 CurrentEntry
= RemoveHeadList(&MessageQueue
->TimerListHead
);
1545 CurrentTimer
= CONTAINING_RECORD(CurrentEntry
, TIMER_ENTRY
, ListEntry
);
1546 ExFreeToPagedLookasideList(&TimerLookasideList
, CurrentTimer
);
1549 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
1550 ExitThread() was called in a SendMessage() umode callback */
1551 while (!IsListEmpty(&MessageQueue
->LocalDispatchingMessagesHead
))
1553 CurrentEntry
= RemoveHeadList(&MessageQueue
->LocalDispatchingMessagesHead
);
1554 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1557 /* remove the message from the dispatching list */
1558 if(CurrentSentMessage
->DispatchingListEntry
.Flink
!= NULL
)
1560 RemoveEntryList(&CurrentSentMessage
->DispatchingListEntry
);
1563 DPRINT("Notify the sender, the thread has been terminated while dispatching a message!\n");
1565 /* wake the sender's thread */
1566 if (CurrentSentMessage
->CompletionEvent
!= NULL
)
1568 KeSetEvent(CurrentSentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1571 /* dereference our and the sender's message queue */
1572 IntDereferenceMessageQueue(MessageQueue
);
1573 IntDereferenceMessageQueue(CurrentSentMessage
->SenderQueue
);
1575 /* free the message */
1576 ExFreePool(CurrentSentMessage
);
1579 /* tell other threads not to bother returning any info to us */
1580 while (! IsListEmpty(&MessageQueue
->DispatchingMessagesHead
))
1582 CurrentEntry
= RemoveHeadList(&MessageQueue
->DispatchingMessagesHead
);
1583 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1584 DispatchingListEntry
);
1585 CurrentSentMessage
->CompletionEvent
= NULL
;
1586 CurrentSentMessage
->Result
= NULL
;
1588 /* do NOT dereference our message queue as it might get attempted to be
1594 PUSER_MESSAGE_QUEUE FASTCALL
1595 MsqCreateMessageQueue(struct _ETHREAD
*Thread
)
1597 PUSER_MESSAGE_QUEUE MessageQueue
;
1599 MessageQueue
= (PUSER_MESSAGE_QUEUE
)ExAllocatePoolWithTag(NonPagedPool
,
1600 sizeof(USER_MESSAGE_QUEUE
) + sizeof(THRDCARETINFO
),
1608 RtlZeroMemory(MessageQueue
, sizeof(USER_MESSAGE_QUEUE
) + sizeof(THRDCARETINFO
));
1609 /* hold at least one reference until it'll be destroyed */
1610 IntReferenceMessageQueue(MessageQueue
);
1611 /* initialize the queue */
1612 if (!MsqInitializeMessageQueue(Thread
, MessageQueue
))
1614 IntDereferenceMessageQueue(MessageQueue
);
1618 return MessageQueue
;
1622 MsqDestroyMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue
)
1626 /* remove the message queue from any desktops */
1627 if ((desk
= InterlockedExchangePointer((PVOID
*)&MessageQueue
->Desktop
, 0)))
1629 (void)InterlockedExchangePointer((PVOID
*)&desk
->ActiveMessageQueue
, 0);
1630 IntDereferenceMessageQueue(MessageQueue
);
1634 MsqCleanupMessageQueue(MessageQueue
);
1636 /* decrease the reference counter, if it hits zero, the queue will be freed */
1637 IntDereferenceMessageQueue(MessageQueue
);
1641 MsqGetHooks(PUSER_MESSAGE_QUEUE Queue
)
1643 return Queue
->Hooks
;
1647 MsqSetHooks(PUSER_MESSAGE_QUEUE Queue
, PHOOKTABLE Hooks
)
1649 Queue
->Hooks
= Hooks
;
1653 MsqSetMessageExtraInfo(LPARAM lParam
)
1657 PUSER_MESSAGE_QUEUE MessageQueue
;
1659 pti
= PsGetCurrentThreadWin32Thread();
1660 MessageQueue
= pti
->MessageQueue
;
1666 Ret
= MessageQueue
->ExtraInfo
;
1667 MessageQueue
->ExtraInfo
= lParam
;
1673 MsqGetMessageExtraInfo(VOID
)
1676 PUSER_MESSAGE_QUEUE MessageQueue
;
1678 pti
= PsGetCurrentThreadWin32Thread();
1679 MessageQueue
= pti
->MessageQueue
;
1685 return MessageQueue
->ExtraInfo
;
1689 MsqSetStateWindow(PUSER_MESSAGE_QUEUE MessageQueue
, ULONG Type
, HWND hWnd
)
1695 case MSQ_STATE_CAPTURE
:
1696 Prev
= MessageQueue
->CaptureWindow
;
1697 MessageQueue
->CaptureWindow
= hWnd
;
1699 case MSQ_STATE_ACTIVE
:
1700 Prev
= MessageQueue
->ActiveWindow
;
1701 MessageQueue
->ActiveWindow
= hWnd
;
1703 case MSQ_STATE_FOCUS
:
1704 Prev
= MessageQueue
->FocusWindow
;
1705 MessageQueue
->FocusWindow
= hWnd
;
1707 case MSQ_STATE_MENUOWNER
:
1708 Prev
= MessageQueue
->MenuOwner
;
1709 MessageQueue
->MenuOwner
= hWnd
;
1711 case MSQ_STATE_MOVESIZE
:
1712 Prev
= MessageQueue
->MoveSize
;
1713 MessageQueue
->MoveSize
= hWnd
;
1715 case MSQ_STATE_CARET
:
1716 ASSERT(MessageQueue
->CaretInfo
);
1717 Prev
= MessageQueue
->CaretInfo
->hWnd
;
1718 MessageQueue
->CaretInfo
->hWnd
= hWnd
;
1726 static VOID FASTCALL
1727 DumpTimerList(PUSER_MESSAGE_QUEUE MessageQueue
)
1729 PLIST_ENTRY Current
;
1732 Current
= MessageQueue
->TimerListHead
.Flink
;
1733 if (Current
== &MessageQueue
->TimerListHead
)
1735 DPRINT("timer list is empty for queue %p\n", MessageQueue
);
1737 while (Current
!= &MessageQueue
->TimerListHead
)
1739 Timer
= CONTAINING_RECORD(Current
, TIMER_ENTRY
, ListEntry
);
1740 DPRINT("queue %p timer %p expiry %I64d wnd %x id %p period %u timerproc %p msg %u\n",
1741 MessageQueue
, Timer
, Timer
->ExpiryTime
.QuadPart
, Timer
->Wnd
, Timer
->IDEvent
,
1742 Timer
->Period
, Timer
->TimerFunc
, Timer
->Msg
);
1743 Current
= Current
->Flink
;
1746 #endif /* ! defined(NDEBUG) */
1748 /* Must have the message queue locked while calling this */
1749 static VOID FASTCALL
1750 InsertTimer(PUSER_MESSAGE_QUEUE MessageQueue
, PTIMER_ENTRY NewTimer
)
1752 PLIST_ENTRY Current
;
1754 Current
= MessageQueue
->TimerListHead
.Flink
;
1755 while (Current
!= &MessageQueue
->TimerListHead
)
1757 if (NewTimer
->ExpiryTime
.QuadPart
<
1758 CONTAINING_RECORD(Current
, TIMER_ENTRY
, ListEntry
)->ExpiryTime
.QuadPart
)
1762 Current
= Current
->Flink
;
1765 InsertTailList(Current
, &NewTimer
->ListEntry
);
1768 /* Must have the message queue locked while calling this */
1769 static PTIMER_ENTRY FASTCALL
1770 RemoveTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
, UINT_PTR IDEvent
, UINT Msg
)
1773 PLIST_ENTRY EnumEntry
;
1775 /* Remove timer if already in the queue */
1776 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1777 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1779 Timer
= CONTAINING_RECORD(EnumEntry
, TIMER_ENTRY
, ListEntry
);
1780 EnumEntry
= EnumEntry
->Flink
;
1782 if (Timer
->Wnd
== Wnd
&&
1783 Timer
->IDEvent
== IDEvent
&&
1786 RemoveEntryList(&Timer
->ListEntry
);
1795 MsqSetTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
,
1796 UINT_PTR IDEvent
, UINT Period
, TIMERPROC TimerFunc
,
1800 LARGE_INTEGER CurrentTime
;
1802 DPRINT("MsqSetTimer queue %p wnd %x id %p period %u timerproc %p msg %d\n",
1803 MessageQueue
, Wnd
, IDEvent
, Period
, TimerFunc
, Msg
);
1805 Timer
= RemoveTimer(MessageQueue
, Wnd
, IDEvent
, Msg
);
1808 Timer
= ExAllocateFromPagedLookasideList(&TimerLookasideList
);
1811 DPRINT1("Failed to allocate timer entry\n");
1814 DPRINT("Allocated new timer entry %p\n", Timer
);
1816 Timer
->IDEvent
= IDEvent
;
1821 DPRINT("Updating existing timer entry %p\n", Timer
);
1824 KeQuerySystemTime(&CurrentTime
);
1825 Timer
->ExpiryTime
.QuadPart
= CurrentTime
.QuadPart
+
1826 (ULONGLONG
) Period
* (ULONGLONG
) 10000;
1827 Timer
->Period
= Period
;
1828 Timer
->TimerFunc
= TimerFunc
;
1829 DPRINT("Insert timer now %I64d expiry %I64d\n", CurrentTime
.QuadPart
,
1830 Timer
->ExpiryTime
.QuadPart
);
1832 InsertTimer(MessageQueue
, Timer
);
1836 DumpTimerList(MessageQueue
);
1837 #endif /* ! defined(NDEBUG) */
1843 MsqKillTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
,
1844 UINT_PTR IDEvent
, UINT Msg
)
1848 DPRINT("MsqKillTimer queue %p wnd %x id %p msg %d\n",
1849 MessageQueue
, Wnd
, IDEvent
, Msg
);
1851 Timer
= RemoveTimer(MessageQueue
, Wnd
, IDEvent
, Msg
);
1855 DPRINT("Failed to remove timer from list, not found\n");
1859 ExFreeToPagedLookasideList(&TimerLookasideList
, Timer
);
1863 DumpTimerList(MessageQueue
);
1864 #endif /* ! defined(NDEBUG) */
1866 return NULL
!= Timer
;
1870 MsqGetTimerMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
1871 PWINDOW_OBJECT WindowFilter
, UINT MsgFilterMin
, UINT MsgFilterMax
,
1872 MSG
*Msg
, BOOLEAN Restart
)
1875 LARGE_INTEGER CurrentTime
;
1876 LARGE_INTEGER LargeTickCount
;
1877 PLIST_ENTRY EnumEntry
;
1881 DPRINT("MsqGetTimerMessage queue %p msg %p restart %s\n",
1882 MessageQueue
, Msg
, Restart
? "TRUE" : "FALSE");
1884 KeQuerySystemTime(&CurrentTime
);
1885 DPRINT("Current time %I64d\n", CurrentTime
.QuadPart
);
1886 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1888 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1890 Timer
= CONTAINING_RECORD(MessageQueue
->TimerListHead
.Flink
,
1891 TIMER_ENTRY
, ListEntry
);
1892 DPRINT("Checking timer %p wnd %x expiry %I64d\n", Timer
, Timer
->Wnd
,
1893 Timer
->ExpiryTime
.QuadPart
);
1894 EnumEntry
= EnumEntry
->Flink
;
1895 if ((NULL
== WindowFilter
|| Timer
->Wnd
== WindowFilter
->hSelf
) &&
1896 ((MsgFilterMin
== 0 && MsgFilterMax
== 0) ||
1897 (MsgFilterMin
<= Timer
->Msg
&&
1898 Timer
->Msg
<= MsgFilterMax
)))
1900 if (Timer
->ExpiryTime
.QuadPart
<= CurrentTime
.QuadPart
)
1902 DPRINT("Timer is expired\n");
1908 DPRINT("No need to check later timers\n");
1914 DPRINT("timer %p (wnd %x msg %d) failed filter wnd %x msgmin %d msgmax %d\n",
1915 Timer
, Timer
->Wnd
, Timer
->Msg
, WindowFilter
->hSelf
, MsgFilterMin
, MsgFilterMax
);
1921 DPRINT("No timer pending\n");
1925 Msg
->hwnd
= Timer
->Wnd
;
1926 Msg
->message
= Timer
->Msg
;
1927 Msg
->wParam
= (WPARAM
) Timer
->IDEvent
;
1928 Msg
->lParam
= (LPARAM
) Timer
->TimerFunc
;
1929 KeQueryTickCount(&LargeTickCount
);
1930 Msg
->time
= MsqCalculateMessageTime(&LargeTickCount
);
1931 pti
= PsGetCurrentThreadWin32Thread();
1932 IntGetCursorLocation(pti
->Desktop
->WindowStation
,
1937 RemoveEntryList(&Timer
->ListEntry
);
1938 Timer
->ExpiryTime
.QuadPart
= CurrentTime
.QuadPart
+
1939 (ULONGLONG
) Timer
->Period
* (ULONGLONG
) 10000;
1940 DPRINT("Restarting timer %p expires %I64d\n", Timer
, Timer
->ExpiryTime
.QuadPart
);
1941 InsertTimer(MessageQueue
, Timer
);
1945 DumpTimerList(MessageQueue
);
1946 #endif /* ! defined(NDEBUG) */
1950 DPRINT("Created message wnd %x msg %d wParam %u lParam %u\n", Msg
->hwnd
, Msg
->message
,
1951 Msg
->wParam
, Msg
->lParam
);
1957 MsqRemoveTimersWindow(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
)
1960 PLIST_ENTRY EnumEntry
;
1962 DPRINT("MsqRemoveTimersWindow queue %p wnd %x\n", MessageQueue
, Wnd
);
1964 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1965 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1967 Timer
= CONTAINING_RECORD(EnumEntry
, TIMER_ENTRY
, ListEntry
);
1968 EnumEntry
= EnumEntry
->Flink
;
1969 if (Timer
->Wnd
== Wnd
)
1971 DPRINT("Removing timer %p because its window is going away\n", Timer
);
1972 RemoveEntryList(&Timer
->ListEntry
);
1973 ExFreeToPagedLookasideList(&TimerLookasideList
, Timer
);
1978 DumpTimerList(MessageQueue
);
1979 #endif /* ! defined(NDEBUG) */
1984 MsqGetFirstTimerExpiry(PUSER_MESSAGE_QUEUE MessageQueue
,
1985 PWINDOW_OBJECT WndFilter
, UINT MsgFilterMin
, UINT MsgFilterMax
,
1986 PLARGE_INTEGER FirstTimerExpiry
)
1989 PLIST_ENTRY EnumEntry
;
1991 DPRINT("MsqGetFirstTimerExpiry queue %p wndfilter %x msgfiltermin %d msgfiltermax %d expiry %p\n",
1992 MessageQueue
, WndFilter
, MsgFilterMin
, MsgFilterMax
, FirstTimerExpiry
);
1994 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1995 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1997 Timer
= CONTAINING_RECORD(MessageQueue
->TimerListHead
.Flink
,
1998 TIMER_ENTRY
, ListEntry
);
1999 EnumEntry
= EnumEntry
->Flink
;
2000 if ((NULL
== WndFilter
|| PtrToInt(WndFilter
) == 1 || Timer
->Wnd
== WndFilter
->hSelf
) &&
2001 ((MsgFilterMin
== 0 && MsgFilterMax
== 0) ||
2002 (MsgFilterMin
<= Timer
->Msg
&&
2003 Timer
->Msg
<= MsgFilterMax
)))
2005 *FirstTimerExpiry
= Timer
->ExpiryTime
;
2006 DPRINT("First timer expires %I64d\n", Timer
->ExpiryTime
);