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
] & KS_DOWN_BIT
) << 8 ) |
56 (MessageQueue
->KeyState
[key
] & KS_LOCK_BIT
);
62 /* change the input key state for a given key */
63 static void set_input_key_state( PUSER_MESSAGE_QUEUE MessageQueue
, UCHAR key
, BOOL down
)
67 if (!(MessageQueue
->KeyState
[key
] & KS_DOWN_BIT
))
69 MessageQueue
->KeyState
[key
] ^= KS_LOCK_BIT
;
71 MessageQueue
->KeyState
[key
] |= KS_DOWN_BIT
;
75 MessageQueue
->KeyState
[key
] &= ~KS_DOWN_BIT
;
79 /* update the input key state for a keyboard message */
80 static void update_input_key_state( PUSER_MESSAGE_QUEUE MessageQueue
, MSG
* msg
)
91 set_input_key_state( MessageQueue
, VK_LBUTTON
, down
);
97 set_input_key_state( MessageQueue
, VK_MBUTTON
, down
);
103 set_input_key_state( MessageQueue
, VK_RBUTTON
, down
);
109 if (msg
->wParam
== XBUTTON1
)
110 set_input_key_state( MessageQueue
, VK_XBUTTON1
, down
);
111 else if (msg
->wParam
== XBUTTON2
)
112 set_input_key_state( MessageQueue
, VK_XBUTTON2
, down
);
120 key
= (UCHAR
)msg
->wParam
;
121 set_input_key_state( MessageQueue
, key
, down
);
126 down
= (MessageQueue
->KeyState
[VK_LCONTROL
] | MessageQueue
->KeyState
[VK_RCONTROL
]) & KS_DOWN_BIT
;
127 set_input_key_state( MessageQueue
, VK_CONTROL
, down
);
131 down
= (MessageQueue
->KeyState
[VK_LMENU
] | MessageQueue
->KeyState
[VK_RMENU
]) & KS_DOWN_BIT
;
132 set_input_key_state( MessageQueue
, VK_MENU
, down
);
136 down
= (MessageQueue
->KeyState
[VK_LSHIFT
] | MessageQueue
->KeyState
[VK_RSHIFT
]) & KS_DOWN_BIT
;
137 set_input_key_state( MessageQueue
, VK_SHIFT
, down
);
145 IntMsqSetWakeMask(DWORD WakeMask
)
147 PTHREADINFO Win32Thread
;
148 PUSER_MESSAGE_QUEUE MessageQueue
;
149 HANDLE MessageEventHandle
;
150 DWORD dwFlags
= HIWORD(WakeMask
);
152 Win32Thread
= PsGetCurrentThreadWin32Thread();
153 if (Win32Thread
== NULL
|| Win32Thread
->MessageQueue
== NULL
)
156 MessageQueue
= Win32Thread
->MessageQueue
;
157 // Win32Thread->pEventQueueServer; IntMsqSetWakeMask returns Win32Thread->hEventQueueClient
158 MessageEventHandle
= MessageQueue
->NewMessagesHandle
;
160 if (Win32Thread
->pcti
)
162 if ( (Win32Thread
->pcti
->fsChangeBits
& LOWORD(WakeMask
)) ||
163 ( (dwFlags
& MWMO_INPUTAVAILABLE
) && (Win32Thread
->pcti
->fsWakeBits
& LOWORD(WakeMask
)) ) )
165 DPRINT1("Chg 0x%x Wake 0x%x Mask 0x%x\n",Win32Thread
->pcti
->fsChangeBits
, Win32Thread
->pcti
->fsWakeBits
, WakeMask
);
166 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
); // Wake it up!
167 return MessageEventHandle
;
173 return MessageEventHandle
;
177 IntMsqClearWakeMask(VOID
)
179 PTHREADINFO Win32Thread
;
181 Win32Thread
= PsGetCurrentThreadWin32Thread();
182 if (Win32Thread
== NULL
|| Win32Thread
->MessageQueue
== NULL
)
184 // Very hacky, but that is what they do.
185 Win32Thread
->pcti
->fsWakeBits
= 0;
193 Due to the uncertainty of knowing what was set in our multilevel message queue,
194 and even if the bits are all cleared. The same as cTimers/cPaintsReady.
195 I think this is the best solution... (jt) */
197 MsqWakeQueue(PUSER_MESSAGE_QUEUE Queue
, DWORD MessageBits
, BOOL KeyEvent
)
201 pti
= Queue
->Thread
->Tcb
.Win32Thread
;
202 pti
->pcti
->fsWakeBits
|= MessageBits
;
203 pti
->pcti
->fsChangeBits
|= MessageBits
;
205 // Start bit accounting to help clear the main set of bits.
206 if (MessageBits
& QS_KEY
) Queue
->nCntsQBits
[QSRosKey
]++;
207 if (MessageBits
& QS_MOUSEMOVE
) Queue
->nCntsQBits
[QSRosMouseMove
]++;
208 if (MessageBits
& QS_MOUSEBUTTON
) Queue
->nCntsQBits
[QSRosMouseButton
]++;
209 if (MessageBits
& QS_POSTMESSAGE
) Queue
->nCntsQBits
[QSRosPostMessage
]++;
210 if (MessageBits
& QS_SENDMESSAGE
) Queue
->nCntsQBits
[QSRosSendMessage
]++;
211 if (MessageBits
& QS_HOTKEY
) Queue
->nCntsQBits
[QSRosHotKey
]++;
214 KeSetEvent(Queue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
218 ClearMsgBitsMask(PUSER_MESSAGE_QUEUE Queue
, UINT MessageBits
)
223 pti
= Queue
->Thread
->Tcb
.Win32Thread
;
225 if (MessageBits
& QS_KEY
)
227 if (--Queue
->nCntsQBits
[QSRosKey
] == 0) ClrMask
|= QS_KEY
;
229 if (MessageBits
& QS_MOUSEMOVE
) // ReactOS hard coded.
230 { // Account for tracking mouse moves..
231 if (--Queue
->nCntsQBits
[QSRosMouseMove
] == 0) ClrMask
|= QS_MOUSEMOVE
;
232 // Handle mouse move bits here.
233 if (Queue
->MouseMoved
) ClrMask
|= QS_MOUSEMOVE
;
235 if (MessageBits
& QS_MOUSEBUTTON
)
237 if (--Queue
->nCntsQBits
[QSRosMouseButton
] == 0) ClrMask
|= QS_MOUSEBUTTON
;
239 if (MessageBits
& QS_POSTMESSAGE
)
241 if (--Queue
->nCntsQBits
[QSRosPostMessage
] == 0) ClrMask
|= QS_POSTMESSAGE
;
243 if (MessageBits
& QS_TIMER
) // ReactOS hard coded.
244 { // Handle timer bits here.
245 if ( pti
->cTimersReady
)
247 if (--pti
->cTimersReady
== 0) ClrMask
|= QS_TIMER
;
250 if (MessageBits
& QS_PAINT
) // ReactOS hard coded.
251 { // Handle paint bits here.
252 if ( pti
->cPaintsReady
)
254 if (--pti
->cPaintsReady
== 0) ClrMask
|= QS_PAINT
;
257 if (MessageBits
& QS_SENDMESSAGE
)
259 if (--Queue
->nCntsQBits
[QSRosSendMessage
] == 0) ClrMask
|= QS_SENDMESSAGE
;
261 if (MessageBits
& QS_HOTKEY
)
263 if (--Queue
->nCntsQBits
[QSRosHotKey
] == 0) ClrMask
|= QS_HOTKEY
;
266 pti
->pcti
->fsWakeBits
&= ~ClrMask
;
267 pti
->pcti
->fsChangeBits
&= ~ClrMask
;
271 MsqIncPaintCountQueue(PUSER_MESSAGE_QUEUE Queue
)
274 pti
= Queue
->Thread
->Tcb
.Win32Thread
;
276 MsqWakeQueue(Queue
, QS_PAINT
, TRUE
);
280 MsqDecPaintCountQueue(PUSER_MESSAGE_QUEUE Queue
)
282 ClearMsgBitsMask(Queue
, QS_PAINT
);
286 MsqPostMouseMove(PUSER_MESSAGE_QUEUE MessageQueue
, MSG
* Msg
)
288 MessageQueue
->MouseMoveMsg
= *Msg
;
289 MessageQueue
->MouseMoved
= TRUE
;
290 MsqWakeQueue(MessageQueue
, QS_MOUSEMOVE
, TRUE
);
294 co_MsqInsertMouseMessage(MSG
* Msg
, DWORD flags
, ULONG_PTR dwExtraInfo
, BOOL Hook
)
296 LARGE_INTEGER LargeTickCount
;
297 MSLLHOOKSTRUCT MouseHookData
;
299 PWND pwnd
, pwndDesktop
;
301 KeQueryTickCount(&LargeTickCount
);
302 Msg
->time
= MsqCalculateMessageTime(&LargeTickCount
);
304 MouseHookData
.pt
.x
= LOWORD(Msg
->lParam
);
305 MouseHookData
.pt
.y
= HIWORD(Msg
->lParam
);
309 MouseHookData
.mouseData
= MAKELONG(0, GET_WHEEL_DELTA_WPARAM(Msg
->wParam
));
313 case WM_XBUTTONDBLCLK
:
314 case WM_NCXBUTTONDOWN
:
316 case WM_NCXBUTTONDBLCLK
:
317 MouseHookData
.mouseData
= MAKELONG(0, HIWORD(Msg
->wParam
));
320 MouseHookData
.mouseData
= 0;
324 MouseHookData
.flags
= flags
; // LLMHF_INJECTED
325 MouseHookData
.time
= Msg
->time
;
326 MouseHookData
.dwExtraInfo
= dwExtraInfo
;
328 /* If the hook procedure returned non zero, dont send the message */
331 if (co_HOOK_CallHooks(WH_MOUSE_LL
, HC_ACTION
, Msg
->message
, (LPARAM
) &MouseHookData
))
335 /* Get the desktop window */
336 pwndDesktop
= UserGetDesktopWindow();
340 /* Set hit somewhere on the desktop */
341 pDesk
= pwndDesktop
->head
.rpdesk
;
342 pDesk
->htEx
= HTNOWHERE
;
343 pDesk
->spwndTrack
= pwndDesktop
;
345 /* Check if the mouse is captured */
346 Msg
->hwnd
= IntGetCaptureWindow();
347 if(Msg
->hwnd
!= NULL
)
349 pwnd
= UserGetWindowObject(Msg
->hwnd
);
350 if ((pwnd
->style
& WS_VISIBLE
) &&
351 IntPtInWindow(pwnd
, Msg
->pt
.x
, Msg
->pt
.y
))
353 pDesk
->htEx
= HTCLIENT
;
354 pDesk
->spwndTrack
= pwnd
;
359 /* Loop all top level windows to find which one should receive input */
360 for( pwnd
= pwndDesktop
->spwndChild
;
362 pwnd
= pwnd
->spwndNext
)
364 if ( pwnd
->state2
& WNDS2_INDESTROY
|| pwnd
->state
& WNDS_DESTROYED
)
366 DPRINT("The Window is in DESTROY!\n");
370 if((pwnd
->style
& WS_VISIBLE
) &&
371 IntPtInWindow(pwnd
, Msg
->pt
.x
, Msg
->pt
.y
))
373 Msg
->hwnd
= pwnd
->head
.h
;
374 pDesk
->htEx
= HTCLIENT
;
375 pDesk
->spwndTrack
= pwnd
;
381 /* Check if we found a window */
382 if(Msg
->hwnd
!= NULL
&& pwnd
!= NULL
)
384 if(Msg
->message
== WM_MOUSEMOVE
)
386 /* Mouse move is a special case*/
387 MsqPostMouseMove(pwnd
->head
.pti
->MessageQueue
, Msg
);
391 DPRINT("Posting mouse message to hwnd=0x%x!\n", UserHMGetHandle(pwnd
));
392 MsqPostMessage(pwnd
->head
.pti
->MessageQueue
, Msg
, TRUE
, QS_MOUSEBUTTON
);
396 /* Do GetMouseMovePointsEx FIFO. */
397 MouseHistoryOfMoves
[gcur_count
].x
= Msg
->pt
.x
;
398 MouseHistoryOfMoves
[gcur_count
].y
= Msg
->pt
.y
;
399 MouseHistoryOfMoves
[gcur_count
].time
= Msg
->time
;
400 MouseHistoryOfMoves
[gcur_count
].dwExtraInfo
= dwExtraInfo
;
401 if (gcur_count
++ == 64) gcur_count
= 0; // 0 - 63 is 64, FIFO forwards.
405 // Note: Only called from input.c.
408 co_MsqPostKeyboardMessage(UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
410 PUSER_MESSAGE_QUEUE FocusMessageQueue
;
412 LARGE_INTEGER LargeTickCount
;
413 KBDLLHOOKSTRUCT KbdHookData
;
415 DPRINT("MsqPostKeyboardMessage(uMsg 0x%x, wParam 0x%x, lParam 0x%x)\n",
416 uMsg
, wParam
, lParam
);
418 // Condition may arise when calling MsqPostMessage and waiting for an event.
419 ASSERT(UserIsEntered());
421 FocusMessageQueue
= IntGetFocusMessageQueue();
425 if (FocusMessageQueue
&& (FocusMessageQueue
->FocusWindow
!= (HWND
)0))
426 Msg
.hwnd
= FocusMessageQueue
->FocusWindow
;
432 KeQueryTickCount(&LargeTickCount
);
433 Msg
.time
= MsqCalculateMessageTime(&LargeTickCount
);
435 /* We can't get the Msg.pt point here since we don't know thread
436 (and thus the window station) the message will end up in yet. */
438 KbdHookData
.vkCode
= Msg
.wParam
;
439 KbdHookData
.scanCode
= (Msg
.lParam
>> 16) & 0xff;
440 KbdHookData
.flags
= (0 == (Msg
.lParam
& 0x01000000) ? 0 : LLKHF_EXTENDED
) |
441 (0 == (Msg
.lParam
& 0x20000000) ? 0 : LLKHF_ALTDOWN
) |
442 (0 == (Msg
.lParam
& 0x80000000) ? 0 : LLKHF_UP
);
443 KbdHookData
.time
= Msg
.time
;
444 KbdHookData
.dwExtraInfo
= 0;
445 if (co_HOOK_CallHooks(WH_KEYBOARD_LL
, HC_ACTION
, Msg
.message
, (LPARAM
) &KbdHookData
))
447 DPRINT1("Kbd msg %d wParam %d lParam 0x%08x dropped by WH_KEYBOARD_LL hook\n",
448 Msg
.message
, Msg
.wParam
, Msg
.lParam
);
452 if (FocusMessageQueue
== NULL
)
454 DPRINT("No focus message queue\n");
458 if (FocusMessageQueue
->FocusWindow
!= (HWND
)0)
460 Msg
.hwnd
= FocusMessageQueue
->FocusWindow
;
461 DPRINT("Msg.hwnd = %x\n", Msg
.hwnd
);
463 FocusMessageQueue
->Desktop
->pDeskInfo
->LastInputWasKbd
= TRUE
;
465 Msg
.pt
= gpsi
->ptCursor
;
466 MsqPostMessage(FocusMessageQueue
, &Msg
, TRUE
, QS_KEY
);
470 DPRINT("Invalid focus window handle\n");
477 MsqPostHotKeyMessage(PVOID Thread
, HWND hWnd
, WPARAM wParam
, LPARAM lParam
)
480 PTHREADINFO Win32Thread
;
482 LARGE_INTEGER LargeTickCount
;
485 Status
= ObReferenceObjectByPointer (Thread
,
489 if (!NT_SUCCESS(Status
))
492 Win32Thread
= ((PETHREAD
)Thread
)->Tcb
.Win32Thread
;
493 if (Win32Thread
== NULL
|| Win32Thread
->MessageQueue
== NULL
)
495 ObDereferenceObject ((PETHREAD
)Thread
);
499 Window
= IntGetWindowObject(hWnd
);
502 ObDereferenceObject ((PETHREAD
)Thread
);
507 Mesg
.message
= WM_HOTKEY
;
508 Mesg
.wParam
= wParam
;
509 Mesg
.lParam
= lParam
;
510 KeQueryTickCount(&LargeTickCount
);
511 Mesg
.time
= MsqCalculateMessageTime(&LargeTickCount
);
512 Mesg
.pt
= gpsi
->ptCursor
;
513 MsqPostMessage(Window
->head
.pti
->MessageQueue
, &Mesg
, FALSE
, QS_HOTKEY
);
514 UserDereferenceObject(Window
);
515 ObDereferenceObject (Thread
);
519 PUSER_MESSAGE FASTCALL
520 MsqCreateMessage(LPMSG Msg
)
522 PUSER_MESSAGE Message
;
524 Message
= ExAllocateFromPagedLookasideList(&MessageLookasideList
);
530 RtlMoveMemory(&Message
->Msg
, Msg
, sizeof(MSG
));
536 MsqDestroyMessage(PUSER_MESSAGE Message
)
538 ExFreeToPagedLookasideList(&MessageLookasideList
, Message
);
542 co_MsqDispatchOneSentMessage(PUSER_MESSAGE_QUEUE MessageQueue
)
544 PUSER_SENT_MESSAGE SaveMsg
, Message
;
549 if (IsListEmpty(&MessageQueue
->SentMessagesListHead
))
554 /* remove it from the list of pending messages */
555 Entry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
556 Message
= CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
);
558 pti
= MessageQueue
->Thread
->Tcb
.Win32Thread
;
560 SaveMsg
= pti
->pusmCurrent
;
561 pti
->pusmCurrent
= Message
;
563 // Processing a message sent to it from another thread.
564 if ( ( Message
->SenderQueue
&& MessageQueue
!= Message
->SenderQueue
) ||
565 ( Message
->CallBackSenderQueue
&& MessageQueue
!= Message
->CallBackSenderQueue
))
566 { // most likely, but, to be sure.
567 pti
->pcti
->CTI_flags
|= CTI_INSENDMESSAGE
; // Let the user know...
570 /* insert it to the list of messages that are currently dispatched by this
572 InsertTailList(&MessageQueue
->LocalDispatchingMessagesHead
,
573 &Message
->ListEntry
);
575 ClearMsgBitsMask(MessageQueue
, Message
->QS_Flags
);
577 if (Message
->HookMessage
== MSQ_ISHOOK
)
578 { // Direct Hook Call processor
579 Result
= co_CallHook( Message
->Msg
.message
, // HookId
580 (INT
)(INT_PTR
)Message
->Msg
.hwnd
, // Code
582 Message
->Msg
.lParam
);
584 else if (Message
->HookMessage
== MSQ_ISEVENT
)
585 { // Direct Event Call processor
586 Result
= co_EVENT_CallEvents( Message
->Msg
.message
,
589 Message
->Msg
.lParam
);
591 else if ((Message
->CompletionCallback
)
592 && (Message
->CallBackSenderQueue
== MessageQueue
))
593 { /* Call the callback routine */
594 if (Message
->QS_Flags
& QS_SMRESULT
)
596 co_IntCallSentMessageCallback(Message
->CompletionCallback
,
598 Message
->Msg
.message
,
599 Message
->CompletionCallbackContext
,
601 /* Set callback to NULL to prevent reentry */
602 Message
->CompletionCallback
= NULL
;
606 /* The message has not been processed yet, reinsert it. */
607 RemoveEntryList(&Message
->ListEntry
);
608 InsertTailList(&Message
->CallBackSenderQueue
->SentMessagesListHead
, &Message
->ListEntry
);
609 DPRINT("Callback Message not processed yet. Requeuing the message\n");
614 { /* Call the window procedure. */
615 Result
= co_IntSendMessage( Message
->Msg
.hwnd
,
616 Message
->Msg
.message
,
618 Message
->Msg
.lParam
);
621 /* remove the message from the local dispatching list, because it doesn't need
622 to be cleaned up on thread termination anymore */
623 RemoveEntryList(&Message
->ListEntry
);
625 /* If the message is a callback, insert it in the callback senders MessageQueue */
626 if (Message
->CompletionCallback
)
628 if (Message
->CallBackSenderQueue
)
630 Message
->lResult
= Result
;
631 Message
->QS_Flags
|= QS_SMRESULT
;
633 /* insert it in the callers message queue */
634 InsertTailList(&Message
->CallBackSenderQueue
->SentMessagesListHead
, &Message
->ListEntry
);
635 MsqWakeQueue(Message
->CallBackSenderQueue
, QS_SENDMESSAGE
, TRUE
);
636 IntDereferenceMessageQueue(Message
->CallBackSenderQueue
);
641 /* remove the message from the dispatching list if needed, so lock the sender's message queue */
642 if (Message
->SenderQueue
)
644 if (Message
->DispatchingListEntry
.Flink
!= NULL
)
646 /* only remove it from the dispatching list if not already removed by a timeout */
647 RemoveEntryList(&Message
->DispatchingListEntry
);
650 /* still keep the sender's message queue locked, so the sender can't exit the
651 MsqSendMessage() function (if timed out) */
653 if (Message
->QS_Flags
& QS_SMRESULT
)
655 Result
= Message
->lResult
;
658 /* Let the sender know the result. */
659 if (Message
->Result
!= NULL
)
661 *Message
->Result
= Result
;
664 if (Message
->HasPackedLParam
== TRUE
)
666 if (Message
->Msg
.lParam
)
667 ExFreePool((PVOID
)Message
->Msg
.lParam
);
670 /* Notify the sender. */
671 if (Message
->CompletionEvent
!= NULL
)
673 KeSetEvent(Message
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
676 /* if the message has a sender */
677 if (Message
->SenderQueue
)
679 /* dereference our and the sender's message queue */
680 IntDereferenceMessageQueue(Message
->SenderQueue
);
681 IntDereferenceMessageQueue(MessageQueue
);
684 /* free the message */
685 ExFreePoolWithTag(Message
, TAG_USRMSG
);
687 /* do not hangup on the user if this is reentering */
688 if (!SaveMsg
) pti
->pcti
->CTI_flags
&= ~CTI_INSENDMESSAGE
;
689 pti
->pusmCurrent
= SaveMsg
;
695 MsqRemoveWindowMessagesFromQueue(PVOID pWindow
)
697 PUSER_SENT_MESSAGE SentMessage
;
698 PUSER_MESSAGE PostedMessage
;
699 PUSER_MESSAGE_QUEUE MessageQueue
;
700 PLIST_ENTRY CurrentEntry
, ListHead
;
701 PWND Window
= pWindow
;
705 MessageQueue
= Window
->head
.pti
->MessageQueue
;
706 ASSERT(MessageQueue
);
708 /* remove the posted messages for this window */
709 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
710 ListHead
= &MessageQueue
->PostedMessagesListHead
;
711 while (CurrentEntry
!= ListHead
)
713 PostedMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
715 if (PostedMessage
->Msg
.hwnd
== Window
->head
.h
)
717 RemoveEntryList(&PostedMessage
->ListEntry
);
718 ClearMsgBitsMask(MessageQueue
, PostedMessage
->QS_Flags
);
719 MsqDestroyMessage(PostedMessage
);
720 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
724 CurrentEntry
= CurrentEntry
->Flink
;
728 /* remove the sent messages for this window */
729 CurrentEntry
= MessageQueue
->SentMessagesListHead
.Flink
;
730 ListHead
= &MessageQueue
->SentMessagesListHead
;
731 while (CurrentEntry
!= ListHead
)
733 SentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
735 if(SentMessage
->Msg
.hwnd
== Window
->head
.h
)
737 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
739 RemoveEntryList(&SentMessage
->ListEntry
);
740 ClearMsgBitsMask(MessageQueue
, SentMessage
->QS_Flags
);
742 /* if it is a callback and this queue is not the sender queue, dereference queue */
743 if ((SentMessage
->CompletionCallback
) && (SentMessage
->CallBackSenderQueue
!= MessageQueue
))
745 IntDereferenceMessageQueue(SentMessage
->CallBackSenderQueue
);
747 /* Only if the message has a sender was the queue referenced */
748 if ((SentMessage
->SenderQueue
)
749 && (SentMessage
->DispatchingListEntry
.Flink
!= NULL
))
751 RemoveEntryList(&SentMessage
->DispatchingListEntry
);
754 /* wake the sender's thread */
755 if (SentMessage
->CompletionEvent
!= NULL
)
757 KeSetEvent(SentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
760 if (SentMessage
->HasPackedLParam
== TRUE
)
762 if (SentMessage
->Msg
.lParam
)
763 ExFreePool((PVOID
)SentMessage
->Msg
.lParam
);
766 /* if the message has a sender */
767 if (SentMessage
->SenderQueue
)
769 /* dereference our and the sender's message queue */
770 IntDereferenceMessageQueue(MessageQueue
);
771 IntDereferenceMessageQueue(SentMessage
->SenderQueue
);
774 /* free the message */
775 ExFreePoolWithTag(SentMessage
, TAG_USRMSG
);
777 CurrentEntry
= MessageQueue
->SentMessagesListHead
.Flink
;
781 CurrentEntry
= CurrentEntry
->Flink
;
787 co_MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
788 HWND Wnd
, UINT Msg
, WPARAM wParam
, LPARAM lParam
,
789 UINT uTimeout
, BOOL Block
, INT HookMessage
,
792 PTHREADINFO pti
, ptirec
;
793 PUSER_SENT_MESSAGE Message
;
794 KEVENT CompletionEvent
;
796 PUSER_MESSAGE_QUEUE ThreadQueue
;
797 LARGE_INTEGER Timeout
;
799 LRESULT Result
= 0; //// Result could be trashed. ////
801 if(!(Message
= ExAllocatePoolWithTag(PagedPool
, sizeof(USER_SENT_MESSAGE
), TAG_USRMSG
)))
803 DPRINT1("MsqSendMessage(): Not enough memory to allocate a message");
804 return STATUS_INSUFFICIENT_RESOURCES
;
807 KeInitializeEvent(&CompletionEvent
, NotificationEvent
, FALSE
);
809 pti
= PsGetCurrentThreadWin32Thread();
810 ThreadQueue
= pti
->MessageQueue
;
811 ptirec
= MessageQueue
->Thread
->Tcb
.Win32Thread
;
812 ASSERT(ThreadQueue
!= MessageQueue
);
813 ASSERT(ptirec
->pcti
); // Send must have a client side to receive it!!!!
815 Timeout
.QuadPart
= (LONGLONG
) uTimeout
* (LONGLONG
) -10000;
817 /* FIXME - increase reference counter of sender's message queue here */
819 Message
->Msg
.hwnd
= Wnd
;
820 Message
->Msg
.message
= Msg
;
821 Message
->Msg
.wParam
= wParam
;
822 Message
->Msg
.lParam
= lParam
;
823 Message
->CompletionEvent
= &CompletionEvent
;
824 Message
->Result
= &Result
;
825 Message
->lResult
= 0;
826 Message
->QS_Flags
= 0;
827 Message
->SenderQueue
= ThreadQueue
;
828 Message
->CallBackSenderQueue
= NULL
;
829 IntReferenceMessageQueue(ThreadQueue
);
830 Message
->CompletionCallback
= NULL
;
831 Message
->CompletionCallbackContext
= 0;
832 Message
->HookMessage
= HookMessage
;
833 Message
->HasPackedLParam
= FALSE
;
835 IntReferenceMessageQueue(MessageQueue
);
837 /* add it to the list of pending messages */
838 InsertTailList(&ThreadQueue
->DispatchingMessagesHead
, &Message
->DispatchingListEntry
);
840 /* queue it in the destination's message queue */
841 InsertTailList(&MessageQueue
->SentMessagesListHead
, &Message
->ListEntry
);
843 Message
->QS_Flags
= QS_SENDMESSAGE
;
844 MsqWakeQueue(MessageQueue
, QS_SENDMESSAGE
, TRUE
);
846 /* we can't access the Message anymore since it could have already been deleted! */
852 /* don't process messages sent to the thread */
853 WaitStatus
= KeWaitForSingleObject(&CompletionEvent
, UserRequest
, UserMode
,
854 FALSE
, (uTimeout
? &Timeout
: NULL
));
858 if(WaitStatus
== STATUS_TIMEOUT
)
860 /* look up if the message has not yet dispatched, if so
861 make sure it can't pass a result and it must not set the completion event anymore */
862 Entry
= MessageQueue
->SentMessagesListHead
.Flink
;
863 while (Entry
!= &MessageQueue
->SentMessagesListHead
)
865 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
)
868 /* we can access Message here, it's secure because the message queue is locked
869 and the message is still hasn't been dispatched */
870 Message
->CompletionEvent
= NULL
;
871 Message
->Result
= NULL
;
874 Entry
= Entry
->Flink
;
877 /* remove from the local dispatching list so the other thread knows,
878 it can't pass a result and it must not set the completion event anymore */
879 Entry
= ThreadQueue
->DispatchingMessagesHead
.Flink
;
880 while (Entry
!= &ThreadQueue
->DispatchingMessagesHead
)
882 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, DispatchingListEntry
)
885 /* we can access Message here, it's secure because the sender's message is locked
886 and the message has definitely not yet been destroyed, otherwise it would
887 have been removed from this list by the dispatching routine right after
888 dispatching the message */
889 Message
->CompletionEvent
= NULL
;
890 Message
->Result
= NULL
;
891 RemoveEntryList(&Message
->DispatchingListEntry
);
892 Message
->DispatchingListEntry
.Flink
= NULL
;
895 Entry
= Entry
->Flink
;
898 DPRINT("MsqSendMessage (blocked) timed out 1\n");
900 while (co_MsqDispatchOneSentMessage(ThreadQueue
))
905 PVOID WaitObjects
[2];
907 WaitObjects
[0] = &CompletionEvent
;
908 WaitObjects
[1] = ThreadQueue
->NewMessages
;
913 WaitStatus
= KeWaitForMultipleObjects(2, WaitObjects
, WaitAny
, UserRequest
,
914 UserMode
, FALSE
, (uTimeout
? &Timeout
: NULL
), NULL
);
918 if(WaitStatus
== STATUS_TIMEOUT
)
920 /* look up if the message has not yet been dispatched, if so
921 make sure it can't pass a result and it must not set the completion event anymore */
922 Entry
= MessageQueue
->SentMessagesListHead
.Flink
;
923 while (Entry
!= &MessageQueue
->SentMessagesListHead
)
925 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
)
928 /* we can access Message here, it's secure because the message queue is locked
929 and the message is still hasn't been dispatched */
930 Message
->CompletionEvent
= NULL
;
931 Message
->Result
= NULL
;
934 Entry
= Entry
->Flink
;
937 /* remove from the local dispatching list so the other thread knows,
938 it can't pass a result and it must not set the completion event anymore */
939 Entry
= ThreadQueue
->DispatchingMessagesHead
.Flink
;
940 while (Entry
!= &ThreadQueue
->DispatchingMessagesHead
)
942 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, DispatchingListEntry
)
945 /* we can access Message here, it's secure because the sender's message is locked
946 and the message has definitely not yet been destroyed, otherwise it would
947 have been removed from this list by the dispatching routine right after
948 dispatching the message */
949 Message
->CompletionEvent
= NULL
;
950 Message
->Result
= NULL
;
951 RemoveEntryList(&Message
->DispatchingListEntry
);
952 Message
->DispatchingListEntry
.Flink
= NULL
;
955 Entry
= Entry
->Flink
;
958 DPRINT("MsqSendMessage timed out 2\n");
961 while (co_MsqDispatchOneSentMessage(ThreadQueue
))
964 while (NT_SUCCESS(WaitStatus
) && STATUS_WAIT_0
!= WaitStatus
);
967 if(WaitStatus
!= STATUS_TIMEOUT
)
968 *uResult
= (STATUS_WAIT_0
== WaitStatus
? Result
: -1);
974 MsqPostMessage(PUSER_MESSAGE_QUEUE MessageQueue
, MSG
* Msg
, BOOLEAN HardwareMessage
,
977 PUSER_MESSAGE Message
;
979 if(!(Message
= MsqCreateMessage(Msg
)))
986 InsertTailList(&MessageQueue
->PostedMessagesListHead
,
987 &Message
->ListEntry
);
991 InsertTailList(&MessageQueue
->HardwareMessagesListHead
,
992 &Message
->ListEntry
);
995 Message
->QS_Flags
= MessageBits
;
996 MsqWakeQueue(MessageQueue
, MessageBits
, (MessageBits
& QS_TIMER
? FALSE
: TRUE
));
1000 MsqPostQuitMessage(PUSER_MESSAGE_QUEUE MessageQueue
, ULONG ExitCode
)
1002 MessageQueue
->QuitPosted
= TRUE
;
1003 MessageQueue
->QuitExitCode
= ExitCode
;
1004 MsqWakeQueue(MessageQueue
, QS_POSTMESSAGE
|QS_ALLPOSTMESSAGE
, TRUE
);
1007 /***********************************************************************
1008 * MsqSendParentNotify
1010 * Send a WM_PARENTNOTIFY to all ancestors of the given window, unless
1011 * the window has the WS_EX_NOPARENTNOTIFY style.
1013 static void MsqSendParentNotify( PWND pwnd
, WORD event
, WORD idChild
, POINT pt
)
1015 PWND pwndDesktop
= UserGetWindowObject(IntGetDesktopWindow());
1017 /* pt has to be in the client coordinates of the parent window */
1018 pt
.x
+= pwndDesktop
->rcClient
.left
- pwnd
->rcClient
.left
;
1019 pt
.y
+= pwndDesktop
->rcClient
.top
- pwnd
->rcClient
.top
;
1025 if (!(pwnd
->style
& WS_CHILD
)) break;
1026 if (pwnd
->ExStyle
& WS_EX_NOPARENTNOTIFY
) break;
1027 if (!(pwndParent
= IntGetParent(pwnd
))) break;
1028 if (pwndParent
== pwndDesktop
) break;
1029 pt
.x
+= pwnd
->rcClient
.left
- pwndParent
->rcClient
.left
;
1030 pt
.y
+= pwnd
->rcClient
.top
- pwndParent
->rcClient
.top
;
1033 co_IntSendMessage( UserHMGetHandle(pwnd
), WM_PARENTNOTIFY
,
1034 MAKEWPARAM( event
, idChild
), MAKELPARAM( pt
.x
, pt
.y
) );
1038 BOOL
co_IntProcessMouseMessage(MSG
* msg
, BOOL
* RemoveMessages
, UINT first
, UINT last
)
1045 MOUSEHOOKSTRUCT hook
;
1048 PWND pwndMsg
, pwndDesktop
;
1049 PUSER_MESSAGE_QUEUE MessageQueue
;
1051 PSYSTEM_CURSORINFO CurInfo
;
1052 DECLARE_RETURN(BOOL
);
1054 pti
= PsGetCurrentThreadWin32Thread();
1055 pwndDesktop
= UserGetDesktopWindow();
1056 MessageQueue
= pti
->MessageQueue
;
1057 CurInfo
= IntGetSysCursorInfo();
1058 pwndMsg
= UserGetWindowObject(msg
->hwnd
);
1059 clk_msg
= MessageQueue
->msgDblClk
;
1061 /* find the window to dispatch this mouse message to */
1062 if (MessageQueue
->CaptureWindow
)
1065 pwndMsg
= IntGetWindowObject(MessageQueue
->CaptureWindow
);
1069 pwndMsg
= co_WinPosWindowFromPoint(pwndMsg
, &msg
->pt
, &hittest
);
1072 DPRINT("Got mouse message for 0x%x, hittest: 0x%x\n", msg
->hwnd
, hittest
);
1074 if (pwndMsg
== NULL
|| pwndMsg
->head
.pti
!= pti
)
1076 /* Remove and ignore the message */
1077 *RemoveMessages
= TRUE
;
1081 msg
->hwnd
= UserHMGetHandle(pwndMsg
);
1084 if (!check_hwnd_filter( msg
, hwnd_filter
)) RETURN(FALSE
);
1088 message
= msg
->message
;
1089 /* Note: windows has no concept of a non-client wheel message */
1090 if (message
!= WM_MOUSEWHEEL
)
1092 if (hittest
!= HTCLIENT
)
1094 message
+= WM_NCMOUSEMOVE
- WM_MOUSEMOVE
;
1095 msg
->wParam
= hittest
;
1099 /* coordinates don't get translated while tracking a menu */
1100 /* FIXME: should differentiate popups and top-level menus */
1101 if (!(MessageQueue
->MenuOwner
))
1103 pt
.x
+= pwndDesktop
->rcClient
.left
- pwndMsg
->rcClient
.left
;
1104 pt
.y
+= pwndDesktop
->rcClient
.top
- pwndMsg
->rcClient
.top
;
1108 msg
->lParam
= MAKELONG( pt
.x
, pt
.y
);
1110 /* translate double clicks */
1112 if ((msg
->message
== WM_LBUTTONDOWN
) ||
1113 (msg
->message
== WM_RBUTTONDOWN
) ||
1114 (msg
->message
== WM_MBUTTONDOWN
) ||
1115 (msg
->message
== WM_XBUTTONDOWN
))
1117 BOOL update
= *RemoveMessages
;
1119 /* translate double clicks -
1120 * note that ...MOUSEMOVEs can slip in between
1121 * ...BUTTONDOWN and ...BUTTONDBLCLK messages */
1123 if ((MessageQueue
->MenuOwner
|| MessageQueue
->MoveSize
) ||
1124 hittest
!= HTCLIENT
||
1125 (pwndMsg
->pcls
->style
& CS_DBLCLKS
))
1127 if ((msg
->message
== clk_msg
.message
) &&
1128 (msg
->hwnd
== clk_msg
.hwnd
) &&
1129 (msg
->wParam
== clk_msg
.wParam
) &&
1130 (msg
->time
- clk_msg
.time
< gspv
.iDblClickTime
) &&
1131 (abs(msg
->pt
.x
- clk_msg
.pt
.x
) < UserGetSystemMetrics(SM_CXDOUBLECLK
)/2) &&
1132 (abs(msg
->pt
.y
- clk_msg
.pt
.y
) < UserGetSystemMetrics(SM_CYDOUBLECLK
)/2))
1134 message
+= (WM_LBUTTONDBLCLK
- WM_LBUTTONDOWN
);
1137 MessageQueue
->msgDblClk
.message
= 0; /* clear the double click conditions */
1143 if (!((first
== 0 && last
== 0) || (message
>= first
|| message
<= last
)))
1145 DPRINT("Message out of range!!!\n");
1149 /* update static double click conditions */
1150 if (update
) MessageQueue
->msgDblClk
= *msg
;
1154 if (!((first
== 0 && last
== 0) || (message
>= first
|| message
<= last
)))
1156 DPRINT("Message out of range!!!\n");
1161 if(gspv
.bMouseClickLock
)
1163 BOOL IsClkLck
= FALSE
;
1165 if(msg
->message
== WM_LBUTTONUP
)
1167 IsClkLck
= ((msg
->time
- CurInfo
->ClickLockTime
) >= gspv
.dwMouseClickLockTime
);
1168 if (IsClkLck
&& (!CurInfo
->ClickLockActive
))
1170 CurInfo
->ClickLockActive
= TRUE
;
1173 else if (msg
->message
== WM_LBUTTONDOWN
)
1175 if (CurInfo
->ClickLockActive
)
1178 CurInfo
->ClickLockActive
= FALSE
;
1181 CurInfo
->ClickLockTime
= msg
->time
;
1186 /* Remove and ignore the message */
1187 *RemoveMessages
= TRUE
;
1192 /* message is accepted now (but may still get dropped) */
1194 event
.message
= msg
->message
;
1195 event
.time
= msg
->time
;
1196 event
.hwnd
= msg
->hwnd
;
1197 event
.paramL
= msg
->pt
.x
;
1198 event
.paramH
= msg
->pt
.y
;
1199 co_HOOK_CallHooks( WH_JOURNALRECORD
, HC_ACTION
, 0, (LPARAM
)&event
);
1202 hook
.hwnd
= msg
->hwnd
;
1203 hook
.wHitTestCode
= hittest
;
1204 hook
.dwExtraInfo
= 0/*extra_info*/;
1205 if (co_HOOK_CallHooks( WH_MOUSE
, *RemoveMessages
? HC_ACTION
: HC_NOREMOVE
,
1206 message
, (LPARAM
)&hook
))
1209 hook
.hwnd
= msg
->hwnd
;
1210 hook
.wHitTestCode
= hittest
;
1211 hook
.dwExtraInfo
= 0/*extra_info*/;
1212 co_HOOK_CallHooks( WH_CBT
, HCBT_CLICKSKIPPED
, message
, (LPARAM
)&hook
);
1214 DPRINT1("WH_MOUSE dorpped mouse message!\n");
1216 /* Remove and skip message */
1217 *RemoveMessages
= TRUE
;
1221 if ((hittest
== HTERROR
) || (hittest
== HTNOWHERE
))
1223 co_IntSendMessage( msg
->hwnd
, WM_SETCURSOR
, (WPARAM
)msg
->hwnd
,
1224 MAKELONG( hittest
, msg
->message
));
1226 /* Remove and skip message */
1227 *RemoveMessages
= TRUE
;
1231 if ((*RemoveMessages
== FALSE
) || MessageQueue
->CaptureWindow
)
1233 /* Accept the message */
1234 msg
->message
= message
;
1240 if ((msg
->message
== WM_LBUTTONDOWN
) ||
1241 (msg
->message
== WM_RBUTTONDOWN
) ||
1242 (msg
->message
== WM_MBUTTONDOWN
) ||
1243 (msg
->message
== WM_XBUTTONDOWN
))
1245 /* Send the WM_PARENTNOTIFY,
1246 * note that even for double/nonclient clicks
1247 * notification message is still WM_L/M/RBUTTONDOWN.
1249 MsqSendParentNotify(pwndMsg
, msg
->message
, 0, msg
->pt
);
1251 /* Activate the window if needed */
1253 if (msg
->hwnd
!= UserGetForegroundWindow())
1255 PWND pwndTop
= pwndMsg
;
1258 if ((pwndTop
->style
& (WS_POPUP
|WS_CHILD
)) != WS_CHILD
) break;
1259 pwndTop
= IntGetParent( pwndTop
);
1262 if (pwndTop
&& pwndTop
!= pwndDesktop
)
1264 LONG ret
= co_IntSendMessage( msg
->hwnd
,
1266 (WPARAM
)UserHMGetHandle(pwndTop
),
1267 MAKELONG( hittest
, msg
->message
));
1270 case MA_NOACTIVATEANDEAT
:
1275 case MA_ACTIVATEANDEAT
:
1280 if(!co_IntMouseActivateWindow(pwndMsg
)) eatMsg
= TRUE
;
1283 DPRINT1( "unknown WM_MOUSEACTIVATE code %d\n", ret
);
1290 /* send the WM_SETCURSOR message */
1292 /* Windows sends the normal mouse message as the message parameter
1293 in the WM_SETCURSOR message even if it's non-client mouse message */
1294 co_IntSendMessage( msg
->hwnd
, WM_SETCURSOR
, (WPARAM
)msg
->hwnd
, MAKELONG( hittest
, msg
->message
));
1296 msg
->message
= message
;
1301 UserDereferenceObject(pwndMsg
);
1306 BOOL
co_IntProcessKeyboardMessage(MSG
* Msg
, BOOL
* RemoveMessages
)
1310 Event
.message
= Msg
->message
;
1311 Event
.hwnd
= Msg
->hwnd
;
1312 Event
.time
= Msg
->time
;
1313 Event
.paramL
= (Msg
->wParam
& 0xFF) | (HIWORD(Msg
->lParam
) << 8);
1314 Event
.paramH
= Msg
->lParam
& 0x7FFF;
1315 if (HIWORD(Msg
->lParam
) & 0x0100) Event
.paramH
|= 0x8000;
1316 co_HOOK_CallHooks( WH_JOURNALRECORD
, HC_ACTION
, 0, (LPARAM
)&Event
);
1318 if (co_HOOK_CallHooks( WH_KEYBOARD
,
1319 *RemoveMessages
? HC_ACTION
: HC_NOREMOVE
,
1320 LOWORD(Msg
->wParam
),
1323 /* skip this message */
1324 co_HOOK_CallHooks( WH_CBT
,
1326 LOWORD(Msg
->wParam
),
1328 DPRINT1("KeyboardMessage WH_CBT Call Hook return!\n");
1334 BOOL
co_IntProcessHardwareMessage(MSG
* Msg
, BOOL
* RemoveMessages
, UINT first
, UINT last
)
1336 if ( IS_MOUSE_MESSAGE(Msg
->message
))
1338 return co_IntProcessMouseMessage(Msg
, RemoveMessages
, first
, last
);
1340 else if ( IS_KBD_MESSAGE(Msg
->message
))
1342 return co_IntProcessKeyboardMessage(Msg
, RemoveMessages
);
1349 co_MsqPeekMouseMove(IN PUSER_MESSAGE_QUEUE MessageQueue
,
1352 IN UINT MsgFilterLow
,
1353 IN UINT MsgFilterHigh
,
1359 if(!(MessageQueue
->MouseMoved
))
1362 msg
= MessageQueue
->MouseMoveMsg
;
1364 AcceptMessage
= co_IntProcessMouseMessage(&msg
, &Remove
, MsgFilterLow
, MsgFilterHigh
);
1371 ClearMsgBitsMask(MessageQueue
, QS_MOUSEMOVE
);
1372 MessageQueue
->MouseMoved
= FALSE
;
1375 return AcceptMessage
;
1378 /* check whether a message filter contains at least one potential hardware message */
1380 filter_contains_hw_range( UINT first
, UINT last
)
1382 /* hardware message ranges are (in numerical order):
1383 * WM_NCMOUSEFIRST .. WM_NCMOUSELAST
1384 * WM_KEYFIRST .. WM_KEYLAST
1385 * WM_MOUSEFIRST .. WM_MOUSELAST
1388 if (last
< WM_NCMOUSEFIRST
) return 0;
1389 if (first
> WM_NCMOUSELAST
&& last
< WM_KEYFIRST
) return 0;
1390 if (first
> WM_KEYLAST
&& last
< WM_MOUSEFIRST
) return 0;
1391 if (first
> WM_MOUSELAST
) return 0;
1396 co_MsqPeekHardwareMessage(IN PUSER_MESSAGE_QUEUE MessageQueue
,
1399 IN UINT MsgFilterLow
,
1400 IN UINT MsgFilterHigh
,
1406 PUSER_MESSAGE CurrentMessage
;
1407 PLIST_ENTRY ListHead
, CurrentEntry
= NULL
;
1410 if (!filter_contains_hw_range( MsgFilterLow
, MsgFilterHigh
)) return FALSE
;
1412 ListHead
= &MessageQueue
->HardwareMessagesListHead
;
1413 CurrentEntry
= ListHead
->Flink
;
1415 if (IsListEmpty(CurrentEntry
)) return FALSE
;
1417 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1421 if (IsListEmpty(CurrentEntry
)) break;
1422 if (!CurrentMessage
) break;
1423 CurrentEntry
= CurrentMessage
->ListEntry
.Flink
;
1425 if ( (( MsgFilterLow
== 0 && MsgFilterHigh
== 0 ) && (CurrentMessage
->QS_Flags
& QSflags
)) ||
1426 ( MsgFilterLow
<= CurrentMessage
->Msg
.message
&& MsgFilterHigh
>= CurrentMessage
->Msg
.message
) )
1428 msg
= CurrentMessage
->Msg
;
1430 AcceptMessage
= co_IntProcessHardwareMessage(&msg
, &Remove
, MsgFilterLow
, MsgFilterHigh
);
1434 update_input_key_state(MessageQueue
, pMsg
);
1435 RemoveEntryList(&CurrentMessage
->ListEntry
);
1436 ClearMsgBitsMask(MessageQueue
, QS_INPUT
);
1437 MsqDestroyMessage(CurrentMessage
);
1446 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1449 while(CurrentEntry
!= ListHead
);
1455 MsqPeekMessage(IN PUSER_MESSAGE_QUEUE MessageQueue
,
1458 IN UINT MsgFilterLow
,
1459 IN UINT MsgFilterHigh
,
1463 PLIST_ENTRY CurrentEntry
;
1464 PUSER_MESSAGE CurrentMessage
;
1465 PLIST_ENTRY ListHead
;
1467 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
1468 ListHead
= &MessageQueue
->PostedMessagesListHead
;
1470 if (IsListEmpty(CurrentEntry
)) return FALSE
;
1472 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1476 if (IsListEmpty(CurrentEntry
)) break;
1477 if (!CurrentMessage
) break;
1478 CurrentEntry
= CurrentEntry
->Flink
;
1481 1: any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL.
1482 2: retrieves only messages on the current thread's message queue whose hwnd value is NULL.
1483 3: handle to the window whose messages are to be retrieved.
1485 if ( ( !Window
|| // 1
1486 ( Window
== HWND_BOTTOM
&& CurrentMessage
->Msg
.hwnd
== NULL
) || // 2
1487 ( Window
!= HWND_BOTTOM
&& Window
->head
.h
== CurrentMessage
->Msg
.hwnd
) ) && // 3
1488 ( ( ( MsgFilterLow
== 0 && MsgFilterHigh
== 0 ) && CurrentMessage
->QS_Flags
& QSflags
) ||
1489 ( MsgFilterLow
<= CurrentMessage
->Msg
.message
&& MsgFilterHigh
>= CurrentMessage
->Msg
.message
) ) )
1491 *Message
= CurrentMessage
->Msg
;
1495 RemoveEntryList(&CurrentMessage
->ListEntry
);
1496 ClearMsgBitsMask(MessageQueue
, QS_POSTMESSAGE
);
1497 MsqDestroyMessage(CurrentMessage
);
1501 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1504 while (CurrentEntry
!= ListHead
);
1510 co_MsqWaitForNewMessages(PUSER_MESSAGE_QUEUE MessageQueue
, PWND WndFilter
,
1511 UINT MsgFilterMin
, UINT MsgFilterMax
)
1515 ret
= KeWaitForSingleObject( MessageQueue
->NewMessages
,
1525 MsqIsHung(PUSER_MESSAGE_QUEUE MessageQueue
)
1527 LARGE_INTEGER LargeTickCount
;
1529 KeQueryTickCount(&LargeTickCount
);
1530 return ((LargeTickCount
.u
.LowPart
- MessageQueue
->LastMsgRead
) > MSQ_HUNG
);
1535 HungAppSysTimerProc(HWND hwnd
, UINT uMsg
, UINT_PTR idEvent
, DWORD dwTime
)
1537 //DoTheScreenSaver();
1538 DPRINT("HungAppSysTimerProc\n");
1539 // Process list of windows that are hung and waiting.
1543 MsqInitializeMessageQueue(struct _ETHREAD
*Thread
, PUSER_MESSAGE_QUEUE MessageQueue
)
1545 LARGE_INTEGER LargeTickCount
;
1548 MessageQueue
->Thread
= Thread
;
1549 MessageQueue
->CaretInfo
= (PTHRDCARETINFO
)(MessageQueue
+ 1);
1550 InitializeListHead(&MessageQueue
->PostedMessagesListHead
);
1551 InitializeListHead(&MessageQueue
->SentMessagesListHead
);
1552 InitializeListHead(&MessageQueue
->HardwareMessagesListHead
);
1553 InitializeListHead(&MessageQueue
->DispatchingMessagesHead
);
1554 InitializeListHead(&MessageQueue
->LocalDispatchingMessagesHead
);
1555 KeInitializeMutex(&MessageQueue
->HardwareLock
, 0);
1556 MessageQueue
->QuitPosted
= FALSE
;
1557 MessageQueue
->QuitExitCode
= 0;
1558 KeQueryTickCount(&LargeTickCount
);
1559 MessageQueue
->LastMsgRead
= LargeTickCount
.u
.LowPart
;
1560 MessageQueue
->FocusWindow
= NULL
;
1561 MessageQueue
->NewMessagesHandle
= NULL
;
1563 Status
= ZwCreateEvent(&MessageQueue
->NewMessagesHandle
, EVENT_ALL_ACCESS
,
1564 NULL
, SynchronizationEvent
, FALSE
);
1565 if (!NT_SUCCESS(Status
))
1570 Status
= ObReferenceObjectByHandle(MessageQueue
->NewMessagesHandle
, 0,
1571 ExEventObjectType
, KernelMode
,
1572 (PVOID
*)&MessageQueue
->NewMessages
, NULL
);
1573 if (!NT_SUCCESS(Status
))
1575 ZwClose(MessageQueue
->NewMessagesHandle
);
1576 MessageQueue
->NewMessagesHandle
= NULL
;
1584 MsqCleanupMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue
)
1586 PLIST_ENTRY CurrentEntry
;
1587 PUSER_MESSAGE CurrentMessage
;
1588 PUSER_SENT_MESSAGE CurrentSentMessage
;
1591 pti
= MessageQueue
->Thread
->Tcb
.Win32Thread
;
1594 /* cleanup posted messages */
1595 while (!IsListEmpty(&MessageQueue
->PostedMessagesListHead
))
1597 CurrentEntry
= RemoveHeadList(&MessageQueue
->PostedMessagesListHead
);
1598 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1600 MsqDestroyMessage(CurrentMessage
);
1603 /* remove the messages that have not yet been dispatched */
1604 while (!IsListEmpty(&MessageQueue
->SentMessagesListHead
))
1606 CurrentEntry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
1607 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1610 /* if it is a callback and this queue is not the sender queue, dereference queue */
1611 if ((CurrentSentMessage
->CompletionCallback
) && (CurrentSentMessage
->CallBackSenderQueue
!= MessageQueue
))
1613 IntDereferenceMessageQueue(CurrentSentMessage
->CallBackSenderQueue
);
1616 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1617 /* Only if the message has a sender was the message in the DispatchingList */
1618 if ((CurrentSentMessage
->SenderQueue
)
1619 && (CurrentSentMessage
->DispatchingListEntry
.Flink
!= NULL
))
1621 RemoveEntryList(&CurrentSentMessage
->DispatchingListEntry
);
1624 /* wake the sender's thread */
1625 if (CurrentSentMessage
->CompletionEvent
!= NULL
)
1627 KeSetEvent(CurrentSentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1630 if (CurrentSentMessage
->HasPackedLParam
== TRUE
)
1632 if (CurrentSentMessage
->Msg
.lParam
)
1633 ExFreePool((PVOID
)CurrentSentMessage
->Msg
.lParam
);
1636 /* if the message has a sender */
1637 if (CurrentSentMessage
->SenderQueue
)
1639 /* dereference our and the sender's message queue */
1640 IntDereferenceMessageQueue(MessageQueue
);
1641 IntDereferenceMessageQueue(CurrentSentMessage
->SenderQueue
);
1644 /* free the message */
1645 ExFreePool(CurrentSentMessage
);
1648 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
1649 ExitThread() was called in a SendMessage() umode callback */
1650 while (!IsListEmpty(&MessageQueue
->LocalDispatchingMessagesHead
))
1652 CurrentEntry
= RemoveHeadList(&MessageQueue
->LocalDispatchingMessagesHead
);
1653 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1656 /* if it is a callback and this queue is not the sender queue, dereference queue */
1657 if ((CurrentSentMessage
->CompletionCallback
) && (CurrentSentMessage
->CallBackSenderQueue
!= MessageQueue
))
1659 IntDereferenceMessageQueue(CurrentSentMessage
->CallBackSenderQueue
);
1662 /* remove the message from the dispatching list */
1663 if(CurrentSentMessage
->DispatchingListEntry
.Flink
!= NULL
)
1665 RemoveEntryList(&CurrentSentMessage
->DispatchingListEntry
);
1668 DPRINT("Notify the sender, the thread has been terminated while dispatching a message!\n");
1670 /* wake the sender's thread */
1671 if (CurrentSentMessage
->CompletionEvent
!= NULL
)
1673 KeSetEvent(CurrentSentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1676 if (CurrentSentMessage
->HasPackedLParam
== TRUE
)
1678 if (CurrentSentMessage
->Msg
.lParam
)
1679 ExFreePool((PVOID
)CurrentSentMessage
->Msg
.lParam
);
1682 /* if the message has a sender */
1683 if (CurrentSentMessage
->SenderQueue
)
1685 /* dereference our and the sender's message queue */
1686 IntDereferenceMessageQueue(MessageQueue
);
1687 IntDereferenceMessageQueue(CurrentSentMessage
->SenderQueue
);
1690 /* free the message */
1691 ExFreePool(CurrentSentMessage
);
1694 /* tell other threads not to bother returning any info to us */
1695 while (! IsListEmpty(&MessageQueue
->DispatchingMessagesHead
))
1697 CurrentEntry
= RemoveHeadList(&MessageQueue
->DispatchingMessagesHead
);
1698 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1699 DispatchingListEntry
);
1700 CurrentSentMessage
->CompletionEvent
= NULL
;
1701 CurrentSentMessage
->Result
= NULL
;
1703 /* do NOT dereference our message queue as it might get attempted to be
1707 // Clear it all out.
1708 pti
->pcti
->fsWakeBits
= 0;
1709 pti
->pcti
->fsChangeBits
= 0;
1711 MessageQueue
->nCntsQBits
[QSRosKey
] = 0;
1712 MessageQueue
->nCntsQBits
[QSRosMouseMove
] = 0;
1713 MessageQueue
->nCntsQBits
[QSRosMouseButton
] = 0;
1714 MessageQueue
->nCntsQBits
[QSRosPostMessage
] = 0;
1715 MessageQueue
->nCntsQBits
[QSRosSendMessage
] = 0;
1716 MessageQueue
->nCntsQBits
[QSRosHotKey
] = 0;
1719 PUSER_MESSAGE_QUEUE FASTCALL
1720 MsqCreateMessageQueue(struct _ETHREAD
*Thread
)
1722 PUSER_MESSAGE_QUEUE MessageQueue
;
1724 MessageQueue
= (PUSER_MESSAGE_QUEUE
)ExAllocatePoolWithTag(NonPagedPool
,
1725 sizeof(USER_MESSAGE_QUEUE
) + sizeof(THRDCARETINFO
),
1733 RtlZeroMemory(MessageQueue
, sizeof(USER_MESSAGE_QUEUE
) + sizeof(THRDCARETINFO
));
1734 /* hold at least one reference until it'll be destroyed */
1735 IntReferenceMessageQueue(MessageQueue
);
1736 /* initialize the queue */
1737 if (!MsqInitializeMessageQueue(Thread
, MessageQueue
))
1739 IntDereferenceMessageQueue(MessageQueue
);
1743 return MessageQueue
;
1747 MsqDestroyMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue
)
1751 MessageQueue
->QF_flags
|= QF_INDESTROY
;
1753 /* remove the message queue from any desktops */
1754 if ((desk
= InterlockedExchangePointer((PVOID
*)&MessageQueue
->Desktop
, 0)))
1756 (void)InterlockedExchangePointer((PVOID
*)&desk
->ActiveMessageQueue
, 0);
1757 IntDereferenceMessageQueue(MessageQueue
);
1761 MsqCleanupMessageQueue(MessageQueue
);
1763 if (MessageQueue
->NewMessagesHandle
!= NULL
)
1764 ZwClose(MessageQueue
->NewMessagesHandle
);
1765 MessageQueue
->NewMessagesHandle
= NULL
;
1766 /* decrease the reference counter, if it hits zero, the queue will be freed */
1767 IntDereferenceMessageQueue(MessageQueue
);
1771 MsqSetMessageExtraInfo(LPARAM lParam
)
1775 PUSER_MESSAGE_QUEUE MessageQueue
;
1777 pti
= PsGetCurrentThreadWin32Thread();
1778 MessageQueue
= pti
->MessageQueue
;
1784 Ret
= MessageQueue
->ExtraInfo
;
1785 MessageQueue
->ExtraInfo
= lParam
;
1791 MsqGetMessageExtraInfo(VOID
)
1794 PUSER_MESSAGE_QUEUE MessageQueue
;
1796 pti
= PsGetCurrentThreadWin32Thread();
1797 MessageQueue
= pti
->MessageQueue
;
1803 return MessageQueue
->ExtraInfo
;
1806 // ReplyMessage is called by the thread receiving the window message.
1808 co_MsqReplyMessage( LRESULT lResult
)
1810 PUSER_SENT_MESSAGE Message
;
1813 pti
= PsGetCurrentThreadWin32Thread();
1814 Message
= pti
->pusmCurrent
;
1816 if (!Message
) return FALSE
;
1818 if (Message
->QS_Flags
& QS_SMRESULT
) return FALSE
;
1820 // SendMessageXxx || Callback msg and not a notify msg
1821 if (Message
->SenderQueue
|| Message
->CompletionCallback
)
1823 Message
->lResult
= lResult
;
1824 Message
->QS_Flags
|= QS_SMRESULT
;
1825 // See co_MsqDispatchOneSentMessage, change bits already accounted for and cleared and this msg is going away..
1831 MsqSetStateWindow(PUSER_MESSAGE_QUEUE MessageQueue
, ULONG Type
, HWND hWnd
)
1837 case MSQ_STATE_CAPTURE
:
1838 Prev
= MessageQueue
->CaptureWindow
;
1839 MessageQueue
->CaptureWindow
= hWnd
;
1841 case MSQ_STATE_ACTIVE
:
1842 Prev
= MessageQueue
->ActiveWindow
;
1843 MessageQueue
->ActiveWindow
= hWnd
;
1845 case MSQ_STATE_FOCUS
:
1846 Prev
= MessageQueue
->FocusWindow
;
1847 MessageQueue
->FocusWindow
= hWnd
;
1849 case MSQ_STATE_MENUOWNER
:
1850 Prev
= MessageQueue
->MenuOwner
;
1851 MessageQueue
->MenuOwner
= hWnd
;
1853 case MSQ_STATE_MOVESIZE
:
1854 Prev
= MessageQueue
->MoveSize
;
1855 MessageQueue
->MoveSize
= hWnd
;
1857 case MSQ_STATE_CARET
:
1858 ASSERT(MessageQueue
->CaretInfo
);
1859 Prev
= MessageQueue
->CaretInfo
->hWnd
;
1860 MessageQueue
->CaretInfo
->hWnd
= hWnd
;
1869 NtUserGetKeyState(INT key
)
1873 UserEnterExclusive();
1875 Ret
= UserGetKeyState(key
);