2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: Message queues
5 * FILE: subsystems/win32/win32k/ntuser/msgqueue.c
6 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
10 * 06-06-2001 CSH Created
13 /* INCLUDES ******************************************************************/
20 /* GLOBALS *******************************************************************/
22 static PAGED_LOOKASIDE_LIST MessageLookasideList
;
23 MOUSEMOVEPOINT MouseHistoryOfMoves
[64];
26 /* FUNCTIONS *****************************************************************/
31 MsqInitializeImpl(VOID
)
33 ExInitializePagedLookasideList(&MessageLookasideList
,
41 return(STATUS_SUCCESS
);
44 DWORD FASTCALL
UserGetKeyState(DWORD key
)
48 PUSER_MESSAGE_QUEUE MessageQueue
;
50 pti
= PsGetCurrentThreadWin32Thread();
51 MessageQueue
= pti
->MessageQueue
;
55 ret
= (DWORD
)MessageQueue
->KeyState
[key
];
61 /* change the input key state for a given key */
62 static void set_input_key_state( PUSER_MESSAGE_QUEUE MessageQueue
, UCHAR key
, BOOL down
)
64 DPRINT("set_input_key_state key:%d, down:%d\n", key
, down
);
68 if (!(MessageQueue
->KeyState
[key
] & KS_DOWN_BIT
))
70 MessageQueue
->KeyState
[key
] ^= KS_LOCK_BIT
;
72 MessageQueue
->KeyState
[key
] |= KS_DOWN_BIT
;
76 MessageQueue
->KeyState
[key
] &= ~KS_DOWN_BIT
;
80 /* update the input key state for a keyboard message */
81 static void update_input_key_state( PUSER_MESSAGE_QUEUE MessageQueue
, MSG
* msg
)
86 DPRINT("update_input_key_state message:%d\n", msg
->message
);
94 set_input_key_state( MessageQueue
, VK_LBUTTON
, down
);
100 set_input_key_state( MessageQueue
, VK_MBUTTON
, down
);
106 set_input_key_state( MessageQueue
, VK_RBUTTON
, down
);
112 if (msg
->wParam
== XBUTTON1
)
113 set_input_key_state( MessageQueue
, VK_XBUTTON1
, down
);
114 else if (msg
->wParam
== XBUTTON2
)
115 set_input_key_state( MessageQueue
, VK_XBUTTON2
, down
);
123 key
= (UCHAR
)msg
->wParam
;
124 set_input_key_state( MessageQueue
, key
, down
);
129 down
= (MessageQueue
->KeyState
[VK_LCONTROL
] | MessageQueue
->KeyState
[VK_RCONTROL
]) & KS_DOWN_BIT
;
130 set_input_key_state( MessageQueue
, VK_CONTROL
, down
);
134 down
= (MessageQueue
->KeyState
[VK_LMENU
] | MessageQueue
->KeyState
[VK_RMENU
]) & KS_DOWN_BIT
;
135 set_input_key_state( MessageQueue
, VK_MENU
, down
);
139 down
= (MessageQueue
->KeyState
[VK_LSHIFT
] | MessageQueue
->KeyState
[VK_RSHIFT
]) & KS_DOWN_BIT
;
140 set_input_key_state( MessageQueue
, VK_SHIFT
, down
);
148 IntMsqSetWakeMask(DWORD WakeMask
)
150 PTHREADINFO Win32Thread
;
151 PUSER_MESSAGE_QUEUE MessageQueue
;
152 HANDLE MessageEventHandle
;
153 DWORD dwFlags
= HIWORD(WakeMask
);
155 Win32Thread
= PsGetCurrentThreadWin32Thread();
156 if (Win32Thread
== NULL
|| Win32Thread
->MessageQueue
== NULL
)
159 MessageQueue
= Win32Thread
->MessageQueue
;
160 // Win32Thread->pEventQueueServer; IntMsqSetWakeMask returns Win32Thread->hEventQueueClient
161 MessageEventHandle
= MessageQueue
->NewMessagesHandle
;
163 if (Win32Thread
->pcti
)
165 if ( (Win32Thread
->pcti
->fsChangeBits
& LOWORD(WakeMask
)) ||
166 ( (dwFlags
& MWMO_INPUTAVAILABLE
) && (Win32Thread
->pcti
->fsWakeBits
& LOWORD(WakeMask
)) ) )
168 DPRINT1("Chg 0x%x Wake 0x%x Mask 0x%x\n",Win32Thread
->pcti
->fsChangeBits
, Win32Thread
->pcti
->fsWakeBits
, WakeMask
);
169 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
); // Wake it up!
170 return MessageEventHandle
;
176 return MessageEventHandle
;
180 IntMsqClearWakeMask(VOID
)
182 PTHREADINFO Win32Thread
;
184 Win32Thread
= PsGetCurrentThreadWin32Thread();
185 if (Win32Thread
== NULL
|| Win32Thread
->MessageQueue
== NULL
)
187 // Very hacky, but that is what they do.
188 Win32Thread
->pcti
->fsWakeBits
= 0;
196 Due to the uncertainty of knowing what was set in our multilevel message queue,
197 and even if the bits are all cleared. The same as cTimers/cPaintsReady.
198 I think this is the best solution... (jt) */
200 MsqWakeQueue(PUSER_MESSAGE_QUEUE Queue
, DWORD MessageBits
, BOOL KeyEvent
)
204 pti
= Queue
->Thread
->Tcb
.Win32Thread
;
205 pti
->pcti
->fsWakeBits
|= MessageBits
;
206 pti
->pcti
->fsChangeBits
|= MessageBits
;
208 // Start bit accounting to help clear the main set of bits.
209 if (MessageBits
& QS_KEY
) Queue
->nCntsQBits
[QSRosKey
]++;
210 if (MessageBits
& QS_MOUSEMOVE
) Queue
->nCntsQBits
[QSRosMouseMove
]++;
211 if (MessageBits
& QS_MOUSEBUTTON
) Queue
->nCntsQBits
[QSRosMouseButton
]++;
212 if (MessageBits
& QS_POSTMESSAGE
) Queue
->nCntsQBits
[QSRosPostMessage
]++;
213 if (MessageBits
& QS_SENDMESSAGE
) Queue
->nCntsQBits
[QSRosSendMessage
]++;
214 if (MessageBits
& QS_HOTKEY
) Queue
->nCntsQBits
[QSRosHotKey
]++;
217 KeSetEvent(Queue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
221 ClearMsgBitsMask(PUSER_MESSAGE_QUEUE Queue
, UINT MessageBits
)
226 pti
= Queue
->Thread
->Tcb
.Win32Thread
;
228 if (MessageBits
& QS_KEY
)
230 if (--Queue
->nCntsQBits
[QSRosKey
] == 0) ClrMask
|= QS_KEY
;
232 if (MessageBits
& QS_MOUSEMOVE
) // ReactOS hard coded.
233 { // Account for tracking mouse moves..
234 if (--Queue
->nCntsQBits
[QSRosMouseMove
] == 0) ClrMask
|= QS_MOUSEMOVE
;
235 // Handle mouse move bits here.
236 if (Queue
->MouseMoved
) ClrMask
|= QS_MOUSEMOVE
;
238 if (MessageBits
& QS_MOUSEBUTTON
)
240 if (--Queue
->nCntsQBits
[QSRosMouseButton
] == 0) ClrMask
|= QS_MOUSEBUTTON
;
242 if (MessageBits
& QS_POSTMESSAGE
)
244 if (--Queue
->nCntsQBits
[QSRosPostMessage
] == 0) ClrMask
|= QS_POSTMESSAGE
;
246 if (MessageBits
& QS_TIMER
) // ReactOS hard coded.
247 { // Handle timer bits here.
248 if ( pti
->cTimersReady
)
250 if (--pti
->cTimersReady
== 0) ClrMask
|= QS_TIMER
;
253 if (MessageBits
& QS_PAINT
) // ReactOS hard coded.
254 { // Handle paint bits here.
255 if ( pti
->cPaintsReady
)
257 if (--pti
->cPaintsReady
== 0) ClrMask
|= QS_PAINT
;
260 if (MessageBits
& QS_SENDMESSAGE
)
262 if (--Queue
->nCntsQBits
[QSRosSendMessage
] == 0) ClrMask
|= QS_SENDMESSAGE
;
264 if (MessageBits
& QS_HOTKEY
)
266 if (--Queue
->nCntsQBits
[QSRosHotKey
] == 0) ClrMask
|= QS_HOTKEY
;
269 pti
->pcti
->fsWakeBits
&= ~ClrMask
;
270 pti
->pcti
->fsChangeBits
&= ~ClrMask
;
274 MsqIncPaintCountQueue(PUSER_MESSAGE_QUEUE Queue
)
277 pti
= Queue
->Thread
->Tcb
.Win32Thread
;
279 MsqWakeQueue(Queue
, QS_PAINT
, TRUE
);
283 MsqDecPaintCountQueue(PUSER_MESSAGE_QUEUE Queue
)
285 ClearMsgBitsMask(Queue
, QS_PAINT
);
289 MsqPostMouseMove(PUSER_MESSAGE_QUEUE MessageQueue
, MSG
* Msg
)
291 MessageQueue
->MouseMoveMsg
= *Msg
;
292 MessageQueue
->MouseMoved
= TRUE
;
293 MsqWakeQueue(MessageQueue
, QS_MOUSEMOVE
, TRUE
);
297 co_MsqInsertMouseMessage(MSG
* Msg
, DWORD flags
, ULONG_PTR dwExtraInfo
, BOOL Hook
)
299 LARGE_INTEGER LargeTickCount
;
300 MSLLHOOKSTRUCT MouseHookData
;
302 PWND pwnd
, pwndDesktop
;
304 KeQueryTickCount(&LargeTickCount
);
305 Msg
->time
= MsqCalculateMessageTime(&LargeTickCount
);
307 MouseHookData
.pt
.x
= LOWORD(Msg
->lParam
);
308 MouseHookData
.pt
.y
= HIWORD(Msg
->lParam
);
312 MouseHookData
.mouseData
= MAKELONG(0, GET_WHEEL_DELTA_WPARAM(Msg
->wParam
));
316 case WM_XBUTTONDBLCLK
:
317 case WM_NCXBUTTONDOWN
:
319 case WM_NCXBUTTONDBLCLK
:
320 MouseHookData
.mouseData
= MAKELONG(0, HIWORD(Msg
->wParam
));
323 MouseHookData
.mouseData
= 0;
327 MouseHookData
.flags
= flags
; // LLMHF_INJECTED
328 MouseHookData
.time
= Msg
->time
;
329 MouseHookData
.dwExtraInfo
= dwExtraInfo
;
331 /* If the hook procedure returned non zero, dont send the message */
334 if (co_HOOK_CallHooks(WH_MOUSE_LL
, HC_ACTION
, Msg
->message
, (LPARAM
) &MouseHookData
))
338 /* Get the desktop window */
339 pwndDesktop
= UserGetDesktopWindow();
343 /* Set hit somewhere on the desktop */
344 pDesk
= pwndDesktop
->head
.rpdesk
;
345 pDesk
->htEx
= HTNOWHERE
;
346 pDesk
->spwndTrack
= pwndDesktop
;
348 /* Check if the mouse is captured */
349 Msg
->hwnd
= IntGetCaptureWindow();
350 if(Msg
->hwnd
!= NULL
)
352 pwnd
= UserGetWindowObject(Msg
->hwnd
);
353 if ((pwnd
->style
& WS_VISIBLE
) &&
354 IntPtInWindow(pwnd
, Msg
->pt
.x
, Msg
->pt
.y
))
356 pDesk
->htEx
= HTCLIENT
;
357 pDesk
->spwndTrack
= pwnd
;
362 /* Loop all top level windows to find which one should receive input */
363 for( pwnd
= pwndDesktop
->spwndChild
;
365 pwnd
= pwnd
->spwndNext
)
367 if ( pwnd
->state2
& WNDS2_INDESTROY
|| pwnd
->state
& WNDS_DESTROYED
)
369 DPRINT("The Window is in DESTROY!\n");
373 if((pwnd
->style
& WS_VISIBLE
) &&
374 IntPtInWindow(pwnd
, Msg
->pt
.x
, Msg
->pt
.y
))
376 Msg
->hwnd
= pwnd
->head
.h
;
377 pDesk
->htEx
= HTCLIENT
;
378 pDesk
->spwndTrack
= pwnd
;
384 /* Check if we found a window */
385 if(Msg
->hwnd
!= NULL
&& pwnd
!= NULL
)
387 if(Msg
->message
== WM_MOUSEMOVE
)
389 /* Mouse move is a special case*/
390 MsqPostMouseMove(pwnd
->head
.pti
->MessageQueue
, Msg
);
394 DPRINT("Posting mouse message to hwnd=0x%x!\n", UserHMGetHandle(pwnd
));
395 MsqPostMessage(pwnd
->head
.pti
->MessageQueue
, Msg
, TRUE
, QS_MOUSEBUTTON
);
399 /* Do GetMouseMovePointsEx FIFO. */
400 MouseHistoryOfMoves
[gcur_count
].x
= Msg
->pt
.x
;
401 MouseHistoryOfMoves
[gcur_count
].y
= Msg
->pt
.y
;
402 MouseHistoryOfMoves
[gcur_count
].time
= Msg
->time
;
403 MouseHistoryOfMoves
[gcur_count
].dwExtraInfo
= dwExtraInfo
;
404 if (gcur_count
++ == 64) gcur_count
= 0; // 0 - 63 is 64, FIFO forwards.
408 // Note: Only called from input.c.
411 co_MsqPostKeyboardMessage(UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
413 PUSER_MESSAGE_QUEUE FocusMessageQueue
;
415 LARGE_INTEGER LargeTickCount
;
416 KBDLLHOOKSTRUCT KbdHookData
;
418 DPRINT("MsqPostKeyboardMessage(uMsg 0x%x, wParam 0x%x, lParam 0x%x)\n",
419 uMsg
, wParam
, lParam
);
421 // Condition may arise when calling MsqPostMessage and waiting for an event.
422 ASSERT(UserIsEntered());
424 FocusMessageQueue
= IntGetFocusMessageQueue();
428 if (FocusMessageQueue
&& (FocusMessageQueue
->FocusWindow
!= (HWND
)0))
429 Msg
.hwnd
= FocusMessageQueue
->FocusWindow
;
435 KeQueryTickCount(&LargeTickCount
);
436 Msg
.time
= MsqCalculateMessageTime(&LargeTickCount
);
438 /* We can't get the Msg.pt point here since we don't know thread
439 (and thus the window station) the message will end up in yet. */
441 KbdHookData
.vkCode
= Msg
.wParam
;
442 KbdHookData
.scanCode
= (Msg
.lParam
>> 16) & 0xff;
443 KbdHookData
.flags
= (0 == (Msg
.lParam
& 0x01000000) ? 0 : LLKHF_EXTENDED
) |
444 (0 == (Msg
.lParam
& 0x20000000) ? 0 : LLKHF_ALTDOWN
) |
445 (0 == (Msg
.lParam
& 0x80000000) ? 0 : LLKHF_UP
);
446 KbdHookData
.time
= Msg
.time
;
447 KbdHookData
.dwExtraInfo
= 0;
448 if (co_HOOK_CallHooks(WH_KEYBOARD_LL
, HC_ACTION
, Msg
.message
, (LPARAM
) &KbdHookData
))
450 DPRINT1("Kbd msg %d wParam %d lParam 0x%08x dropped by WH_KEYBOARD_LL hook\n",
451 Msg
.message
, Msg
.wParam
, Msg
.lParam
);
455 if (FocusMessageQueue
== NULL
)
457 DPRINT("No focus message queue\n");
461 if (FocusMessageQueue
->FocusWindow
!= (HWND
)0)
463 Msg
.hwnd
= FocusMessageQueue
->FocusWindow
;
464 DPRINT("Msg.hwnd = %x\n", Msg
.hwnd
);
466 FocusMessageQueue
->Desktop
->pDeskInfo
->LastInputWasKbd
= TRUE
;
468 Msg
.pt
= gpsi
->ptCursor
;
469 update_input_key_state(FocusMessageQueue
, &Msg
);
470 MsqPostMessage(FocusMessageQueue
, &Msg
, TRUE
, QS_KEY
);
474 DPRINT("Invalid focus window handle\n");
481 MsqPostHotKeyMessage(PVOID Thread
, HWND hWnd
, WPARAM wParam
, LPARAM lParam
)
484 PTHREADINFO Win32Thread
;
486 LARGE_INTEGER LargeTickCount
;
491 Status
= ObReferenceObjectByPointer (Thread
,
495 if (!NT_SUCCESS(Status
))
498 Win32Thread
= ((PETHREAD
)Thread
)->Tcb
.Win32Thread
;
499 if (Win32Thread
== NULL
|| Win32Thread
->MessageQueue
== NULL
)
501 ObDereferenceObject ((PETHREAD
)Thread
);
505 Window
= IntGetWindowObject(hWnd
);
508 ObDereferenceObject ((PETHREAD
)Thread
);
512 id
= wParam
; // Check for hot keys unrelated to the hot keys set by RegisterHotKey.
515 Mesg
.message
= id
!= IDHOT_REACTOS
? WM_HOTKEY
: WM_SYSCOMMAND
;
516 Mesg
.wParam
= id
!= IDHOT_REACTOS
? wParam
: SC_HOTKEY
;
517 Mesg
.lParam
= id
!= IDHOT_REACTOS
? lParam
: (LPARAM
)hWnd
;
518 Type
= id
!= IDHOT_REACTOS
? QS_HOTKEY
: QS_POSTMESSAGE
;
519 KeQueryTickCount(&LargeTickCount
);
520 Mesg
.time
= MsqCalculateMessageTime(&LargeTickCount
);
521 Mesg
.pt
= gpsi
->ptCursor
;
522 MsqPostMessage(Window
->head
.pti
->MessageQueue
, &Mesg
, FALSE
, Type
);
523 UserDereferenceObject(Window
);
524 ObDereferenceObject (Thread
);
528 PUSER_MESSAGE FASTCALL
529 MsqCreateMessage(LPMSG Msg
)
531 PUSER_MESSAGE Message
;
533 Message
= ExAllocateFromPagedLookasideList(&MessageLookasideList
);
539 RtlMoveMemory(&Message
->Msg
, Msg
, sizeof(MSG
));
545 MsqDestroyMessage(PUSER_MESSAGE Message
)
547 ExFreeToPagedLookasideList(&MessageLookasideList
, Message
);
551 co_MsqDispatchOneSentMessage(PUSER_MESSAGE_QUEUE MessageQueue
)
553 PUSER_SENT_MESSAGE SaveMsg
, Message
;
558 if (IsListEmpty(&MessageQueue
->SentMessagesListHead
))
563 /* remove it from the list of pending messages */
564 Entry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
565 Message
= CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
);
567 pti
= MessageQueue
->Thread
->Tcb
.Win32Thread
;
569 SaveMsg
= pti
->pusmCurrent
;
570 pti
->pusmCurrent
= Message
;
572 // Processing a message sent to it from another thread.
573 if ( ( Message
->SenderQueue
&& MessageQueue
!= Message
->SenderQueue
) ||
574 ( Message
->CallBackSenderQueue
&& MessageQueue
!= Message
->CallBackSenderQueue
))
575 { // most likely, but, to be sure.
576 pti
->pcti
->CTI_flags
|= CTI_INSENDMESSAGE
; // Let the user know...
579 /* insert it to the list of messages that are currently dispatched by this
581 InsertTailList(&MessageQueue
->LocalDispatchingMessagesHead
,
582 &Message
->ListEntry
);
584 ClearMsgBitsMask(MessageQueue
, Message
->QS_Flags
);
586 if (Message
->HookMessage
== MSQ_ISHOOK
)
587 { // Direct Hook Call processor
588 Result
= co_CallHook( Message
->Msg
.message
, // HookId
589 (INT
)(INT_PTR
)Message
->Msg
.hwnd
, // Code
591 Message
->Msg
.lParam
);
593 else if (Message
->HookMessage
== MSQ_ISEVENT
)
594 { // Direct Event Call processor
595 Result
= co_EVENT_CallEvents( Message
->Msg
.message
,
598 Message
->Msg
.lParam
);
600 else if ((Message
->CompletionCallback
)
601 && (Message
->CallBackSenderQueue
== MessageQueue
))
602 { /* Call the callback routine */
603 if (Message
->QS_Flags
& QS_SMRESULT
)
605 co_IntCallSentMessageCallback(Message
->CompletionCallback
,
607 Message
->Msg
.message
,
608 Message
->CompletionCallbackContext
,
610 /* Set callback to NULL to prevent reentry */
611 Message
->CompletionCallback
= NULL
;
615 /* The message has not been processed yet, reinsert it. */
616 RemoveEntryList(&Message
->ListEntry
);
617 InsertTailList(&Message
->CallBackSenderQueue
->SentMessagesListHead
, &Message
->ListEntry
);
618 DPRINT("Callback Message not processed yet. Requeuing the message\n");
623 { /* Call the window procedure. */
624 Result
= co_IntSendMessage( Message
->Msg
.hwnd
,
625 Message
->Msg
.message
,
627 Message
->Msg
.lParam
);
630 /* remove the message from the local dispatching list, because it doesn't need
631 to be cleaned up on thread termination anymore */
632 RemoveEntryList(&Message
->ListEntry
);
634 /* If the message is a callback, insert it in the callback senders MessageQueue */
635 if (Message
->CompletionCallback
)
637 if (Message
->CallBackSenderQueue
)
639 Message
->lResult
= Result
;
640 Message
->QS_Flags
|= QS_SMRESULT
;
642 /* insert it in the callers message queue */
643 InsertTailList(&Message
->CallBackSenderQueue
->SentMessagesListHead
, &Message
->ListEntry
);
644 MsqWakeQueue(Message
->CallBackSenderQueue
, QS_SENDMESSAGE
, TRUE
);
645 IntDereferenceMessageQueue(Message
->CallBackSenderQueue
);
650 /* remove the message from the dispatching list if needed, so lock the sender's message queue */
651 if (Message
->SenderQueue
)
653 if (Message
->DispatchingListEntry
.Flink
!= NULL
)
655 /* only remove it from the dispatching list if not already removed by a timeout */
656 RemoveEntryList(&Message
->DispatchingListEntry
);
659 /* still keep the sender's message queue locked, so the sender can't exit the
660 MsqSendMessage() function (if timed out) */
662 if (Message
->QS_Flags
& QS_SMRESULT
)
664 Result
= Message
->lResult
;
667 /* Let the sender know the result. */
668 if (Message
->Result
!= NULL
)
670 *Message
->Result
= Result
;
673 if (Message
->HasPackedLParam
== TRUE
)
675 if (Message
->Msg
.lParam
)
676 ExFreePool((PVOID
)Message
->Msg
.lParam
);
679 /* Notify the sender. */
680 if (Message
->CompletionEvent
!= NULL
)
682 KeSetEvent(Message
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
685 /* if the message has a sender */
686 if (Message
->SenderQueue
)
688 /* dereference our and the sender's message queue */
689 IntDereferenceMessageQueue(Message
->SenderQueue
);
690 IntDereferenceMessageQueue(MessageQueue
);
693 /* free the message */
694 ExFreePoolWithTag(Message
, TAG_USRMSG
);
696 /* do not hangup on the user if this is reentering */
697 if (!SaveMsg
) pti
->pcti
->CTI_flags
&= ~CTI_INSENDMESSAGE
;
698 pti
->pusmCurrent
= SaveMsg
;
704 MsqRemoveWindowMessagesFromQueue(PVOID pWindow
)
706 PUSER_SENT_MESSAGE SentMessage
;
707 PUSER_MESSAGE PostedMessage
;
708 PUSER_MESSAGE_QUEUE MessageQueue
;
709 PLIST_ENTRY CurrentEntry
, ListHead
;
710 PWND Window
= pWindow
;
714 MessageQueue
= Window
->head
.pti
->MessageQueue
;
715 ASSERT(MessageQueue
);
717 /* remove the posted messages for this window */
718 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
719 ListHead
= &MessageQueue
->PostedMessagesListHead
;
720 while (CurrentEntry
!= ListHead
)
722 PostedMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
724 if (PostedMessage
->Msg
.hwnd
== Window
->head
.h
)
726 RemoveEntryList(&PostedMessage
->ListEntry
);
727 ClearMsgBitsMask(MessageQueue
, PostedMessage
->QS_Flags
);
728 MsqDestroyMessage(PostedMessage
);
729 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
733 CurrentEntry
= CurrentEntry
->Flink
;
737 /* remove the sent messages for this window */
738 CurrentEntry
= MessageQueue
->SentMessagesListHead
.Flink
;
739 ListHead
= &MessageQueue
->SentMessagesListHead
;
740 while (CurrentEntry
!= ListHead
)
742 SentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
744 if(SentMessage
->Msg
.hwnd
== Window
->head
.h
)
746 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
748 RemoveEntryList(&SentMessage
->ListEntry
);
749 ClearMsgBitsMask(MessageQueue
, SentMessage
->QS_Flags
);
751 /* if it is a callback and this queue is not the sender queue, dereference queue */
752 if ((SentMessage
->CompletionCallback
) && (SentMessage
->CallBackSenderQueue
!= MessageQueue
))
754 IntDereferenceMessageQueue(SentMessage
->CallBackSenderQueue
);
756 /* Only if the message has a sender was the queue referenced */
757 if ((SentMessage
->SenderQueue
)
758 && (SentMessage
->DispatchingListEntry
.Flink
!= NULL
))
760 RemoveEntryList(&SentMessage
->DispatchingListEntry
);
763 /* wake the sender's thread */
764 if (SentMessage
->CompletionEvent
!= NULL
)
766 KeSetEvent(SentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
769 if (SentMessage
->HasPackedLParam
== TRUE
)
771 if (SentMessage
->Msg
.lParam
)
772 ExFreePool((PVOID
)SentMessage
->Msg
.lParam
);
775 /* if the message has a sender */
776 if (SentMessage
->SenderQueue
)
778 /* dereference our and the sender's message queue */
779 IntDereferenceMessageQueue(MessageQueue
);
780 IntDereferenceMessageQueue(SentMessage
->SenderQueue
);
783 /* free the message */
784 ExFreePoolWithTag(SentMessage
, TAG_USRMSG
);
786 CurrentEntry
= MessageQueue
->SentMessagesListHead
.Flink
;
790 CurrentEntry
= CurrentEntry
->Flink
;
796 co_MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
797 HWND Wnd
, UINT Msg
, WPARAM wParam
, LPARAM lParam
,
798 UINT uTimeout
, BOOL Block
, INT HookMessage
,
801 PTHREADINFO pti
, ptirec
;
802 PUSER_SENT_MESSAGE Message
;
803 KEVENT CompletionEvent
;
805 PUSER_MESSAGE_QUEUE ThreadQueue
;
806 LARGE_INTEGER Timeout
;
808 LRESULT Result
= 0; //// Result could be trashed. ////
810 if(!(Message
= ExAllocatePoolWithTag(PagedPool
, sizeof(USER_SENT_MESSAGE
), TAG_USRMSG
)))
812 DPRINT1("MsqSendMessage(): Not enough memory to allocate a message");
813 return STATUS_INSUFFICIENT_RESOURCES
;
816 KeInitializeEvent(&CompletionEvent
, NotificationEvent
, FALSE
);
818 pti
= PsGetCurrentThreadWin32Thread();
819 ThreadQueue
= pti
->MessageQueue
;
820 ptirec
= MessageQueue
->Thread
->Tcb
.Win32Thread
;
821 ASSERT(ThreadQueue
!= MessageQueue
);
822 ASSERT(ptirec
->pcti
); // Send must have a client side to receive it!!!!
824 /* Don't send from or to a dying thread */
825 if (pti
->TIF_flags
& TIF_INCLEANUP
|| ptirec
->TIF_flags
& TIF_INCLEANUP
)
828 return STATUS_TIMEOUT
;
831 Timeout
.QuadPart
= (LONGLONG
) uTimeout
* (LONGLONG
) -10000;
833 /* FIXME - increase reference counter of sender's message queue here */
835 Message
->Msg
.hwnd
= Wnd
;
836 Message
->Msg
.message
= Msg
;
837 Message
->Msg
.wParam
= wParam
;
838 Message
->Msg
.lParam
= lParam
;
839 Message
->CompletionEvent
= &CompletionEvent
;
840 Message
->Result
= &Result
;
841 Message
->lResult
= 0;
842 Message
->QS_Flags
= 0;
843 Message
->SenderQueue
= ThreadQueue
;
844 Message
->CallBackSenderQueue
= NULL
;
845 IntReferenceMessageQueue(ThreadQueue
);
846 Message
->CompletionCallback
= NULL
;
847 Message
->CompletionCallbackContext
= 0;
848 Message
->HookMessage
= HookMessage
;
849 Message
->HasPackedLParam
= FALSE
;
851 IntReferenceMessageQueue(MessageQueue
);
853 /* add it to the list of pending messages */
854 InsertTailList(&ThreadQueue
->DispatchingMessagesHead
, &Message
->DispatchingListEntry
);
856 /* queue it in the destination's message queue */
857 InsertTailList(&MessageQueue
->SentMessagesListHead
, &Message
->ListEntry
);
859 Message
->QS_Flags
= QS_SENDMESSAGE
;
860 MsqWakeQueue(MessageQueue
, QS_SENDMESSAGE
, TRUE
);
862 /* we can't access the Message anymore since it could have already been deleted! */
868 /* don't process messages sent to the thread */
869 WaitStatus
= KeWaitForSingleObject(&CompletionEvent
, UserRequest
, UserMode
,
870 FALSE
, (uTimeout
? &Timeout
: NULL
));
874 if(WaitStatus
== STATUS_TIMEOUT
)
876 /* look up if the message has not yet dispatched, if so
877 make sure it can't pass a result and it must not set the completion event anymore */
878 Entry
= MessageQueue
->SentMessagesListHead
.Flink
;
879 while (Entry
!= &MessageQueue
->SentMessagesListHead
)
881 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
)
884 /* we can access Message here, it's secure because the message queue is locked
885 and the message is still hasn't been dispatched */
886 Message
->CompletionEvent
= NULL
;
887 Message
->Result
= NULL
;
890 Entry
= Entry
->Flink
;
893 /* remove from the local dispatching list so the other thread knows,
894 it can't pass a result and it must not set the completion event anymore */
895 Entry
= ThreadQueue
->DispatchingMessagesHead
.Flink
;
896 while (Entry
!= &ThreadQueue
->DispatchingMessagesHead
)
898 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, DispatchingListEntry
)
901 /* we can access Message here, it's secure because the sender's message is locked
902 and the message has definitely not yet been destroyed, otherwise it would
903 have been removed from this list by the dispatching routine right after
904 dispatching the message */
905 Message
->CompletionEvent
= NULL
;
906 Message
->Result
= NULL
;
907 RemoveEntryList(&Message
->DispatchingListEntry
);
908 Message
->DispatchingListEntry
.Flink
= NULL
;
911 Entry
= Entry
->Flink
;
914 DPRINT("MsqSendMessage (blocked) timed out 1\n");
916 while (co_MsqDispatchOneSentMessage(ThreadQueue
))
921 PVOID WaitObjects
[2];
923 WaitObjects
[0] = &CompletionEvent
;
924 WaitObjects
[1] = ThreadQueue
->NewMessages
;
929 WaitStatus
= KeWaitForMultipleObjects(2, WaitObjects
, WaitAny
, UserRequest
,
930 UserMode
, FALSE
, (uTimeout
? &Timeout
: NULL
), NULL
);
934 if(WaitStatus
== STATUS_TIMEOUT
)
936 /* look up if the message has not yet been dispatched, if so
937 make sure it can't pass a result and it must not set the completion event anymore */
938 Entry
= MessageQueue
->SentMessagesListHead
.Flink
;
939 while (Entry
!= &MessageQueue
->SentMessagesListHead
)
941 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
)
944 /* we can access Message here, it's secure because the message queue is locked
945 and the message is still hasn't been dispatched */
946 Message
->CompletionEvent
= NULL
;
947 Message
->Result
= NULL
;
950 Entry
= Entry
->Flink
;
953 /* remove from the local dispatching list so the other thread knows,
954 it can't pass a result and it must not set the completion event anymore */
955 Entry
= ThreadQueue
->DispatchingMessagesHead
.Flink
;
956 while (Entry
!= &ThreadQueue
->DispatchingMessagesHead
)
958 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, DispatchingListEntry
)
961 /* we can access Message here, it's secure because the sender's message is locked
962 and the message has definitely not yet been destroyed, otherwise it would
963 have been removed from this list by the dispatching routine right after
964 dispatching the message */
965 Message
->CompletionEvent
= NULL
;
966 Message
->Result
= NULL
;
967 RemoveEntryList(&Message
->DispatchingListEntry
);
968 Message
->DispatchingListEntry
.Flink
= NULL
;
971 Entry
= Entry
->Flink
;
974 DPRINT("MsqSendMessage timed out 2\n");
977 while (co_MsqDispatchOneSentMessage(ThreadQueue
))
980 while (NT_SUCCESS(WaitStatus
) && STATUS_WAIT_0
!= WaitStatus
);
983 if(WaitStatus
!= STATUS_TIMEOUT
)
984 *uResult
= (STATUS_WAIT_0
== WaitStatus
? Result
: -1);
990 MsqPostMessage(PUSER_MESSAGE_QUEUE MessageQueue
, MSG
* Msg
, BOOLEAN HardwareMessage
,
993 PUSER_MESSAGE Message
;
995 if(!(Message
= MsqCreateMessage(Msg
)))
1000 if(!HardwareMessage
)
1002 InsertTailList(&MessageQueue
->PostedMessagesListHead
,
1003 &Message
->ListEntry
);
1007 InsertTailList(&MessageQueue
->HardwareMessagesListHead
,
1008 &Message
->ListEntry
);
1011 Message
->QS_Flags
= MessageBits
;
1012 MsqWakeQueue(MessageQueue
, MessageBits
, (MessageBits
& QS_TIMER
? FALSE
: TRUE
));
1016 MsqPostQuitMessage(PUSER_MESSAGE_QUEUE MessageQueue
, ULONG ExitCode
)
1018 MessageQueue
->QuitPosted
= TRUE
;
1019 MessageQueue
->QuitExitCode
= ExitCode
;
1020 MsqWakeQueue(MessageQueue
, QS_POSTMESSAGE
|QS_ALLPOSTMESSAGE
, TRUE
);
1023 /***********************************************************************
1024 * MsqSendParentNotify
1026 * Send a WM_PARENTNOTIFY to all ancestors of the given window, unless
1027 * the window has the WS_EX_NOPARENTNOTIFY style.
1029 static void MsqSendParentNotify( PWND pwnd
, WORD event
, WORD idChild
, POINT pt
)
1031 PWND pwndDesktop
= UserGetWindowObject(IntGetDesktopWindow());
1033 /* pt has to be in the client coordinates of the parent window */
1034 pt
.x
+= pwndDesktop
->rcClient
.left
- pwnd
->rcClient
.left
;
1035 pt
.y
+= pwndDesktop
->rcClient
.top
- pwnd
->rcClient
.top
;
1041 if (!(pwnd
->style
& WS_CHILD
)) break;
1042 if (pwnd
->ExStyle
& WS_EX_NOPARENTNOTIFY
) break;
1043 if (!(pwndParent
= IntGetParent(pwnd
))) break;
1044 if (pwndParent
== pwndDesktop
) break;
1045 pt
.x
+= pwnd
->rcClient
.left
- pwndParent
->rcClient
.left
;
1046 pt
.y
+= pwnd
->rcClient
.top
- pwndParent
->rcClient
.top
;
1049 co_IntSendMessage( UserHMGetHandle(pwnd
), WM_PARENTNOTIFY
,
1050 MAKEWPARAM( event
, idChild
), MAKELPARAM( pt
.x
, pt
.y
) );
1054 BOOL
co_IntProcessMouseMessage(MSG
* msg
, BOOL
* RemoveMessages
, UINT first
, UINT last
)
1061 MOUSEHOOKSTRUCT hook
;
1064 PWND pwndMsg
, pwndDesktop
;
1065 PUSER_MESSAGE_QUEUE MessageQueue
;
1067 PSYSTEM_CURSORINFO CurInfo
;
1068 DECLARE_RETURN(BOOL
);
1070 pti
= PsGetCurrentThreadWin32Thread();
1071 pwndDesktop
= UserGetDesktopWindow();
1072 MessageQueue
= pti
->MessageQueue
;
1073 CurInfo
= IntGetSysCursorInfo();
1074 pwndMsg
= UserGetWindowObject(msg
->hwnd
);
1075 clk_msg
= MessageQueue
->msgDblClk
;
1077 /* find the window to dispatch this mouse message to */
1078 if (MessageQueue
->CaptureWindow
)
1081 pwndMsg
= IntGetWindowObject(MessageQueue
->CaptureWindow
);
1085 pwndMsg
= co_WinPosWindowFromPoint(pwndMsg
, &msg
->pt
, &hittest
);
1088 DPRINT("Got mouse message for 0x%x, hittest: 0x%x\n", msg
->hwnd
, hittest
);
1090 if (pwndMsg
== NULL
|| pwndMsg
->head
.pti
!= pti
)
1092 /* Remove and ignore the message */
1093 *RemoveMessages
= TRUE
;
1097 msg
->hwnd
= UserHMGetHandle(pwndMsg
);
1100 if (!check_hwnd_filter( msg
, hwnd_filter
)) RETURN(FALSE
);
1104 message
= msg
->message
;
1105 /* Note: windows has no concept of a non-client wheel message */
1106 if (message
!= WM_MOUSEWHEEL
)
1108 if (hittest
!= HTCLIENT
)
1110 message
+= WM_NCMOUSEMOVE
- WM_MOUSEMOVE
;
1111 msg
->wParam
= hittest
;
1115 /* coordinates don't get translated while tracking a menu */
1116 /* FIXME: should differentiate popups and top-level menus */
1117 if (!(MessageQueue
->MenuOwner
))
1119 pt
.x
+= pwndDesktop
->rcClient
.left
- pwndMsg
->rcClient
.left
;
1120 pt
.y
+= pwndDesktop
->rcClient
.top
- pwndMsg
->rcClient
.top
;
1124 msg
->lParam
= MAKELONG( pt
.x
, pt
.y
);
1126 /* translate double clicks */
1128 if ((msg
->message
== WM_LBUTTONDOWN
) ||
1129 (msg
->message
== WM_RBUTTONDOWN
) ||
1130 (msg
->message
== WM_MBUTTONDOWN
) ||
1131 (msg
->message
== WM_XBUTTONDOWN
))
1133 BOOL update
= *RemoveMessages
;
1135 /* translate double clicks -
1136 * note that ...MOUSEMOVEs can slip in between
1137 * ...BUTTONDOWN and ...BUTTONDBLCLK messages */
1139 if ((MessageQueue
->MenuOwner
|| MessageQueue
->MoveSize
) ||
1140 hittest
!= HTCLIENT
||
1141 (pwndMsg
->pcls
->style
& CS_DBLCLKS
))
1143 if ((msg
->message
== clk_msg
.message
) &&
1144 (msg
->hwnd
== clk_msg
.hwnd
) &&
1145 (msg
->wParam
== clk_msg
.wParam
) &&
1146 (msg
->time
- clk_msg
.time
< gspv
.iDblClickTime
) &&
1147 (abs(msg
->pt
.x
- clk_msg
.pt
.x
) < UserGetSystemMetrics(SM_CXDOUBLECLK
)/2) &&
1148 (abs(msg
->pt
.y
- clk_msg
.pt
.y
) < UserGetSystemMetrics(SM_CYDOUBLECLK
)/2))
1150 message
+= (WM_LBUTTONDBLCLK
- WM_LBUTTONDOWN
);
1153 MessageQueue
->msgDblClk
.message
= 0; /* clear the double click conditions */
1159 if (!((first
== 0 && last
== 0) || (message
>= first
|| message
<= last
)))
1161 DPRINT("Message out of range!!!\n");
1165 /* update static double click conditions */
1166 if (update
) MessageQueue
->msgDblClk
= *msg
;
1170 if (!((first
== 0 && last
== 0) || (message
>= first
|| message
<= last
)))
1172 DPRINT("Message out of range!!!\n");
1177 if(gspv
.bMouseClickLock
)
1179 BOOL IsClkLck
= FALSE
;
1181 if(msg
->message
== WM_LBUTTONUP
)
1183 IsClkLck
= ((msg
->time
- CurInfo
->ClickLockTime
) >= gspv
.dwMouseClickLockTime
);
1184 if (IsClkLck
&& (!CurInfo
->ClickLockActive
))
1186 CurInfo
->ClickLockActive
= TRUE
;
1189 else if (msg
->message
== WM_LBUTTONDOWN
)
1191 if (CurInfo
->ClickLockActive
)
1194 CurInfo
->ClickLockActive
= FALSE
;
1197 CurInfo
->ClickLockTime
= msg
->time
;
1202 /* Remove and ignore the message */
1203 *RemoveMessages
= TRUE
;
1208 /* message is accepted now (but may still get dropped) */
1210 event
.message
= msg
->message
;
1211 event
.time
= msg
->time
;
1212 event
.hwnd
= msg
->hwnd
;
1213 event
.paramL
= msg
->pt
.x
;
1214 event
.paramH
= msg
->pt
.y
;
1215 co_HOOK_CallHooks( WH_JOURNALRECORD
, HC_ACTION
, 0, (LPARAM
)&event
);
1218 hook
.hwnd
= msg
->hwnd
;
1219 hook
.wHitTestCode
= hittest
;
1220 hook
.dwExtraInfo
= 0/*extra_info*/;
1221 if (co_HOOK_CallHooks( WH_MOUSE
, *RemoveMessages
? HC_ACTION
: HC_NOREMOVE
,
1222 message
, (LPARAM
)&hook
))
1225 hook
.hwnd
= msg
->hwnd
;
1226 hook
.wHitTestCode
= hittest
;
1227 hook
.dwExtraInfo
= 0/*extra_info*/;
1228 co_HOOK_CallHooks( WH_CBT
, HCBT_CLICKSKIPPED
, message
, (LPARAM
)&hook
);
1230 DPRINT1("WH_MOUSE dorpped mouse message!\n");
1232 /* Remove and skip message */
1233 *RemoveMessages
= TRUE
;
1237 if ((hittest
== HTERROR
) || (hittest
== HTNOWHERE
))
1239 co_IntSendMessage( msg
->hwnd
, WM_SETCURSOR
, (WPARAM
)msg
->hwnd
,
1240 MAKELONG( hittest
, msg
->message
));
1242 /* Remove and skip message */
1243 *RemoveMessages
= TRUE
;
1247 if ((*RemoveMessages
== FALSE
) || MessageQueue
->CaptureWindow
)
1249 /* Accept the message */
1250 msg
->message
= message
;
1256 if ((msg
->message
== WM_LBUTTONDOWN
) ||
1257 (msg
->message
== WM_RBUTTONDOWN
) ||
1258 (msg
->message
== WM_MBUTTONDOWN
) ||
1259 (msg
->message
== WM_XBUTTONDOWN
))
1261 /* Send the WM_PARENTNOTIFY,
1262 * note that even for double/nonclient clicks
1263 * notification message is still WM_L/M/RBUTTONDOWN.
1265 MsqSendParentNotify(pwndMsg
, msg
->message
, 0, msg
->pt
);
1267 /* Activate the window if needed */
1269 if (msg
->hwnd
!= UserGetForegroundWindow())
1271 PWND pwndTop
= pwndMsg
;
1274 if ((pwndTop
->style
& (WS_POPUP
|WS_CHILD
)) != WS_CHILD
) break;
1275 pwndTop
= IntGetParent( pwndTop
);
1278 if (pwndTop
&& pwndTop
!= pwndDesktop
)
1280 LONG ret
= co_IntSendMessage( msg
->hwnd
,
1282 (WPARAM
)UserHMGetHandle(pwndTop
),
1283 MAKELONG( hittest
, msg
->message
));
1286 case MA_NOACTIVATEANDEAT
:
1291 case MA_ACTIVATEANDEAT
:
1296 if(!co_IntMouseActivateWindow(pwndMsg
)) eatMsg
= TRUE
;
1299 DPRINT1( "unknown WM_MOUSEACTIVATE code %d\n", ret
);
1306 /* send the WM_SETCURSOR message */
1308 /* Windows sends the normal mouse message as the message parameter
1309 in the WM_SETCURSOR message even if it's non-client mouse message */
1310 co_IntSendMessage( msg
->hwnd
, WM_SETCURSOR
, (WPARAM
)msg
->hwnd
, MAKELONG( hittest
, msg
->message
));
1312 msg
->message
= message
;
1317 UserDereferenceObject(pwndMsg
);
1322 BOOL
co_IntProcessKeyboardMessage(MSG
* Msg
, BOOL
* RemoveMessages
)
1326 if (Msg
->message
== WM_KEYDOWN
|| Msg
->message
== WM_SYSKEYDOWN
||
1327 Msg
->message
== WM_KEYUP
|| Msg
->message
== WM_SYSKEYUP
)
1329 switch (Msg
->wParam
)
1331 case VK_LSHIFT
: case VK_RSHIFT
:
1332 Msg
->wParam
= VK_SHIFT
;
1334 case VK_LCONTROL
: case VK_RCONTROL
:
1335 Msg
->wParam
= VK_CONTROL
;
1337 case VK_LMENU
: case VK_RMENU
:
1338 Msg
->wParam
= VK_MENU
;
1341 if (Msg
->message
== WM_KEYUP
) Msg
->message
= WM_SYSKEYUP
;
1342 if (Msg
->message
== WM_KEYDOWN
) Msg
->message
= WM_SYSKEYDOWN
;
1347 Event
.message
= Msg
->message
;
1348 Event
.hwnd
= Msg
->hwnd
;
1349 Event
.time
= Msg
->time
;
1350 Event
.paramL
= (Msg
->wParam
& 0xFF) | (HIWORD(Msg
->lParam
) << 8);
1351 Event
.paramH
= Msg
->lParam
& 0x7FFF;
1352 if (HIWORD(Msg
->lParam
) & 0x0100) Event
.paramH
|= 0x8000;
1353 co_HOOK_CallHooks( WH_JOURNALRECORD
, HC_ACTION
, 0, (LPARAM
)&Event
);
1355 if (co_HOOK_CallHooks( WH_KEYBOARD
,
1356 *RemoveMessages
? HC_ACTION
: HC_NOREMOVE
,
1357 LOWORD(Msg
->wParam
),
1360 /* skip this message */
1361 co_HOOK_CallHooks( WH_CBT
,
1363 LOWORD(Msg
->wParam
),
1365 DPRINT1("KeyboardMessage WH_CBT Call Hook return!\n");
1371 BOOL
co_IntProcessHardwareMessage(MSG
* Msg
, BOOL
* RemoveMessages
, UINT first
, UINT last
)
1373 if ( IS_MOUSE_MESSAGE(Msg
->message
))
1375 return co_IntProcessMouseMessage(Msg
, RemoveMessages
, first
, last
);
1377 else if ( IS_KBD_MESSAGE(Msg
->message
))
1379 return co_IntProcessKeyboardMessage(Msg
, RemoveMessages
);
1386 co_MsqPeekMouseMove(IN PUSER_MESSAGE_QUEUE MessageQueue
,
1389 IN UINT MsgFilterLow
,
1390 IN UINT MsgFilterHigh
,
1396 if(!(MessageQueue
->MouseMoved
))
1399 msg
= MessageQueue
->MouseMoveMsg
;
1401 AcceptMessage
= co_IntProcessMouseMessage(&msg
, &Remove
, MsgFilterLow
, MsgFilterHigh
);
1408 ClearMsgBitsMask(MessageQueue
, QS_MOUSEMOVE
);
1409 MessageQueue
->MouseMoved
= FALSE
;
1412 return AcceptMessage
;
1415 /* check whether a message filter contains at least one potential hardware message */
1417 filter_contains_hw_range( UINT first
, UINT last
)
1419 /* hardware message ranges are (in numerical order):
1420 * WM_NCMOUSEFIRST .. WM_NCMOUSELAST
1421 * WM_KEYFIRST .. WM_KEYLAST
1422 * WM_MOUSEFIRST .. WM_MOUSELAST
1425 if (last
< WM_NCMOUSEFIRST
) return 0;
1426 if (first
> WM_NCMOUSELAST
&& last
< WM_KEYFIRST
) return 0;
1427 if (first
> WM_KEYLAST
&& last
< WM_MOUSEFIRST
) return 0;
1428 if (first
> WM_MOUSELAST
) return 0;
1433 co_MsqPeekHardwareMessage(IN PUSER_MESSAGE_QUEUE MessageQueue
,
1436 IN UINT MsgFilterLow
,
1437 IN UINT MsgFilterHigh
,
1443 PUSER_MESSAGE CurrentMessage
;
1444 PLIST_ENTRY ListHead
, CurrentEntry
= NULL
;
1447 if (!filter_contains_hw_range( MsgFilterLow
, MsgFilterHigh
)) return FALSE
;
1449 ListHead
= &MessageQueue
->HardwareMessagesListHead
;
1450 CurrentEntry
= ListHead
->Flink
;
1452 if (IsListEmpty(CurrentEntry
)) return FALSE
;
1454 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1458 if (IsListEmpty(CurrentEntry
)) break;
1459 if (!CurrentMessage
) break;
1460 CurrentEntry
= CurrentMessage
->ListEntry
.Flink
;
1462 if ( (( MsgFilterLow
== 0 && MsgFilterHigh
== 0 ) && (CurrentMessage
->QS_Flags
& QSflags
)) ||
1463 ( MsgFilterLow
<= CurrentMessage
->Msg
.message
&& MsgFilterHigh
>= CurrentMessage
->Msg
.message
) )
1465 msg
= CurrentMessage
->Msg
;
1467 AcceptMessage
= co_IntProcessHardwareMessage(&msg
, &Remove
, MsgFilterLow
, MsgFilterHigh
);
1471 update_input_key_state(MessageQueue
, &msg
);
1472 RemoveEntryList(&CurrentMessage
->ListEntry
);
1473 ClearMsgBitsMask(MessageQueue
, CurrentMessage
->QS_Flags
);
1474 MsqDestroyMessage(CurrentMessage
);
1483 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1486 while(CurrentEntry
!= ListHead
);
1492 MsqPeekMessage(IN PUSER_MESSAGE_QUEUE MessageQueue
,
1495 IN UINT MsgFilterLow
,
1496 IN UINT MsgFilterHigh
,
1500 PLIST_ENTRY CurrentEntry
;
1501 PUSER_MESSAGE CurrentMessage
;
1502 PLIST_ENTRY ListHead
;
1504 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
1505 ListHead
= &MessageQueue
->PostedMessagesListHead
;
1507 if (IsListEmpty(CurrentEntry
)) return FALSE
;
1509 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1513 if (IsListEmpty(CurrentEntry
)) break;
1514 if (!CurrentMessage
) break;
1515 CurrentEntry
= CurrentEntry
->Flink
;
1518 1: any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL.
1519 2: retrieves only messages on the current thread's message queue whose hwnd value is NULL.
1520 3: handle to the window whose messages are to be retrieved.
1522 if ( ( !Window
|| // 1
1523 ( Window
== HWND_BOTTOM
&& CurrentMessage
->Msg
.hwnd
== NULL
) || // 2
1524 ( Window
!= HWND_BOTTOM
&& Window
->head
.h
== CurrentMessage
->Msg
.hwnd
) ) && // 3
1525 ( ( ( MsgFilterLow
== 0 && MsgFilterHigh
== 0 ) && CurrentMessage
->QS_Flags
& QSflags
) ||
1526 ( MsgFilterLow
<= CurrentMessage
->Msg
.message
&& MsgFilterHigh
>= CurrentMessage
->Msg
.message
) ) )
1528 *Message
= CurrentMessage
->Msg
;
1532 RemoveEntryList(&CurrentMessage
->ListEntry
);
1533 ClearMsgBitsMask(MessageQueue
, CurrentMessage
->QS_Flags
);
1534 MsqDestroyMessage(CurrentMessage
);
1538 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1541 while (CurrentEntry
!= ListHead
);
1547 co_MsqWaitForNewMessages(PUSER_MESSAGE_QUEUE MessageQueue
, PWND WndFilter
,
1548 UINT MsgFilterMin
, UINT MsgFilterMax
)
1552 ret
= KeWaitForSingleObject( MessageQueue
->NewMessages
,
1562 MsqIsHung(PUSER_MESSAGE_QUEUE MessageQueue
)
1564 LARGE_INTEGER LargeTickCount
;
1566 KeQueryTickCount(&LargeTickCount
);
1567 return ((LargeTickCount
.u
.LowPart
- MessageQueue
->LastMsgRead
) > MSQ_HUNG
);
1572 HungAppSysTimerProc(HWND hwnd
, UINT uMsg
, UINT_PTR idEvent
, DWORD dwTime
)
1574 //DoTheScreenSaver();
1575 DPRINT("HungAppSysTimerProc\n");
1576 // Process list of windows that are hung and waiting.
1580 MsqInitializeMessageQueue(struct _ETHREAD
*Thread
, PUSER_MESSAGE_QUEUE MessageQueue
)
1582 LARGE_INTEGER LargeTickCount
;
1585 MessageQueue
->Thread
= Thread
;
1586 MessageQueue
->CaretInfo
= (PTHRDCARETINFO
)(MessageQueue
+ 1);
1587 InitializeListHead(&MessageQueue
->PostedMessagesListHead
);
1588 InitializeListHead(&MessageQueue
->SentMessagesListHead
);
1589 InitializeListHead(&MessageQueue
->HardwareMessagesListHead
);
1590 InitializeListHead(&MessageQueue
->DispatchingMessagesHead
);
1591 InitializeListHead(&MessageQueue
->LocalDispatchingMessagesHead
);
1592 KeInitializeMutex(&MessageQueue
->HardwareLock
, 0);
1593 MessageQueue
->QuitPosted
= FALSE
;
1594 MessageQueue
->QuitExitCode
= 0;
1595 KeQueryTickCount(&LargeTickCount
);
1596 MessageQueue
->LastMsgRead
= LargeTickCount
.u
.LowPart
;
1597 MessageQueue
->FocusWindow
= NULL
;
1598 MessageQueue
->NewMessagesHandle
= NULL
;
1600 Status
= ZwCreateEvent(&MessageQueue
->NewMessagesHandle
, EVENT_ALL_ACCESS
,
1601 NULL
, SynchronizationEvent
, FALSE
);
1602 if (!NT_SUCCESS(Status
))
1607 Status
= ObReferenceObjectByHandle(MessageQueue
->NewMessagesHandle
, 0,
1608 ExEventObjectType
, KernelMode
,
1609 (PVOID
*)&MessageQueue
->NewMessages
, NULL
);
1610 if (!NT_SUCCESS(Status
))
1612 ZwClose(MessageQueue
->NewMessagesHandle
);
1613 MessageQueue
->NewMessagesHandle
= NULL
;
1621 MsqCleanupMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue
)
1623 PLIST_ENTRY CurrentEntry
;
1624 PUSER_MESSAGE CurrentMessage
;
1625 PUSER_SENT_MESSAGE CurrentSentMessage
;
1628 pti
= MessageQueue
->Thread
->Tcb
.Win32Thread
;
1631 /* cleanup posted messages */
1632 while (!IsListEmpty(&MessageQueue
->PostedMessagesListHead
))
1634 CurrentEntry
= RemoveHeadList(&MessageQueue
->PostedMessagesListHead
);
1635 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1637 MsqDestroyMessage(CurrentMessage
);
1640 /* remove the messages that have not yet been dispatched */
1641 while (!IsListEmpty(&MessageQueue
->SentMessagesListHead
))
1643 CurrentEntry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
1644 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1647 /* if it is a callback and this queue is not the sender queue, dereference queue */
1648 if ((CurrentSentMessage
->CompletionCallback
) && (CurrentSentMessage
->CallBackSenderQueue
!= MessageQueue
))
1650 IntDereferenceMessageQueue(CurrentSentMessage
->CallBackSenderQueue
);
1653 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1654 /* Only if the message has a sender was the message in the DispatchingList */
1655 if ((CurrentSentMessage
->SenderQueue
)
1656 && (CurrentSentMessage
->DispatchingListEntry
.Flink
!= NULL
))
1658 RemoveEntryList(&CurrentSentMessage
->DispatchingListEntry
);
1661 /* wake the sender's thread */
1662 if (CurrentSentMessage
->CompletionEvent
!= NULL
)
1664 KeSetEvent(CurrentSentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1667 if (CurrentSentMessage
->HasPackedLParam
== TRUE
)
1669 if (CurrentSentMessage
->Msg
.lParam
)
1670 ExFreePool((PVOID
)CurrentSentMessage
->Msg
.lParam
);
1673 /* if the message has a sender */
1674 if (CurrentSentMessage
->SenderQueue
)
1676 /* dereference our and the sender's message queue */
1677 IntDereferenceMessageQueue(MessageQueue
);
1678 IntDereferenceMessageQueue(CurrentSentMessage
->SenderQueue
);
1681 /* free the message */
1682 ExFreePool(CurrentSentMessage
);
1685 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
1686 ExitThread() was called in a SendMessage() umode callback */
1687 while (!IsListEmpty(&MessageQueue
->LocalDispatchingMessagesHead
))
1689 CurrentEntry
= RemoveHeadList(&MessageQueue
->LocalDispatchingMessagesHead
);
1690 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1693 /* if it is a callback and this queue is not the sender queue, dereference queue */
1694 if ((CurrentSentMessage
->CompletionCallback
) && (CurrentSentMessage
->CallBackSenderQueue
!= MessageQueue
))
1696 IntDereferenceMessageQueue(CurrentSentMessage
->CallBackSenderQueue
);
1699 /* remove the message from the dispatching list */
1700 if(CurrentSentMessage
->DispatchingListEntry
.Flink
!= NULL
)
1702 RemoveEntryList(&CurrentSentMessage
->DispatchingListEntry
);
1705 DPRINT("Notify the sender, the thread has been terminated while dispatching a message!\n");
1707 /* wake the sender's thread */
1708 if (CurrentSentMessage
->CompletionEvent
!= NULL
)
1710 KeSetEvent(CurrentSentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1713 if (CurrentSentMessage
->HasPackedLParam
== TRUE
)
1715 if (CurrentSentMessage
->Msg
.lParam
)
1716 ExFreePool((PVOID
)CurrentSentMessage
->Msg
.lParam
);
1719 /* if the message has a sender */
1720 if (CurrentSentMessage
->SenderQueue
)
1722 /* dereference our and the sender's message queue */
1723 IntDereferenceMessageQueue(MessageQueue
);
1724 IntDereferenceMessageQueue(CurrentSentMessage
->SenderQueue
);
1727 /* free the message */
1728 ExFreePool(CurrentSentMessage
);
1731 /* tell other threads not to bother returning any info to us */
1732 while (! IsListEmpty(&MessageQueue
->DispatchingMessagesHead
))
1734 CurrentEntry
= RemoveHeadList(&MessageQueue
->DispatchingMessagesHead
);
1735 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1736 DispatchingListEntry
);
1737 CurrentSentMessage
->CompletionEvent
= NULL
;
1738 CurrentSentMessage
->Result
= NULL
;
1740 /* do NOT dereference our message queue as it might get attempted to be
1744 // Clear it all out.
1745 pti
->pcti
->fsWakeBits
= 0;
1746 pti
->pcti
->fsChangeBits
= 0;
1748 MessageQueue
->nCntsQBits
[QSRosKey
] = 0;
1749 MessageQueue
->nCntsQBits
[QSRosMouseMove
] = 0;
1750 MessageQueue
->nCntsQBits
[QSRosMouseButton
] = 0;
1751 MessageQueue
->nCntsQBits
[QSRosPostMessage
] = 0;
1752 MessageQueue
->nCntsQBits
[QSRosSendMessage
] = 0;
1753 MessageQueue
->nCntsQBits
[QSRosHotKey
] = 0;
1756 PUSER_MESSAGE_QUEUE FASTCALL
1757 MsqCreateMessageQueue(struct _ETHREAD
*Thread
)
1759 PUSER_MESSAGE_QUEUE MessageQueue
;
1761 MessageQueue
= (PUSER_MESSAGE_QUEUE
)ExAllocatePoolWithTag(NonPagedPool
,
1762 sizeof(USER_MESSAGE_QUEUE
) + sizeof(THRDCARETINFO
),
1770 RtlZeroMemory(MessageQueue
, sizeof(USER_MESSAGE_QUEUE
) + sizeof(THRDCARETINFO
));
1771 /* hold at least one reference until it'll be destroyed */
1772 IntReferenceMessageQueue(MessageQueue
);
1773 /* initialize the queue */
1774 if (!MsqInitializeMessageQueue(Thread
, MessageQueue
))
1776 IntDereferenceMessageQueue(MessageQueue
);
1780 return MessageQueue
;
1784 MsqDestroyMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue
)
1788 MessageQueue
->QF_flags
|= QF_INDESTROY
;
1790 /* remove the message queue from any desktops */
1791 if ((desk
= InterlockedExchangePointer((PVOID
*)&MessageQueue
->Desktop
, 0)))
1793 (void)InterlockedExchangePointer((PVOID
*)&desk
->ActiveMessageQueue
, 0);
1794 IntDereferenceMessageQueue(MessageQueue
);
1798 MsqCleanupMessageQueue(MessageQueue
);
1800 if (MessageQueue
->NewMessagesHandle
!= NULL
)
1801 ZwClose(MessageQueue
->NewMessagesHandle
);
1802 MessageQueue
->NewMessagesHandle
= NULL
;
1803 /* decrease the reference counter, if it hits zero, the queue will be freed */
1804 IntDereferenceMessageQueue(MessageQueue
);
1808 MsqSetMessageExtraInfo(LPARAM lParam
)
1812 PUSER_MESSAGE_QUEUE MessageQueue
;
1814 pti
= PsGetCurrentThreadWin32Thread();
1815 MessageQueue
= pti
->MessageQueue
;
1821 Ret
= MessageQueue
->ExtraInfo
;
1822 MessageQueue
->ExtraInfo
= lParam
;
1828 MsqGetMessageExtraInfo(VOID
)
1831 PUSER_MESSAGE_QUEUE MessageQueue
;
1833 pti
= PsGetCurrentThreadWin32Thread();
1834 MessageQueue
= pti
->MessageQueue
;
1840 return MessageQueue
->ExtraInfo
;
1843 // ReplyMessage is called by the thread receiving the window message.
1845 co_MsqReplyMessage( LRESULT lResult
)
1847 PUSER_SENT_MESSAGE Message
;
1850 pti
= PsGetCurrentThreadWin32Thread();
1851 Message
= pti
->pusmCurrent
;
1853 if (!Message
) return FALSE
;
1855 if (Message
->QS_Flags
& QS_SMRESULT
) return FALSE
;
1857 // SendMessageXxx || Callback msg and not a notify msg
1858 if (Message
->SenderQueue
|| Message
->CompletionCallback
)
1860 Message
->lResult
= lResult
;
1861 Message
->QS_Flags
|= QS_SMRESULT
;
1862 // See co_MsqDispatchOneSentMessage, change bits already accounted for and cleared and this msg is going away..
1868 MsqSetStateWindow(PUSER_MESSAGE_QUEUE MessageQueue
, ULONG Type
, HWND hWnd
)
1874 case MSQ_STATE_CAPTURE
:
1875 Prev
= MessageQueue
->CaptureWindow
;
1876 MessageQueue
->CaptureWindow
= hWnd
;
1878 case MSQ_STATE_ACTIVE
:
1879 Prev
= MessageQueue
->ActiveWindow
;
1880 MessageQueue
->ActiveWindow
= hWnd
;
1882 case MSQ_STATE_FOCUS
:
1883 Prev
= MessageQueue
->FocusWindow
;
1884 MessageQueue
->FocusWindow
= hWnd
;
1886 case MSQ_STATE_MENUOWNER
:
1887 Prev
= MessageQueue
->MenuOwner
;
1888 MessageQueue
->MenuOwner
= hWnd
;
1890 case MSQ_STATE_MOVESIZE
:
1891 Prev
= MessageQueue
->MoveSize
;
1892 MessageQueue
->MoveSize
= hWnd
;
1894 case MSQ_STATE_CARET
:
1895 ASSERT(MessageQueue
->CaretInfo
);
1896 Prev
= MessageQueue
->CaretInfo
->hWnd
;
1897 MessageQueue
->CaretInfo
->hWnd
= hWnd
;
1906 NtUserGetKeyState(INT key
)
1910 UserEnterExclusive();
1912 Ret
= UserGetKeyState(key
);
1922 NtUserGetKeyboardState(LPBYTE lpKeyState
)
1926 PUSER_MESSAGE_QUEUE MessageQueue
;
1930 pti
= PsGetCurrentThreadWin32Thread();
1931 MessageQueue
= pti
->MessageQueue
;
1935 ProbeForWrite(lpKeyState
,sizeof(MessageQueue
->KeyState
) ,1);
1936 RtlCopyMemory(lpKeyState
,MessageQueue
->KeyState
,sizeof(MessageQueue
->KeyState
));
1938 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1940 SetLastNtError(_SEH2_GetExceptionCode());
1952 NtUserSetKeyboardState(LPBYTE lpKeyState
)
1956 PUSER_MESSAGE_QUEUE MessageQueue
;
1958 UserEnterExclusive();
1960 pti
= PsGetCurrentThreadWin32Thread();
1961 MessageQueue
= pti
->MessageQueue
;
1965 ProbeForRead(lpKeyState
,sizeof(MessageQueue
->KeyState
) ,1);
1966 RtlCopyMemory(MessageQueue
->KeyState
,lpKeyState
,sizeof(MessageQueue
->KeyState
));
1968 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1970 SetLastNtError(_SEH2_GetExceptionCode());