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
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, 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 MsqIsDblClk(LPMSG Msg
, BOOL Remove
)
239 PWINSTATION_OBJECT WinStaObject
;
240 PSYSTEM_CURSORINFO CurInfo
;
244 pti
= PsGetCurrentThreadWin32Thread();
245 if (pti
->Desktop
== NULL
)
250 WinStaObject
= pti
->Desktop
->WindowStation
;
252 CurInfo
= IntGetSysCursorInfo(WinStaObject
);
253 Res
= (Msg
->hwnd
== (HWND
)CurInfo
->LastClkWnd
) &&
254 ((Msg
->time
- CurInfo
->LastBtnDown
) < CurInfo
->DblClickSpeed
);
258 dX
= CurInfo
->LastBtnDownX
- Msg
->pt
.x
;
259 dY
= CurInfo
->LastBtnDownY
- Msg
->pt
.y
;
265 Res
= (dX
<= CurInfo
->DblClickWidth
) &&
266 (dY
<= CurInfo
->DblClickHeight
);
270 if(CurInfo
->ButtonsDown
)
271 Res
= (CurInfo
->ButtonsDown
== Msg
->message
);
279 CurInfo
->LastBtnDown
= 0;
280 CurInfo
->LastBtnDownX
= Msg
->pt
.x
;
281 CurInfo
->LastBtnDownY
= Msg
->pt
.y
;
282 CurInfo
->LastClkWnd
= NULL
;
283 CurInfo
->ButtonsDown
= Msg
->message
;
287 CurInfo
->LastBtnDownX
= Msg
->pt
.x
;
288 CurInfo
->LastBtnDownY
= Msg
->pt
.y
;
289 CurInfo
->LastClkWnd
= (HANDLE
)Msg
->hwnd
;
290 CurInfo
->LastBtnDown
= Msg
->time
;
291 CurInfo
->ButtonsDown
= Msg
->message
;
299 co_MsqTranslateMouseMessage(PUSER_MESSAGE_QUEUE MessageQueue
, PWINDOW_OBJECT Window
, UINT FilterLow
, UINT FilterHigh
,
300 PUSER_MESSAGE Message
, BOOL Remove
, PBOOL Freed
,
301 PWINDOW_OBJECT ScopeWin
, PPOINT ScreenPoint
, BOOL FromGlobalQueue
, PLIST_ENTRY
*Next
)
303 USHORT Msg
= Message
->Msg
.message
;
304 PWINDOW_OBJECT CaptureWindow
= NULL
;
307 ASSERT_REFS_CO(ScopeWin
);
310 co_WinPosWindowFromPoint can return a Window, and in that case
311 that window has a ref that we need to deref. Thats why we add "dummy"
312 refs in all other cases.
315 hCaptureWin
= IntGetCaptureWindow();
316 if (hCaptureWin
== NULL
)
318 if (Msg
== WM_MOUSEWHEEL
)
320 CaptureWindow
= UserGetWindowObject(IntGetFocusWindow());
321 if (CaptureWindow
) UserReferenceObject(CaptureWindow
);
325 co_WinPosWindowFromPoint(ScopeWin
, NULL
, &Message
->Msg
.pt
, &CaptureWindow
);
326 if(CaptureWindow
== NULL
)
328 CaptureWindow
= ScopeWin
;
329 if (CaptureWindow
) UserReferenceObject(CaptureWindow
);
333 /* this is the one case where we dont add a ref, since the returned
334 window is already referenced */
340 /* FIXME - window messages should go to the right window if no buttons are
342 CaptureWindow
= UserGetWindowObject(hCaptureWin
);
343 if (CaptureWindow
) UserReferenceObject(CaptureWindow
);
348 if (CaptureWindow
== NULL
)
352 RemoveEntryList(&Message
->ListEntry
);
353 if(MessageQueue
->MouseMoveMsg
== Message
)
355 MessageQueue
->MouseMoveMsg
= NULL
;
358 // when FromGlobalQueue is true, the caller has already removed the Message
364 if (CaptureWindow
->MessageQueue
!= MessageQueue
)
366 if (! FromGlobalQueue
)
368 DPRINT("Moving msg between private queues\n");
369 /* This message is already queued in a private queue, but we need
370 * to move it to a different queue, perhaps because a new window
371 * was created which now covers the screen area previously taken
372 * by another window. To move it, we need to take it out of the
373 * old queue. Note that we're already holding the lock mutexes of the
375 RemoveEntryList(&Message
->ListEntry
);
377 /* remove the pointer for the current WM_MOUSEMOVE message in case we
379 if(MessageQueue
->MouseMoveMsg
== Message
)
381 MessageQueue
->MouseMoveMsg
= NULL
;
385 /* lock the destination message queue, so we don't get in trouble with other
386 threads, messing with it at the same time */
387 IntLockHardwareMessageQueue(CaptureWindow
->MessageQueue
);
388 InsertTailList(&CaptureWindow
->MessageQueue
->HardwareMessagesListHead
,
389 &Message
->ListEntry
);
390 if(Message
->Msg
.message
== WM_MOUSEMOVE
)
392 if(CaptureWindow
->MessageQueue
->MouseMoveMsg
)
394 /* remove the old WM_MOUSEMOVE message, we're processing a more recent
396 RemoveEntryList(&CaptureWindow
->MessageQueue
->MouseMoveMsg
->ListEntry
);
397 ExFreePool(CaptureWindow
->MessageQueue
->MouseMoveMsg
);
399 /* save the pointer to the WM_MOUSEMOVE message in the new queue */
400 CaptureWindow
->MessageQueue
->MouseMoveMsg
= Message
;
402 CaptureWindow
->MessageQueue
->QueueBits
|= QS_MOUSEMOVE
;
403 CaptureWindow
->MessageQueue
->ChangedBits
|= QS_MOUSEMOVE
;
404 if (CaptureWindow
->MessageQueue
->WakeMask
& QS_MOUSEMOVE
)
405 KeSetEvent(CaptureWindow
->MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
409 CaptureWindow
->MessageQueue
->QueueBits
|= QS_MOUSEBUTTON
;
410 CaptureWindow
->MessageQueue
->ChangedBits
|= QS_MOUSEBUTTON
;
411 if (CaptureWindow
->MessageQueue
->WakeMask
& QS_MOUSEBUTTON
)
412 KeSetEvent(CaptureWindow
->MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
414 IntUnLockHardwareMessageQueue(CaptureWindow
->MessageQueue
);
417 UserDereferenceObject(CaptureWindow
);
421 /* From here on, we're in the same message queue as the caller! */
423 *ScreenPoint
= Message
->Msg
.pt
;
425 if((Window
!= NULL
&& (int)Window
!= 1 && CaptureWindow
->hSelf
!= Window
->hSelf
) ||
426 ((FilterLow
!= 0 || FilterHigh
!= 0) && (Msg
< FilterLow
|| Msg
> FilterHigh
)))
428 /* Reject the message because it doesn't match the filter */
432 /* Lock the message queue so no other thread can mess with it.
433 Our own message queue is not locked while fetching from the global
434 queue, so we have to make sure nothing interferes! */
435 IntLockHardwareMessageQueue(CaptureWindow
->MessageQueue
);
436 /* if we're from the global queue, we need to add our message to our
437 private queue so we don't loose it! */
438 InsertTailList(&CaptureWindow
->MessageQueue
->HardwareMessagesListHead
,
439 &Message
->ListEntry
);
442 if (Message
->Msg
.message
== WM_MOUSEMOVE
)
444 if(CaptureWindow
->MessageQueue
->MouseMoveMsg
&&
445 (CaptureWindow
->MessageQueue
->MouseMoveMsg
!= Message
))
447 /* delete the old message */
448 RemoveEntryList(&CaptureWindow
->MessageQueue
->MouseMoveMsg
->ListEntry
);
449 ExFreePool(CaptureWindow
->MessageQueue
->MouseMoveMsg
);
450 if (!FromGlobalQueue
)
452 // We might have deleted the next one in our queue, so fix next
453 *Next
= Message
->ListEntry
.Flink
;
456 /* always save a pointer to this WM_MOUSEMOVE message here because we're
457 sure that the message is in the private queue */
458 CaptureWindow
->MessageQueue
->MouseMoveMsg
= Message
;
462 IntUnLockHardwareMessageQueue(CaptureWindow
->MessageQueue
);
465 UserDereferenceObject(CaptureWindow
);
470 /* FIXME - only assign if removing? */
471 Message
->Msg
.hwnd
= CaptureWindow
->hSelf
;
472 Message
->Msg
.message
= Msg
;
473 Message
->Msg
.lParam
= MAKELONG(Message
->Msg
.pt
.x
, Message
->Msg
.pt
.y
);
475 /* remove the reference to the current WM_(NC)MOUSEMOVE message, if this message
477 if (Message
->Msg
.message
== WM_MOUSEMOVE
||
478 Message
->Msg
.message
== WM_NCMOUSEMOVE
)
482 /* Lock the message queue so no other thread can mess with it.
483 Our own message queue is not locked while fetching from the global
484 queue, so we have to make sure nothing interferes! */
485 IntLockHardwareMessageQueue(CaptureWindow
->MessageQueue
);
486 if(CaptureWindow
->MessageQueue
->MouseMoveMsg
)
488 /* delete the WM_(NC)MOUSEMOVE message in the private queue, we're dealing
489 with one that's been sent later */
490 RemoveEntryList(&CaptureWindow
->MessageQueue
->MouseMoveMsg
->ListEntry
);
491 ExFreePool(CaptureWindow
->MessageQueue
->MouseMoveMsg
);
492 /* our message is not in the private queue so we can remove the pointer
493 instead of setting it to the current message we're processing */
494 CaptureWindow
->MessageQueue
->MouseMoveMsg
= NULL
;
496 IntUnLockHardwareMessageQueue(CaptureWindow
->MessageQueue
);
498 else if (CaptureWindow
->MessageQueue
->MouseMoveMsg
== Message
)
500 CaptureWindow
->MessageQueue
->MouseMoveMsg
= NULL
;
504 UserDereferenceObject(CaptureWindow
);
510 co_MsqPeekHardwareMessage(PUSER_MESSAGE_QUEUE MessageQueue
, PWINDOW_OBJECT Window
,
511 UINT FilterLow
, UINT FilterHigh
, BOOL Remove
,
512 PUSER_MESSAGE
* Message
)
517 PLIST_ENTRY CurrentEntry
;
518 PWINDOW_OBJECT DesktopWindow
= NULL
;
519 PVOID WaitObjects
[2];
521 DECLARE_RETURN(BOOL
);
522 USER_REFERENCE_ENTRY Ref
;
523 PDESKTOPINFO Desk
= NULL
;
525 WaitObjects
[1] = MessageQueue
->NewMessages
;
526 WaitObjects
[0] = &HardwareMessageQueueLock
;
533 WaitStatus
= KeWaitForMultipleObjects(2, WaitObjects
, WaitAny
, UserRequest
,
534 UserMode
, FALSE
, NULL
, NULL
);
538 while (co_MsqDispatchOneSentMessage(MessageQueue
))
543 while (NT_SUCCESS(WaitStatus
) && STATUS_WAIT_0
!= WaitStatus
);
545 DesktopWindow
= UserGetWindowObject(IntGetDesktopWindow());
549 UserRefObjectCo(DesktopWindow
, &Ref
);//can DesktopWindow be NULL?
550 Desk
= DesktopWindow
->ti
->pDeskInfo
;
553 /* Process messages in the message queue itself. */
554 IntLockHardwareMessageQueue(MessageQueue
);
555 CurrentEntry
= MessageQueue
->HardwareMessagesListHead
.Flink
;
556 while (CurrentEntry
!= &MessageQueue
->HardwareMessagesListHead
)
558 PUSER_MESSAGE Current
=
559 CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
, ListEntry
);
560 CurrentEntry
= CurrentEntry
->Flink
;
561 if (Current
->Msg
.message
>= WM_MOUSEFIRST
&&
562 Current
->Msg
.message
<= WM_MOUSELAST
)
566 Accept
= co_MsqTranslateMouseMessage(MessageQueue
, Window
, FilterLow
, FilterHigh
,
567 Current
, Remove
, &Freed
,
568 DesktopWindow
, &ScreenPoint
, FALSE
, &CurrentEntry
);
573 RemoveEntryList(&Current
->ListEntry
);
575 IntUnLockHardwareMessageQueue(MessageQueue
);
576 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
580 Desk
->LastInputWasKbd
= FALSE
;
587 IntUnLockHardwareMessageQueue(MessageQueue
);
589 /* Now try the global queue. */
591 /* Transfer all messages from the DPC accessible queue to the main queue. */
592 IntLockSystemMessageQueue(OldIrql
);
593 while (SystemMessageQueueCount
> 0)
595 PUSER_MESSAGE UserMsg
;
599 ASSERT(SystemMessageQueueHead
< SYSTEM_MESSAGE_QUEUE_SIZE
);
600 Msg
= SystemMessageQueue
[SystemMessageQueueHead
];
601 SystemMessageQueueHead
=
602 (SystemMessageQueueHead
+ 1) % SYSTEM_MESSAGE_QUEUE_SIZE
;
603 SystemMessageQueueCount
--;
604 IntUnLockSystemMessageQueue(OldIrql
);
605 if (WM_MOUSEFIRST
<= Msg
.message
&& Msg
.message
<= WM_MOUSELAST
)
607 MSLLHOOKSTRUCT MouseHookData
;
609 MouseHookData
.pt
.x
= LOWORD(Msg
.lParam
);
610 MouseHookData
.pt
.y
= HIWORD(Msg
.lParam
);
614 MouseHookData
.mouseData
= MAKELONG(0, GET_WHEEL_DELTA_WPARAM(Msg
.wParam
));
618 case WM_XBUTTONDBLCLK
:
619 case WM_NCXBUTTONDOWN
:
621 case WM_NCXBUTTONDBLCLK
:
622 MouseHookData
.mouseData
= MAKELONG(0, HIWORD(Msg
.wParam
));
625 MouseHookData
.mouseData
= 0;
628 MouseHookData
.flags
= 0;
629 MouseHookData
.time
= Msg
.time
;
630 MouseHookData
.dwExtraInfo
= 0;
631 ProcessMessage
= (0 == co_HOOK_CallHooks(WH_MOUSE_LL
, HC_ACTION
,
632 Msg
.message
, (LPARAM
) &MouseHookData
));
636 ProcessMessage
= TRUE
;
640 UserMsg
= ExAllocateFromPagedLookasideList(&MessageLookasideList
);
641 /* What to do if out of memory? For now we just panic a bit in debug */
643 UserMsg
->FreeLParam
= FALSE
;
645 InsertTailList(&HardwareMessageQueueHead
, &UserMsg
->ListEntry
);
647 IntLockSystemMessageQueue(OldIrql
);
649 HardwareMessageQueueStamp
++;
650 IntUnLockSystemMessageQueue(OldIrql
);
652 /* Process messages in the queue until we find one to return. */
653 CurrentEntry
= HardwareMessageQueueHead
.Flink
;
654 while (CurrentEntry
!= &HardwareMessageQueueHead
)
656 PUSER_MESSAGE Current
=
657 CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
, ListEntry
);
658 CurrentEntry
= CurrentEntry
->Flink
;
659 RemoveEntryList(&Current
->ListEntry
);
660 HardwareMessageQueueStamp
++;
661 if (Current
->Msg
.message
>= WM_MOUSEFIRST
&&
662 Current
->Msg
.message
<= WM_MOUSELAST
)
664 const ULONG ActiveStamp
= HardwareMessageQueueStamp
;
665 /* Translate the message. */
666 Accept
= co_MsqTranslateMouseMessage(MessageQueue
, Window
, FilterLow
, FilterHigh
,
667 Current
, Remove
, &Freed
,
668 DesktopWindow
, &ScreenPoint
, TRUE
, NULL
);
671 /* Check for no more messages in the system queue. */
672 IntLockSystemMessageQueue(OldIrql
);
673 if (SystemMessageQueueCount
== 0 &&
674 IsListEmpty(&HardwareMessageQueueHead
))
676 KeClearEvent(&HardwareMessageEvent
);
678 IntUnLockSystemMessageQueue(OldIrql
);
681 If we aren't removing the message then add it to the private
686 IntLockHardwareMessageQueue(MessageQueue
);
687 if(Current
->Msg
.message
== WM_MOUSEMOVE
)
689 if(MessageQueue
->MouseMoveMsg
)
691 RemoveEntryList(&MessageQueue
->MouseMoveMsg
->ListEntry
);
692 ExFreePool(MessageQueue
->MouseMoveMsg
);
694 MessageQueue
->MouseMoveMsg
= Current
;
696 InsertTailList(&MessageQueue
->HardwareMessagesListHead
,
697 &Current
->ListEntry
);
698 IntUnLockHardwareMessageQueue(MessageQueue
);
700 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
705 /* If the contents of the queue changed then restart processing. */
706 if (HardwareMessageQueueStamp
!= ActiveStamp
)
708 CurrentEntry
= HardwareMessageQueueHead
.Flink
;
714 /* Check if the system message queue is now empty. */
715 IntLockSystemMessageQueue(OldIrql
);
716 if (SystemMessageQueueCount
== 0 && IsListEmpty(&HardwareMessageQueueHead
))
718 KeClearEvent(&HardwareMessageEvent
);
720 IntUnLockSystemMessageQueue(OldIrql
);
721 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
726 if (DesktopWindow
) UserDerefObjectCo(DesktopWindow
);
732 // Note: Only called from input.c.
735 co_MsqPostKeyboardMessage(UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
737 PUSER_MESSAGE_QUEUE FocusMessageQueue
;
739 LARGE_INTEGER LargeTickCount
;
740 KBDLLHOOKSTRUCT KbdHookData
;
742 BOOLEAN Entered
= FALSE
;
744 DPRINT("MsqPostKeyboardMessage(uMsg 0x%x, wParam 0x%x, lParam 0x%x)\n",
745 uMsg
, wParam
, lParam
);
747 // Condition may arise when calling MsqPostMessage and waiting for an event.
748 if (!UserIsEntered())
750 // Fixme: Not sure ATM if this thread is locked.
751 UserEnterExclusive();
755 FocusMessageQueue
= IntGetFocusMessageQueue();
759 if (FocusMessageQueue
&& (FocusMessageQueue
->FocusWindow
!= (HWND
)0))
760 Msg
.hwnd
= FocusMessageQueue
->FocusWindow
;
766 KeQueryTickCount(&LargeTickCount
);
767 Msg
.time
= MsqCalculateMessageTime(&LargeTickCount
);
769 Event
.message
= Msg
.message
;
770 Event
.hwnd
= Msg
.hwnd
;
771 Event
.time
= Msg
.time
;
772 Event
.paramL
= (Msg
.wParam
& 0xFF) | (HIWORD(Msg
.lParam
) << 8);
773 Event
.paramH
= Msg
.lParam
& 0x7FFF;
774 if (HIWORD(Msg
.lParam
) & 0x0100) Event
.paramH
|= 0x8000;
775 co_HOOK_CallHooks( WH_JOURNALRECORD
, HC_ACTION
, 0, (LPARAM
)&Event
);
777 /* We can't get the Msg.pt point here since we don't know thread
778 (and thus the window station) the message will end up in yet. */
780 KbdHookData
.vkCode
= Msg
.wParam
;
781 KbdHookData
.scanCode
= (Msg
.lParam
>> 16) & 0xff;
782 KbdHookData
.flags
= (0 == (Msg
.lParam
& 0x01000000) ? 0 : LLKHF_EXTENDED
) |
783 (0 == (Msg
.lParam
& 0x20000000) ? 0 : LLKHF_ALTDOWN
) |
784 (0 == (Msg
.lParam
& 0x80000000) ? 0 : LLKHF_UP
);
785 KbdHookData
.time
= Msg
.time
;
786 KbdHookData
.dwExtraInfo
= 0;
787 if (co_HOOK_CallHooks(WH_KEYBOARD_LL
, HC_ACTION
, Msg
.message
, (LPARAM
) &KbdHookData
))
789 DPRINT("Kbd msg %d wParam %d lParam 0x%08x dropped by WH_KEYBOARD_LL hook\n",
790 Msg
.message
, Msg
.wParam
, Msg
.lParam
);
791 if (Entered
) UserLeave();
795 if (FocusMessageQueue
== NULL
)
797 DPRINT("No focus message queue\n");
798 if (Entered
) UserLeave();
802 if (FocusMessageQueue
->FocusWindow
!= (HWND
)0)
804 Msg
.hwnd
= FocusMessageQueue
->FocusWindow
;
805 DPRINT("Msg.hwnd = %x\n", Msg
.hwnd
);
807 FocusMessageQueue
->Desktop
->DesktopInfo
->LastInputWasKbd
= TRUE
;
809 IntGetCursorLocation(FocusMessageQueue
->Desktop
->WindowStation
,
811 MsqPostMessage(FocusMessageQueue
, &Msg
, FALSE
, QS_KEY
);
815 DPRINT("Invalid focus window handle\n");
818 if (Entered
) UserLeave();
823 MsqPostHotKeyMessage(PVOID Thread
, HWND hWnd
, WPARAM wParam
, LPARAM lParam
)
825 PWINDOW_OBJECT Window
;
826 PTHREADINFO Win32Thread
;
827 PWINSTATION_OBJECT WinSta
;
829 LARGE_INTEGER LargeTickCount
;
832 Status
= ObReferenceObjectByPointer (Thread
,
836 if (!NT_SUCCESS(Status
))
839 Win32Thread
= ((PETHREAD
)Thread
)->Tcb
.Win32Thread
;
840 if (Win32Thread
== NULL
|| Win32Thread
->MessageQueue
== NULL
)
842 ObDereferenceObject ((PETHREAD
)Thread
);
846 WinSta
= Win32Thread
->Desktop
->WindowStation
;
847 Window
= IntGetWindowObject(hWnd
);
850 ObDereferenceObject ((PETHREAD
)Thread
);
855 Mesg
.message
= WM_HOTKEY
;
856 Mesg
.wParam
= wParam
;
857 Mesg
.lParam
= lParam
;
858 KeQueryTickCount(&LargeTickCount
);
859 Mesg
.time
= MsqCalculateMessageTime(&LargeTickCount
);
860 IntGetCursorLocation(WinSta
, &Mesg
.pt
);
861 MsqPostMessage(Window
->MessageQueue
, &Mesg
, FALSE
, QS_HOTKEY
);
862 UserDereferenceObject(Window
);
863 ObDereferenceObject (Thread
);
865 // InsertHeadList(&pThread->MessageQueue->PostedMessagesListHead,
866 // &Message->ListEntry);
867 // KeSetEvent(pThread->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
870 PUSER_MESSAGE FASTCALL
871 MsqCreateMessage(LPMSG Msg
, BOOLEAN FreeLParam
)
873 PUSER_MESSAGE Message
;
875 Message
= ExAllocateFromPagedLookasideList(&MessageLookasideList
);
881 Message
->FreeLParam
= FreeLParam
;
882 RtlMoveMemory(&Message
->Msg
, Msg
, sizeof(MSG
));
888 MsqDestroyMessage(PUSER_MESSAGE Message
)
890 ExFreeToPagedLookasideList(&MessageLookasideList
, Message
);
894 co_MsqDispatchSentNotifyMessages(PUSER_MESSAGE_QUEUE MessageQueue
)
896 PLIST_ENTRY ListEntry
;
897 PUSER_SENT_MESSAGE_NOTIFY Message
;
899 while (!IsListEmpty(&MessageQueue
->SentMessagesListHead
))
901 ListEntry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
902 Message
= CONTAINING_RECORD(ListEntry
, USER_SENT_MESSAGE_NOTIFY
,
905 co_IntCallSentMessageCallback(Message
->CompletionCallback
,
908 Message
->CompletionCallbackContext
,
916 MsqPeekSentMessages(PUSER_MESSAGE_QUEUE MessageQueue
)
918 return(!IsListEmpty(&MessageQueue
->SentMessagesListHead
));
922 co_MsqDispatchOneSentMessage(PUSER_MESSAGE_QUEUE MessageQueue
)
924 PUSER_SENT_MESSAGE Message
;
928 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage
;
930 if (IsListEmpty(&MessageQueue
->SentMessagesListHead
))
935 /* remove it from the list of pending messages */
936 Entry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
937 Message
= CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
);
939 /* insert it to the list of messages that are currently dispatched by this
941 InsertTailList(&MessageQueue
->LocalDispatchingMessagesHead
,
942 &Message
->ListEntry
);
944 if (Message
->HookMessage
== MSQ_ISHOOK
)
946 Result
= co_HOOK_CallHooks(Message
->Msg
.message
,
947 (INT
)(INT_PTR
)Message
->Msg
.hwnd
,
949 Message
->Msg
.lParam
);
951 else if (Message
->HookMessage
== MSQ_ISEVENT
)
953 Result
= co_EVENT_CallEvents( Message
->Msg
.message
,
956 Message
->Msg
.lParam
);
960 /* Call the window procedure. */
961 Result
= co_IntSendMessage(Message
->Msg
.hwnd
,
962 Message
->Msg
.message
,
964 Message
->Msg
.lParam
);
967 /* remove the message from the local dispatching list, because it doesn't need
968 to be cleaned up on thread termination anymore */
969 RemoveEntryList(&Message
->ListEntry
);
971 /* remove the message from the dispatching list, so lock the sender's message queue */
972 SenderReturned
= (Message
->DispatchingListEntry
.Flink
== NULL
);
975 /* only remove it from the dispatching list if not already removed by a timeout */
976 RemoveEntryList(&Message
->DispatchingListEntry
);
978 /* still keep the sender's message queue locked, so the sender can't exit the
979 MsqSendMessage() function (if timed out) */
981 /* Let the sender know the result. */
982 if (Message
->Result
!= NULL
)
984 *Message
->Result
= Result
;
987 /* Notify the sender. */
988 if (Message
->CompletionEvent
!= NULL
)
990 KeSetEvent(Message
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
993 /* Notify the sender if they specified a callback. */
994 if (!SenderReturned
&& Message
->CompletionCallback
!= NULL
)
996 if(!(NotifyMessage
= ExAllocatePoolWithTag(NonPagedPool
,
997 sizeof(USER_SENT_MESSAGE_NOTIFY
), TAG_USRMSG
)))
999 DPRINT1("MsqDispatchOneSentMessage(): Not enough memory to create a callback notify message\n");
1002 NotifyMessage
->CompletionCallback
=
1003 Message
->CompletionCallback
;
1004 NotifyMessage
->CompletionCallbackContext
=
1005 Message
->CompletionCallbackContext
;
1006 NotifyMessage
->Result
= Result
;
1007 NotifyMessage
->hWnd
= Message
->Msg
.hwnd
;
1008 NotifyMessage
->Msg
= Message
->Msg
.message
;
1009 MsqSendNotifyMessage(Message
->SenderQueue
, NotifyMessage
);
1014 /* dereference both sender and our queue */
1015 IntDereferenceMessageQueue(MessageQueue
);
1016 IntDereferenceMessageQueue(Message
->SenderQueue
);
1018 /* free the message */
1019 ExFreePool(Message
);
1024 MsqRemoveWindowMessagesFromQueue(PVOID pWindow
)
1026 PUSER_SENT_MESSAGE SentMessage
;
1027 PUSER_MESSAGE PostedMessage
;
1028 PUSER_MESSAGE_QUEUE MessageQueue
;
1029 PLIST_ENTRY CurrentEntry
, ListHead
;
1030 PWINDOW_OBJECT Window
= pWindow
;
1034 MessageQueue
= Window
->MessageQueue
;
1035 ASSERT(MessageQueue
);
1037 /* remove the posted messages for this window */
1038 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
1039 ListHead
= &MessageQueue
->PostedMessagesListHead
;
1040 while (CurrentEntry
!= ListHead
)
1042 PostedMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1044 if (PostedMessage
->Msg
.hwnd
== Window
->hSelf
)
1046 RemoveEntryList(&PostedMessage
->ListEntry
);
1047 MsqDestroyMessage(PostedMessage
);
1048 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
1052 CurrentEntry
= CurrentEntry
->Flink
;
1056 /* remove the sent messages for this window */
1057 CurrentEntry
= MessageQueue
->SentMessagesListHead
.Flink
;
1058 ListHead
= &MessageQueue
->SentMessagesListHead
;
1059 while (CurrentEntry
!= ListHead
)
1061 SentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1063 if(SentMessage
->Msg
.hwnd
== Window
->hSelf
)
1065 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1067 RemoveEntryList(&SentMessage
->ListEntry
);
1069 /* remove the message from the dispatching list */
1070 if(SentMessage
->DispatchingListEntry
.Flink
!= NULL
)
1072 RemoveEntryList(&SentMessage
->DispatchingListEntry
);
1075 /* wake the sender's thread */
1076 if (SentMessage
->CompletionEvent
!= NULL
)
1078 KeSetEvent(SentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1081 /* dereference our and the sender's message queue */
1082 IntDereferenceMessageQueue(MessageQueue
);
1083 IntDereferenceMessageQueue(SentMessage
->SenderQueue
);
1085 /* free the message */
1086 ExFreePool(SentMessage
);
1088 CurrentEntry
= MessageQueue
->SentMessagesListHead
.Flink
;
1092 CurrentEntry
= CurrentEntry
->Flink
;
1098 MsqSendNotifyMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
1099 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage
)
1101 InsertTailList(&MessageQueue
->NotifyMessagesListHead
,
1102 &NotifyMessage
->ListEntry
);
1103 MessageQueue
->QueueBits
|= QS_SENDMESSAGE
;
1104 MessageQueue
->ChangedBits
|= QS_SENDMESSAGE
;
1105 if (MessageQueue
->WakeMask
& QS_SENDMESSAGE
)
1106 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1110 co_MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
1111 HWND Wnd
, UINT Msg
, WPARAM wParam
, LPARAM lParam
,
1112 UINT uTimeout
, BOOL Block
, INT HookMessage
,
1116 PUSER_SENT_MESSAGE Message
;
1117 KEVENT CompletionEvent
;
1118 NTSTATUS WaitStatus
;
1120 PUSER_MESSAGE_QUEUE ThreadQueue
;
1121 LARGE_INTEGER Timeout
;
1124 if(!(Message
= ExAllocatePoolWithTag(PagedPool
, sizeof(USER_SENT_MESSAGE
), TAG_USRMSG
)))
1126 DPRINT1("MsqSendMessage(): Not enough memory to allocate a message");
1127 return STATUS_INSUFFICIENT_RESOURCES
;
1130 KeInitializeEvent(&CompletionEvent
, NotificationEvent
, FALSE
);
1132 pti
= PsGetCurrentThreadWin32Thread();
1133 ThreadQueue
= pti
->MessageQueue
;
1134 ASSERT(ThreadQueue
!= MessageQueue
);
1136 Timeout
.QuadPart
= (LONGLONG
) uTimeout
* (LONGLONG
) -10000;
1138 /* FIXME - increase reference counter of sender's message queue here */
1141 Message
->Msg
.hwnd
= Wnd
;
1142 Message
->Msg
.message
= Msg
;
1143 Message
->Msg
.wParam
= wParam
;
1144 Message
->Msg
.lParam
= lParam
;
1145 Message
->CompletionEvent
= &CompletionEvent
;
1146 Message
->Result
= &Result
;
1147 Message
->SenderQueue
= ThreadQueue
;
1148 IntReferenceMessageQueue(ThreadQueue
);
1149 Message
->CompletionCallback
= NULL
;
1150 Message
->HookMessage
= HookMessage
;
1152 IntReferenceMessageQueue(MessageQueue
);
1154 /* add it to the list of pending messages */
1155 InsertTailList(&ThreadQueue
->DispatchingMessagesHead
, &Message
->DispatchingListEntry
);
1157 /* queue it in the destination's message queue */
1158 InsertTailList(&MessageQueue
->SentMessagesListHead
, &Message
->ListEntry
);
1160 MessageQueue
->QueueBits
|= QS_SENDMESSAGE
;
1161 MessageQueue
->ChangedBits
|= QS_SENDMESSAGE
;
1162 if (MessageQueue
->WakeMask
& QS_SENDMESSAGE
)
1163 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1165 /* we can't access the Message anymore since it could have already been deleted! */
1173 /* don't process messages sent to the thread */
1174 WaitStatus
= KeWaitForSingleObject(&CompletionEvent
, UserRequest
, UserMode
,
1175 FALSE
, (uTimeout
? &Timeout
: NULL
));
1179 if(WaitStatus
== STATUS_TIMEOUT
)
1181 /* look up if the message has not yet dispatched, if so
1182 make sure it can't pass a result and it must not set the completion event anymore */
1183 Entry
= MessageQueue
->SentMessagesListHead
.Flink
;
1184 while (Entry
!= &MessageQueue
->SentMessagesListHead
)
1186 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
)
1189 /* we can access Message here, it's secure because the message queue is locked
1190 and the message is still hasn't been dispatched */
1191 Message
->CompletionEvent
= NULL
;
1192 Message
->Result
= NULL
;
1195 Entry
= Entry
->Flink
;
1198 /* remove from the local dispatching list so the other thread knows,
1199 it can't pass a result and it must not set the completion event anymore */
1200 Entry
= ThreadQueue
->DispatchingMessagesHead
.Flink
;
1201 while (Entry
!= &ThreadQueue
->DispatchingMessagesHead
)
1203 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, DispatchingListEntry
)
1206 /* we can access Message here, it's secure because the sender's message is locked
1207 and the message has definitely not yet been destroyed, otherwise it would
1208 have been removed from this list by the dispatching routine right after
1209 dispatching the message */
1210 Message
->CompletionEvent
= NULL
;
1211 Message
->Result
= NULL
;
1212 RemoveEntryList(&Message
->DispatchingListEntry
);
1213 Message
->DispatchingListEntry
.Flink
= NULL
;
1216 Entry
= Entry
->Flink
;
1219 DPRINT("MsqSendMessage (blocked) timed out\n");
1221 while (co_MsqDispatchOneSentMessage(ThreadQueue
))
1226 PVOID WaitObjects
[2];
1228 WaitObjects
[0] = &CompletionEvent
;
1229 WaitObjects
[1] = ThreadQueue
->NewMessages
;
1236 WaitStatus
= KeWaitForMultipleObjects(2, WaitObjects
, WaitAny
, UserRequest
,
1237 UserMode
, FALSE
, (uTimeout
? &Timeout
: NULL
), NULL
);
1241 if(WaitStatus
== STATUS_TIMEOUT
)
1243 /* look up if the message has not yet been dispatched, if so
1244 make sure it can't pass a result and it must not set the completion event anymore */
1245 Entry
= MessageQueue
->SentMessagesListHead
.Flink
;
1246 while (Entry
!= &MessageQueue
->SentMessagesListHead
)
1248 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
)
1251 /* we can access Message here, it's secure because the message queue is locked
1252 and the message is still hasn't been dispatched */
1253 Message
->CompletionEvent
= NULL
;
1254 Message
->Result
= NULL
;
1257 Entry
= Entry
->Flink
;
1260 /* remove from the local dispatching list so the other thread knows,
1261 it can't pass a result and it must not set the completion event anymore */
1262 Entry
= ThreadQueue
->DispatchingMessagesHead
.Flink
;
1263 while (Entry
!= &ThreadQueue
->DispatchingMessagesHead
)
1265 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, DispatchingListEntry
)
1268 /* we can access Message here, it's secure because the sender's message is locked
1269 and the message has definitely not yet been destroyed, otherwise it would
1270 have been removed from this list by the dispatching routine right after
1271 dispatching the message */
1272 Message
->CompletionEvent
= NULL
;
1273 Message
->Result
= NULL
;
1274 RemoveEntryList(&Message
->DispatchingListEntry
);
1275 Message
->DispatchingListEntry
.Flink
= NULL
;
1278 Entry
= Entry
->Flink
;
1281 DPRINT("MsqSendMessage timed out\n");
1284 while (co_MsqDispatchOneSentMessage(ThreadQueue
))
1287 while (NT_SUCCESS(WaitStatus
) && STATUS_WAIT_0
!= WaitStatus
);
1290 if(WaitStatus
!= STATUS_TIMEOUT
)
1291 *uResult
= (STATUS_WAIT_0
== WaitStatus
? Result
: -1);
1297 MsqPostMessage(PUSER_MESSAGE_QUEUE MessageQueue
, MSG
* Msg
, BOOLEAN FreeLParam
,
1300 PUSER_MESSAGE Message
;
1302 if(!(Message
= MsqCreateMessage(Msg
, FreeLParam
)))
1306 InsertTailList(&MessageQueue
->PostedMessagesListHead
,
1307 &Message
->ListEntry
);
1308 MessageQueue
->QueueBits
|= MessageBits
;
1309 MessageQueue
->ChangedBits
|= MessageBits
;
1310 if (MessageQueue
->WakeMask
& MessageBits
)
1311 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1315 MsqPostQuitMessage(PUSER_MESSAGE_QUEUE MessageQueue
, ULONG ExitCode
)
1317 MessageQueue
->QuitPosted
= TRUE
;
1318 MessageQueue
->QuitExitCode
= ExitCode
;
1319 MessageQueue
->QueueBits
|= QS_POSTMESSAGE
;
1320 MessageQueue
->ChangedBits
|= QS_POSTMESSAGE
;
1321 if (MessageQueue
->WakeMask
& QS_POSTMESSAGE
)
1322 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1326 co_MsqFindMessage(IN PUSER_MESSAGE_QUEUE MessageQueue
,
1327 IN BOOLEAN Hardware
,
1329 IN PWINDOW_OBJECT Window
,
1330 IN UINT MsgFilterLow
,
1331 IN UINT MsgFilterHigh
,
1332 OUT PUSER_MESSAGE
* Message
)
1334 PLIST_ENTRY CurrentEntry
;
1335 PUSER_MESSAGE CurrentMessage
;
1336 PLIST_ENTRY ListHead
;
1340 return(co_MsqPeekHardwareMessage(MessageQueue
, Window
,
1341 MsgFilterLow
, MsgFilterHigh
,
1345 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
1346 ListHead
= &MessageQueue
->PostedMessagesListHead
;
1347 while (CurrentEntry
!= ListHead
)
1349 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1351 if ((!Window
|| (int)Window
== 1 || Window
->hSelf
== CurrentMessage
->Msg
.hwnd
) &&
1352 ((MsgFilterLow
== 0 && MsgFilterHigh
== 0) ||
1353 (MsgFilterLow
<= CurrentMessage
->Msg
.message
&&
1354 MsgFilterHigh
>= CurrentMessage
->Msg
.message
)))
1358 RemoveEntryList(&CurrentMessage
->ListEntry
);
1361 *Message
= CurrentMessage
;
1364 CurrentEntry
= CurrentEntry
->Flink
;
1371 co_MsqWaitForNewMessages(PUSER_MESSAGE_QUEUE MessageQueue
, PWINDOW_OBJECT WndFilter
,
1372 UINT MsgFilterMin
, UINT MsgFilterMax
)
1374 PVOID WaitObjects
[2] = {MessageQueue
->NewMessages
, &HardwareMessageEvent
};
1375 LARGE_INTEGER TimerExpiry
;
1376 PLARGE_INTEGER Timeout
;
1379 if (MsqGetFirstTimerExpiry(MessageQueue
, WndFilter
, MsgFilterMin
, MsgFilterMax
, &TimerExpiry
))
1381 Timeout
= &TimerExpiry
;
1388 IdlePing(); // Going to wait so send Idle ping.
1392 ret
= KeWaitForMultipleObjects(2,
1407 MsqIsHung(PUSER_MESSAGE_QUEUE MessageQueue
)
1409 LARGE_INTEGER LargeTickCount
;
1411 KeQueryTickCount(&LargeTickCount
);
1412 return ((LargeTickCount
.u
.LowPart
- MessageQueue
->LastMsgRead
) > MSQ_HUNG
);
1416 MsqInitializeMessageQueue(struct _ETHREAD
*Thread
, PUSER_MESSAGE_QUEUE MessageQueue
)
1418 LARGE_INTEGER LargeTickCount
;
1421 MessageQueue
->Thread
= Thread
;
1422 MessageQueue
->CaretInfo
= (PTHRDCARETINFO
)(MessageQueue
+ 1);
1423 InitializeListHead(&MessageQueue
->PostedMessagesListHead
);
1424 InitializeListHead(&MessageQueue
->SentMessagesListHead
);
1425 InitializeListHead(&MessageQueue
->HardwareMessagesListHead
);
1426 InitializeListHead(&MessageQueue
->TimerListHead
);
1427 InitializeListHead(&MessageQueue
->DispatchingMessagesHead
);
1428 InitializeListHead(&MessageQueue
->LocalDispatchingMessagesHead
);
1429 KeInitializeMutex(&MessageQueue
->HardwareLock
, 0);
1430 MessageQueue
->QuitPosted
= FALSE
;
1431 MessageQueue
->QuitExitCode
= 0;
1432 KeQueryTickCount(&LargeTickCount
);
1433 MessageQueue
->LastMsgRead
= LargeTickCount
.u
.LowPart
;
1434 MessageQueue
->FocusWindow
= NULL
;
1435 MessageQueue
->PaintCount
= 0;
1436 // HACK!!!!!!! Newbies that wrote this should hold your head down in shame! (jt)
1437 MessageQueue
->WakeMask
= ~0;
1438 MessageQueue
->NewMessagesHandle
= NULL
;
1440 Status
= ZwCreateEvent(&MessageQueue
->NewMessagesHandle
, EVENT_ALL_ACCESS
,
1441 NULL
, SynchronizationEvent
, FALSE
);
1442 if (!NT_SUCCESS(Status
))
1447 Status
= ObReferenceObjectByHandle(MessageQueue
->NewMessagesHandle
, 0,
1448 ExEventObjectType
, KernelMode
,
1449 (PVOID
*)&MessageQueue
->NewMessages
, NULL
);
1450 if (!NT_SUCCESS(Status
))
1452 ZwClose(MessageQueue
->NewMessagesHandle
);
1453 MessageQueue
->NewMessagesHandle
= NULL
;
1461 MsqCleanupMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue
)
1463 PLIST_ENTRY CurrentEntry
;
1464 PUSER_MESSAGE CurrentMessage
;
1465 PTIMER_ENTRY CurrentTimer
;
1466 PUSER_SENT_MESSAGE CurrentSentMessage
;
1468 /* cleanup posted messages */
1469 while (!IsListEmpty(&MessageQueue
->PostedMessagesListHead
))
1471 CurrentEntry
= RemoveHeadList(&MessageQueue
->PostedMessagesListHead
);
1472 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1474 MsqDestroyMessage(CurrentMessage
);
1477 /* remove the messages that have not yet been dispatched */
1478 while (!IsListEmpty(&MessageQueue
->SentMessagesListHead
))
1480 CurrentEntry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
1481 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1484 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1486 /* remove the message from the dispatching list */
1487 if(CurrentSentMessage
->DispatchingListEntry
.Flink
!= NULL
)
1489 RemoveEntryList(&CurrentSentMessage
->DispatchingListEntry
);
1492 /* wake the sender's thread */
1493 if (CurrentSentMessage
->CompletionEvent
!= NULL
)
1495 KeSetEvent(CurrentSentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1498 /* dereference our and the sender's message queue */
1499 IntDereferenceMessageQueue(MessageQueue
);
1500 IntDereferenceMessageQueue(CurrentSentMessage
->SenderQueue
);
1502 /* free the message */
1503 ExFreePool(CurrentSentMessage
);
1506 /* cleanup timers */
1507 while (! IsListEmpty(&MessageQueue
->TimerListHead
))
1509 CurrentEntry
= RemoveHeadList(&MessageQueue
->TimerListHead
);
1510 CurrentTimer
= CONTAINING_RECORD(CurrentEntry
, TIMER_ENTRY
, ListEntry
);
1511 ExFreeToPagedLookasideList(&TimerLookasideList
, CurrentTimer
);
1514 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
1515 ExitThread() was called in a SendMessage() umode callback */
1516 while (!IsListEmpty(&MessageQueue
->LocalDispatchingMessagesHead
))
1518 CurrentEntry
= RemoveHeadList(&MessageQueue
->LocalDispatchingMessagesHead
);
1519 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1522 /* remove the message from the dispatching list */
1523 if(CurrentSentMessage
->DispatchingListEntry
.Flink
!= NULL
)
1525 RemoveEntryList(&CurrentSentMessage
->DispatchingListEntry
);
1528 DPRINT("Notify the sender, the thread has been terminated while dispatching a message!\n");
1530 /* wake the sender's thread */
1531 if (CurrentSentMessage
->CompletionEvent
!= NULL
)
1533 KeSetEvent(CurrentSentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1536 /* dereference our and the sender's message queue */
1537 IntDereferenceMessageQueue(MessageQueue
);
1538 IntDereferenceMessageQueue(CurrentSentMessage
->SenderQueue
);
1540 /* free the message */
1541 ExFreePool(CurrentSentMessage
);
1544 /* tell other threads not to bother returning any info to us */
1545 while (! IsListEmpty(&MessageQueue
->DispatchingMessagesHead
))
1547 CurrentEntry
= RemoveHeadList(&MessageQueue
->DispatchingMessagesHead
);
1548 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1549 DispatchingListEntry
);
1550 CurrentSentMessage
->CompletionEvent
= NULL
;
1551 CurrentSentMessage
->Result
= NULL
;
1553 /* do NOT dereference our message queue as it might get attempted to be
1559 PUSER_MESSAGE_QUEUE FASTCALL
1560 MsqCreateMessageQueue(struct _ETHREAD
*Thread
)
1562 PUSER_MESSAGE_QUEUE MessageQueue
;
1564 MessageQueue
= (PUSER_MESSAGE_QUEUE
)ExAllocatePoolWithTag(NonPagedPool
,
1565 sizeof(USER_MESSAGE_QUEUE
) + sizeof(THRDCARETINFO
),
1573 RtlZeroMemory(MessageQueue
, sizeof(USER_MESSAGE_QUEUE
) + sizeof(THRDCARETINFO
));
1574 /* hold at least one reference until it'll be destroyed */
1575 IntReferenceMessageQueue(MessageQueue
);
1576 /* initialize the queue */
1577 if (!MsqInitializeMessageQueue(Thread
, MessageQueue
))
1579 IntDereferenceMessageQueue(MessageQueue
);
1583 return MessageQueue
;
1587 MsqDestroyMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue
)
1591 /* remove the message queue from any desktops */
1592 if ((desk
= InterlockedExchangePointer((PVOID
*)&MessageQueue
->Desktop
, 0)))
1594 (void)InterlockedExchangePointer((PVOID
*)&desk
->ActiveMessageQueue
, 0);
1595 IntDereferenceMessageQueue(MessageQueue
);
1599 MsqCleanupMessageQueue(MessageQueue
);
1601 /* decrease the reference counter, if it hits zero, the queue will be freed */
1602 IntDereferenceMessageQueue(MessageQueue
);
1606 MsqGetHooks(PUSER_MESSAGE_QUEUE Queue
)
1608 return Queue
->Hooks
;
1612 MsqSetHooks(PUSER_MESSAGE_QUEUE Queue
, PHOOKTABLE Hooks
)
1614 Queue
->Hooks
= Hooks
;
1618 MsqSetMessageExtraInfo(LPARAM lParam
)
1622 PUSER_MESSAGE_QUEUE MessageQueue
;
1624 pti
= PsGetCurrentThreadWin32Thread();
1625 MessageQueue
= pti
->MessageQueue
;
1631 Ret
= MessageQueue
->ExtraInfo
;
1632 MessageQueue
->ExtraInfo
= lParam
;
1638 MsqGetMessageExtraInfo(VOID
)
1641 PUSER_MESSAGE_QUEUE MessageQueue
;
1643 pti
= PsGetCurrentThreadWin32Thread();
1644 MessageQueue
= pti
->MessageQueue
;
1650 return MessageQueue
->ExtraInfo
;
1654 MsqSetStateWindow(PUSER_MESSAGE_QUEUE MessageQueue
, ULONG Type
, HWND hWnd
)
1660 case MSQ_STATE_CAPTURE
:
1661 Prev
= MessageQueue
->CaptureWindow
;
1662 MessageQueue
->CaptureWindow
= hWnd
;
1664 case MSQ_STATE_ACTIVE
:
1665 Prev
= MessageQueue
->ActiveWindow
;
1666 MessageQueue
->ActiveWindow
= hWnd
;
1668 case MSQ_STATE_FOCUS
:
1669 Prev
= MessageQueue
->FocusWindow
;
1670 MessageQueue
->FocusWindow
= hWnd
;
1672 case MSQ_STATE_MENUOWNER
:
1673 Prev
= MessageQueue
->MenuOwner
;
1674 MessageQueue
->MenuOwner
= hWnd
;
1676 case MSQ_STATE_MOVESIZE
:
1677 Prev
= MessageQueue
->MoveSize
;
1678 MessageQueue
->MoveSize
= hWnd
;
1680 case MSQ_STATE_CARET
:
1681 ASSERT(MessageQueue
->CaretInfo
);
1682 Prev
= MessageQueue
->CaretInfo
->hWnd
;
1683 MessageQueue
->CaretInfo
->hWnd
= hWnd
;
1691 static VOID FASTCALL
1692 DumpTimerList(PUSER_MESSAGE_QUEUE MessageQueue
)
1694 PLIST_ENTRY Current
;
1697 Current
= MessageQueue
->TimerListHead
.Flink
;
1698 if (Current
== &MessageQueue
->TimerListHead
)
1700 DPRINT("timer list is empty for queue %p\n", MessageQueue
);
1702 while (Current
!= &MessageQueue
->TimerListHead
)
1704 Timer
= CONTAINING_RECORD(Current
, TIMER_ENTRY
, ListEntry
);
1705 DPRINT("queue %p timer %p expiry %I64d wnd %x id %p period %u timerproc %p msg %u\n",
1706 MessageQueue
, Timer
, Timer
->ExpiryTime
.QuadPart
, Timer
->Wnd
, Timer
->IDEvent
,
1707 Timer
->Period
, Timer
->TimerFunc
, Timer
->Msg
);
1708 Current
= Current
->Flink
;
1711 #endif /* ! defined(NDEBUG) */
1713 /* Must have the message queue locked while calling this */
1714 static VOID FASTCALL
1715 InsertTimer(PUSER_MESSAGE_QUEUE MessageQueue
, PTIMER_ENTRY NewTimer
)
1717 PLIST_ENTRY Current
;
1719 Current
= MessageQueue
->TimerListHead
.Flink
;
1720 while (Current
!= &MessageQueue
->TimerListHead
)
1722 if (NewTimer
->ExpiryTime
.QuadPart
<
1723 CONTAINING_RECORD(Current
, TIMER_ENTRY
, ListEntry
)->ExpiryTime
.QuadPart
)
1727 Current
= Current
->Flink
;
1730 InsertTailList(Current
, &NewTimer
->ListEntry
);
1733 /* Must have the message queue locked while calling this */
1734 static PTIMER_ENTRY FASTCALL
1735 RemoveTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
, UINT_PTR IDEvent
, UINT Msg
)
1738 PLIST_ENTRY EnumEntry
;
1740 /* Remove timer if already in the queue */
1741 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1742 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1744 Timer
= CONTAINING_RECORD(EnumEntry
, TIMER_ENTRY
, ListEntry
);
1745 EnumEntry
= EnumEntry
->Flink
;
1747 if (Timer
->Wnd
== Wnd
&&
1748 Timer
->IDEvent
== IDEvent
&&
1751 RemoveEntryList(&Timer
->ListEntry
);
1760 MsqSetTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
,
1761 UINT_PTR IDEvent
, UINT Period
, TIMERPROC TimerFunc
,
1765 LARGE_INTEGER CurrentTime
;
1767 DPRINT("MsqSetTimer queue %p wnd %x id %p period %u timerproc %p msg %d\n",
1768 MessageQueue
, Wnd
, IDEvent
, Period
, TimerFunc
, Msg
);
1770 Timer
= RemoveTimer(MessageQueue
, Wnd
, IDEvent
, Msg
);
1773 Timer
= ExAllocateFromPagedLookasideList(&TimerLookasideList
);
1776 DPRINT1("Failed to allocate timer entry\n");
1779 DPRINT("Allocated new timer entry %p\n", Timer
);
1781 Timer
->IDEvent
= IDEvent
;
1786 DPRINT("Updating existing timer entry %p\n", Timer
);
1789 KeQuerySystemTime(&CurrentTime
);
1790 Timer
->ExpiryTime
.QuadPart
= CurrentTime
.QuadPart
+
1791 (ULONGLONG
) Period
* (ULONGLONG
) 10000;
1792 Timer
->Period
= Period
;
1793 Timer
->TimerFunc
= TimerFunc
;
1794 DPRINT("Insert timer now %I64d expiry %I64d\n", CurrentTime
.QuadPart
,
1795 Timer
->ExpiryTime
.QuadPart
);
1797 InsertTimer(MessageQueue
, Timer
);
1801 DumpTimerList(MessageQueue
);
1802 #endif /* ! defined(NDEBUG) */
1808 MsqKillTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
,
1809 UINT_PTR IDEvent
, UINT Msg
)
1813 DPRINT("MsqKillTimer queue %p wnd %x id %p msg %d\n",
1814 MessageQueue
, Wnd
, IDEvent
, Msg
);
1816 Timer
= RemoveTimer(MessageQueue
, Wnd
, IDEvent
, Msg
);
1820 DPRINT("Failed to remove timer from list, not found\n");
1824 ExFreeToPagedLookasideList(&TimerLookasideList
, Timer
);
1828 DumpTimerList(MessageQueue
);
1829 #endif /* ! defined(NDEBUG) */
1831 return NULL
!= Timer
;
1835 MsqGetTimerMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
1836 PWINDOW_OBJECT WindowFilter
, UINT MsgFilterMin
, UINT MsgFilterMax
,
1837 MSG
*Msg
, BOOLEAN Restart
)
1840 LARGE_INTEGER CurrentTime
;
1841 LARGE_INTEGER LargeTickCount
;
1842 PLIST_ENTRY EnumEntry
;
1846 DPRINT("MsqGetTimerMessage queue %p msg %p restart %s\n",
1847 MessageQueue
, Msg
, Restart
? "TRUE" : "FALSE");
1849 KeQuerySystemTime(&CurrentTime
);
1850 DPRINT("Current time %I64d\n", CurrentTime
.QuadPart
);
1851 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1853 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1855 Timer
= CONTAINING_RECORD(MessageQueue
->TimerListHead
.Flink
,
1856 TIMER_ENTRY
, ListEntry
);
1857 DPRINT("Checking timer %p wnd %x expiry %I64d\n", Timer
, Timer
->Wnd
,
1858 Timer
->ExpiryTime
.QuadPart
);
1859 EnumEntry
= EnumEntry
->Flink
;
1860 if ((NULL
== WindowFilter
|| Timer
->Wnd
== WindowFilter
->hSelf
) &&
1861 ((MsgFilterMin
== 0 && MsgFilterMax
== 0) ||
1862 (MsgFilterMin
<= Timer
->Msg
&&
1863 Timer
->Msg
<= MsgFilterMax
)))
1865 if (Timer
->ExpiryTime
.QuadPart
<= CurrentTime
.QuadPart
)
1867 DPRINT("Timer is expired\n");
1873 DPRINT("No need to check later timers\n");
1879 DPRINT("timer %p (wnd %x msg %d) failed filter wnd %x msgmin %d msgmax %d\n",
1880 Timer
, Timer
->Wnd
, Timer
->Msg
, WindowFilter
->hSelf
, MsgFilterMin
, MsgFilterMax
);
1886 DPRINT("No timer pending\n");
1890 Msg
->hwnd
= Timer
->Wnd
;
1891 Msg
->message
= Timer
->Msg
;
1892 Msg
->wParam
= (WPARAM
) Timer
->IDEvent
;
1893 Msg
->lParam
= (LPARAM
) Timer
->TimerFunc
;
1894 KeQueryTickCount(&LargeTickCount
);
1895 Msg
->time
= MsqCalculateMessageTime(&LargeTickCount
);
1896 pti
= PsGetCurrentThreadWin32Thread();
1897 IntGetCursorLocation(pti
->Desktop
->WindowStation
,
1902 RemoveEntryList(&Timer
->ListEntry
);
1903 Timer
->ExpiryTime
.QuadPart
= CurrentTime
.QuadPart
+
1904 (ULONGLONG
) Timer
->Period
* (ULONGLONG
) 10000;
1905 DPRINT("Restarting timer %p expires %I64d\n", Timer
, Timer
->ExpiryTime
.QuadPart
);
1906 InsertTimer(MessageQueue
, Timer
);
1910 DumpTimerList(MessageQueue
);
1911 #endif /* ! defined(NDEBUG) */
1915 DPRINT("Created message wnd %x msg %d wParam %u lParam %u\n", Msg
->hwnd
, Msg
->message
,
1916 Msg
->wParam
, Msg
->lParam
);
1922 MsqRemoveTimersWindow(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
)
1925 PLIST_ENTRY EnumEntry
;
1927 DPRINT("MsqRemoveTimersWindow queue %p wnd %x\n", MessageQueue
, Wnd
);
1929 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1930 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1932 Timer
= CONTAINING_RECORD(EnumEntry
, TIMER_ENTRY
, ListEntry
);
1933 EnumEntry
= EnumEntry
->Flink
;
1934 if (Timer
->Wnd
== Wnd
)
1936 DPRINT("Removing timer %p because its window is going away\n", Timer
);
1937 RemoveEntryList(&Timer
->ListEntry
);
1938 ExFreeToPagedLookasideList(&TimerLookasideList
, Timer
);
1943 DumpTimerList(MessageQueue
);
1944 #endif /* ! defined(NDEBUG) */
1949 MsqGetFirstTimerExpiry(PUSER_MESSAGE_QUEUE MessageQueue
,
1950 PWINDOW_OBJECT WndFilter
, UINT MsgFilterMin
, UINT MsgFilterMax
,
1951 PLARGE_INTEGER FirstTimerExpiry
)
1954 PLIST_ENTRY EnumEntry
;
1956 DPRINT("MsqGetFirstTimerExpiry queue %p wndfilter %x msgfiltermin %d msgfiltermax %d expiry %p\n",
1957 MessageQueue
, WndFilter
, MsgFilterMin
, MsgFilterMax
, FirstTimerExpiry
);
1959 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1960 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1962 Timer
= CONTAINING_RECORD(MessageQueue
->TimerListHead
.Flink
,
1963 TIMER_ENTRY
, ListEntry
);
1964 EnumEntry
= EnumEntry
->Flink
;
1965 if ((NULL
== WndFilter
|| (int)WndFilter
== 1 || Timer
->Wnd
== WndFilter
->hSelf
) &&
1966 ((MsgFilterMin
== 0 && MsgFilterMax
== 0) ||
1967 (MsgFilterMin
<= Timer
->Msg
&&
1968 Timer
->Msg
<= MsgFilterMax
)))
1970 *FirstTimerExpiry
= Timer
->ExpiryTime
;
1971 DPRINT("First timer expires %I64d\n", Timer
->ExpiryTime
);