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 (NT_SUCCESS(WaitStatus
) && STATUS_WAIT_0
!= WaitStatus
);
540 DesktopWindow
= UserGetWindowObject(IntGetDesktopWindow());
544 UserRefObjectCo(DesktopWindow
, &Ref
);//can DesktopWindow be NULL?
545 Desk
= DesktopWindow
->ti
->pDeskInfo
;
548 /* Process messages in the message queue itself. */
549 IntLockHardwareMessageQueue(MessageQueue
);
550 CurrentEntry
= MessageQueue
->HardwareMessagesListHead
.Flink
;
551 while (CurrentEntry
!= &MessageQueue
->HardwareMessagesListHead
)
553 PUSER_MESSAGE Current
=
554 CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
, ListEntry
);
555 CurrentEntry
= CurrentEntry
->Flink
;
556 if (Current
->Msg
.message
>= WM_MOUSEFIRST
&&
557 Current
->Msg
.message
<= WM_MOUSELAST
)
561 Accept
= co_MsqTranslateMouseMessage(MessageQueue
, Window
, FilterLow
, FilterHigh
,
562 Current
, Remove
, &Freed
,
563 DesktopWindow
, &ScreenPoint
, FALSE
, &CurrentEntry
);
568 RemoveEntryList(&Current
->ListEntry
);
570 IntUnLockHardwareMessageQueue(MessageQueue
);
571 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
575 Desk
->LastInputWasKbd
= FALSE
;
582 IntUnLockHardwareMessageQueue(MessageQueue
);
584 /* Now try the global queue. */
586 /* Transfer all messages from the DPC accessible queue to the main queue. */
587 IntLockSystemMessageQueue(OldIrql
);
588 while (SystemMessageQueueCount
> 0)
590 PUSER_MESSAGE UserMsg
;
594 ASSERT(SystemMessageQueueHead
< SYSTEM_MESSAGE_QUEUE_SIZE
);
595 Msg
= SystemMessageQueue
[SystemMessageQueueHead
];
596 SystemMessageQueueHead
=
597 (SystemMessageQueueHead
+ 1) % SYSTEM_MESSAGE_QUEUE_SIZE
;
598 SystemMessageQueueCount
--;
599 IntUnLockSystemMessageQueue(OldIrql
);
600 if (WM_MOUSEFIRST
<= Msg
.message
&& Msg
.message
<= WM_MOUSELAST
)
602 MSLLHOOKSTRUCT MouseHookData
;
604 MouseHookData
.pt
.x
= LOWORD(Msg
.lParam
);
605 MouseHookData
.pt
.y
= HIWORD(Msg
.lParam
);
609 MouseHookData
.mouseData
= MAKELONG(0, GET_WHEEL_DELTA_WPARAM(Msg
.wParam
));
613 case WM_XBUTTONDBLCLK
:
614 case WM_NCXBUTTONDOWN
:
616 case WM_NCXBUTTONDBLCLK
:
617 MouseHookData
.mouseData
= MAKELONG(0, HIWORD(Msg
.wParam
));
620 MouseHookData
.mouseData
= 0;
623 MouseHookData
.flags
= 0;
624 MouseHookData
.time
= Msg
.time
;
625 MouseHookData
.dwExtraInfo
= 0;
626 ProcessMessage
= (0 == co_HOOK_CallHooks(WH_MOUSE_LL
, HC_ACTION
,
627 Msg
.message
, (LPARAM
) &MouseHookData
));
631 ProcessMessage
= TRUE
;
635 UserMsg
= ExAllocateFromPagedLookasideList(&MessageLookasideList
);
636 /* What to do if out of memory? For now we just panic a bit in debug */
638 UserMsg
->FreeLParam
= FALSE
;
640 InsertTailList(&HardwareMessageQueueHead
, &UserMsg
->ListEntry
);
642 IntLockSystemMessageQueue(OldIrql
);
644 HardwareMessageQueueStamp
++;
645 IntUnLockSystemMessageQueue(OldIrql
);
647 /* Process messages in the queue until we find one to return. */
648 CurrentEntry
= HardwareMessageQueueHead
.Flink
;
649 while (CurrentEntry
!= &HardwareMessageQueueHead
)
651 PUSER_MESSAGE Current
=
652 CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
, ListEntry
);
653 CurrentEntry
= CurrentEntry
->Flink
;
654 RemoveEntryList(&Current
->ListEntry
);
655 HardwareMessageQueueStamp
++;
656 if (Current
->Msg
.message
>= WM_MOUSEFIRST
&&
657 Current
->Msg
.message
<= WM_MOUSELAST
)
659 const ULONG ActiveStamp
= HardwareMessageQueueStamp
;
660 /* Translate the message. */
661 Accept
= co_MsqTranslateMouseMessage(MessageQueue
, Window
, FilterLow
, FilterHigh
,
662 Current
, Remove
, &Freed
,
663 DesktopWindow
, &ScreenPoint
, TRUE
, NULL
);
666 /* Check for no more messages in the system queue. */
667 IntLockSystemMessageQueue(OldIrql
);
668 if (SystemMessageQueueCount
== 0 &&
669 IsListEmpty(&HardwareMessageQueueHead
))
671 KeClearEvent(&HardwareMessageEvent
);
673 IntUnLockSystemMessageQueue(OldIrql
);
676 If we aren't removing the message then add it to the private
681 IntLockHardwareMessageQueue(MessageQueue
);
682 if(Current
->Msg
.message
== WM_MOUSEMOVE
)
684 if(MessageQueue
->MouseMoveMsg
)
686 RemoveEntryList(&MessageQueue
->MouseMoveMsg
->ListEntry
);
687 ExFreePool(MessageQueue
->MouseMoveMsg
);
689 MessageQueue
->MouseMoveMsg
= Current
;
691 InsertTailList(&MessageQueue
->HardwareMessagesListHead
,
692 &Current
->ListEntry
);
693 IntUnLockHardwareMessageQueue(MessageQueue
);
695 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
700 /* If the contents of the queue changed then restart processing. */
701 if (HardwareMessageQueueStamp
!= ActiveStamp
)
703 CurrentEntry
= HardwareMessageQueueHead
.Flink
;
709 /* Check if the system message queue is now empty. */
710 IntLockSystemMessageQueue(OldIrql
);
711 if (SystemMessageQueueCount
== 0 && IsListEmpty(&HardwareMessageQueueHead
))
713 KeClearEvent(&HardwareMessageEvent
);
715 IntUnLockSystemMessageQueue(OldIrql
);
716 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
721 if (DesktopWindow
) UserDerefObjectCo(DesktopWindow
);
727 // Note: Only called from input.c.
730 co_MsqPostKeyboardMessage(UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
732 PUSER_MESSAGE_QUEUE FocusMessageQueue
;
734 LARGE_INTEGER LargeTickCount
;
735 KBDLLHOOKSTRUCT KbdHookData
;
737 BOOLEAN Entered
= FALSE
;
739 DPRINT("MsqPostKeyboardMessage(uMsg 0x%x, wParam 0x%x, lParam 0x%x)\n",
740 uMsg
, wParam
, lParam
);
742 // Condition may arise when calling MsqPostMessage and waiting for an event.
743 if (!UserIsEntered())
745 // Fixme: Not sure ATM if this thread is locked.
746 UserEnterExclusive();
750 FocusMessageQueue
= IntGetFocusMessageQueue();
754 if (FocusMessageQueue
&& (FocusMessageQueue
->FocusWindow
!= (HWND
)0))
755 Msg
.hwnd
= FocusMessageQueue
->FocusWindow
;
761 KeQueryTickCount(&LargeTickCount
);
762 Msg
.time
= MsqCalculateMessageTime(&LargeTickCount
);
764 Event
.message
= Msg
.message
;
765 Event
.hwnd
= Msg
.hwnd
;
766 Event
.time
= Msg
.time
;
767 Event
.paramL
= (Msg
.wParam
& 0xFF) | (HIWORD(Msg
.lParam
) << 8);
768 Event
.paramH
= Msg
.lParam
& 0x7FFF;
769 if (HIWORD(Msg
.lParam
) & 0x0100) Event
.paramH
|= 0x8000;
770 co_HOOK_CallHooks( WH_JOURNALRECORD
, HC_ACTION
, 0, (LPARAM
)&Event
);
772 /* We can't get the Msg.pt point here since we don't know thread
773 (and thus the window station) the message will end up in yet. */
775 KbdHookData
.vkCode
= Msg
.wParam
;
776 KbdHookData
.scanCode
= (Msg
.lParam
>> 16) & 0xff;
777 KbdHookData
.flags
= (0 == (Msg
.lParam
& 0x01000000) ? 0 : LLKHF_EXTENDED
) |
778 (0 == (Msg
.lParam
& 0x20000000) ? 0 : LLKHF_ALTDOWN
) |
779 (0 == (Msg
.lParam
& 0x80000000) ? 0 : LLKHF_UP
);
780 KbdHookData
.time
= Msg
.time
;
781 KbdHookData
.dwExtraInfo
= 0;
782 if (co_HOOK_CallHooks(WH_KEYBOARD_LL
, HC_ACTION
, Msg
.message
, (LPARAM
) &KbdHookData
))
784 DPRINT("Kbd msg %d wParam %d lParam 0x%08x dropped by WH_KEYBOARD_LL hook\n",
785 Msg
.message
, Msg
.wParam
, Msg
.lParam
);
786 if (Entered
) UserLeave();
790 if (FocusMessageQueue
== NULL
)
792 DPRINT("No focus message queue\n");
793 if (Entered
) UserLeave();
797 if (FocusMessageQueue
->FocusWindow
!= (HWND
)0)
799 Msg
.hwnd
= FocusMessageQueue
->FocusWindow
;
800 DPRINT("Msg.hwnd = %x\n", Msg
.hwnd
);
802 FocusMessageQueue
->Desktop
->DesktopInfo
->LastInputWasKbd
= TRUE
;
804 IntGetCursorLocation(FocusMessageQueue
->Desktop
->WindowStation
,
806 MsqPostMessage(FocusMessageQueue
, &Msg
, FALSE
, QS_KEY
);
810 DPRINT("Invalid focus window handle\n");
813 if (Entered
) UserLeave();
818 MsqPostHotKeyMessage(PVOID Thread
, HWND hWnd
, WPARAM wParam
, LPARAM lParam
)
820 PWINDOW_OBJECT Window
;
821 PTHREADINFO Win32Thread
;
822 PWINSTATION_OBJECT WinSta
;
824 LARGE_INTEGER LargeTickCount
;
827 Status
= ObReferenceObjectByPointer (Thread
,
831 if (!NT_SUCCESS(Status
))
834 Win32Thread
= ((PETHREAD
)Thread
)->Tcb
.Win32Thread
;
835 if (Win32Thread
== NULL
|| Win32Thread
->MessageQueue
== NULL
)
837 ObDereferenceObject ((PETHREAD
)Thread
);
841 WinSta
= Win32Thread
->Desktop
->WindowStation
;
842 Window
= IntGetWindowObject(hWnd
);
845 ObDereferenceObject ((PETHREAD
)Thread
);
850 Mesg
.message
= WM_HOTKEY
;
851 Mesg
.wParam
= wParam
;
852 Mesg
.lParam
= lParam
;
853 KeQueryTickCount(&LargeTickCount
);
854 Mesg
.time
= MsqCalculateMessageTime(&LargeTickCount
);
855 IntGetCursorLocation(WinSta
, &Mesg
.pt
);
856 MsqPostMessage(Window
->MessageQueue
, &Mesg
, FALSE
, QS_HOTKEY
);
857 UserDereferenceObject(Window
);
858 ObDereferenceObject (Thread
);
860 // InsertHeadList(&pThread->MessageQueue->PostedMessagesListHead,
861 // &Message->ListEntry);
862 // KeSetEvent(pThread->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
865 PUSER_MESSAGE FASTCALL
866 MsqCreateMessage(LPMSG Msg
, BOOLEAN FreeLParam
)
868 PUSER_MESSAGE Message
;
870 Message
= ExAllocateFromPagedLookasideList(&MessageLookasideList
);
876 Message
->FreeLParam
= FreeLParam
;
877 RtlMoveMemory(&Message
->Msg
, Msg
, sizeof(MSG
));
883 MsqDestroyMessage(PUSER_MESSAGE Message
)
885 ExFreeToPagedLookasideList(&MessageLookasideList
, Message
);
889 co_MsqDispatchSentNotifyMessages(PUSER_MESSAGE_QUEUE MessageQueue
)
891 PLIST_ENTRY ListEntry
;
892 PUSER_SENT_MESSAGE_NOTIFY Message
;
894 while (!IsListEmpty(&MessageQueue
->SentMessagesListHead
))
896 ListEntry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
897 Message
= CONTAINING_RECORD(ListEntry
, USER_SENT_MESSAGE_NOTIFY
,
900 co_IntCallSentMessageCallback(Message
->CompletionCallback
,
903 Message
->CompletionCallbackContext
,
911 MsqPeekSentMessages(PUSER_MESSAGE_QUEUE MessageQueue
)
913 return(!IsListEmpty(&MessageQueue
->SentMessagesListHead
));
917 co_MsqDispatchOneSentMessage(PUSER_MESSAGE_QUEUE MessageQueue
)
919 PUSER_SENT_MESSAGE Message
;
923 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage
;
925 if (IsListEmpty(&MessageQueue
->SentMessagesListHead
))
930 /* remove it from the list of pending messages */
931 Entry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
932 Message
= CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
);
934 /* insert it to the list of messages that are currently dispatched by this
936 InsertTailList(&MessageQueue
->LocalDispatchingMessagesHead
,
937 &Message
->ListEntry
);
939 if (Message
->HookMessage
== MSQ_ISHOOK
)
941 Result
= co_HOOK_CallHooks(Message
->Msg
.message
,
942 (INT
)(INT_PTR
)Message
->Msg
.hwnd
,
944 Message
->Msg
.lParam
);
946 else if (Message
->HookMessage
== MSQ_ISEVENT
)
948 Result
= co_EVENT_CallEvents( Message
->Msg
.message
,
951 Message
->Msg
.lParam
);
955 /* Call the window procedure. */
956 Result
= co_IntSendMessage(Message
->Msg
.hwnd
,
957 Message
->Msg
.message
,
959 Message
->Msg
.lParam
);
962 /* remove the message from the local dispatching list, because it doesn't need
963 to be cleaned up on thread termination anymore */
964 RemoveEntryList(&Message
->ListEntry
);
966 /* remove the message from the dispatching list, so lock the sender's message queue */
967 SenderReturned
= (Message
->DispatchingListEntry
.Flink
== NULL
);
970 /* only remove it from the dispatching list if not already removed by a timeout */
971 RemoveEntryList(&Message
->DispatchingListEntry
);
973 /* still keep the sender's message queue locked, so the sender can't exit the
974 MsqSendMessage() function (if timed out) */
976 /* Let the sender know the result. */
977 if (Message
->Result
!= NULL
)
979 *Message
->Result
= Result
;
982 /* Notify the sender. */
983 if (Message
->CompletionEvent
!= NULL
)
985 KeSetEvent(Message
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
988 /* Notify the sender if they specified a callback. */
989 if (!SenderReturned
&& Message
->CompletionCallback
!= NULL
)
991 if(!(NotifyMessage
= ExAllocatePoolWithTag(NonPagedPool
,
992 sizeof(USER_SENT_MESSAGE_NOTIFY
), TAG_USRMSG
)))
994 DPRINT1("MsqDispatchOneSentMessage(): Not enough memory to create a callback notify message\n");
997 NotifyMessage
->CompletionCallback
=
998 Message
->CompletionCallback
;
999 NotifyMessage
->CompletionCallbackContext
=
1000 Message
->CompletionCallbackContext
;
1001 NotifyMessage
->Result
= Result
;
1002 NotifyMessage
->hWnd
= Message
->Msg
.hwnd
;
1003 NotifyMessage
->Msg
= Message
->Msg
.message
;
1004 MsqSendNotifyMessage(Message
->SenderQueue
, NotifyMessage
);
1009 /* dereference both sender and our queue */
1010 IntDereferenceMessageQueue(MessageQueue
);
1011 IntDereferenceMessageQueue(Message
->SenderQueue
);
1013 /* free the message */
1014 ExFreePool(Message
);
1019 MsqRemoveWindowMessagesFromQueue(PVOID pWindow
)
1021 PUSER_SENT_MESSAGE SentMessage
;
1022 PUSER_MESSAGE PostedMessage
;
1023 PUSER_MESSAGE_QUEUE MessageQueue
;
1024 PLIST_ENTRY CurrentEntry
, ListHead
;
1025 PWINDOW_OBJECT Window
= pWindow
;
1029 MessageQueue
= Window
->MessageQueue
;
1030 ASSERT(MessageQueue
);
1032 /* remove the posted messages for this window */
1033 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
1034 ListHead
= &MessageQueue
->PostedMessagesListHead
;
1035 while (CurrentEntry
!= ListHead
)
1037 PostedMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1039 if (PostedMessage
->Msg
.hwnd
== Window
->hSelf
)
1041 RemoveEntryList(&PostedMessage
->ListEntry
);
1042 MsqDestroyMessage(PostedMessage
);
1043 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
1047 CurrentEntry
= CurrentEntry
->Flink
;
1051 /* remove the sent messages for this window */
1052 CurrentEntry
= MessageQueue
->SentMessagesListHead
.Flink
;
1053 ListHead
= &MessageQueue
->SentMessagesListHead
;
1054 while (CurrentEntry
!= ListHead
)
1056 SentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1058 if(SentMessage
->Msg
.hwnd
== Window
->hSelf
)
1060 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1062 RemoveEntryList(&SentMessage
->ListEntry
);
1064 /* remove the message from the dispatching list */
1065 if(SentMessage
->DispatchingListEntry
.Flink
!= NULL
)
1067 RemoveEntryList(&SentMessage
->DispatchingListEntry
);
1070 /* wake the sender's thread */
1071 if (SentMessage
->CompletionEvent
!= NULL
)
1073 KeSetEvent(SentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1076 /* dereference our and the sender's message queue */
1077 IntDereferenceMessageQueue(MessageQueue
);
1078 IntDereferenceMessageQueue(SentMessage
->SenderQueue
);
1080 /* free the message */
1081 ExFreePool(SentMessage
);
1083 CurrentEntry
= MessageQueue
->SentMessagesListHead
.Flink
;
1087 CurrentEntry
= CurrentEntry
->Flink
;
1093 MsqSendNotifyMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
1094 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage
)
1096 InsertTailList(&MessageQueue
->NotifyMessagesListHead
,
1097 &NotifyMessage
->ListEntry
);
1098 MessageQueue
->QueueBits
|= QS_SENDMESSAGE
;
1099 MessageQueue
->ChangedBits
|= QS_SENDMESSAGE
;
1100 if (MessageQueue
->WakeMask
& QS_SENDMESSAGE
)
1101 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1105 co_MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
1106 HWND Wnd
, UINT Msg
, WPARAM wParam
, LPARAM lParam
,
1107 UINT uTimeout
, BOOL Block
, INT HookMessage
,
1111 PUSER_SENT_MESSAGE Message
;
1112 KEVENT CompletionEvent
;
1113 NTSTATUS WaitStatus
;
1115 PUSER_MESSAGE_QUEUE ThreadQueue
;
1116 LARGE_INTEGER Timeout
;
1119 if(!(Message
= ExAllocatePoolWithTag(PagedPool
, sizeof(USER_SENT_MESSAGE
), TAG_USRMSG
)))
1121 DPRINT1("MsqSendMessage(): Not enough memory to allocate a message");
1122 return STATUS_INSUFFICIENT_RESOURCES
;
1125 KeInitializeEvent(&CompletionEvent
, NotificationEvent
, FALSE
);
1127 pti
= PsGetCurrentThreadWin32Thread();
1128 ThreadQueue
= pti
->MessageQueue
;
1129 ASSERT(ThreadQueue
!= MessageQueue
);
1131 Timeout
.QuadPart
= (LONGLONG
) uTimeout
* (LONGLONG
) -10000;
1133 /* FIXME - increase reference counter of sender's message queue here */
1136 Message
->Msg
.hwnd
= Wnd
;
1137 Message
->Msg
.message
= Msg
;
1138 Message
->Msg
.wParam
= wParam
;
1139 Message
->Msg
.lParam
= lParam
;
1140 Message
->CompletionEvent
= &CompletionEvent
;
1141 Message
->Result
= &Result
;
1142 Message
->SenderQueue
= ThreadQueue
;
1143 IntReferenceMessageQueue(ThreadQueue
);
1144 Message
->CompletionCallback
= NULL
;
1145 Message
->HookMessage
= HookMessage
;
1147 IntReferenceMessageQueue(MessageQueue
);
1149 /* add it to the list of pending messages */
1150 InsertTailList(&ThreadQueue
->DispatchingMessagesHead
, &Message
->DispatchingListEntry
);
1152 /* queue it in the destination's message queue */
1153 InsertTailList(&MessageQueue
->SentMessagesListHead
, &Message
->ListEntry
);
1155 MessageQueue
->QueueBits
|= QS_SENDMESSAGE
;
1156 MessageQueue
->ChangedBits
|= QS_SENDMESSAGE
;
1157 if (MessageQueue
->WakeMask
& QS_SENDMESSAGE
)
1158 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1160 /* we can't access the Message anymore since it could have already been deleted! */
1168 /* don't process messages sent to the thread */
1169 WaitStatus
= KeWaitForSingleObject(&CompletionEvent
, UserRequest
, UserMode
,
1170 FALSE
, (uTimeout
? &Timeout
: NULL
));
1174 if(WaitStatus
== STATUS_TIMEOUT
)
1176 /* look up if the message has not yet dispatched, if so
1177 make sure it can't pass a result and it must not set the completion event anymore */
1178 Entry
= MessageQueue
->SentMessagesListHead
.Flink
;
1179 while (Entry
!= &MessageQueue
->SentMessagesListHead
)
1181 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
)
1184 /* we can access Message here, it's secure because the message queue is locked
1185 and the message is still hasn't been dispatched */
1186 Message
->CompletionEvent
= NULL
;
1187 Message
->Result
= NULL
;
1190 Entry
= Entry
->Flink
;
1193 /* remove from the local dispatching list so the other thread knows,
1194 it can't pass a result and it must not set the completion event anymore */
1195 Entry
= ThreadQueue
->DispatchingMessagesHead
.Flink
;
1196 while (Entry
!= &ThreadQueue
->DispatchingMessagesHead
)
1198 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, DispatchingListEntry
)
1201 /* we can access Message here, it's secure because the sender's message is locked
1202 and the message has definitely not yet been destroyed, otherwise it would
1203 have been removed from this list by the dispatching routine right after
1204 dispatching the message */
1205 Message
->CompletionEvent
= NULL
;
1206 Message
->Result
= NULL
;
1207 RemoveEntryList(&Message
->DispatchingListEntry
);
1208 Message
->DispatchingListEntry
.Flink
= NULL
;
1211 Entry
= Entry
->Flink
;
1214 DPRINT("MsqSendMessage (blocked) timed out\n");
1216 while (co_MsqDispatchOneSentMessage(ThreadQueue
))
1221 PVOID WaitObjects
[2];
1223 WaitObjects
[0] = &CompletionEvent
;
1224 WaitObjects
[1] = ThreadQueue
->NewMessages
;
1231 WaitStatus
= KeWaitForMultipleObjects(2, WaitObjects
, WaitAny
, UserRequest
,
1232 UserMode
, FALSE
, (uTimeout
? &Timeout
: NULL
), NULL
);
1236 if(WaitStatus
== STATUS_TIMEOUT
)
1238 /* look up if the message has not yet been dispatched, if so
1239 make sure it can't pass a result and it must not set the completion event anymore */
1240 Entry
= MessageQueue
->SentMessagesListHead
.Flink
;
1241 while (Entry
!= &MessageQueue
->SentMessagesListHead
)
1243 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
)
1246 /* we can access Message here, it's secure because the message queue is locked
1247 and the message is still hasn't been dispatched */
1248 Message
->CompletionEvent
= NULL
;
1249 Message
->Result
= NULL
;
1252 Entry
= Entry
->Flink
;
1255 /* remove from the local dispatching list so the other thread knows,
1256 it can't pass a result and it must not set the completion event anymore */
1257 Entry
= ThreadQueue
->DispatchingMessagesHead
.Flink
;
1258 while (Entry
!= &ThreadQueue
->DispatchingMessagesHead
)
1260 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, DispatchingListEntry
)
1263 /* we can access Message here, it's secure because the sender's message is locked
1264 and the message has definitely not yet been destroyed, otherwise it would
1265 have been removed from this list by the dispatching routine right after
1266 dispatching the message */
1267 Message
->CompletionEvent
= NULL
;
1268 Message
->Result
= NULL
;
1269 RemoveEntryList(&Message
->DispatchingListEntry
);
1270 Message
->DispatchingListEntry
.Flink
= NULL
;
1273 Entry
= Entry
->Flink
;
1276 DPRINT("MsqSendMessage timed out\n");
1279 while (co_MsqDispatchOneSentMessage(ThreadQueue
))
1282 while (NT_SUCCESS(WaitStatus
) && STATUS_WAIT_0
!= WaitStatus
);
1285 if(WaitStatus
!= STATUS_TIMEOUT
)
1286 *uResult
= (STATUS_WAIT_0
== WaitStatus
? Result
: -1);
1292 MsqPostMessage(PUSER_MESSAGE_QUEUE MessageQueue
, MSG
* Msg
, BOOLEAN FreeLParam
,
1295 PUSER_MESSAGE Message
;
1297 if(!(Message
= MsqCreateMessage(Msg
, FreeLParam
)))
1301 InsertTailList(&MessageQueue
->PostedMessagesListHead
,
1302 &Message
->ListEntry
);
1303 MessageQueue
->QueueBits
|= MessageBits
;
1304 MessageQueue
->ChangedBits
|= MessageBits
;
1305 if (MessageQueue
->WakeMask
& MessageBits
)
1306 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1310 MsqPostQuitMessage(PUSER_MESSAGE_QUEUE MessageQueue
, ULONG ExitCode
)
1312 MessageQueue
->QuitPosted
= TRUE
;
1313 MessageQueue
->QuitExitCode
= ExitCode
;
1314 MessageQueue
->QueueBits
|= QS_POSTMESSAGE
;
1315 MessageQueue
->ChangedBits
|= QS_POSTMESSAGE
;
1316 if (MessageQueue
->WakeMask
& QS_POSTMESSAGE
)
1317 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1321 co_MsqFindMessage(IN PUSER_MESSAGE_QUEUE MessageQueue
,
1322 IN BOOLEAN Hardware
,
1324 IN PWINDOW_OBJECT Window
,
1325 IN UINT MsgFilterLow
,
1326 IN UINT MsgFilterHigh
,
1327 OUT PUSER_MESSAGE
* Message
)
1329 PLIST_ENTRY CurrentEntry
;
1330 PUSER_MESSAGE CurrentMessage
;
1331 PLIST_ENTRY ListHead
;
1335 return(co_MsqPeekHardwareMessage(MessageQueue
, Window
,
1336 MsgFilterLow
, MsgFilterHigh
,
1340 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
1341 ListHead
= &MessageQueue
->PostedMessagesListHead
;
1342 while (CurrentEntry
!= ListHead
)
1344 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1346 if ((!Window
|| (int)Window
== 1 || Window
->hSelf
== CurrentMessage
->Msg
.hwnd
) &&
1347 ((MsgFilterLow
== 0 && MsgFilterHigh
== 0) ||
1348 (MsgFilterLow
<= CurrentMessage
->Msg
.message
&&
1349 MsgFilterHigh
>= CurrentMessage
->Msg
.message
)))
1353 RemoveEntryList(&CurrentMessage
->ListEntry
);
1356 *Message
= CurrentMessage
;
1359 CurrentEntry
= CurrentEntry
->Flink
;
1366 co_MsqWaitForNewMessages(PUSER_MESSAGE_QUEUE MessageQueue
, PWINDOW_OBJECT WndFilter
,
1367 UINT MsgFilterMin
, UINT MsgFilterMax
)
1369 PVOID WaitObjects
[2] = {MessageQueue
->NewMessages
, &HardwareMessageEvent
};
1370 LARGE_INTEGER TimerExpiry
;
1371 PLARGE_INTEGER Timeout
;
1374 if (MsqGetFirstTimerExpiry(MessageQueue
, WndFilter
, MsgFilterMin
, MsgFilterMax
, &TimerExpiry
))
1376 Timeout
= &TimerExpiry
;
1383 IdlePing(); // Going to wait so send Idle ping.
1387 ret
= KeWaitForMultipleObjects(2,
1402 MsqIsHung(PUSER_MESSAGE_QUEUE MessageQueue
)
1404 LARGE_INTEGER LargeTickCount
;
1406 KeQueryTickCount(&LargeTickCount
);
1407 return ((LargeTickCount
.u
.LowPart
- MessageQueue
->LastMsgRead
) > MSQ_HUNG
);
1411 MsqInitializeMessageQueue(struct _ETHREAD
*Thread
, PUSER_MESSAGE_QUEUE MessageQueue
)
1413 LARGE_INTEGER LargeTickCount
;
1416 MessageQueue
->Thread
= Thread
;
1417 MessageQueue
->CaretInfo
= (PTHRDCARETINFO
)(MessageQueue
+ 1);
1418 InitializeListHead(&MessageQueue
->PostedMessagesListHead
);
1419 InitializeListHead(&MessageQueue
->SentMessagesListHead
);
1420 InitializeListHead(&MessageQueue
->HardwareMessagesListHead
);
1421 InitializeListHead(&MessageQueue
->TimerListHead
);
1422 InitializeListHead(&MessageQueue
->DispatchingMessagesHead
);
1423 InitializeListHead(&MessageQueue
->LocalDispatchingMessagesHead
);
1424 KeInitializeMutex(&MessageQueue
->HardwareLock
, 0);
1425 MessageQueue
->QuitPosted
= FALSE
;
1426 MessageQueue
->QuitExitCode
= 0;
1427 KeQueryTickCount(&LargeTickCount
);
1428 MessageQueue
->LastMsgRead
= LargeTickCount
.u
.LowPart
;
1429 MessageQueue
->FocusWindow
= NULL
;
1430 MessageQueue
->PaintCount
= 0;
1431 // HACK!!!!!!! Newbies that wrote this should hold your head down in shame! (jt)
1432 MessageQueue
->WakeMask
= ~0;
1433 MessageQueue
->NewMessagesHandle
= NULL
;
1435 Status
= ZwCreateEvent(&MessageQueue
->NewMessagesHandle
, EVENT_ALL_ACCESS
,
1436 NULL
, SynchronizationEvent
, FALSE
);
1437 if (!NT_SUCCESS(Status
))
1442 Status
= ObReferenceObjectByHandle(MessageQueue
->NewMessagesHandle
, 0,
1443 ExEventObjectType
, KernelMode
,
1444 (PVOID
*)&MessageQueue
->NewMessages
, NULL
);
1445 if (!NT_SUCCESS(Status
))
1447 ZwClose(MessageQueue
->NewMessagesHandle
);
1448 MessageQueue
->NewMessagesHandle
= NULL
;
1456 MsqCleanupMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue
)
1458 PLIST_ENTRY CurrentEntry
;
1459 PUSER_MESSAGE CurrentMessage
;
1460 PTIMER_ENTRY CurrentTimer
;
1461 PUSER_SENT_MESSAGE CurrentSentMessage
;
1463 /* cleanup posted messages */
1464 while (!IsListEmpty(&MessageQueue
->PostedMessagesListHead
))
1466 CurrentEntry
= RemoveHeadList(&MessageQueue
->PostedMessagesListHead
);
1467 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1469 MsqDestroyMessage(CurrentMessage
);
1472 /* remove the messages that have not yet been dispatched */
1473 while (!IsListEmpty(&MessageQueue
->SentMessagesListHead
))
1475 CurrentEntry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
1476 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1479 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1481 /* remove the message from the dispatching list */
1482 if(CurrentSentMessage
->DispatchingListEntry
.Flink
!= NULL
)
1484 RemoveEntryList(&CurrentSentMessage
->DispatchingListEntry
);
1487 /* wake the sender's thread */
1488 if (CurrentSentMessage
->CompletionEvent
!= NULL
)
1490 KeSetEvent(CurrentSentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1493 /* dereference our and the sender's message queue */
1494 IntDereferenceMessageQueue(MessageQueue
);
1495 IntDereferenceMessageQueue(CurrentSentMessage
->SenderQueue
);
1497 /* free the message */
1498 ExFreePool(CurrentSentMessage
);
1501 /* cleanup timers */
1502 while (! IsListEmpty(&MessageQueue
->TimerListHead
))
1504 CurrentEntry
= RemoveHeadList(&MessageQueue
->TimerListHead
);
1505 CurrentTimer
= CONTAINING_RECORD(CurrentEntry
, TIMER_ENTRY
, ListEntry
);
1506 ExFreeToPagedLookasideList(&TimerLookasideList
, CurrentTimer
);
1509 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
1510 ExitThread() was called in a SendMessage() umode callback */
1511 while (!IsListEmpty(&MessageQueue
->LocalDispatchingMessagesHead
))
1513 CurrentEntry
= RemoveHeadList(&MessageQueue
->LocalDispatchingMessagesHead
);
1514 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1517 /* remove the message from the dispatching list */
1518 if(CurrentSentMessage
->DispatchingListEntry
.Flink
!= NULL
)
1520 RemoveEntryList(&CurrentSentMessage
->DispatchingListEntry
);
1523 DPRINT("Notify the sender, the thread has been terminated while dispatching a message!\n");
1525 /* wake the sender's thread */
1526 if (CurrentSentMessage
->CompletionEvent
!= NULL
)
1528 KeSetEvent(CurrentSentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1531 /* dereference our and the sender's message queue */
1532 IntDereferenceMessageQueue(MessageQueue
);
1533 IntDereferenceMessageQueue(CurrentSentMessage
->SenderQueue
);
1535 /* free the message */
1536 ExFreePool(CurrentSentMessage
);
1539 /* tell other threads not to bother returning any info to us */
1540 while (! IsListEmpty(&MessageQueue
->DispatchingMessagesHead
))
1542 CurrentEntry
= RemoveHeadList(&MessageQueue
->DispatchingMessagesHead
);
1543 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1544 DispatchingListEntry
);
1545 CurrentSentMessage
->CompletionEvent
= NULL
;
1546 CurrentSentMessage
->Result
= NULL
;
1548 /* do NOT dereference our message queue as it might get attempted to be
1554 PUSER_MESSAGE_QUEUE FASTCALL
1555 MsqCreateMessageQueue(struct _ETHREAD
*Thread
)
1557 PUSER_MESSAGE_QUEUE MessageQueue
;
1559 MessageQueue
= (PUSER_MESSAGE_QUEUE
)ExAllocatePoolWithTag(NonPagedPool
,
1560 sizeof(USER_MESSAGE_QUEUE
) + sizeof(THRDCARETINFO
),
1568 RtlZeroMemory(MessageQueue
, sizeof(USER_MESSAGE_QUEUE
) + sizeof(THRDCARETINFO
));
1569 /* hold at least one reference until it'll be destroyed */
1570 IntReferenceMessageQueue(MessageQueue
);
1571 /* initialize the queue */
1572 if (!MsqInitializeMessageQueue(Thread
, MessageQueue
))
1574 IntDereferenceMessageQueue(MessageQueue
);
1578 return MessageQueue
;
1582 MsqDestroyMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue
)
1586 /* remove the message queue from any desktops */
1587 if ((desk
= InterlockedExchangePointer((PVOID
*)&MessageQueue
->Desktop
, 0)))
1589 (void)InterlockedExchangePointer((PVOID
*)&desk
->ActiveMessageQueue
, 0);
1590 IntDereferenceMessageQueue(MessageQueue
);
1594 MsqCleanupMessageQueue(MessageQueue
);
1596 /* decrease the reference counter, if it hits zero, the queue will be freed */
1597 IntDereferenceMessageQueue(MessageQueue
);
1601 MsqGetHooks(PUSER_MESSAGE_QUEUE Queue
)
1603 return Queue
->Hooks
;
1607 MsqSetHooks(PUSER_MESSAGE_QUEUE Queue
, PHOOKTABLE Hooks
)
1609 Queue
->Hooks
= Hooks
;
1613 MsqSetMessageExtraInfo(LPARAM lParam
)
1617 PUSER_MESSAGE_QUEUE MessageQueue
;
1619 pti
= PsGetCurrentThreadWin32Thread();
1620 MessageQueue
= pti
->MessageQueue
;
1626 Ret
= MessageQueue
->ExtraInfo
;
1627 MessageQueue
->ExtraInfo
= lParam
;
1633 MsqGetMessageExtraInfo(VOID
)
1636 PUSER_MESSAGE_QUEUE MessageQueue
;
1638 pti
= PsGetCurrentThreadWin32Thread();
1639 MessageQueue
= pti
->MessageQueue
;
1645 return MessageQueue
->ExtraInfo
;
1649 MsqSetStateWindow(PUSER_MESSAGE_QUEUE MessageQueue
, ULONG Type
, HWND hWnd
)
1655 case MSQ_STATE_CAPTURE
:
1656 Prev
= MessageQueue
->CaptureWindow
;
1657 MessageQueue
->CaptureWindow
= hWnd
;
1659 case MSQ_STATE_ACTIVE
:
1660 Prev
= MessageQueue
->ActiveWindow
;
1661 MessageQueue
->ActiveWindow
= hWnd
;
1663 case MSQ_STATE_FOCUS
:
1664 Prev
= MessageQueue
->FocusWindow
;
1665 MessageQueue
->FocusWindow
= hWnd
;
1667 case MSQ_STATE_MENUOWNER
:
1668 Prev
= MessageQueue
->MenuOwner
;
1669 MessageQueue
->MenuOwner
= hWnd
;
1671 case MSQ_STATE_MOVESIZE
:
1672 Prev
= MessageQueue
->MoveSize
;
1673 MessageQueue
->MoveSize
= hWnd
;
1675 case MSQ_STATE_CARET
:
1676 ASSERT(MessageQueue
->CaretInfo
);
1677 Prev
= MessageQueue
->CaretInfo
->hWnd
;
1678 MessageQueue
->CaretInfo
->hWnd
= hWnd
;
1686 static VOID FASTCALL
1687 DumpTimerList(PUSER_MESSAGE_QUEUE MessageQueue
)
1689 PLIST_ENTRY Current
;
1692 Current
= MessageQueue
->TimerListHead
.Flink
;
1693 if (Current
== &MessageQueue
->TimerListHead
)
1695 DPRINT("timer list is empty for queue %p\n", MessageQueue
);
1697 while (Current
!= &MessageQueue
->TimerListHead
)
1699 Timer
= CONTAINING_RECORD(Current
, TIMER_ENTRY
, ListEntry
);
1700 DPRINT("queue %p timer %p expiry %I64d wnd %x id %p period %u timerproc %p msg %u\n",
1701 MessageQueue
, Timer
, Timer
->ExpiryTime
.QuadPart
, Timer
->Wnd
, Timer
->IDEvent
,
1702 Timer
->Period
, Timer
->TimerFunc
, Timer
->Msg
);
1703 Current
= Current
->Flink
;
1706 #endif /* ! defined(NDEBUG) */
1708 /* Must have the message queue locked while calling this */
1709 static VOID FASTCALL
1710 InsertTimer(PUSER_MESSAGE_QUEUE MessageQueue
, PTIMER_ENTRY NewTimer
)
1712 PLIST_ENTRY Current
;
1714 Current
= MessageQueue
->TimerListHead
.Flink
;
1715 while (Current
!= &MessageQueue
->TimerListHead
)
1717 if (NewTimer
->ExpiryTime
.QuadPart
<
1718 CONTAINING_RECORD(Current
, TIMER_ENTRY
, ListEntry
)->ExpiryTime
.QuadPart
)
1722 Current
= Current
->Flink
;
1725 InsertTailList(Current
, &NewTimer
->ListEntry
);
1728 /* Must have the message queue locked while calling this */
1729 static PTIMER_ENTRY FASTCALL
1730 RemoveTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
, UINT_PTR IDEvent
, UINT Msg
)
1733 PLIST_ENTRY EnumEntry
;
1735 /* Remove timer if already in the queue */
1736 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1737 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1739 Timer
= CONTAINING_RECORD(EnumEntry
, TIMER_ENTRY
, ListEntry
);
1740 EnumEntry
= EnumEntry
->Flink
;
1742 if (Timer
->Wnd
== Wnd
&&
1743 Timer
->IDEvent
== IDEvent
&&
1746 RemoveEntryList(&Timer
->ListEntry
);
1755 MsqSetTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
,
1756 UINT_PTR IDEvent
, UINT Period
, TIMERPROC TimerFunc
,
1760 LARGE_INTEGER CurrentTime
;
1762 DPRINT("MsqSetTimer queue %p wnd %x id %p period %u timerproc %p msg %d\n",
1763 MessageQueue
, Wnd
, IDEvent
, Period
, TimerFunc
, Msg
);
1765 Timer
= RemoveTimer(MessageQueue
, Wnd
, IDEvent
, Msg
);
1768 Timer
= ExAllocateFromPagedLookasideList(&TimerLookasideList
);
1771 DPRINT1("Failed to allocate timer entry\n");
1774 DPRINT("Allocated new timer entry %p\n", Timer
);
1776 Timer
->IDEvent
= IDEvent
;
1781 DPRINT("Updating existing timer entry %p\n", Timer
);
1784 KeQuerySystemTime(&CurrentTime
);
1785 Timer
->ExpiryTime
.QuadPart
= CurrentTime
.QuadPart
+
1786 (ULONGLONG
) Period
* (ULONGLONG
) 10000;
1787 Timer
->Period
= Period
;
1788 Timer
->TimerFunc
= TimerFunc
;
1789 DPRINT("Insert timer now %I64d expiry %I64d\n", CurrentTime
.QuadPart
,
1790 Timer
->ExpiryTime
.QuadPart
);
1792 InsertTimer(MessageQueue
, Timer
);
1796 DumpTimerList(MessageQueue
);
1797 #endif /* ! defined(NDEBUG) */
1803 MsqKillTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
,
1804 UINT_PTR IDEvent
, UINT Msg
)
1808 DPRINT("MsqKillTimer queue %p wnd %x id %p msg %d\n",
1809 MessageQueue
, Wnd
, IDEvent
, Msg
);
1811 Timer
= RemoveTimer(MessageQueue
, Wnd
, IDEvent
, Msg
);
1815 DPRINT("Failed to remove timer from list, not found\n");
1819 ExFreeToPagedLookasideList(&TimerLookasideList
, Timer
);
1823 DumpTimerList(MessageQueue
);
1824 #endif /* ! defined(NDEBUG) */
1826 return NULL
!= Timer
;
1830 MsqGetTimerMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
1831 PWINDOW_OBJECT WindowFilter
, UINT MsgFilterMin
, UINT MsgFilterMax
,
1832 MSG
*Msg
, BOOLEAN Restart
)
1835 LARGE_INTEGER CurrentTime
;
1836 LARGE_INTEGER LargeTickCount
;
1837 PLIST_ENTRY EnumEntry
;
1841 DPRINT("MsqGetTimerMessage queue %p msg %p restart %s\n",
1842 MessageQueue
, Msg
, Restart
? "TRUE" : "FALSE");
1844 KeQuerySystemTime(&CurrentTime
);
1845 DPRINT("Current time %I64d\n", CurrentTime
.QuadPart
);
1846 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1848 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1850 Timer
= CONTAINING_RECORD(MessageQueue
->TimerListHead
.Flink
,
1851 TIMER_ENTRY
, ListEntry
);
1852 DPRINT("Checking timer %p wnd %x expiry %I64d\n", Timer
, Timer
->Wnd
,
1853 Timer
->ExpiryTime
.QuadPart
);
1854 EnumEntry
= EnumEntry
->Flink
;
1855 if ((NULL
== WindowFilter
|| Timer
->Wnd
== WindowFilter
->hSelf
) &&
1856 ((MsgFilterMin
== 0 && MsgFilterMax
== 0) ||
1857 (MsgFilterMin
<= Timer
->Msg
&&
1858 Timer
->Msg
<= MsgFilterMax
)))
1860 if (Timer
->ExpiryTime
.QuadPart
<= CurrentTime
.QuadPart
)
1862 DPRINT("Timer is expired\n");
1868 DPRINT("No need to check later timers\n");
1874 DPRINT("timer %p (wnd %x msg %d) failed filter wnd %x msgmin %d msgmax %d\n",
1875 Timer
, Timer
->Wnd
, Timer
->Msg
, WindowFilter
->hSelf
, MsgFilterMin
, MsgFilterMax
);
1881 DPRINT("No timer pending\n");
1885 Msg
->hwnd
= Timer
->Wnd
;
1886 Msg
->message
= Timer
->Msg
;
1887 Msg
->wParam
= (WPARAM
) Timer
->IDEvent
;
1888 Msg
->lParam
= (LPARAM
) Timer
->TimerFunc
;
1889 KeQueryTickCount(&LargeTickCount
);
1890 Msg
->time
= MsqCalculateMessageTime(&LargeTickCount
);
1891 pti
= PsGetCurrentThreadWin32Thread();
1892 IntGetCursorLocation(pti
->Desktop
->WindowStation
,
1897 RemoveEntryList(&Timer
->ListEntry
);
1898 Timer
->ExpiryTime
.QuadPart
= CurrentTime
.QuadPart
+
1899 (ULONGLONG
) Timer
->Period
* (ULONGLONG
) 10000;
1900 DPRINT("Restarting timer %p expires %I64d\n", Timer
, Timer
->ExpiryTime
.QuadPart
);
1901 InsertTimer(MessageQueue
, Timer
);
1905 DumpTimerList(MessageQueue
);
1906 #endif /* ! defined(NDEBUG) */
1910 DPRINT("Created message wnd %x msg %d wParam %u lParam %u\n", Msg
->hwnd
, Msg
->message
,
1911 Msg
->wParam
, Msg
->lParam
);
1917 MsqRemoveTimersWindow(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
)
1920 PLIST_ENTRY EnumEntry
;
1922 DPRINT("MsqRemoveTimersWindow queue %p wnd %x\n", MessageQueue
, Wnd
);
1924 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1925 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1927 Timer
= CONTAINING_RECORD(EnumEntry
, TIMER_ENTRY
, ListEntry
);
1928 EnumEntry
= EnumEntry
->Flink
;
1929 if (Timer
->Wnd
== Wnd
)
1931 DPRINT("Removing timer %p because its window is going away\n", Timer
);
1932 RemoveEntryList(&Timer
->ListEntry
);
1933 ExFreeToPagedLookasideList(&TimerLookasideList
, Timer
);
1938 DumpTimerList(MessageQueue
);
1939 #endif /* ! defined(NDEBUG) */
1944 MsqGetFirstTimerExpiry(PUSER_MESSAGE_QUEUE MessageQueue
,
1945 PWINDOW_OBJECT WndFilter
, UINT MsgFilterMin
, UINT MsgFilterMax
,
1946 PLARGE_INTEGER FirstTimerExpiry
)
1949 PLIST_ENTRY EnumEntry
;
1951 DPRINT("MsqGetFirstTimerExpiry queue %p wndfilter %x msgfiltermin %d msgfiltermax %d expiry %p\n",
1952 MessageQueue
, WndFilter
, MsgFilterMin
, MsgFilterMax
, FirstTimerExpiry
);
1954 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1955 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1957 Timer
= CONTAINING_RECORD(MessageQueue
->TimerListHead
.Flink
,
1958 TIMER_ENTRY
, ListEntry
);
1959 EnumEntry
= EnumEntry
->Flink
;
1960 if ((NULL
== WndFilter
|| (int)WndFilter
== 1 || Timer
->Wnd
== WndFilter
->hSelf
) &&
1961 ((MsgFilterMin
== 0 && MsgFilterMax
== 0) ||
1962 (MsgFilterMin
<= Timer
->Msg
&&
1963 Timer
->Msg
<= MsgFilterMax
)))
1965 *FirstTimerExpiry
= Timer
->ExpiryTime
;
1966 DPRINT("First timer expires %I64d\n", Timer
->ExpiryTime
);