2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Win32k subsystem
4 * PURPOSE: Message queues
5 * FILE: win32ss/user/ntuser/msgqueue.c
6 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
12 DBG_DEFAULT_CHANNEL(UserMsgQ
);
14 /* GLOBALS *******************************************************************/
16 static PPAGED_LOOKASIDE_LIST pgMessageLookasideList
;
17 PUSER_MESSAGE_QUEUE gpqCursor
;
18 ULONG_PTR gdwMouseMoveExtraInfo
= 0;
19 DWORD gdwMouseMoveTimeStamp
= 0;
22 /* FUNCTIONS *****************************************************************/
27 MsqInitializeImpl(VOID
)
29 pgMessageLookasideList
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(PAGED_LOOKASIDE_LIST
), TAG_USRMSG
);
30 if (!pgMessageLookasideList
)
31 return STATUS_NO_MEMORY
;
32 ExInitializePagedLookasideList(pgMessageLookasideList
,
40 return(STATUS_SUCCESS
);
44 IntTopLevelWindowFromPoint(INT x
, INT y
)
46 PWND pWnd
, pwndDesktop
;
48 /* Get the desktop window */
49 pwndDesktop
= UserGetDesktopWindow();
53 /* Loop all top level windows */
54 for (pWnd
= pwndDesktop
->spwndChild
;
56 pWnd
= pWnd
->spwndNext
)
58 if (pWnd
->state2
& WNDS2_INDESTROY
|| pWnd
->state
& WNDS_DESTROYED
)
60 TRACE("The Window is in DESTROY!\n");
64 if ((pWnd
->style
& WS_VISIBLE
) &&
65 (pWnd
->ExStyle
& (WS_EX_LAYERED
|WS_EX_TRANSPARENT
)) == 0 &&
66 IntPtInWindow(pWnd
, x
, y
))
70 /* Window has not been found */
77 PCURICON_OBJECT NewCursor
,
80 PCURICON_OBJECT OldCursor
;
83 PUSER_MESSAGE_QUEUE MessageQueue
;
86 pti
= PsGetCurrentThreadWin32Thread();
87 MessageQueue
= pti
->MessageQueue
;
89 OldCursor
= MessageQueue
->CursorObject
;
91 /* Check if cursors are different */
92 if (OldCursor
== NewCursor
)
95 /* Update cursor for this message queue */
96 MessageQueue
->CursorObject
= NewCursor
;
98 /* If cursor is not visible we have nothing to do */
99 if (MessageQueue
->iCursorLevel
< 0)
102 // Fixes the error message "Not the same cursor!".
103 if (gpqCursor
== NULL
)
105 gpqCursor
= MessageQueue
;
108 /* Update cursor if this message queue controls it */
109 pWnd
= IntTopLevelWindowFromPoint(gpsi
->ptCursor
.x
, gpsi
->ptCursor
.y
);
110 if (pWnd
&& pWnd
->head
.pti
->MessageQueue
== MessageQueue
)
112 /* Get the screen DC */
113 if (!(hdcScreen
= IntGetScreenDC()))
120 /* Call GDI to set the new screen cursor */
121 PCURICON_OBJECT CursorFrame
= NewCursor
;
122 if(NewCursor
->CURSORF_flags
& CURSORF_ACON
)
124 FIXME("Should animate the cursor, using only the first frame now.\n");
125 CursorFrame
= ((PACON
)NewCursor
)->aspcur
[0];
127 GreSetPointerShape(hdcScreen
,
128 CursorFrame
->hbmAlpha
? NULL
: NewCursor
->hbmMask
,
129 CursorFrame
->hbmAlpha
? NewCursor
->hbmAlpha
: NewCursor
->hbmColor
,
130 CursorFrame
->xHotspot
,
131 CursorFrame
->yHotspot
,
134 CursorFrame
->hbmAlpha
? SPS_ALPHA
: 0);
136 else /* Note: OldCursor != NewCursor so we have to hide cursor */
138 /* Remove the cursor */
139 GreMovePointer(hdcScreen
, -1, -1);
140 TRACE("Removing pointer!\n");
142 IntGetSysCursorInfo()->CurrentCursorObject
= NewCursor
;
145 /* Return the old cursor */
149 /* Called from NtUserCallOneParam with Routine ONEPARAM_ROUTINE_SHOWCURSOR
150 * User32 macro NtUserShowCursor */
151 int UserShowCursor(BOOL bShow
)
155 PUSER_MESSAGE_QUEUE MessageQueue
;
158 if (!(hdcScreen
= IntGetScreenDC()))
160 return -1; /* No mouse */
163 pti
= PsGetCurrentThreadWin32Thread();
164 MessageQueue
= pti
->MessageQueue
;
167 MessageQueue
->iCursorLevel
+= bShow
? 1 : -1;
168 pti
->iCursorLevel
+= bShow
? 1 : -1;
170 /* Check for trivial cases */
171 if ((bShow
&& MessageQueue
->iCursorLevel
!= 0) ||
172 (!bShow
&& MessageQueue
->iCursorLevel
!= -1))
174 /* Note: w don't update global info here because it is used only
175 internally to check if cursor is visible */
176 return MessageQueue
->iCursorLevel
;
179 /* Check if cursor is above window owned by this MessageQueue */
180 pWnd
= IntTopLevelWindowFromPoint(gpsi
->ptCursor
.x
, gpsi
->ptCursor
.y
);
181 if (pWnd
&& pWnd
->head
.pti
->MessageQueue
== MessageQueue
)
185 /* Show the pointer */
186 GreMovePointer(hdcScreen
, gpsi
->ptCursor
.x
, gpsi
->ptCursor
.y
);
187 TRACE("Showing pointer!\n");
191 /* Remove the pointer */
192 GreMovePointer(hdcScreen
, -1, -1);
193 TRACE("Removing pointer!\n");
196 /* Update global info */
197 IntGetSysCursorInfo()->ShowingCursor
= MessageQueue
->iCursorLevel
;
200 return MessageQueue
->iCursorLevel
;
204 UserGetKeyState(DWORD dwKey
)
208 PUSER_MESSAGE_QUEUE MessageQueue
;
210 pti
= PsGetCurrentThreadWin32Thread();
211 MessageQueue
= pti
->MessageQueue
;
215 if (IS_KEY_DOWN(MessageQueue
->afKeyState
, dwKey
))
216 dwRet
|= 0xFF80; // If down, windows returns 0xFF80.
217 if (IS_KEY_LOCKED(MessageQueue
->afKeyState
, dwKey
))
222 EngSetLastError(ERROR_INVALID_PARAMETER
);
227 /* change the input key state for a given key */
229 UpdateKeyState(PUSER_MESSAGE_QUEUE MessageQueue
, WORD wVk
, BOOL bIsDown
)
231 TRACE("UpdateKeyState wVk: %u, bIsDown: %d\n", wVk
, bIsDown
);
235 /* If it's first key down event, xor lock bit */
236 if (!IS_KEY_DOWN(MessageQueue
->afKeyState
, wVk
))
237 SET_KEY_LOCKED(MessageQueue
->afKeyState
, wVk
, !IS_KEY_LOCKED(MessageQueue
->afKeyState
, wVk
));
239 SET_KEY_DOWN(MessageQueue
->afKeyState
, wVk
, TRUE
);
240 MessageQueue
->afKeyRecentDown
[wVk
/ 8] |= (1 << (wVk
% 8));
243 SET_KEY_DOWN(MessageQueue
->afKeyState
, wVk
, FALSE
);
246 /* update the input key state for a keyboard message */
248 UpdateKeyStateFromMsg(PUSER_MESSAGE_QUEUE MessageQueue
, MSG
* msg
)
253 TRACE("UpdateKeyStateFromMsg message:%u\n", msg
->message
);
255 switch (msg
->message
)
261 UpdateKeyState(MessageQueue
, VK_LBUTTON
, down
);
267 UpdateKeyState(MessageQueue
, VK_MBUTTON
, down
);
273 UpdateKeyState(MessageQueue
, VK_RBUTTON
, down
);
279 if (msg
->wParam
== XBUTTON1
)
280 UpdateKeyState(MessageQueue
, VK_XBUTTON1
, down
);
281 else if (msg
->wParam
== XBUTTON2
)
282 UpdateKeyState(MessageQueue
, VK_XBUTTON2
, down
);
290 key
= (UCHAR
)msg
->wParam
;
291 UpdateKeyState(MessageQueue
, key
, down
);
296 down
= IS_KEY_DOWN(MessageQueue
->afKeyState
, VK_LCONTROL
) || IS_KEY_DOWN(MessageQueue
->afKeyState
, VK_RCONTROL
);
297 UpdateKeyState(MessageQueue
, VK_CONTROL
, down
);
301 down
= IS_KEY_DOWN(MessageQueue
->afKeyState
, VK_LMENU
) || IS_KEY_DOWN(MessageQueue
->afKeyState
, VK_RMENU
);
302 UpdateKeyState(MessageQueue
, VK_MENU
, down
);
306 down
= IS_KEY_DOWN(MessageQueue
->afKeyState
, VK_LSHIFT
) || IS_KEY_DOWN(MessageQueue
->afKeyState
, VK_RSHIFT
);
307 UpdateKeyState(MessageQueue
, VK_SHIFT
, down
);
315 Get down key states from the queue of prior processed input message key states.
317 This fixes the left button dragging on the desktop and release sticking outline issue.
318 USB Tablet pointer seems to stick the most and leaves the box outline displayed.
321 MsqGetDownKeyState(PUSER_MESSAGE_QUEUE MessageQueue
)
325 if (gspv
.bMouseBtnSwap
)
327 if (IS_KEY_DOWN(MessageQueue
->afKeyState
, VK_RBUTTON
)) ret
|= MK_LBUTTON
;
328 if (IS_KEY_DOWN(MessageQueue
->afKeyState
, VK_LBUTTON
)) ret
|= MK_RBUTTON
;
332 if (IS_KEY_DOWN(MessageQueue
->afKeyState
, VK_LBUTTON
)) ret
|= MK_LBUTTON
;
333 if (IS_KEY_DOWN(MessageQueue
->afKeyState
, VK_RBUTTON
)) ret
|= MK_RBUTTON
;
336 if (IS_KEY_DOWN(MessageQueue
->afKeyState
, VK_MBUTTON
)) ret
|= MK_MBUTTON
;
337 if (IS_KEY_DOWN(MessageQueue
->afKeyState
, VK_SHIFT
)) ret
|= MK_SHIFT
;
338 if (IS_KEY_DOWN(MessageQueue
->afKeyState
, VK_CONTROL
)) ret
|= MK_CONTROL
;
339 if (IS_KEY_DOWN(MessageQueue
->afKeyState
, VK_XBUTTON1
)) ret
|= MK_XBUTTON1
;
340 if (IS_KEY_DOWN(MessageQueue
->afKeyState
, VK_XBUTTON2
)) ret
|= MK_XBUTTON2
;
345 IntMsqSetWakeMask(DWORD WakeMask
)
347 PTHREADINFO Win32Thread
;
348 HANDLE MessageEventHandle
;
349 DWORD dwFlags
= HIWORD(WakeMask
);
351 Win32Thread
= PsGetCurrentThreadWin32Thread();
352 if (Win32Thread
== NULL
|| Win32Thread
->MessageQueue
== NULL
)
355 // Win32Thread->pEventQueueServer; IntMsqSetWakeMask returns Win32Thread->hEventQueueClient
356 MessageEventHandle
= Win32Thread
->hEventQueueClient
;
358 if (Win32Thread
->pcti
)
360 if ( (Win32Thread
->pcti
->fsChangeBits
& LOWORD(WakeMask
)) ||
361 ( (dwFlags
& MWMO_INPUTAVAILABLE
) && (Win32Thread
->pcti
->fsWakeBits
& LOWORD(WakeMask
)) ) )
363 ERR("Chg 0x%x Wake 0x%x Mask 0x%x\n",Win32Thread
->pcti
->fsChangeBits
, Win32Thread
->pcti
->fsWakeBits
, WakeMask
);
364 KeSetEvent(Win32Thread
->pEventQueueServer
, IO_NO_INCREMENT
, FALSE
); // Wake it up!
365 return MessageEventHandle
;
371 return MessageEventHandle
;
375 IntMsqClearWakeMask(VOID
)
377 PTHREADINFO Win32Thread
;
379 Win32Thread
= PsGetCurrentThreadWin32Thread();
380 if (Win32Thread
== NULL
|| Win32Thread
->MessageQueue
== NULL
)
382 // Very hacky, but that is what they do.
383 Win32Thread
->pcti
->fsWakeBits
= 0;
391 Due to the uncertainty of knowing what was set in our multilevel message queue,
392 and even if the bits are all cleared. The same as cTimers/cPaintsReady.
393 I think this is the best solution... (jt) */
395 MsqWakeQueue(PTHREADINFO pti
, DWORD MessageBits
, BOOL KeyEvent
)
397 PUSER_MESSAGE_QUEUE Queue
;
399 Queue
= pti
->MessageQueue
;
401 if (Queue
->QF_flags
& QF_INDESTROY
)
403 ERR("This Message Queue is in Destroy!\n");
405 pti
->pcti
->fsWakeBits
|= MessageBits
;
406 pti
->pcti
->fsChangeBits
|= MessageBits
;
408 // Start bit accounting to help clear the main set of bits.
409 if (MessageBits
& QS_KEY
)
411 pti
->nCntsQBits
[QSRosKey
]++;
413 if (MessageBits
& QS_MOUSE
)
415 if (MessageBits
& QS_MOUSEMOVE
) pti
->nCntsQBits
[QSRosMouseMove
]++;
416 if (MessageBits
& QS_MOUSEBUTTON
) pti
->nCntsQBits
[QSRosMouseButton
]++;
418 if (MessageBits
& QS_POSTMESSAGE
) pti
->nCntsQBits
[QSRosPostMessage
]++;
419 if (MessageBits
& QS_SENDMESSAGE
) pti
->nCntsQBits
[QSRosSendMessage
]++;
420 if (MessageBits
& QS_HOTKEY
) pti
->nCntsQBits
[QSRosHotKey
]++;
421 if (MessageBits
& QS_EVENT
) pti
->nCntsQBits
[QSRosEvent
]++;
424 KeSetEvent(pti
->pEventQueueServer
, IO_NO_INCREMENT
, FALSE
);
428 ClearMsgBitsMask(PTHREADINFO pti
, UINT MessageBits
)
432 if (MessageBits
& QS_KEY
)
434 if (--pti
->nCntsQBits
[QSRosKey
] == 0) ClrMask
|= QS_KEY
;
436 if (MessageBits
& QS_MOUSEMOVE
)
437 { // Account for tracking mouse moves..
438 if (pti
->nCntsQBits
[QSRosMouseMove
])
440 pti
->nCntsQBits
[QSRosMouseMove
] = 0; // Throttle down count. Up to > 3:1 entries are ignored.
441 ClrMask
|= QS_MOUSEMOVE
;
444 if (MessageBits
& QS_MOUSEBUTTON
)
446 if (--pti
->nCntsQBits
[QSRosMouseButton
] == 0) ClrMask
|= QS_MOUSEBUTTON
;
448 if (MessageBits
& QS_POSTMESSAGE
)
450 if (--pti
->nCntsQBits
[QSRosPostMessage
] == 0) ClrMask
|= QS_POSTMESSAGE
;
452 if (MessageBits
& QS_TIMER
) // ReactOS hard coded.
453 { // Handle timer bits here.
454 if ( pti
->cTimersReady
)
456 if (--pti
->cTimersReady
== 0) ClrMask
|= QS_TIMER
;
459 if (MessageBits
& QS_PAINT
) // ReactOS hard coded.
460 { // Handle paint bits here.
461 if ( pti
->cPaintsReady
)
463 if (--pti
->cPaintsReady
== 0) ClrMask
|= QS_PAINT
;
466 if (MessageBits
& QS_SENDMESSAGE
)
468 if (--pti
->nCntsQBits
[QSRosSendMessage
] == 0) ClrMask
|= QS_SENDMESSAGE
;
470 if (MessageBits
& QS_HOTKEY
)
472 if (--pti
->nCntsQBits
[QSRosHotKey
] == 0) ClrMask
|= QS_HOTKEY
;
474 if (MessageBits
& QS_EVENT
)
476 if (--pti
->nCntsQBits
[QSRosEvent
] == 0) ClrMask
|= QS_EVENT
;
479 pti
->pcti
->fsWakeBits
&= ~ClrMask
;
480 pti
->pcti
->fsChangeBits
&= ~ClrMask
;
484 MsqIncPaintCountQueue(PTHREADINFO pti
)
487 MsqWakeQueue(pti
, QS_PAINT
, TRUE
);
491 MsqDecPaintCountQueue(PTHREADINFO pti
)
493 ClearMsgBitsMask(pti
, QS_PAINT
);
497 Post the move or update the message still pending to be processed.
498 Do not overload the queue with mouse move messages.
501 MsqPostMouseMove(PTHREADINFO pti
, MSG
* Msg
, LONG_PTR ExtraInfo
)
503 PUSER_MESSAGE Message
;
504 PLIST_ENTRY ListHead
;
505 PUSER_MESSAGE_QUEUE MessageQueue
= pti
->MessageQueue
;
507 ListHead
= &MessageQueue
->HardwareMessagesListHead
;
509 // Do nothing if empty.
510 if (!IsListEmpty(ListHead
->Flink
))
512 // Look at the end of the list,
513 Message
= CONTAINING_RECORD(ListHead
->Blink
, USER_MESSAGE
, ListEntry
);
515 // If the mouse move message is existing on the list,
516 if (Message
->Msg
.message
== WM_MOUSEMOVE
)
518 // Overwrite the message with updated data!
521 MsqWakeQueue(pti
, QS_MOUSEMOVE
, TRUE
);
526 MsqPostMessage(pti
, Msg
, TRUE
, QS_MOUSEMOVE
, 0, ExtraInfo
);
530 Bring together the mouse move message.
531 Named "Coalesce" from Amine email ;^) (jt).
534 IntCoalesceMouseMove(PTHREADINFO pti
)
537 LARGE_INTEGER LargeTickCount
;
539 // Force time stamp to update, keeping message time in sync.
540 if (gdwMouseMoveTimeStamp
== 0)
542 KeQueryTickCount(&LargeTickCount
);
543 gdwMouseMoveTimeStamp
= MsqCalculateMessageTime(&LargeTickCount
);
546 // Build mouse move message.
548 Msg
.message
= WM_MOUSEMOVE
;
550 Msg
.lParam
= MAKELONG(gpsi
->ptCursor
.x
, gpsi
->ptCursor
.y
);
551 Msg
.time
= gdwMouseMoveTimeStamp
;
552 Msg
.pt
= gpsi
->ptCursor
;
555 MsqPostMouseMove(pti
, &Msg
, gdwMouseMoveExtraInfo
);
557 // Zero the time stamp.
558 gdwMouseMoveTimeStamp
= 0;
560 // Clear flag since the move was posted.
561 pti
->MessageQueue
->QF_flags
&= ~QF_MOUSEMOVED
;
565 co_MsqInsertMouseMessage(MSG
* Msg
, DWORD flags
, ULONG_PTR dwExtraInfo
, BOOL Hook
)
567 LARGE_INTEGER LargeTickCount
;
568 MSLLHOOKSTRUCT MouseHookData
;
570 PWND pwnd
, pwndDesktop
;
573 PUSER_MESSAGE_QUEUE MessageQueue
;
574 PSYSTEM_CURSORINFO CurInfo
;
576 KeQueryTickCount(&LargeTickCount
);
577 Msg
->time
= MsqCalculateMessageTime(&LargeTickCount
);
579 MouseHookData
.pt
.x
= LOWORD(Msg
->lParam
);
580 MouseHookData
.pt
.y
= HIWORD(Msg
->lParam
);
581 switch (Msg
->message
)
584 MouseHookData
.mouseData
= MAKELONG(0, GET_WHEEL_DELTA_WPARAM(Msg
->wParam
));
588 case WM_XBUTTONDBLCLK
:
589 case WM_NCXBUTTONDOWN
:
591 case WM_NCXBUTTONDBLCLK
:
592 MouseHookData
.mouseData
= MAKELONG(0, HIWORD(Msg
->wParam
));
595 MouseHookData
.mouseData
= 0;
599 MouseHookData
.flags
= flags
; // LLMHF_INJECTED
600 MouseHookData
.time
= Msg
->time
;
601 MouseHookData
.dwExtraInfo
= dwExtraInfo
;
603 /* If the hook procedure returned non zero, dont send the message */
606 if (co_HOOK_CallHooks(WH_MOUSE_LL
, HC_ACTION
, Msg
->message
, (LPARAM
) &MouseHookData
))
610 /* Get the desktop window */
611 pwndDesktop
= UserGetDesktopWindow();
612 if (!pwndDesktop
) return;
613 // pDesk = pwndDesktop->head.rpdesk;
615 /* Check if the mouse is captured */
616 Msg
->hwnd
= IntGetCaptureWindow();
617 if (Msg
->hwnd
!= NULL
)
619 pwnd
= UserGetWindowObject(Msg
->hwnd
);
623 pwnd
= IntTopLevelWindowFromPoint(Msg
->pt
.x
, Msg
->pt
.y
);
624 if (pwnd
) Msg
->hwnd
= pwnd
->head
.h
;
627 hdcScreen
= IntGetScreenDC();
628 CurInfo
= IntGetSysCursorInfo();
630 /* Check if we found a window */
631 if (Msg
->hwnd
!= NULL
&& pwnd
!= NULL
)
633 pti
= pwnd
->head
.pti
;
634 MessageQueue
= pti
->MessageQueue
;
636 if (MessageQueue
->QF_flags
& QF_INDESTROY
)
638 ERR("Mouse is over a Window with a Dead Message Queue!\n");
642 // Check to see if this is attached.
643 if ( pti
!= MessageQueue
->ptiMouse
&&
644 MessageQueue
->cThreads
> 1 )
646 // Set the send pti to the message queue mouse pti.
647 pti
= MessageQueue
->ptiMouse
;
650 if (Msg
->message
== WM_MOUSEMOVE
)
652 /* Check if cursor should be visible */
654 MessageQueue
->CursorObject
&&
655 MessageQueue
->iCursorLevel
>= 0)
657 /* Check if shape has changed */
658 if(CurInfo
->CurrentCursorObject
!= MessageQueue
->CursorObject
)
660 /* Call GDI to set the new screen cursor */
661 GreSetPointerShape(hdcScreen
,
662 MessageQueue
->CursorObject
->hbmAlpha
?
663 NULL
: MessageQueue
->CursorObject
->hbmMask
,
664 MessageQueue
->CursorObject
->hbmAlpha
?
665 MessageQueue
->CursorObject
->hbmAlpha
: MessageQueue
->CursorObject
->hbmColor
,
666 MessageQueue
->CursorObject
->xHotspot
,
667 MessageQueue
->CursorObject
->yHotspot
,
670 MessageQueue
->CursorObject
->hbmAlpha
? SPS_ALPHA
: 0);
673 GreMovePointer(hdcScreen
, Msg
->pt
.x
, Msg
->pt
.y
);
675 /* Check if we have to hide cursor */
676 else if (CurInfo
->ShowingCursor
>= 0)
677 GreMovePointer(hdcScreen
, -1, -1);
679 /* Update global cursor info */
680 CurInfo
->ShowingCursor
= MessageQueue
->iCursorLevel
;
681 CurInfo
->CurrentCursorObject
= MessageQueue
->CursorObject
;
682 gpqCursor
= MessageQueue
;
684 /* Mouse move is a special case */
685 MessageQueue
->QF_flags
|= QF_MOUSEMOVED
;
686 gdwMouseMoveExtraInfo
= dwExtraInfo
;
687 gdwMouseMoveTimeStamp
= Msg
->time
;
688 MsqWakeQueue(pti
, QS_MOUSEMOVE
, TRUE
);
692 if (!IntGetCaptureWindow())
694 // ERR("ptiLastInput is set\n");
695 // ptiLastInput = pti; // Once this is set during Reboot or Shutdown, this prevents the exit window having foreground.
696 // Find all the Move Mouse calls and fix mouse set active focus issues......
699 // Post mouse move before posting mouse buttons, keep it in sync.
700 if (pti
->MessageQueue
->QF_flags
& QF_MOUSEMOVED
)
702 IntCoalesceMouseMove(pti
);
705 TRACE("Posting mouse message to hwnd=%p!\n", UserHMGetHandle(pwnd
));
706 MsqPostMessage(pti
, Msg
, TRUE
, QS_MOUSEBUTTON
, 0, dwExtraInfo
);
711 /* always show cursor on background; FIXME: set default pointer */
712 GreMovePointer(hdcScreen
, Msg
->pt
.x
, Msg
->pt
.y
);
713 CurInfo
->ShowingCursor
= 0;
717 PUSER_MESSAGE FASTCALL
718 MsqCreateMessage(LPMSG Msg
)
720 PUSER_MESSAGE Message
;
722 Message
= ExAllocateFromPagedLookasideList(pgMessageLookasideList
);
728 RtlZeroMemory(Message
, sizeof(*Message
));
729 RtlMoveMemory(&Message
->Msg
, Msg
, sizeof(MSG
));
735 MsqDestroyMessage(PUSER_MESSAGE Message
)
737 if (Message
->pti
== NULL
)
739 ERR("Double Free Message\n");
743 ExFreeToPagedLookasideList(pgMessageLookasideList
, Message
);
747 co_MsqDispatchOneSentMessage(
748 _In_ PTHREADINFO pti
)
750 PUSER_SENT_MESSAGE SaveMsg
, Message
;
755 ASSERT(pti
== PsGetCurrentThreadWin32Thread());
757 if (IsListEmpty(&pti
->SentMessagesListHead
))
762 /* remove it from the list of pending messages */
763 Entry
= RemoveHeadList(&pti
->SentMessagesListHead
);
764 Message
= CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
);
766 SaveMsg
= pti
->pusmCurrent
;
767 pti
->pusmCurrent
= Message
;
769 // Processing a message sent to it from another thread.
770 if ( ( Message
->ptiSender
&& pti
!= Message
->ptiSender
) ||
771 ( Message
->ptiCallBackSender
&& pti
!= Message
->ptiCallBackSender
))
772 { // most likely, but, to be sure.
773 pti
->pcti
->CTI_flags
|= CTI_INSENDMESSAGE
; // Let the user know...
776 /* insert it to the list of messages that are currently dispatched by this
778 InsertTailList(&pti
->LocalDispatchingMessagesHead
, &Message
->ListEntry
);
780 ClearMsgBitsMask(pti
, Message
->QS_Flags
);
782 if (Message
->HookMessage
== MSQ_ISHOOK
)
783 { // Direct Hook Call processor
784 Result
= co_CallHook( Message
->Msg
.message
, // HookId
785 (INT
)(INT_PTR
)Message
->Msg
.hwnd
, // Code
787 Message
->Msg
.lParam
);
789 else if(Message
->HookMessage
== MSQ_INJECTMODULE
)
791 Result
= IntLoadHookModule(Message
->Msg
.message
,
792 (HHOOK
)Message
->Msg
.lParam
,
793 Message
->Msg
.wParam
);
795 else if ((Message
->CompletionCallback
) &&
796 (Message
->ptiCallBackSender
== pti
))
797 { /* Call the callback routine */
798 if (Message
->QS_Flags
& QS_SMRESULT
)
800 co_IntCallSentMessageCallback(Message
->CompletionCallback
,
802 Message
->Msg
.message
,
803 Message
->CompletionCallbackContext
,
805 /* Set callback to NULL to prevent reentry */
806 Message
->CompletionCallback
= NULL
;
810 /* The message has not been processed yet, reinsert it. */
811 RemoveEntryList(&Message
->ListEntry
);
812 InsertTailList(&Message
->ptiCallBackSender
->SentMessagesListHead
, &Message
->ListEntry
);
813 TRACE("Callback Message not processed yet. Requeuing the message\n");
819 { /* Call the window procedure. */
820 Result
= co_IntSendMessage( Message
->Msg
.hwnd
,
821 Message
->Msg
.message
,
823 Message
->Msg
.lParam
);
826 /* remove the message from the local dispatching list, because it doesn't need
827 to be cleaned up on thread termination anymore */
828 RemoveEntryList(&Message
->ListEntry
);
830 /* If the message is a callback, insert it in the callback senders MessageQueue */
831 if (Message
->CompletionCallback
)
833 if (Message
->ptiCallBackSender
)
835 Message
->lResult
= Result
;
836 Message
->QS_Flags
|= QS_SMRESULT
;
838 /* insert it in the callers message queue */
839 InsertTailList(&Message
->ptiCallBackSender
->SentMessagesListHead
, &Message
->ListEntry
);
840 MsqWakeQueue(Message
->ptiCallBackSender
, QS_SENDMESSAGE
, TRUE
);
846 /* remove the message from the dispatching list if needed, so lock the sender's message queue */
847 if (Message
->ptiSender
&& !(Message
->ptiSender
->TIF_flags
& TIF_INCLEANUP
))
849 if (!IsListEmpty(&Message
->DispatchingListEntry
))
851 /* only remove it from the dispatching list if not already removed by a timeout */
852 RemoveEntryList(&Message
->DispatchingListEntry
);
853 InitializeListHead(&Message
->DispatchingListEntry
);
857 /* still keep the sender's message queue locked, so the sender can't exit the
858 MsqSendMessage() function (if timed out) */
859 if (Message
->QS_Flags
& QS_SMRESULT
)
861 Result
= Message
->lResult
;
864 /* Let the sender know the result. */
865 if (Message
->Result
!= NULL
)
867 *Message
->Result
= Result
;
870 if (Message
->HasPackedLParam
)
872 if (Message
->Msg
.lParam
)
873 ExFreePool((PVOID
)Message
->Msg
.lParam
);
876 /* Notify the sender. */
877 if (Message
->CompletionEvent
!= NULL
)
879 KeSetEvent(Message
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
882 /* free the message */
883 ExFreePoolWithTag(Message
, TAG_USRMSG
);
886 /* do not hangup on the user if this is reentering */
887 if (!SaveMsg
) pti
->pcti
->CTI_flags
&= ~CTI_INSENDMESSAGE
;
888 pti
->pusmCurrent
= SaveMsg
;
894 MsqRemoveWindowMessagesFromQueue(PWND Window
)
897 PUSER_SENT_MESSAGE SentMessage
;
898 PUSER_MESSAGE PostedMessage
;
899 PLIST_ENTRY CurrentEntry
, ListHead
;
903 pti
= Window
->head
.pti
;
905 /* remove the posted messages for this window */
906 CurrentEntry
= pti
->PostedMessagesListHead
.Flink
;
907 ListHead
= &pti
->PostedMessagesListHead
;
908 while (CurrentEntry
!= ListHead
)
910 PostedMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
912 if (PostedMessage
->Msg
.hwnd
== Window
->head
.h
)
914 if (PostedMessage
->Msg
.message
== WM_QUIT
&& pti
->QuitPosted
== 0)
917 pti
->exitCode
= PostedMessage
->Msg
.wParam
;
919 RemoveEntryList(&PostedMessage
->ListEntry
);
920 ClearMsgBitsMask(pti
, PostedMessage
->QS_Flags
);
921 MsqDestroyMessage(PostedMessage
);
922 CurrentEntry
= pti
->PostedMessagesListHead
.Flink
;
926 CurrentEntry
= CurrentEntry
->Flink
;
930 /* remove the sent messages for this window */
931 CurrentEntry
= pti
->SentMessagesListHead
.Flink
;
932 ListHead
= &pti
->SentMessagesListHead
;
933 while (CurrentEntry
!= ListHead
)
935 SentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
, ListEntry
);
937 if(SentMessage
->Msg
.hwnd
== Window
->head
.h
)
939 TRACE("Notify the sender and remove a message from the queue that had not been dispatched\n");
941 RemoveEntryList(&SentMessage
->ListEntry
);
942 ClearMsgBitsMask(pti
, SentMessage
->QS_Flags
);
944 /* Only if the message has a sender was the queue referenced */
945 if ((SentMessage
->ptiSender
) &&
946 (!IsListEmpty(&SentMessage
->DispatchingListEntry
)))
948 RemoveEntryList(&SentMessage
->DispatchingListEntry
);
949 InitializeListHead(&SentMessage
->DispatchingListEntry
);
952 /* wake the sender's thread */
953 if (SentMessage
->CompletionEvent
!= NULL
)
955 KeSetEvent(SentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
958 if (SentMessage
->HasPackedLParam
)
960 if (SentMessage
->Msg
.lParam
)
961 ExFreePool((PVOID
)SentMessage
->Msg
.lParam
);
964 /* free the message */
965 ExFreePoolWithTag(SentMessage
, TAG_USRMSG
);
967 CurrentEntry
= pti
->SentMessagesListHead
.Flink
;
971 CurrentEntry
= CurrentEntry
->Flink
;
977 co_MsqSendMessageAsync(PTHREADINFO ptiReceiver
,
982 SENDASYNCPROC CompletionCallback
,
983 ULONG_PTR CompletionCallbackContext
,
984 BOOL HasPackedLParam
,
988 PTHREADINFO ptiSender
;
989 PUSER_SENT_MESSAGE Message
;
991 if(!(Message
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(USER_SENT_MESSAGE
), TAG_USRMSG
)))
993 ERR("MsqSendMessage(): Not enough memory to allocate a message");
997 ptiSender
= PsGetCurrentThreadWin32Thread();
999 Message
->Msg
.hwnd
= hwnd
;
1000 Message
->Msg
.message
= Msg
;
1001 Message
->Msg
.wParam
= wParam
;
1002 Message
->Msg
.lParam
= lParam
;
1003 Message
->CompletionEvent
= NULL
;
1004 Message
->Result
= 0;
1005 Message
->lResult
= 0;
1006 Message
->ptiReceiver
= ptiReceiver
;
1007 Message
->ptiSender
= NULL
;
1008 Message
->ptiCallBackSender
= ptiSender
;
1009 InitializeListHead(&Message
->DispatchingListEntry
);
1010 Message
->CompletionCallback
= CompletionCallback
;
1011 Message
->CompletionCallbackContext
= CompletionCallbackContext
;
1012 Message
->HookMessage
= HookMessage
;
1013 Message
->HasPackedLParam
= HasPackedLParam
;
1014 Message
->QS_Flags
= QS_SENDMESSAGE
;
1016 InsertTailList(&ptiReceiver
->SentMessagesListHead
, &Message
->ListEntry
);
1017 MsqWakeQueue(ptiReceiver
, QS_SENDMESSAGE
, TRUE
);
1023 co_MsqSendMessage(PTHREADINFO ptirec
,
1034 PUSER_SENT_MESSAGE Message
;
1035 KEVENT CompletionEvent
;
1036 NTSTATUS WaitStatus
;
1037 LARGE_INTEGER Timeout
;
1040 LRESULT Result
= 0; //// Result could be trashed. ////
1042 pti
= PsGetCurrentThreadWin32Thread();
1043 ASSERT(pti
!= ptirec
);
1044 ASSERT(ptirec
->pcti
); // Send must have a client side to receive it!!!!
1046 /* Don't send from or to a dying thread */
1047 if (pti
->TIF_flags
& TIF_INCLEANUP
|| ptirec
->TIF_flags
& TIF_INCLEANUP
)
1049 // Unless we are dying and need to tell our parents.
1050 if (pti
->TIF_flags
& TIF_INCLEANUP
&& !(ptirec
->TIF_flags
& TIF_INCLEANUP
))
1052 // Parent notify is the big one. Fire and forget!
1053 TRACE("Send message from dying thread %u\n", Msg
);
1054 co_MsqSendMessageAsync(ptirec
, Wnd
, Msg
, wParam
, lParam
, NULL
, 0, FALSE
, HookMessage
);
1056 if (uResult
) *uResult
= -1;
1057 TRACE("MsqSM: Msg %u Current pti %lu or Rec pti %lu\n", Msg
, pti
->TIF_flags
& TIF_INCLEANUP
, ptirec
->TIF_flags
& TIF_INCLEANUP
);
1058 return STATUS_UNSUCCESSFUL
;
1061 // Should we do the same for No Wait?
1062 if ( HookMessage
== MSQ_NORMAL
)
1064 pWnd
= ValidateHwndNoErr(Wnd
);
1066 // These can not cross International Border lines!
1067 if ( pti
->ppi
!= ptirec
->ppi
&& pWnd
)
1071 // Handle the special case when working with password transfers across bordering processes.
1073 case EM_SETPASSWORDCHAR
:
1075 // Look for edit controls setup for passwords.
1076 if ( gpsi
->atomSysClass
[ICLS_EDIT
] == pWnd
->pcls
->atomClassName
&& // Use atomNVClassName.
1077 pWnd
->style
& ES_PASSWORD
)
1079 if (uResult
) *uResult
= -1;
1080 ERR("Running across the border without a passport!\n");
1081 EngSetLastError(ERROR_ACCESS_DENIED
);
1082 return STATUS_UNSUCCESSFUL
;
1086 if (uResult
) *uResult
= -1;
1087 ERR("Running across the border without a passport!\n");
1088 return STATUS_UNSUCCESSFUL
;
1092 // These can not cross State lines!
1093 if ( Msg
== WM_CREATE
|| Msg
== WM_NCCREATE
)
1095 if (uResult
) *uResult
= -1;
1096 ERR("Can not tell the other State we have Create!\n");
1097 return STATUS_UNSUCCESSFUL
;
1101 if(!(Message
= ExAllocatePoolWithTag(PagedPool
, sizeof(USER_SENT_MESSAGE
), TAG_USRMSG
)))
1103 ERR("MsqSendMessage(): Not enough memory to allocate a message\n");
1104 return STATUS_INSUFFICIENT_RESOURCES
;
1107 KeInitializeEvent(&CompletionEvent
, NotificationEvent
, FALSE
);
1109 Timeout
.QuadPart
= Int32x32To64(-10000,uTimeout
); // Pass SMTO test with a TO of 0x80000000.
1110 TRACE("Timeout val %lld\n",Timeout
.QuadPart
)
1112 /* FIXME: Increase reference counter of sender's message queue here */
1114 Message
->Msg
.hwnd
= Wnd
;
1115 Message
->Msg
.message
= Msg
;
1116 Message
->Msg
.wParam
= wParam
;
1117 Message
->Msg
.lParam
= lParam
;
1118 Message
->CompletionEvent
= &CompletionEvent
;
1119 Message
->Result
= &Result
;
1120 Message
->lResult
= 0;
1121 Message
->QS_Flags
= 0;
1122 Message
->ptiReceiver
= ptirec
;
1123 Message
->ptiSender
= pti
;
1124 Message
->ptiCallBackSender
= NULL
;
1125 Message
->CompletionCallback
= NULL
;
1126 Message
->CompletionCallbackContext
= 0;
1127 Message
->HookMessage
= HookMessage
;
1128 Message
->HasPackedLParam
= FALSE
;
1130 /* Add it to the list of pending messages */
1131 InsertTailList(&pti
->DispatchingMessagesHead
, &Message
->DispatchingListEntry
);
1133 /* Queue it in the destination's message queue */
1134 InsertTailList(&ptirec
->SentMessagesListHead
, &Message
->ListEntry
);
1136 Message
->QS_Flags
= QS_SENDMESSAGE
;
1137 MsqWakeQueue(ptirec
, QS_SENDMESSAGE
, TRUE
);
1139 /* We can't access the Message anymore since it could have already been deleted! */
1143 PVOID WaitObjects
[2];
1145 WaitObjects
[0] = &CompletionEvent
; // Wait 0
1146 WaitObjects
[1] = ptirec
->pEThread
; // Wait 1
1150 WaitStatus
= KeWaitForMultipleObjects(2, WaitObjects
, WaitAny
, UserRequest
,
1151 UserMode
, FALSE
, (uTimeout
? &Timeout
: NULL
), NULL
);
1155 if (WaitStatus
== STATUS_TIMEOUT
)
1157 /* Look up if the message has not yet dispatched, if so
1158 make sure it can't pass a result and it must not set the completion event anymore */
1159 Entry
= ptirec
->SentMessagesListHead
.Flink
;
1160 while (Entry
!= &ptirec
->SentMessagesListHead
)
1162 if (CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
) == Message
)
1164 /* We can access Message here, it's secure because the message queue is locked
1165 and the message is still hasn't been dispatched */
1166 Message
->CompletionEvent
= NULL
;
1167 Message
->Result
= NULL
;
1168 RemoveEntryList(&Message
->ListEntry
);
1169 RemoveEntryList(&Message
->DispatchingListEntry
);
1170 ClearMsgBitsMask(ptirec
, Message
->QS_Flags
);
1171 ExFreePoolWithTag(Message
, TAG_USRMSG
);
1174 Entry
= Entry
->Flink
;
1177 TRACE("MsqSendMessage (blocked) timed out 1 Status %lx\n", WaitStatus
);
1179 // Receiving thread passed on and left us hanging with issues still pending.
1180 else if (WaitStatus
== STATUS_WAIT_1
)
1182 ERR("Bk Receiving Thread woken up dead!\n");
1183 Entry
= pti
->DispatchingMessagesHead
.Flink
;
1184 while (Entry
!= &pti
->DispatchingMessagesHead
)
1186 if (CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, DispatchingListEntry
) == Message
)
1188 Message
->CompletionEvent
= NULL
;
1189 Message
->Result
= NULL
;
1190 RemoveEntryList(&Message
->DispatchingListEntry
);
1191 InitializeListHead(&Message
->DispatchingListEntry
);
1194 Entry
= Entry
->Flink
;
1197 while (co_MsqDispatchOneSentMessage(pti
))
1202 PVOID WaitObjects
[3];
1204 WaitObjects
[0] = &CompletionEvent
; // Wait 0
1205 WaitObjects
[1] = pti
->pEventQueueServer
; // Wait 1
1206 WaitObjects
[2] = ptirec
->pEThread
; // Wait 2
1212 WaitStatus
= KeWaitForMultipleObjects(3, WaitObjects
, WaitAny
, UserRequest
,
1213 UserMode
, FALSE
, (uTimeout
? &Timeout
: NULL
), NULL
);
1217 if (WaitStatus
== STATUS_TIMEOUT
)
1219 /* Look up if the message has not yet been dispatched, if so
1220 make sure it can't pass a result and it must not set the completion event anymore */
1221 Entry
= ptirec
->SentMessagesListHead
.Flink
;
1222 while (Entry
!= &ptirec
->SentMessagesListHead
)
1224 if (CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
) == Message
)
1226 /* We can access Message here, it's secure because the message queue is locked
1227 and the message is still hasn't been dispatched */
1228 Message
->CompletionEvent
= NULL
;
1229 Message
->Result
= NULL
;
1230 RemoveEntryList(&Message
->ListEntry
);
1231 RemoveEntryList(&Message
->DispatchingListEntry
);
1232 InitializeListHead(&Message
->DispatchingListEntry
);
1233 ClearMsgBitsMask(ptirec
, Message
->QS_Flags
);
1234 ExFreePoolWithTag(Message
, TAG_USRMSG
);
1237 Entry
= Entry
->Flink
;
1240 TRACE("MsqSendMessage timed out 2 Status %lx\n", WaitStatus
);
1244 // Receiving thread passed on and left us hanging with issues still pending.
1245 else if (WaitStatus
== STATUS_WAIT_2
)
1247 ERR("NB Receiving Thread woken up dead!\n");
1248 Entry
= pti
->DispatchingMessagesHead
.Flink
;
1249 while (Entry
!= &pti
->DispatchingMessagesHead
)
1251 if (CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, DispatchingListEntry
) == Message
)
1253 Message
->CompletionEvent
= NULL
;
1254 Message
->Result
= NULL
;
1255 RemoveEntryList(&Message
->DispatchingListEntry
);
1256 InitializeListHead(&Message
->DispatchingListEntry
);
1259 Entry
= Entry
->Flink
;
1264 if (WaitStatus
== STATUS_USER_APC
) break;
1266 while (co_MsqDispatchOneSentMessage(pti
))
1268 } while (WaitStatus
== STATUS_WAIT_1
);
1271 if (WaitStatus
== STATUS_USER_APC
)
1273 // The current thread is dying!
1274 TRACE("User APC\n");
1275 co_IntDeliverUserAPC();
1276 ERR("User APC Returned\n"); // Should not see this message.
1279 if (WaitStatus
!= STATUS_TIMEOUT
)
1283 *uResult
= (STATUS_WAIT_0
== WaitStatus
? Result
: -1);
1291 MsqPostMessage(PTHREADINFO pti
,
1293 BOOLEAN HardwareMessage
,
1298 PUSER_MESSAGE Message
;
1299 PUSER_MESSAGE_QUEUE MessageQueue
;
1301 if ( pti
->TIF_flags
& TIF_INCLEANUP
|| pti
->MessageQueue
->QF_flags
& QF_INDESTROY
)
1303 ERR("Post Msg; Thread or Q is Dead!\n");
1307 if(!(Message
= MsqCreateMessage(Msg
)))
1312 MessageQueue
= pti
->MessageQueue
;
1314 if (!HardwareMessage
)
1316 InsertTailList(&pti
->PostedMessagesListHead
, &Message
->ListEntry
);
1320 InsertTailList(&MessageQueue
->HardwareMessagesListHead
, &Message
->ListEntry
);
1323 if (Msg
->message
== WM_HOTKEY
) MessageBits
|= QS_HOTKEY
; // Justin Case, just set it.
1324 Message
->dwQEvent
= dwQEvent
;
1325 Message
->ExtraInfo
= ExtraInfo
;
1326 Message
->QS_Flags
= MessageBits
;
1328 MsqWakeQueue(pti
, MessageBits
, TRUE
);
1332 MsqPostQuitMessage(PTHREADINFO pti
, ULONG ExitCode
)
1334 pti
->QuitPosted
= TRUE
;
1335 pti
->exitCode
= ExitCode
;
1336 MsqWakeQueue(pti
, QS_POSTMESSAGE
|QS_ALLPOSTMESSAGE
, TRUE
);
1339 /***********************************************************************
1340 * MsqSendParentNotify
1342 * Send a WM_PARENTNOTIFY to all ancestors of the given window, unless
1343 * the window has the WS_EX_NOPARENTNOTIFY style.
1345 static void MsqSendParentNotify( PWND pwnd
, WORD event
, WORD idChild
, POINT pt
)
1347 PWND pwndDesktop
= UserGetDesktopWindow();
1349 /* pt has to be in the client coordinates of the parent window */
1350 pt
.x
+= pwndDesktop
->rcClient
.left
- pwnd
->rcClient
.left
;
1351 pt
.y
+= pwndDesktop
->rcClient
.top
- pwnd
->rcClient
.top
;
1357 if (!(pwnd
->style
& WS_CHILD
)) break;
1358 if (pwnd
->ExStyle
& WS_EX_NOPARENTNOTIFY
) break;
1359 if (!(pwndParent
= IntGetParent(pwnd
))) break;
1360 if (pwndParent
== pwndDesktop
) break;
1361 pt
.x
+= pwnd
->rcClient
.left
- pwndParent
->rcClient
.left
;
1362 pt
.y
+= pwnd
->rcClient
.top
- pwndParent
->rcClient
.top
;
1365 co_IntSendMessage( UserHMGetHandle(pwnd
), WM_PARENTNOTIFY
,
1366 MAKEWPARAM( event
, idChild
), MAKELPARAM( pt
.x
, pt
.y
) );
1372 IntTrackMouseMove(PWND pwndTrack
, PDESKTOP pDesk
, PMSG msg
, USHORT hittest
)
1374 // PWND pwndTrack = IntChildrenWindowFromPoint(pwndMsg, msg->pt.x, msg->pt.y);
1375 // hittest = (USHORT)GetNCHitEx(pwndTrack, msg->pt); /// @todo WTF is this???
1377 if ( pDesk
->spwndTrack
!= pwndTrack
|| // Change with tracking window or
1378 msg
->message
!= WM_MOUSEMOVE
|| // Mouse click changes or
1379 pDesk
->htEx
!= hittest
) // Change in current hit test states.
1381 TRACE("ITMM: Track Mouse Move!\n");
1383 /* Handle only the changing window track and mouse move across a border. */
1384 if ( pDesk
->spwndTrack
!= pwndTrack
||
1385 (pDesk
->htEx
== HTCLIENT
) ^ (hittest
== HTCLIENT
) )
1387 TRACE("ITMM: Another Wnd %d or Across Border %d\n",
1388 pDesk
->spwndTrack
!= pwndTrack
,(pDesk
->htEx
== HTCLIENT
) ^ (hittest
== HTCLIENT
));
1390 if ( pDesk
->dwDTFlags
& DF_TME_LEAVE
)
1391 UserPostMessage( UserHMGetHandle(pDesk
->spwndTrack
),
1392 (pDesk
->htEx
!= HTCLIENT
) ? WM_NCMOUSELEAVE
: WM_MOUSELEAVE
,
1395 if ( pDesk
->dwDTFlags
& DF_TME_HOVER
)
1396 IntKillTimer(pDesk
->spwndTrack
, ID_EVENT_SYSTIMER_MOUSEHOVER
, TRUE
);
1398 /* Clear the flags to sign a change. */
1399 pDesk
->dwDTFlags
&= ~(DF_TME_LEAVE
|DF_TME_HOVER
);
1401 /* Set the Track window and hit test. */
1402 pDesk
->spwndTrack
= pwndTrack
;
1403 pDesk
->htEx
= hittest
;
1406 /* Reset, Same Track window, Hover set and Mouse Clicks or Clobbered Hover box. */
1407 if ( pDesk
->spwndTrack
== pwndTrack
&&
1408 ( msg
->message
!= WM_MOUSEMOVE
|| !RECTL_bPointInRect(&pDesk
->rcMouseHover
, msg
->pt
.x
, msg
->pt
.y
)) &&
1409 pDesk
->dwDTFlags
& DF_TME_HOVER
)
1411 TRACE("ITMM: Reset Hover points!\n");
1412 // Restart timer for the hover period.
1413 IntSetTimer(pDesk
->spwndTrack
, ID_EVENT_SYSTIMER_MOUSEHOVER
, pDesk
->dwMouseHoverTime
, SystemTimerProc
, TMRF_SYSTEM
);
1414 // Reset desktop mouse hover from the system default hover rectangle.
1415 RECTL_vSetRect(&pDesk
->rcMouseHover
,
1416 msg
->pt
.x
- gspv
.iMouseHoverWidth
/ 2,
1417 msg
->pt
.y
- gspv
.iMouseHoverHeight
/ 2,
1418 msg
->pt
.x
+ gspv
.iMouseHoverWidth
/ 2,
1419 msg
->pt
.y
+ gspv
.iMouseHoverHeight
/ 2);
1423 BOOL
co_IntProcessMouseMessage(MSG
* msg
, BOOL
* RemoveMessages
, BOOL
* NotForUs
, UINT first
, UINT last
)
1430 MOUSEHOOKSTRUCT hook
;
1431 BOOL eatMsg
= FALSE
;
1433 PWND pwndMsg
, pwndDesktop
;
1434 PUSER_MESSAGE_QUEUE MessageQueue
;
1436 PSYSTEM_CURSORINFO CurInfo
;
1439 pti
= PsGetCurrentThreadWin32Thread();
1440 pwndDesktop
= UserGetDesktopWindow();
1441 MessageQueue
= pti
->MessageQueue
;
1442 CurInfo
= IntGetSysCursorInfo();
1443 pwndMsg
= ValidateHwndNoErr(msg
->hwnd
);
1444 clk_msg
= MessageQueue
->msgDblClk
;
1445 pDesk
= pwndDesktop
->head
.rpdesk
;
1447 /* find the window to dispatch this mouse message to */
1448 if (MessageQueue
->spwndCapture
)
1451 pwndMsg
= MessageQueue
->spwndCapture
;
1456 Start with null window. See wine win.c:test_mouse_input:WM_COMMAND tests.
1458 pwndMsg
= co_WinPosWindowFromPoint( NULL
, &msg
->pt
, &hittest
, FALSE
);
1461 TRACE("Got mouse message for %p, hittest: 0x%x\n", msg
->hwnd
, hittest
);
1463 // Null window or not the same "Hardware" message queue.
1464 if (pwndMsg
== NULL
|| pwndMsg
->head
.pti
->MessageQueue
!= MessageQueue
)
1466 // Crossing a boundary, so set cursor. See default message queue cursor.
1467 UserSetCursor(SYSTEMCUR(ARROW
), FALSE
);
1468 /* Remove and ignore the message */
1469 *RemoveMessages
= TRUE
;
1473 // Check to see if this is attached,
1474 if ( pwndMsg
->head
.pti
!= pti
&& // window thread is not current,
1475 MessageQueue
->cThreads
> 1 ) // and is attached...
1477 // This is not for us and we should leave so the other thread can check for messages!!!
1479 *RemoveMessages
= TRUE
;
1483 if ( MessageQueue
== gpqCursor
) // Cursor must use the same Queue!
1485 IntTrackMouseMove(pwndMsg
, pDesk
, msg
, hittest
);
1489 ERR("Not the same cursor!\n");
1492 msg
->hwnd
= UserHMGetHandle(pwndMsg
);
1495 message
= msg
->message
;
1497 /* Note: windows has no concept of a non-client wheel message */
1498 if (message
!= WM_MOUSEWHEEL
)
1500 if (hittest
!= HTCLIENT
)
1502 message
+= WM_NCMOUSEMOVE
- WM_MOUSEMOVE
;
1503 msg
->wParam
= hittest
; // Caution! This might break wParam check in DblClk.
1507 /* coordinates don't get translated while tracking a menu */
1508 /* FIXME: should differentiate popups and top-level menus */
1509 if (!(MessageQueue
->MenuOwner
))
1511 pt
.x
+= pwndDesktop
->rcClient
.left
- pwndMsg
->rcClient
.left
;
1512 pt
.y
+= pwndDesktop
->rcClient
.top
- pwndMsg
->rcClient
.top
;
1516 msg
->lParam
= MAKELONG( pt
.x
, pt
.y
);
1518 /* translate double clicks */
1520 if ((msg
->message
== WM_LBUTTONDOWN
) ||
1521 (msg
->message
== WM_RBUTTONDOWN
) ||
1522 (msg
->message
== WM_MBUTTONDOWN
) ||
1523 (msg
->message
== WM_XBUTTONDOWN
))
1525 BOOL update
= *RemoveMessages
;
1527 /* translate double clicks -
1528 * note that ...MOUSEMOVEs can slip in between
1529 * ...BUTTONDOWN and ...BUTTONDBLCLK messages */
1531 if ((MessageQueue
->MenuOwner
|| MessageQueue
->MoveSize
) ||
1532 hittest
!= HTCLIENT
||
1533 (pwndMsg
->pcls
->style
& CS_DBLCLKS
))
1535 if ((msg
->message
== clk_msg
.message
) &&
1536 (msg
->hwnd
== clk_msg
.hwnd
) &&
1537 // Only worry about XButton wParam.
1538 (msg
->message
!= WM_XBUTTONDOWN
|| GET_XBUTTON_WPARAM(msg
->wParam
) == GET_XBUTTON_WPARAM(clk_msg
.wParam
)) &&
1539 ((msg
->time
- clk_msg
.time
) < (ULONG
)gspv
.iDblClickTime
) &&
1540 (abs(msg
->pt
.x
- clk_msg
.pt
.x
) < UserGetSystemMetrics(SM_CXDOUBLECLK
)/2) &&
1541 (abs(msg
->pt
.y
- clk_msg
.pt
.y
) < UserGetSystemMetrics(SM_CYDOUBLECLK
)/2))
1543 message
+= (WM_LBUTTONDBLCLK
- WM_LBUTTONDOWN
);
1546 MessageQueue
->msgDblClk
.message
= 0; /* clear the double click conditions */
1552 if (!((first
== 0 && last
== 0) || (message
>= first
|| message
<= last
)))
1554 TRACE("Message out of range!!!\n");
1558 /* update static double click conditions */
1559 if (update
) MessageQueue
->msgDblClk
= *msg
;
1563 if (!((first
== 0 && last
== 0) || (message
>= first
|| message
<= last
)))
1565 TRACE("Message out of range!!!\n");
1569 // Update mouse move down keys.
1570 if (message
== WM_MOUSEMOVE
)
1572 msg
->wParam
= MsqGetDownKeyState(MessageQueue
);
1576 if (gspv
.bMouseClickLock
)
1578 BOOL IsClkLck
= FALSE
;
1580 if(msg
->message
== WM_LBUTTONUP
)
1582 IsClkLck
= ((msg
->time
- CurInfo
->ClickLockTime
) >= gspv
.dwMouseClickLockTime
);
1583 if (IsClkLck
&& (!CurInfo
->ClickLockActive
))
1585 CurInfo
->ClickLockActive
= TRUE
;
1588 else if (msg
->message
== WM_LBUTTONDOWN
)
1590 if (CurInfo
->ClickLockActive
)
1593 CurInfo
->ClickLockActive
= FALSE
;
1596 CurInfo
->ClickLockTime
= msg
->time
;
1601 /* Remove and ignore the message */
1602 *RemoveMessages
= TRUE
;
1603 TRACE("Remove and ignore the message\n");
1608 /* message is accepted now (but still get dropped) */
1610 event
.message
= msg
->message
;
1611 event
.time
= msg
->time
;
1612 event
.hwnd
= msg
->hwnd
;
1613 event
.paramL
= msg
->pt
.x
;
1614 event
.paramH
= msg
->pt
.y
;
1615 co_HOOK_CallHooks( WH_JOURNALRECORD
, HC_ACTION
, 0, (LPARAM
)&event
);
1618 hook
.hwnd
= msg
->hwnd
;
1619 hook
.wHitTestCode
= hittest
;
1620 hook
.dwExtraInfo
= 0 /* extra_info */ ;
1621 if (co_HOOK_CallHooks( WH_MOUSE
, *RemoveMessages
? HC_ACTION
: HC_NOREMOVE
,
1622 message
, (LPARAM
)&hook
))
1625 hook
.hwnd
= msg
->hwnd
;
1626 hook
.wHitTestCode
= hittest
;
1627 hook
.dwExtraInfo
= 0 /* extra_info */ ;
1628 co_HOOK_CallHooks( WH_CBT
, HCBT_CLICKSKIPPED
, message
, (LPARAM
)&hook
);
1630 ERR("WH_MOUSE dropped mouse message!\n");
1632 /* Remove and skip message */
1633 *RemoveMessages
= TRUE
;
1637 if ((hittest
== (USHORT
)HTERROR
) || (hittest
== (USHORT
)HTNOWHERE
))
1639 co_IntSendMessage( msg
->hwnd
, WM_SETCURSOR
, (WPARAM
)msg
->hwnd
, MAKELONG( hittest
, msg
->message
));
1641 /* Remove and skip message */
1642 *RemoveMessages
= TRUE
;
1646 if ((*RemoveMessages
== FALSE
) || MessageQueue
->spwndCapture
)
1648 /* Accept the message */
1649 msg
->message
= message
;
1653 if ((msg
->message
== WM_LBUTTONDOWN
) ||
1654 (msg
->message
== WM_RBUTTONDOWN
) ||
1655 (msg
->message
== WM_MBUTTONDOWN
) ||
1656 (msg
->message
== WM_XBUTTONDOWN
))
1658 /* Send the WM_PARENTNOTIFY,
1659 * note that even for double/nonclient clicks
1660 * notification message is still WM_L/M/RBUTTONDOWN.
1662 MsqSendParentNotify(pwndMsg
, msg
->message
, 0, msg
->pt
);
1664 /* Activate the window if needed */
1666 if (pwndMsg
!= MessageQueue
->spwndActive
)
1668 PWND pwndTop
= pwndMsg
;
1669 pwndTop
= IntGetNonChildAncestor(pwndTop
);
1671 TRACE("Mouse pti %p pwndMsg pti %p pwndTop pti %p\n",MessageQueue
->ptiMouse
,pwndMsg
->head
.pti
,pwndTop
->head
.pti
);
1673 if (pwndTop
&& pwndTop
!= pwndDesktop
)
1675 LONG ret
= co_IntSendMessage( msg
->hwnd
,
1677 (WPARAM
)UserHMGetHandle(pwndTop
),
1678 MAKELONG( hittest
, msg
->message
));
1681 case MA_NOACTIVATEANDEAT
:
1686 case MA_ACTIVATEANDEAT
:
1691 if (!co_IntMouseActivateWindow( pwndTop
)) eatMsg
= TRUE
;
1694 ERR( "unknown WM_MOUSEACTIVATE code %d\n", ret
);
1701 /* send the WM_SETCURSOR message */
1703 /* Windows sends the normal mouse message as the message parameter
1704 in the WM_SETCURSOR message even if it's non-client mouse message */
1705 co_IntSendMessage( msg
->hwnd
, WM_SETCURSOR
, (WPARAM
)msg
->hwnd
, MAKELONG( hittest
, msg
->message
));
1707 msg
->message
= message
;
1711 BOOL
co_IntProcessKeyboardMessage(MSG
* Msg
, BOOL
* RemoveMessages
)
1714 USER_REFERENCE_ENTRY Ref
;
1718 PTHREADINFO pti
= PsGetCurrentThreadWin32Thread();
1720 if (Msg
->message
== VK_PACKET
)
1722 pti
->wchInjected
= HIWORD(Msg
->wParam
);
1725 if (Msg
->message
== WM_KEYDOWN
|| Msg
->message
== WM_SYSKEYDOWN
||
1726 Msg
->message
== WM_KEYUP
|| Msg
->message
== WM_SYSKEYUP
)
1728 switch (Msg
->wParam
)
1730 case VK_LSHIFT
: case VK_RSHIFT
:
1731 Msg
->wParam
= VK_SHIFT
;
1733 case VK_LCONTROL
: case VK_RCONTROL
:
1734 Msg
->wParam
= VK_CONTROL
;
1736 case VK_LMENU
: case VK_RMENU
:
1737 Msg
->wParam
= VK_MENU
;
1742 pWnd
= ValidateHwndNoErr(Msg
->hwnd
);
1743 if (pWnd
) UserRefObjectCo(pWnd
, &Ref
);
1745 Event
.message
= Msg
->message
;
1746 Event
.hwnd
= Msg
->hwnd
;
1747 Event
.time
= Msg
->time
;
1748 Event
.paramL
= (Msg
->wParam
& 0xFF) | (HIWORD(Msg
->lParam
) << 8);
1749 Event
.paramH
= Msg
->lParam
& 0x7FFF;
1750 if (HIWORD(Msg
->lParam
) & 0x0100) Event
.paramH
|= 0x8000;
1751 co_HOOK_CallHooks( WH_JOURNALRECORD
, HC_ACTION
, 0, (LPARAM
)&Event
);
1753 if (*RemoveMessages
)
1755 if ((Msg
->message
== WM_KEYDOWN
) &&
1756 (Msg
->hwnd
!= IntGetDesktopWindow()))
1758 /* Handle F1 key by sending out WM_HELP message */
1759 if (Msg
->wParam
== VK_F1
)
1761 UserPostMessage( Msg
->hwnd
, WM_KEYF1
, 0, 0 );
1763 else if (Msg
->wParam
>= VK_BROWSER_BACK
&&
1764 Msg
->wParam
<= VK_LAUNCH_APP2
)
1766 /* FIXME: Process keystate */
1767 co_IntSendMessage(Msg
->hwnd
, WM_APPCOMMAND
, (WPARAM
)Msg
->hwnd
, MAKELPARAM(0, (FAPPCOMMAND_KEY
| (Msg
->wParam
- VK_BROWSER_BACK
+ 1))));
1770 else if (Msg
->message
== WM_KEYUP
)
1772 /* Handle VK_APPS key by posting a WM_CONTEXTMENU message */
1773 if (Msg
->wParam
== VK_APPS
&& pti
->MessageQueue
->MenuOwner
== NULL
)
1774 UserPostMessage( Msg
->hwnd
, WM_CONTEXTMENU
, (WPARAM
)Msg
->hwnd
, -1 );
1778 if (co_HOOK_CallHooks( WH_KEYBOARD
,
1779 *RemoveMessages
? HC_ACTION
: HC_NOREMOVE
,
1780 LOWORD(Msg
->wParam
),
1783 /* skip this message */
1784 co_HOOK_CallHooks( WH_CBT
,
1786 LOWORD(Msg
->wParam
),
1789 ERR("KeyboardMessage WH_KEYBOARD Call Hook return!\n");
1791 *RemoveMessages
= TRUE
;
1796 if ( pWnd
&& Ret
&& *RemoveMessages
&& Msg
->message
== WM_KEYDOWN
&& !(pti
->TIF_flags
& TIF_DISABLEIME
))
1798 if ( (ImmRet
= IntImmProcessKey(pti
->MessageQueue
, pWnd
, Msg
->message
, Msg
->wParam
, Msg
->lParam
)) )
1800 if ( ImmRet
& (IPHK_HOTKEY
|IPHK_SKIPTHISKEY
) )
1804 if ( ImmRet
& IPHK_PROCESSBYIME
)
1806 Msg
->wParam
= VK_PROCESSKEY
;
1811 if (pWnd
) UserDerefObjectCo(pWnd
);
1815 BOOL
co_IntProcessHardwareMessage(MSG
* Msg
, BOOL
* RemoveMessages
, BOOL
* NotForUs
, UINT first
, UINT last
)
1817 if ( IS_MOUSE_MESSAGE(Msg
->message
))
1819 return co_IntProcessMouseMessage(Msg
, RemoveMessages
, NotForUs
, first
, last
);
1821 else if ( IS_KBD_MESSAGE(Msg
->message
))
1823 return co_IntProcessKeyboardMessage(Msg
, RemoveMessages
);
1829 /* check whether a message filter contains at least one potential hardware message */
1831 filter_contains_hw_range( UINT first
, UINT last
)
1833 /* hardware message ranges are (in numerical order):
1834 * WM_NCMOUSEFIRST .. WM_NCMOUSELAST
1835 * WM_KEYFIRST .. WM_KEYLAST
1836 * WM_MOUSEFIRST .. WM_MOUSELAST
1839 if (last
< WM_NCMOUSEFIRST
) return 0;
1840 if (first
> WM_NCMOUSELAST
&& last
< WM_KEYFIRST
) return 0;
1841 if (first
> WM_KEYLAST
&& last
< WM_MOUSEFIRST
) return 0;
1842 if (first
> WM_MOUSELAST
) return 0;
1847 co_MsqPeekHardwareMessage(IN PTHREADINFO pti
,
1850 IN UINT MsgFilterLow
,
1851 IN UINT MsgFilterHigh
,
1855 BOOL AcceptMessage
, NotForUs
;
1856 PUSER_MESSAGE CurrentMessage
;
1857 PLIST_ENTRY ListHead
;
1862 PUSER_MESSAGE_QUEUE MessageQueue
= pti
->MessageQueue
;
1864 if (!filter_contains_hw_range( MsgFilterLow
, MsgFilterHigh
)) return FALSE
;
1866 ListHead
= MessageQueue
->HardwareMessagesListHead
.Flink
;
1868 if (IsListEmpty(ListHead
)) return FALSE
;
1870 if (!MessageQueue
->ptiSysLock
)
1872 MessageQueue
->ptiSysLock
= pti
;
1873 pti
->pcti
->CTI_flags
|= CTI_THREADSYSLOCK
;
1876 if (MessageQueue
->ptiSysLock
!= pti
)
1878 ERR("Thread Q is locked to ptiSysLock 0x%p pti 0x%p\n",MessageQueue
->ptiSysLock
,pti
);
1882 while (ListHead
!= &MessageQueue
->HardwareMessagesListHead
)
1884 CurrentMessage
= CONTAINING_RECORD(ListHead
, USER_MESSAGE
, ListEntry
);
1885 ListHead
= ListHead
->Flink
;
1887 if (MessageQueue
->idSysPeek
== (ULONG_PTR
)CurrentMessage
)
1889 TRACE("Skip this message due to it is in play!\n");
1894 1: any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL.
1895 2: retrieves only messages on the current thread's message queue whose hwnd value is NULL.
1896 3: handle to the window whose messages are to be retrieved.
1898 if ( ( !Window
|| // 1
1899 ( Window
== PWND_BOTTOM
&& CurrentMessage
->Msg
.hwnd
== NULL
) || // 2
1900 ( Window
!= PWND_BOTTOM
&& Window
->head
.h
== CurrentMessage
->Msg
.hwnd
) || // 3
1901 ( CurrentMessage
->Msg
.message
== WM_MOUSEMOVE
) ) && // Null window for mouse moves.
1902 ( ( ( MsgFilterLow
== 0 && MsgFilterHigh
== 0 ) && CurrentMessage
->QS_Flags
& QSflags
) ||
1903 ( MsgFilterLow
<= CurrentMessage
->Msg
.message
&& MsgFilterHigh
>= CurrentMessage
->Msg
.message
) ) )
1905 idSave
= MessageQueue
->idSysPeek
;
1906 MessageQueue
->idSysPeek
= (ULONG_PTR
)CurrentMessage
;
1908 msg
= CurrentMessage
->Msg
;
1909 QS_Flags
= CurrentMessage
->QS_Flags
;
1913 UpdateKeyStateFromMsg(MessageQueue
, &msg
);
1914 AcceptMessage
= co_IntProcessHardwareMessage(&msg
, &Remove
, &NotForUs
, MsgFilterLow
, MsgFilterHigh
);
1918 if (CurrentMessage
->pti
!= NULL
)
1920 RemoveEntryList(&CurrentMessage
->ListEntry
);
1921 MsqDestroyMessage(CurrentMessage
);
1923 ClearMsgBitsMask(pti
, QS_Flags
);
1926 MessageQueue
->idSysPeek
= idSave
;
1943 MessageQueue
->ptiSysLock
= NULL
;
1944 pti
->pcti
->CTI_flags
&= ~CTI_THREADSYSLOCK
;
1949 MsqPeekMessage(IN PTHREADINFO pti
,
1952 IN UINT MsgFilterLow
,
1953 IN UINT MsgFilterHigh
,
1955 OUT LONG_PTR
*ExtraInfo
,
1958 PUSER_MESSAGE CurrentMessage
;
1959 PLIST_ENTRY ListHead
;
1963 ListHead
= pti
->PostedMessagesListHead
.Flink
;
1965 if (IsListEmpty(ListHead
)) return FALSE
;
1967 while(ListHead
!= &pti
->PostedMessagesListHead
)
1969 CurrentMessage
= CONTAINING_RECORD(ListHead
, USER_MESSAGE
, ListEntry
);
1970 ListHead
= ListHead
->Flink
;
1973 1: any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL.
1974 2: retrieves only messages on the current thread's message queue whose hwnd value is NULL.
1975 3: handle to the window whose messages are to be retrieved.
1977 if ( ( !Window
|| // 1
1978 ( Window
== PWND_BOTTOM
&& CurrentMessage
->Msg
.hwnd
== NULL
) || // 2
1979 ( Window
!= PWND_BOTTOM
&& Window
->head
.h
== CurrentMessage
->Msg
.hwnd
) ) && // 3
1980 ( ( ( MsgFilterLow
== 0 && MsgFilterHigh
== 0 ) && CurrentMessage
->QS_Flags
& QSflags
) ||
1981 ( MsgFilterLow
<= CurrentMessage
->Msg
.message
&& MsgFilterHigh
>= CurrentMessage
->Msg
.message
) ) )
1983 *Message
= CurrentMessage
->Msg
;
1984 *ExtraInfo
= CurrentMessage
->ExtraInfo
;
1985 QS_Flags
= CurrentMessage
->QS_Flags
;
1989 if (CurrentMessage
->pti
!= NULL
)
1991 RemoveEntryList(&CurrentMessage
->ListEntry
);
1992 MsqDestroyMessage(CurrentMessage
);
1994 ClearMsgBitsMask(pti
, QS_Flags
);
2005 co_MsqWaitForNewMessages(PTHREADINFO pti
, PWND WndFilter
,
2006 UINT MsgFilterMin
, UINT MsgFilterMax
)
2008 NTSTATUS ret
= STATUS_SUCCESS
;
2010 // Post mouse moves before waiting for messages.
2011 if (pti
->MessageQueue
->QF_flags
& QF_MOUSEMOVED
)
2013 IntCoalesceMouseMove(pti
);
2016 if ( pti
->nCntsQBits
[QSRosMouseButton
] != 0 ||
2017 pti
->nCntsQBits
[QSRosMouseMove
] != 0 ||
2018 pti
->nCntsQBits
[QSRosKey
] != 0 ||
2019 pti
->nCntsQBits
[QSRosSendMessage
] != 0 ||
2020 pti
->nCntsQBits
[QSRosPostMessage
] != 0 )
2022 TRACE("No time to wait!\n");
2028 ZwYieldExecution(); // Let someone else run!
2030 ret
= KeWaitForSingleObject( pti
->pEventQueueServer
,
2036 if ( ret
== STATUS_USER_APC
)
2038 TRACE("MWFNW User APC\n");
2039 co_IntDeliverUserAPC();
2045 MsqIsHung(PTHREADINFO pti
)
2047 LARGE_INTEGER LargeTickCount
;
2049 KeQueryTickCount(&LargeTickCount
);
2050 return ((LargeTickCount
.u
.LowPart
- pti
->timeLast
) > MSQ_HUNG
);
2055 HungAppSysTimerProc(HWND hwnd
, UINT uMsg
, UINT_PTR idEvent
, DWORD dwTime
)
2058 TRACE("HungAppSysTimerProc\n");
2059 // Process list of windows that are hung and waiting.
2063 MsqInitializeMessageQueue(PTHREADINFO pti
, PUSER_MESSAGE_QUEUE MessageQueue
)
2065 MessageQueue
->CaretInfo
= (PTHRDCARETINFO
)(MessageQueue
+ 1);
2066 InitializeListHead(&MessageQueue
->HardwareMessagesListHead
); // Keep here!
2067 MessageQueue
->spwndFocus
= NULL
;
2068 MessageQueue
->iCursorLevel
= 0;
2069 MessageQueue
->CursorObject
= SYSTEMCUR(WAIT
); // See test_initial_cursor.
2070 if (MessageQueue
->CursorObject
)
2072 TRACE("Default cursor hcur %p\n",UserHMGetHandle(MessageQueue
->CursorObject
));
2073 UserReferenceObject(MessageQueue
->CursorObject
);
2075 RtlCopyMemory(MessageQueue
->afKeyState
, gafAsyncKeyState
, sizeof(gafAsyncKeyState
));
2076 MessageQueue
->ptiMouse
= pti
;
2077 MessageQueue
->ptiKeyboard
= pti
;
2078 MessageQueue
->cThreads
++;
2084 MsqCleanupThreadMsgs(PTHREADINFO pti
)
2086 PLIST_ENTRY CurrentEntry
;
2087 PUSER_MESSAGE CurrentMessage
;
2088 PUSER_SENT_MESSAGE CurrentSentMessage
;
2090 /* cleanup posted messages */
2091 while (!IsListEmpty(&pti
->PostedMessagesListHead
))
2093 CurrentEntry
= RemoveHeadList(&pti
->PostedMessagesListHead
);
2094 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
, ListEntry
);
2095 if (CurrentMessage
->dwQEvent
)
2097 if (CurrentMessage
->dwQEvent
== POSTEVENT_NWE
)
2099 ExFreePoolWithTag( (PVOID
)CurrentMessage
->ExtraInfo
, TAG_HOOK
);
2102 MsqDestroyMessage(CurrentMessage
);
2105 /* remove the messages that have not yet been dispatched */
2106 while (!IsListEmpty(&pti
->SentMessagesListHead
))
2108 CurrentEntry
= RemoveHeadList(&pti
->SentMessagesListHead
);
2109 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
, ListEntry
);
2111 TRACE("Notify the sender and remove a message from the queue that had not been dispatched\n");
2112 /* Only if the message has a sender was the message in the DispatchingList */
2113 if ((CurrentSentMessage
->ptiSender
) &&
2114 (!IsListEmpty(&CurrentSentMessage
->DispatchingListEntry
)))
2116 RemoveEntryList(&CurrentSentMessage
->DispatchingListEntry
);
2117 InitializeListHead(&CurrentSentMessage
->DispatchingListEntry
);
2120 /* wake the sender's thread */
2121 if (CurrentSentMessage
->CompletionEvent
!= NULL
)
2123 KeSetEvent(CurrentSentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
2126 if (CurrentSentMessage
->HasPackedLParam
)
2128 if (CurrentSentMessage
->Msg
.lParam
)
2129 ExFreePool((PVOID
)CurrentSentMessage
->Msg
.lParam
);
2132 /* free the message */
2133 ExFreePool(CurrentSentMessage
);
2136 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
2137 ExitThread() was called in a SendMessage() umode callback */
2138 while (!IsListEmpty(&pti
->LocalDispatchingMessagesHead
))
2140 CurrentEntry
= RemoveHeadList(&pti
->LocalDispatchingMessagesHead
);
2141 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
, ListEntry
);
2143 /* remove the message from the dispatching list */
2144 if (!IsListEmpty(&CurrentSentMessage
->DispatchingListEntry
))
2146 RemoveEntryList(&CurrentSentMessage
->DispatchingListEntry
);
2147 InitializeListHead(&CurrentSentMessage
->DispatchingListEntry
);
2150 TRACE("Notify the sender, the thread has been terminated while dispatching a message!\n");
2152 /* wake the sender's thread */
2153 if (CurrentSentMessage
->CompletionEvent
!= NULL
)
2155 KeSetEvent(CurrentSentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
2158 if (CurrentSentMessage
->HasPackedLParam
)
2160 if (CurrentSentMessage
->Msg
.lParam
)
2162 _PRAGMA_WARNING_SUPPRESS(__WARNING_USING_UNINIT_VAR
);
2163 ExFreePool((PVOID
)CurrentSentMessage
->Msg
.lParam
);
2167 /* free the message */
2168 ExFreePool(CurrentSentMessage
);
2171 /* tell other threads not to bother returning any info to us */
2172 while (! IsListEmpty(&pti
->DispatchingMessagesHead
))
2174 CurrentEntry
= RemoveHeadList(&pti
->DispatchingMessagesHead
);
2175 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
, DispatchingListEntry
);
2176 InitializeListHead(&CurrentSentMessage
->DispatchingListEntry
);
2177 CurrentSentMessage
->CompletionEvent
= NULL
;
2178 CurrentSentMessage
->Result
= NULL
;
2180 /* do NOT dereference our message queue as it might get attempted to be
2184 // Clear it all out.
2187 pti
->pcti
->fsWakeBits
= 0;
2188 pti
->pcti
->fsChangeBits
= 0;
2191 pti
->nCntsQBits
[QSRosKey
] = 0;
2192 pti
->nCntsQBits
[QSRosMouseMove
] = 0;
2193 pti
->nCntsQBits
[QSRosMouseButton
] = 0;
2194 pti
->nCntsQBits
[QSRosPostMessage
] = 0;
2195 pti
->nCntsQBits
[QSRosSendMessage
] = 0;
2196 pti
->nCntsQBits
[QSRosHotKey
] = 0;
2197 pti
->nCntsQBits
[QSRosEvent
] = 0;
2201 MsqCleanupMessageQueue(PTHREADINFO pti
)
2203 PUSER_MESSAGE_QUEUE MessageQueue
;
2205 MessageQueue
= pti
->MessageQueue
;
2206 MessageQueue
->cThreads
--;
2208 if (MessageQueue
->cThreads
)
2210 if (MessageQueue
->ptiSysLock
== pti
) MessageQueue
->ptiSysLock
= NULL
;
2213 if (MessageQueue
->CursorObject
)
2215 PCURICON_OBJECT pCursor
= MessageQueue
->CursorObject
;
2217 /* Change to another cursor if we going to dereference current one
2218 Note: we can't use UserSetCursor because it uses current thread
2219 message queue instead of queue given for cleanup */
2220 if (IntGetSysCursorInfo()->CurrentCursorObject
== pCursor
)
2224 /* Get the screen DC */
2225 hdcScreen
= IntGetScreenDC();
2227 GreMovePointer(hdcScreen
, -1, -1);
2228 IntGetSysCursorInfo()->CurrentCursorObject
= NULL
;
2231 TRACE("DereferenceObject pCursor\n");
2232 UserDereferenceObject(pCursor
);
2235 if (gpqForeground
== MessageQueue
)
2237 IntSetFocusMessageQueue(NULL
);
2239 if (gpqForegroundPrev
== MessageQueue
)
2241 gpqForegroundPrev
= NULL
;
2243 if (gpqCursor
== MessageQueue
)
2249 PUSER_MESSAGE_QUEUE FASTCALL
2250 MsqCreateMessageQueue(PTHREADINFO pti
)
2252 PUSER_MESSAGE_QUEUE MessageQueue
;
2254 MessageQueue
= (PUSER_MESSAGE_QUEUE
)ExAllocatePoolWithTag(NonPagedPool
,
2255 sizeof(USER_MESSAGE_QUEUE
) + sizeof(THRDCARETINFO
),
2263 RtlZeroMemory(MessageQueue
, sizeof(USER_MESSAGE_QUEUE
) + sizeof(THRDCARETINFO
));
2264 /* hold at least one reference until it'll be destroyed */
2265 IntReferenceMessageQueue(MessageQueue
);
2266 /* initialize the queue */
2267 if (!MsqInitializeMessageQueue(pti
, MessageQueue
))
2269 IntDereferenceMessageQueue(MessageQueue
);
2273 return MessageQueue
;
2277 MsqDestroyMessageQueue(_In_ PTHREADINFO pti
)
2280 PUSER_MESSAGE_QUEUE MessageQueue
= pti
->MessageQueue
;
2282 NT_ASSERT(MessageQueue
!= NULL
);
2283 MessageQueue
->QF_flags
|= QF_INDESTROY
;
2285 /* remove the message queue from any desktops */
2286 if ((desk
= InterlockedExchangePointer((PVOID
*)&MessageQueue
->Desktop
, 0)))
2288 (void)InterlockedExchangePointer((PVOID
*)&desk
->ActiveMessageQueue
, 0);
2289 IntDereferenceMessageQueue(MessageQueue
);
2293 MsqCleanupMessageQueue(pti
);
2295 /* decrease the reference counter, if it hits zero, the queue will be freed */
2296 _PRAGMA_WARNING_SUPPRESS(__WARNING_USING_UNINIT_VAR
);
2297 IntDereferenceMessageQueue(MessageQueue
);
2301 MsqSetMessageExtraInfo(LPARAM lParam
)
2305 PUSER_MESSAGE_QUEUE MessageQueue
;
2307 pti
= PsGetCurrentThreadWin32Thread();
2308 MessageQueue
= pti
->MessageQueue
;
2314 Ret
= MessageQueue
->ExtraInfo
;
2315 MessageQueue
->ExtraInfo
= lParam
;
2321 MsqGetMessageExtraInfo(VOID
)
2324 PUSER_MESSAGE_QUEUE MessageQueue
;
2326 pti
= PsGetCurrentThreadWin32Thread();
2327 MessageQueue
= pti
->MessageQueue
;
2333 return MessageQueue
->ExtraInfo
;
2336 // ReplyMessage is called by the thread receiving the window message.
2338 co_MsqReplyMessage( LRESULT lResult
)
2340 PUSER_SENT_MESSAGE Message
;
2343 pti
= PsGetCurrentThreadWin32Thread();
2344 Message
= pti
->pusmCurrent
;
2346 if (!Message
) return FALSE
;
2348 if (Message
->QS_Flags
& QS_SMRESULT
) return FALSE
;
2350 // SendMessageXxx || Callback msg and not a notify msg
2351 if (Message
->ptiSender
|| Message
->CompletionCallback
)
2353 Message
->lResult
= lResult
;
2354 Message
->QS_Flags
|= QS_SMRESULT
;
2355 // See co_MsqDispatchOneSentMessage, change bits already accounted for and cleared and this msg is going away..
2361 MsqSetStateWindow(PTHREADINFO pti
, ULONG Type
, HWND hWnd
)
2364 PUSER_MESSAGE_QUEUE MessageQueue
;
2366 MessageQueue
= pti
->MessageQueue
;
2370 case MSQ_STATE_CAPTURE
:
2371 Prev
= MessageQueue
->spwndCapture
? UserHMGetHandle(MessageQueue
->spwndCapture
) : 0;
2372 MessageQueue
->spwndCapture
= ValidateHwndNoErr(hWnd
);
2374 case MSQ_STATE_ACTIVE
:
2375 Prev
= MessageQueue
->spwndActive
? UserHMGetHandle(MessageQueue
->spwndActive
) : 0;
2376 MessageQueue
->spwndActive
= ValidateHwndNoErr(hWnd
);
2378 case MSQ_STATE_FOCUS
:
2379 Prev
= MessageQueue
->spwndFocus
? UserHMGetHandle(MessageQueue
->spwndFocus
) : 0;
2380 MessageQueue
->spwndFocus
= ValidateHwndNoErr(hWnd
);
2382 case MSQ_STATE_MENUOWNER
:
2383 Prev
= MessageQueue
->MenuOwner
;
2384 MessageQueue
->MenuOwner
= hWnd
;
2386 case MSQ_STATE_MOVESIZE
:
2387 Prev
= MessageQueue
->MoveSize
;
2388 MessageQueue
->MoveSize
= hWnd
;
2390 case MSQ_STATE_CARET
:
2391 ASSERT(MessageQueue
->CaretInfo
);
2392 Prev
= MessageQueue
->CaretInfo
->hWnd
;
2393 MessageQueue
->CaretInfo
->hWnd
= hWnd
;
2402 NtUserGetKeyState(INT key
)
2408 Ret
= UserGetKeyState(key
);
2418 NtUserGetKeyboardState(LPBYTE lpKeyState
)
2420 DWORD i
, ret
= TRUE
;
2422 PUSER_MESSAGE_QUEUE MessageQueue
;
2426 pti
= PsGetCurrentThreadWin32Thread();
2427 MessageQueue
= pti
->MessageQueue
;
2431 /* Probe and copy key state to an array */
2432 ProbeForWrite(lpKeyState
, 256 * sizeof(BYTE
), 1);
2433 for (i
= 0; i
< 256; ++i
)
2436 if (IS_KEY_DOWN(MessageQueue
->afKeyState
, i
))
2437 lpKeyState
[i
] |= KS_DOWN_BIT
;
2438 if (IS_KEY_LOCKED(MessageQueue
->afKeyState
, i
))
2439 lpKeyState
[i
] |= KS_LOCK_BIT
;
2442 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2444 SetLastNtError(_SEH2_GetExceptionCode());
2456 NtUserSetKeyboardState(LPBYTE pKeyState
)
2461 PUSER_MESSAGE_QUEUE MessageQueue
;
2463 UserEnterExclusive();
2465 pti
= PsGetCurrentThreadWin32Thread();
2466 MessageQueue
= pti
->MessageQueue
;
2470 ProbeForRead(pKeyState
, 256 * sizeof(BYTE
), 1);
2471 for (i
= 0; i
< 256; ++i
)
2473 SET_KEY_DOWN(MessageQueue
->afKeyState
, i
, pKeyState
[i
] & KS_DOWN_BIT
);
2474 SET_KEY_LOCKED(MessageQueue
->afKeyState
, i
, pKeyState
[i
] & KS_LOCK_BIT
);
2477 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2479 SetLastNtError(_SEH2_GetExceptionCode());