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 #ifdef NEW_CURSORICON
122 PCURICON_OBJECT CursorFrame
= NewCursor
;
123 if(NewCursor
->CURSORF_flags
& CURSORF_ACON
)
125 FIXME("Should animate the cursor, using only the first frame now.\n");
126 CursorFrame
= ((PACON
)NewCursor
)->aspcur
[0];
128 GreSetPointerShape(hdcScreen
,
129 CursorFrame
->hbmAlpha
? NULL
: NewCursor
->hbmMask
,
130 CursorFrame
->hbmAlpha
? NewCursor
->hbmAlpha
: NewCursor
->hbmColor
,
131 CursorFrame
->xHotspot
,
132 CursorFrame
->yHotspot
,
135 CursorFrame
->hbmAlpha
? SPS_ALPHA
: 0);
137 GreSetPointerShape(hdcScreen
,
138 NewCursor
->IconInfo
.hbmMask
,
139 NewCursor
->IconInfo
.hbmColor
,
140 NewCursor
->IconInfo
.xHotspot
,
141 NewCursor
->IconInfo
.yHotspot
,
147 else /* Note: OldCursor != NewCursor so we have to hide cursor */
149 /* Remove the cursor */
150 GreMovePointer(hdcScreen
, -1, -1);
151 TRACE("Removing pointer!\n");
153 IntGetSysCursorInfo()->CurrentCursorObject
= NewCursor
;
156 /* Return the old cursor */
160 /* Called from NtUserCallOneParam with Routine ONEPARAM_ROUTINE_SHOWCURSOR
161 * User32 macro NtUserShowCursor */
162 int UserShowCursor(BOOL bShow
)
166 PUSER_MESSAGE_QUEUE MessageQueue
;
169 if (!(hdcScreen
= IntGetScreenDC()))
171 return -1; /* No mouse */
174 pti
= PsGetCurrentThreadWin32Thread();
175 MessageQueue
= pti
->MessageQueue
;
178 MessageQueue
->iCursorLevel
+= bShow
? 1 : -1;
179 pti
->iCursorLevel
+= bShow
? 1 : -1;
181 /* Check for trivial cases */
182 if ((bShow
&& MessageQueue
->iCursorLevel
!= 0) ||
183 (!bShow
&& MessageQueue
->iCursorLevel
!= -1))
185 /* Note: w don't update global info here because it is used only
186 internally to check if cursor is visible */
187 return MessageQueue
->iCursorLevel
;
190 /* Check if cursor is above window owned by this MessageQueue */
191 pWnd
= IntTopLevelWindowFromPoint(gpsi
->ptCursor
.x
, gpsi
->ptCursor
.y
);
192 if (pWnd
&& pWnd
->head
.pti
->MessageQueue
== MessageQueue
)
196 /* Show the pointer */
197 GreMovePointer(hdcScreen
, gpsi
->ptCursor
.x
, gpsi
->ptCursor
.y
);
198 TRACE("Showing pointer!\n");
202 /* Remove the pointer */
203 GreMovePointer(hdcScreen
, -1, -1);
204 TRACE("Removing pointer!\n");
207 /* Update global info */
208 IntGetSysCursorInfo()->ShowingCursor
= MessageQueue
->iCursorLevel
;
211 return MessageQueue
->iCursorLevel
;
215 UserGetKeyState(DWORD dwKey
)
219 PUSER_MESSAGE_QUEUE MessageQueue
;
221 pti
= PsGetCurrentThreadWin32Thread();
222 MessageQueue
= pti
->MessageQueue
;
226 if (IS_KEY_DOWN(MessageQueue
->afKeyState
, dwKey
))
227 dwRet
|= 0xFF80; // If down, windows returns 0xFF80.
228 if (IS_KEY_LOCKED(MessageQueue
->afKeyState
, dwKey
))
233 EngSetLastError(ERROR_INVALID_PARAMETER
);
238 /* change the input key state for a given key */
240 UpdateKeyState(PUSER_MESSAGE_QUEUE MessageQueue
, WORD wVk
, BOOL bIsDown
)
242 TRACE("UpdateKeyState wVk: %u, bIsDown: %d\n", wVk
, bIsDown
);
246 /* If it's first key down event, xor lock bit */
247 if (!IS_KEY_DOWN(MessageQueue
->afKeyState
, wVk
))
248 SET_KEY_LOCKED(MessageQueue
->afKeyState
, wVk
, !IS_KEY_LOCKED(MessageQueue
->afKeyState
, wVk
));
250 SET_KEY_DOWN(MessageQueue
->afKeyState
, wVk
, TRUE
);
251 MessageQueue
->afKeyRecentDown
[wVk
/ 8] |= (1 << (wVk
% 8));
254 SET_KEY_DOWN(MessageQueue
->afKeyState
, wVk
, FALSE
);
257 /* update the input key state for a keyboard message */
259 UpdateKeyStateFromMsg(PUSER_MESSAGE_QUEUE MessageQueue
, MSG
* msg
)
264 TRACE("UpdateKeyStateFromMsg message:%u\n", msg
->message
);
266 switch (msg
->message
)
272 UpdateKeyState(MessageQueue
, VK_LBUTTON
, down
);
278 UpdateKeyState(MessageQueue
, VK_MBUTTON
, down
);
284 UpdateKeyState(MessageQueue
, VK_RBUTTON
, down
);
290 if (msg
->wParam
== XBUTTON1
)
291 UpdateKeyState(MessageQueue
, VK_XBUTTON1
, down
);
292 else if (msg
->wParam
== XBUTTON2
)
293 UpdateKeyState(MessageQueue
, VK_XBUTTON2
, down
);
301 key
= (UCHAR
)msg
->wParam
;
302 UpdateKeyState(MessageQueue
, key
, down
);
307 down
= IS_KEY_DOWN(MessageQueue
->afKeyState
, VK_LCONTROL
) || IS_KEY_DOWN(MessageQueue
->afKeyState
, VK_RCONTROL
);
308 UpdateKeyState(MessageQueue
, VK_CONTROL
, down
);
312 down
= IS_KEY_DOWN(MessageQueue
->afKeyState
, VK_LMENU
) || IS_KEY_DOWN(MessageQueue
->afKeyState
, VK_RMENU
);
313 UpdateKeyState(MessageQueue
, VK_MENU
, down
);
317 down
= IS_KEY_DOWN(MessageQueue
->afKeyState
, VK_LSHIFT
) || IS_KEY_DOWN(MessageQueue
->afKeyState
, VK_RSHIFT
);
318 UpdateKeyState(MessageQueue
, VK_SHIFT
, down
);
326 Get down key states from the queue of prior processed input message key states.
328 This fixes the left button dragging on the desktop and release sticking outline issue.
329 USB Tablet pointer seems to stick the most and leaves the box outline displayed.
332 MsqGetDownKeyState(PUSER_MESSAGE_QUEUE MessageQueue
)
336 if (gspv
.bMouseBtnSwap
)
338 if (IS_KEY_DOWN(MessageQueue
->afKeyState
, VK_RBUTTON
)) ret
|= MK_LBUTTON
;
339 if (IS_KEY_DOWN(MessageQueue
->afKeyState
, VK_LBUTTON
)) ret
|= MK_RBUTTON
;
343 if (IS_KEY_DOWN(MessageQueue
->afKeyState
, VK_LBUTTON
)) ret
|= MK_LBUTTON
;
344 if (IS_KEY_DOWN(MessageQueue
->afKeyState
, VK_RBUTTON
)) ret
|= MK_RBUTTON
;
347 if (IS_KEY_DOWN(MessageQueue
->afKeyState
, VK_MBUTTON
)) ret
|= MK_MBUTTON
;
348 if (IS_KEY_DOWN(MessageQueue
->afKeyState
, VK_SHIFT
)) ret
|= MK_SHIFT
;
349 if (IS_KEY_DOWN(MessageQueue
->afKeyState
, VK_CONTROL
)) ret
|= MK_CONTROL
;
350 if (IS_KEY_DOWN(MessageQueue
->afKeyState
, VK_XBUTTON1
)) ret
|= MK_XBUTTON1
;
351 if (IS_KEY_DOWN(MessageQueue
->afKeyState
, VK_XBUTTON2
)) ret
|= MK_XBUTTON2
;
356 IntMsqSetWakeMask(DWORD WakeMask
)
358 PTHREADINFO Win32Thread
;
359 HANDLE MessageEventHandle
;
360 DWORD dwFlags
= HIWORD(WakeMask
);
362 Win32Thread
= PsGetCurrentThreadWin32Thread();
363 if (Win32Thread
== NULL
|| Win32Thread
->MessageQueue
== NULL
)
366 // Win32Thread->pEventQueueServer; IntMsqSetWakeMask returns Win32Thread->hEventQueueClient
367 MessageEventHandle
= Win32Thread
->hEventQueueClient
;
369 if (Win32Thread
->pcti
)
371 if ( (Win32Thread
->pcti
->fsChangeBits
& LOWORD(WakeMask
)) ||
372 ( (dwFlags
& MWMO_INPUTAVAILABLE
) && (Win32Thread
->pcti
->fsWakeBits
& LOWORD(WakeMask
)) ) )
374 ERR("Chg 0x%x Wake 0x%x Mask 0x%x\n",Win32Thread
->pcti
->fsChangeBits
, Win32Thread
->pcti
->fsWakeBits
, WakeMask
);
375 KeSetEvent(Win32Thread
->pEventQueueServer
, IO_NO_INCREMENT
, FALSE
); // Wake it up!
376 return MessageEventHandle
;
382 return MessageEventHandle
;
386 IntMsqClearWakeMask(VOID
)
388 PTHREADINFO Win32Thread
;
390 Win32Thread
= PsGetCurrentThreadWin32Thread();
391 if (Win32Thread
== NULL
|| Win32Thread
->MessageQueue
== NULL
)
393 // Very hacky, but that is what they do.
394 Win32Thread
->pcti
->fsWakeBits
= 0;
402 Due to the uncertainty of knowing what was set in our multilevel message queue,
403 and even if the bits are all cleared. The same as cTimers/cPaintsReady.
404 I think this is the best solution... (jt) */
406 MsqWakeQueue(PTHREADINFO pti
, DWORD MessageBits
, BOOL KeyEvent
)
408 PUSER_MESSAGE_QUEUE Queue
;
410 Queue
= pti
->MessageQueue
;
412 if (Queue
->QF_flags
& QF_INDESTROY
)
414 ERR("This Message Queue is in Destroy!\n");
416 pti
->pcti
->fsWakeBits
|= MessageBits
;
417 pti
->pcti
->fsChangeBits
|= MessageBits
;
419 // Start bit accounting to help clear the main set of bits.
420 if (MessageBits
& QS_KEY
)
422 pti
->nCntsQBits
[QSRosKey
]++;
424 if (MessageBits
& QS_MOUSE
)
426 if (MessageBits
& QS_MOUSEMOVE
) pti
->nCntsQBits
[QSRosMouseMove
]++;
427 if (MessageBits
& QS_MOUSEBUTTON
) pti
->nCntsQBits
[QSRosMouseButton
]++;
429 if (MessageBits
& QS_POSTMESSAGE
) pti
->nCntsQBits
[QSRosPostMessage
]++;
430 if (MessageBits
& QS_SENDMESSAGE
) pti
->nCntsQBits
[QSRosSendMessage
]++;
431 if (MessageBits
& QS_HOTKEY
) pti
->nCntsQBits
[QSRosHotKey
]++;
432 if (MessageBits
& QS_EVENT
) pti
->nCntsQBits
[QSRosEvent
]++;
435 KeSetEvent(pti
->pEventQueueServer
, IO_NO_INCREMENT
, FALSE
);
439 ClearMsgBitsMask(PTHREADINFO pti
, UINT MessageBits
)
443 if (MessageBits
& QS_KEY
)
445 if (--pti
->nCntsQBits
[QSRosKey
] == 0) ClrMask
|= QS_KEY
;
447 if (MessageBits
& QS_MOUSEMOVE
)
448 { // Account for tracking mouse moves..
449 if (pti
->nCntsQBits
[QSRosMouseMove
])
451 pti
->nCntsQBits
[QSRosMouseMove
] = 0; // Throttle down count. Up to > 3:1 entries are ignored.
452 ClrMask
|= QS_MOUSEMOVE
;
455 if (MessageBits
& QS_MOUSEBUTTON
)
457 if (--pti
->nCntsQBits
[QSRosMouseButton
] == 0) ClrMask
|= QS_MOUSEBUTTON
;
459 if (MessageBits
& QS_POSTMESSAGE
)
461 if (--pti
->nCntsQBits
[QSRosPostMessage
] == 0) ClrMask
|= QS_POSTMESSAGE
;
463 if (MessageBits
& QS_TIMER
) // ReactOS hard coded.
464 { // Handle timer bits here.
465 if ( pti
->cTimersReady
)
467 if (--pti
->cTimersReady
== 0) ClrMask
|= QS_TIMER
;
470 if (MessageBits
& QS_PAINT
) // ReactOS hard coded.
471 { // Handle paint bits here.
472 if ( pti
->cPaintsReady
)
474 if (--pti
->cPaintsReady
== 0) ClrMask
|= QS_PAINT
;
477 if (MessageBits
& QS_SENDMESSAGE
)
479 if (--pti
->nCntsQBits
[QSRosSendMessage
] == 0) ClrMask
|= QS_SENDMESSAGE
;
481 if (MessageBits
& QS_HOTKEY
)
483 if (--pti
->nCntsQBits
[QSRosHotKey
] == 0) ClrMask
|= QS_HOTKEY
;
485 if (MessageBits
& QS_EVENT
)
487 if (--pti
->nCntsQBits
[QSRosEvent
] == 0) ClrMask
|= QS_EVENT
;
490 pti
->pcti
->fsWakeBits
&= ~ClrMask
;
491 pti
->pcti
->fsChangeBits
&= ~ClrMask
;
495 MsqIncPaintCountQueue(PTHREADINFO pti
)
498 MsqWakeQueue(pti
, QS_PAINT
, TRUE
);
502 MsqDecPaintCountQueue(PTHREADINFO pti
)
504 ClearMsgBitsMask(pti
, QS_PAINT
);
508 Post the move or update the message still pending to be processed.
509 Do not overload the queue with mouse move messages.
512 MsqPostMouseMove(PTHREADINFO pti
, MSG
* Msg
, LONG_PTR ExtraInfo
)
514 PUSER_MESSAGE Message
;
515 PLIST_ENTRY ListHead
;
516 PUSER_MESSAGE_QUEUE MessageQueue
= pti
->MessageQueue
;
518 ListHead
= &MessageQueue
->HardwareMessagesListHead
;
520 // Do nothing if empty.
521 if (!IsListEmpty(ListHead
->Flink
))
523 // Look at the end of the list,
524 Message
= CONTAINING_RECORD(ListHead
->Blink
, USER_MESSAGE
, ListEntry
);
526 // If the mouse move message is existing on the list,
527 if (Message
->Msg
.message
== WM_MOUSEMOVE
)
529 // Overwrite the message with updated data!
532 MsqWakeQueue(pti
, QS_MOUSEMOVE
, TRUE
);
537 MsqPostMessage(pti
, Msg
, TRUE
, QS_MOUSEMOVE
, 0, ExtraInfo
);
541 Bring together the mouse move message.
542 Named "Coalesce" from Amine email ;^) (jt).
545 IntCoalesceMouseMove(PTHREADINFO pti
)
548 LARGE_INTEGER LargeTickCount
;
550 // Force time stamp to update, keeping message time in sync.
551 if (gdwMouseMoveTimeStamp
== 0)
553 KeQueryTickCount(&LargeTickCount
);
554 gdwMouseMoveTimeStamp
= MsqCalculateMessageTime(&LargeTickCount
);
557 // Build mouse move message.
559 Msg
.message
= WM_MOUSEMOVE
;
561 Msg
.lParam
= MAKELONG(gpsi
->ptCursor
.x
, gpsi
->ptCursor
.y
);
562 Msg
.time
= gdwMouseMoveTimeStamp
;
563 Msg
.pt
= gpsi
->ptCursor
;
566 MsqPostMouseMove(pti
, &Msg
, gdwMouseMoveExtraInfo
);
568 // Zero the time stamp.
569 gdwMouseMoveTimeStamp
= 0;
571 // Clear flag since the move was posted.
572 pti
->MessageQueue
->QF_flags
&= ~QF_MOUSEMOVED
;
576 co_MsqInsertMouseMessage(MSG
* Msg
, DWORD flags
, ULONG_PTR dwExtraInfo
, BOOL Hook
)
578 LARGE_INTEGER LargeTickCount
;
579 MSLLHOOKSTRUCT MouseHookData
;
581 PWND pwnd
, pwndDesktop
;
584 PUSER_MESSAGE_QUEUE MessageQueue
;
585 PSYSTEM_CURSORINFO CurInfo
;
587 KeQueryTickCount(&LargeTickCount
);
588 Msg
->time
= MsqCalculateMessageTime(&LargeTickCount
);
590 MouseHookData
.pt
.x
= LOWORD(Msg
->lParam
);
591 MouseHookData
.pt
.y
= HIWORD(Msg
->lParam
);
592 switch (Msg
->message
)
595 MouseHookData
.mouseData
= MAKELONG(0, GET_WHEEL_DELTA_WPARAM(Msg
->wParam
));
599 case WM_XBUTTONDBLCLK
:
600 case WM_NCXBUTTONDOWN
:
602 case WM_NCXBUTTONDBLCLK
:
603 MouseHookData
.mouseData
= MAKELONG(0, HIWORD(Msg
->wParam
));
606 MouseHookData
.mouseData
= 0;
610 MouseHookData
.flags
= flags
; // LLMHF_INJECTED
611 MouseHookData
.time
= Msg
->time
;
612 MouseHookData
.dwExtraInfo
= dwExtraInfo
;
614 /* If the hook procedure returned non zero, dont send the message */
617 if (co_HOOK_CallHooks(WH_MOUSE_LL
, HC_ACTION
, Msg
->message
, (LPARAM
) &MouseHookData
))
621 /* Get the desktop window */
622 pwndDesktop
= UserGetDesktopWindow();
623 if (!pwndDesktop
) return;
624 // pDesk = pwndDesktop->head.rpdesk;
626 /* Check if the mouse is captured */
627 Msg
->hwnd
= IntGetCaptureWindow();
628 if (Msg
->hwnd
!= NULL
)
630 pwnd
= UserGetWindowObject(Msg
->hwnd
);
634 pwnd
= IntTopLevelWindowFromPoint(Msg
->pt
.x
, Msg
->pt
.y
);
635 if (pwnd
) Msg
->hwnd
= pwnd
->head
.h
;
638 hdcScreen
= IntGetScreenDC();
639 CurInfo
= IntGetSysCursorInfo();
641 /* Check if we found a window */
642 if (Msg
->hwnd
!= NULL
&& pwnd
!= NULL
)
644 pti
= pwnd
->head
.pti
;
645 MessageQueue
= pti
->MessageQueue
;
647 if (MessageQueue
->QF_flags
& QF_INDESTROY
)
649 ERR("Mouse is over a Window with a Dead Message Queue!\n");
653 MessageQueue
->ptiMouse
= pti
;
655 if (Msg
->message
== WM_MOUSEMOVE
)
657 /* Check if cursor should be visible */
659 MessageQueue
->CursorObject
&&
660 MessageQueue
->iCursorLevel
>= 0)
662 /* Check if shape has changed */
663 if(CurInfo
->CurrentCursorObject
!= MessageQueue
->CursorObject
)
665 /* Call GDI to set the new screen cursor */
666 #ifdef NEW_CURSORICON
667 GreSetPointerShape(hdcScreen
,
668 MessageQueue
->CursorObject
->hbmAlpha
?
669 NULL
: MessageQueue
->CursorObject
->hbmMask
,
670 MessageQueue
->CursorObject
->hbmAlpha
?
671 MessageQueue
->CursorObject
->hbmAlpha
: MessageQueue
->CursorObject
->hbmColor
,
672 MessageQueue
->CursorObject
->xHotspot
,
673 MessageQueue
->CursorObject
->yHotspot
,
676 MessageQueue
->CursorObject
->hbmAlpha
? SPS_ALPHA
: 0);
678 GreSetPointerShape(hdcScreen
,
679 MessageQueue
->CursorObject
->IconInfo
.hbmMask
,
680 MessageQueue
->CursorObject
->IconInfo
.hbmColor
,
681 MessageQueue
->CursorObject
->IconInfo
.xHotspot
,
682 MessageQueue
->CursorObject
->IconInfo
.yHotspot
,
688 GreMovePointer(hdcScreen
, Msg
->pt
.x
, Msg
->pt
.y
);
690 /* Check if we have to hide cursor */
691 else if (CurInfo
->ShowingCursor
>= 0)
692 GreMovePointer(hdcScreen
, -1, -1);
694 /* Update global cursor info */
695 CurInfo
->ShowingCursor
= MessageQueue
->iCursorLevel
;
696 CurInfo
->CurrentCursorObject
= MessageQueue
->CursorObject
;
697 gpqCursor
= MessageQueue
;
699 /* Mouse move is a special case */
700 MessageQueue
->QF_flags
|= QF_MOUSEMOVED
;
701 gdwMouseMoveExtraInfo
= dwExtraInfo
;
702 gdwMouseMoveTimeStamp
= Msg
->time
;
703 MsqWakeQueue(pti
, QS_MOUSEMOVE
, TRUE
);
707 if (!IntGetCaptureWindow())
709 // ERR("ptiLastInput is set\n");
710 // ptiLastInput = pti; // Once this is set during Reboot or Shutdown, this prevents the exit window having foreground.
711 // Find all the Move Mouse calls and fix mouse set active focus issues......
714 // Post mouse move before posting mouse buttons, keep it in sync.
715 if (pti
->MessageQueue
->QF_flags
& QF_MOUSEMOVED
)
717 IntCoalesceMouseMove(pti
);
720 TRACE("Posting mouse message to hwnd=%p!\n", UserHMGetHandle(pwnd
));
721 MsqPostMessage(pti
, Msg
, TRUE
, QS_MOUSEBUTTON
, 0, dwExtraInfo
);
726 /* always show cursor on background; FIXME: set default pointer */
727 GreMovePointer(hdcScreen
, Msg
->pt
.x
, Msg
->pt
.y
);
728 CurInfo
->ShowingCursor
= 0;
732 PUSER_MESSAGE FASTCALL
733 MsqCreateMessage(LPMSG Msg
)
735 PUSER_MESSAGE Message
;
737 Message
= ExAllocateFromPagedLookasideList(pgMessageLookasideList
);
743 RtlZeroMemory(Message
, sizeof(*Message
));
744 RtlMoveMemory(&Message
->Msg
, Msg
, sizeof(MSG
));
750 MsqDestroyMessage(PUSER_MESSAGE Message
)
752 if (Message
->pti
== NULL
)
754 ERR("Double Free Message\n");
758 ExFreeToPagedLookasideList(pgMessageLookasideList
, Message
);
762 co_MsqDispatchOneSentMessage(PTHREADINFO pti
)
764 PUSER_SENT_MESSAGE SaveMsg
, Message
;
769 ASSERT(pti
== PsGetCurrentThreadWin32Thread());
771 if (IsListEmpty(&pti
->SentMessagesListHead
))
776 /* remove it from the list of pending messages */
777 Entry
= RemoveHeadList(&pti
->SentMessagesListHead
);
778 Message
= CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
);
780 SaveMsg
= pti
->pusmCurrent
;
781 pti
->pusmCurrent
= Message
;
783 // Processing a message sent to it from another thread.
784 if ( ( Message
->ptiSender
&& pti
!= Message
->ptiSender
) ||
785 ( Message
->ptiCallBackSender
&& pti
!= Message
->ptiCallBackSender
))
786 { // most likely, but, to be sure.
787 pti
->pcti
->CTI_flags
|= CTI_INSENDMESSAGE
; // Let the user know...
790 /* insert it to the list of messages that are currently dispatched by this
792 InsertTailList(&pti
->LocalDispatchingMessagesHead
, &Message
->ListEntry
);
794 ClearMsgBitsMask(pti
, Message
->QS_Flags
);
796 if (Message
->HookMessage
== MSQ_ISHOOK
)
797 { // Direct Hook Call processor
798 Result
= co_CallHook( Message
->Msg
.message
, // HookId
799 (INT
)(INT_PTR
)Message
->Msg
.hwnd
, // Code
801 Message
->Msg
.lParam
);
803 else if(Message
->HookMessage
== MSQ_INJECTMODULE
)
805 Result
= IntLoadHookModule(Message
->Msg
.message
,
806 (HHOOK
)Message
->Msg
.lParam
,
807 Message
->Msg
.wParam
);
809 else if ((Message
->CompletionCallback
) &&
810 (Message
->ptiCallBackSender
== pti
))
811 { /* Call the callback routine */
812 if (Message
->QS_Flags
& QS_SMRESULT
)
814 co_IntCallSentMessageCallback(Message
->CompletionCallback
,
816 Message
->Msg
.message
,
817 Message
->CompletionCallbackContext
,
819 /* Set callback to NULL to prevent reentry */
820 Message
->CompletionCallback
= NULL
;
824 /* The message has not been processed yet, reinsert it. */
825 RemoveEntryList(&Message
->ListEntry
);
826 InsertTailList(&Message
->ptiCallBackSender
->SentMessagesListHead
, &Message
->ListEntry
);
827 TRACE("Callback Message not processed yet. Requeuing the message\n");
833 { /* Call the window procedure. */
834 Result
= co_IntSendMessage( Message
->Msg
.hwnd
,
835 Message
->Msg
.message
,
837 Message
->Msg
.lParam
);
840 /* remove the message from the local dispatching list, because it doesn't need
841 to be cleaned up on thread termination anymore */
842 RemoveEntryList(&Message
->ListEntry
);
844 /* If the message is a callback, insert it in the callback senders MessageQueue */
845 if (Message
->CompletionCallback
)
847 if (Message
->ptiCallBackSender
)
849 Message
->lResult
= Result
;
850 Message
->QS_Flags
|= QS_SMRESULT
;
852 /* insert it in the callers message queue */
853 InsertTailList(&Message
->ptiCallBackSender
->SentMessagesListHead
, &Message
->ListEntry
);
854 MsqWakeQueue(Message
->ptiCallBackSender
, QS_SENDMESSAGE
, TRUE
);
860 /* remove the message from the dispatching list if needed, so lock the sender's message queue */
861 if (Message
->ptiSender
&& !(Message
->ptiSender
->TIF_flags
& TIF_INCLEANUP
))
863 if (Message
->DispatchingListEntry
.Flink
!= NULL
)
865 /* only remove it from the dispatching list if not already removed by a timeout */
866 RemoveEntryList(&Message
->DispatchingListEntry
);
869 /* still keep the sender's message queue locked, so the sender can't exit the
870 MsqSendMessage() function (if timed out) */
872 if (Message
->QS_Flags
& QS_SMRESULT
)
874 Result
= Message
->lResult
;
877 /* Let the sender know the result. */
878 if (Message
->Result
!= NULL
)
880 *Message
->Result
= Result
;
883 if (Message
->HasPackedLParam
)
885 if (Message
->Msg
.lParam
)
886 ExFreePool((PVOID
)Message
->Msg
.lParam
);
889 /* Notify the sender. */
890 if (Message
->CompletionEvent
!= NULL
)
892 KeSetEvent(Message
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
895 /* free the message */
896 ExFreePoolWithTag(Message
, TAG_USRMSG
);
899 /* do not hangup on the user if this is reentering */
900 if (!SaveMsg
) pti
->pcti
->CTI_flags
&= ~CTI_INSENDMESSAGE
;
901 pti
->pusmCurrent
= SaveMsg
;
907 MsqRemoveWindowMessagesFromQueue(PWND Window
)
910 PUSER_SENT_MESSAGE SentMessage
;
911 PUSER_MESSAGE PostedMessage
;
912 PLIST_ENTRY CurrentEntry
, ListHead
;
916 pti
= Window
->head
.pti
;
918 /* remove the posted messages for this window */
919 CurrentEntry
= pti
->PostedMessagesListHead
.Flink
;
920 ListHead
= &pti
->PostedMessagesListHead
;
921 while (CurrentEntry
!= ListHead
)
923 PostedMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
925 if (PostedMessage
->Msg
.hwnd
== Window
->head
.h
)
927 if (PostedMessage
->Msg
.message
== WM_QUIT
&& pti
->QuitPosted
== 0)
930 pti
->exitCode
= PostedMessage
->Msg
.wParam
;
932 RemoveEntryList(&PostedMessage
->ListEntry
);
933 ClearMsgBitsMask(pti
, PostedMessage
->QS_Flags
);
934 MsqDestroyMessage(PostedMessage
);
935 CurrentEntry
= pti
->PostedMessagesListHead
.Flink
;
939 CurrentEntry
= CurrentEntry
->Flink
;
943 /* remove the sent messages for this window */
944 CurrentEntry
= pti
->SentMessagesListHead
.Flink
;
945 ListHead
= &pti
->SentMessagesListHead
;
946 while (CurrentEntry
!= ListHead
)
948 SentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
, ListEntry
);
950 if(SentMessage
->Msg
.hwnd
== Window
->head
.h
)
952 TRACE("Notify the sender and remove a message from the queue that had not been dispatched\n");
954 RemoveEntryList(&SentMessage
->ListEntry
);
955 ClearMsgBitsMask(pti
, SentMessage
->QS_Flags
);
957 /* Only if the message has a sender was the queue referenced */
958 if ((SentMessage
->ptiSender
)
959 && (SentMessage
->DispatchingListEntry
.Flink
!= NULL
))
961 RemoveEntryList(&SentMessage
->DispatchingListEntry
);
964 /* wake the sender's thread */
965 if (SentMessage
->CompletionEvent
!= NULL
)
967 KeSetEvent(SentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
970 if (SentMessage
->HasPackedLParam
)
972 if (SentMessage
->Msg
.lParam
)
973 ExFreePool((PVOID
)SentMessage
->Msg
.lParam
);
976 /* free the message */
977 ExFreePoolWithTag(SentMessage
, TAG_USRMSG
);
979 CurrentEntry
= pti
->SentMessagesListHead
.Flink
;
983 CurrentEntry
= CurrentEntry
->Flink
;
989 co_MsqSendMessageAsync(PTHREADINFO ptiReceiver
,
994 SENDASYNCPROC CompletionCallback
,
995 ULONG_PTR CompletionCallbackContext
,
996 BOOL HasPackedLParam
,
1000 PTHREADINFO ptiSender
;
1001 PUSER_SENT_MESSAGE Message
;
1003 if(!(Message
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(USER_SENT_MESSAGE
), TAG_USRMSG
)))
1005 ERR("MsqSendMessage(): Not enough memory to allocate a message");
1009 ptiSender
= PsGetCurrentThreadWin32Thread();
1011 Message
->Msg
.hwnd
= hwnd
;
1012 Message
->Msg
.message
= Msg
;
1013 Message
->Msg
.wParam
= wParam
;
1014 Message
->Msg
.lParam
= lParam
;
1015 Message
->CompletionEvent
= NULL
;
1016 Message
->Result
= 0;
1017 Message
->lResult
= 0;
1018 Message
->ptiReceiver
= ptiReceiver
;
1019 Message
->ptiSender
= NULL
;
1020 Message
->ptiCallBackSender
= ptiSender
;
1021 Message
->DispatchingListEntry
.Flink
= NULL
;
1022 Message
->CompletionCallback
= CompletionCallback
;
1023 Message
->CompletionCallbackContext
= CompletionCallbackContext
;
1024 Message
->HookMessage
= HookMessage
;
1025 Message
->HasPackedLParam
= HasPackedLParam
;
1026 Message
->QS_Flags
= QS_SENDMESSAGE
;
1028 InsertTailList(&ptiReceiver
->SentMessagesListHead
, &Message
->ListEntry
);
1029 MsqWakeQueue(ptiReceiver
, QS_SENDMESSAGE
, TRUE
);
1035 co_MsqSendMessage(PTHREADINFO ptirec
,
1046 PUSER_SENT_MESSAGE Message
;
1047 KEVENT CompletionEvent
;
1048 NTSTATUS WaitStatus
;
1049 LARGE_INTEGER Timeout
;
1052 LRESULT Result
= 0; //// Result could be trashed. ////
1054 pti
= PsGetCurrentThreadWin32Thread();
1055 ASSERT(pti
!= ptirec
);
1056 ASSERT(ptirec
->pcti
); // Send must have a client side to receive it!!!!
1058 /* Don't send from or to a dying thread */
1059 if (pti
->TIF_flags
& TIF_INCLEANUP
|| ptirec
->TIF_flags
& TIF_INCLEANUP
)
1061 // Unless we are dying and need to tell our parents.
1062 if (pti
->TIF_flags
& TIF_INCLEANUP
&& !(ptirec
->TIF_flags
& TIF_INCLEANUP
))
1064 // Parent notify is the big one. Fire and forget!
1065 TRACE("Send message from dying thread %u\n", Msg
);
1066 co_MsqSendMessageAsync(ptirec
, Wnd
, Msg
, wParam
, lParam
, NULL
, 0, FALSE
, HookMessage
);
1068 if (uResult
) *uResult
= -1;
1069 TRACE("MsqSM: Msg %u Current pti %lu or Rec pti %lu\n", Msg
, pti
->TIF_flags
& TIF_INCLEANUP
, ptirec
->TIF_flags
& TIF_INCLEANUP
);
1070 return STATUS_UNSUCCESSFUL
;
1073 // Should we do the same for No Wait?
1074 if ( HookMessage
== MSQ_NORMAL
)
1076 pWnd
= ValidateHwndNoErr(Wnd
);
1078 // These can not cross International Border lines!
1079 if ( pti
->ppi
!= ptirec
->ppi
&& pWnd
)
1083 // Handle the special case when working with password transfers across bordering processes.
1085 case EM_SETPASSWORDCHAR
:
1087 // Look for edit controls setup for passwords.
1088 if ( gpsi
->atomSysClass
[ICLS_EDIT
] == pWnd
->pcls
->atomClassName
&& // Use atomNVClassName.
1089 pWnd
->style
& ES_PASSWORD
)
1091 if (uResult
) *uResult
= -1;
1092 ERR("Running across the border without a passport!\n");
1093 EngSetLastError(ERROR_ACCESS_DENIED
);
1094 return STATUS_UNSUCCESSFUL
;
1098 if (uResult
) *uResult
= -1;
1099 ERR("Running across the border without a passport!\n");
1100 return STATUS_UNSUCCESSFUL
;
1104 // These can not cross State lines!
1105 if ( Msg
== WM_CREATE
|| Msg
== WM_NCCREATE
)
1107 if (uResult
) *uResult
= -1;
1108 ERR("Can not tell the other State we have Create!\n");
1109 return STATUS_UNSUCCESSFUL
;
1113 if(!(Message
= ExAllocatePoolWithTag(PagedPool
, sizeof(USER_SENT_MESSAGE
), TAG_USRMSG
)))
1115 ERR("MsqSendMessage(): Not enough memory to allocate a message\n");
1116 return STATUS_INSUFFICIENT_RESOURCES
;
1119 KeInitializeEvent(&CompletionEvent
, NotificationEvent
, FALSE
);
1121 Timeout
.QuadPart
= Int32x32To64(-10000,uTimeout
); // Pass SMTO test with a TO of 0x80000000.
1122 TRACE("Timeout val %lld\n",Timeout
.QuadPart
)
1124 /* FIXME: Increase reference counter of sender's message queue here */
1126 Message
->Msg
.hwnd
= Wnd
;
1127 Message
->Msg
.message
= Msg
;
1128 Message
->Msg
.wParam
= wParam
;
1129 Message
->Msg
.lParam
= lParam
;
1130 Message
->CompletionEvent
= &CompletionEvent
;
1131 Message
->Result
= &Result
;
1132 Message
->lResult
= 0;
1133 Message
->QS_Flags
= 0;
1134 Message
->ptiReceiver
= ptirec
;
1135 Message
->ptiSender
= pti
;
1136 Message
->ptiCallBackSender
= NULL
;
1137 Message
->CompletionCallback
= NULL
;
1138 Message
->CompletionCallbackContext
= 0;
1139 Message
->HookMessage
= HookMessage
;
1140 Message
->HasPackedLParam
= FALSE
;
1142 /* Add it to the list of pending messages */
1143 InsertTailList(&pti
->DispatchingMessagesHead
, &Message
->DispatchingListEntry
);
1145 /* Queue it in the destination's message queue */
1146 InsertTailList(&ptirec
->SentMessagesListHead
, &Message
->ListEntry
);
1148 Message
->QS_Flags
= QS_SENDMESSAGE
;
1149 MsqWakeQueue(ptirec
, QS_SENDMESSAGE
, TRUE
);
1151 /* We can't access the Message anymore since it could have already been deleted! */
1155 PVOID WaitObjects
[2];
1157 WaitObjects
[0] = &CompletionEvent
; // Wait 0
1158 WaitObjects
[1] = ptirec
->pEThread
; // Wait 1
1162 WaitStatus
= KeWaitForMultipleObjects(2, WaitObjects
, WaitAny
, UserRequest
,
1163 UserMode
, FALSE
, (uTimeout
? &Timeout
: NULL
), NULL
);
1167 if (WaitStatus
== STATUS_TIMEOUT
)
1169 /* Look up if the message has not yet dispatched, if so
1170 make sure it can't pass a result and it must not set the completion event anymore */
1171 Entry
= ptirec
->SentMessagesListHead
.Flink
;
1172 while (Entry
!= &ptirec
->SentMessagesListHead
)
1174 if (CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
) == Message
)
1176 /* We can access Message here, it's secure because the message queue is locked
1177 and the message is still hasn't been dispatched */
1178 Message
->CompletionEvent
= NULL
;
1179 Message
->Result
= NULL
;
1180 RemoveEntryList(&Message
->ListEntry
);
1181 RemoveEntryList(&Message
->DispatchingListEntry
);
1182 ClearMsgBitsMask(ptirec
, Message
->QS_Flags
);
1183 ExFreePoolWithTag(Message
, TAG_USRMSG
);
1186 Entry
= Entry
->Flink
;
1189 TRACE("MsqSendMessage (blocked) timed out 1 Status %lx\n", WaitStatus
);
1191 // Receiving thread passed on and left us hanging with issues still pending.
1192 else if (WaitStatus
== STATUS_WAIT_1
)
1194 ERR("Bk Receiving Thread woken up dead!\n");
1195 Entry
= pti
->DispatchingMessagesHead
.Flink
;
1196 while (Entry
!= &pti
->DispatchingMessagesHead
)
1198 if (CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, DispatchingListEntry
) == Message
)
1200 Message
->CompletionEvent
= NULL
;
1201 Message
->Result
= NULL
;
1202 RemoveEntryList(&Message
->DispatchingListEntry
);
1203 Message
->DispatchingListEntry
.Flink
= NULL
;
1206 Entry
= Entry
->Flink
;
1209 while (co_MsqDispatchOneSentMessage(pti
))
1214 PVOID WaitObjects
[3];
1216 WaitObjects
[0] = &CompletionEvent
; // Wait 0
1217 WaitObjects
[1] = pti
->pEventQueueServer
; // Wait 1
1218 WaitObjects
[2] = ptirec
->pEThread
; // Wait 2
1224 WaitStatus
= KeWaitForMultipleObjects(3, WaitObjects
, WaitAny
, UserRequest
,
1225 UserMode
, FALSE
, (uTimeout
? &Timeout
: NULL
), NULL
);
1229 if (WaitStatus
== STATUS_TIMEOUT
)
1231 /* Look up if the message has not yet been dispatched, if so
1232 make sure it can't pass a result and it must not set the completion event anymore */
1233 Entry
= ptirec
->SentMessagesListHead
.Flink
;
1234 while (Entry
!= &ptirec
->SentMessagesListHead
)
1236 if (CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
) == Message
)
1238 /* We can access Message here, it's secure because the message queue is locked
1239 and the message is still hasn't been dispatched */
1240 Message
->CompletionEvent
= NULL
;
1241 Message
->Result
= NULL
;
1242 RemoveEntryList(&Message
->ListEntry
);
1243 RemoveEntryList(&Message
->DispatchingListEntry
);
1244 ClearMsgBitsMask(ptirec
, Message
->QS_Flags
);
1245 ExFreePoolWithTag(Message
, TAG_USRMSG
);
1248 Entry
= Entry
->Flink
;
1251 TRACE("MsqSendMessage timed out 2 Status %lx\n", WaitStatus
);
1255 // Receiving thread passed on and left us hanging with issues still pending.
1256 else if (WaitStatus
== STATUS_WAIT_2
)
1258 ERR("NB Receiving Thread woken up dead!\n");
1259 Entry
= pti
->DispatchingMessagesHead
.Flink
;
1260 while (Entry
!= &pti
->DispatchingMessagesHead
)
1262 if (CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, DispatchingListEntry
) == Message
)
1264 Message
->CompletionEvent
= NULL
;
1265 Message
->Result
= NULL
;
1266 RemoveEntryList(&Message
->DispatchingListEntry
);
1267 Message
->DispatchingListEntry
.Flink
= NULL
;
1270 Entry
= Entry
->Flink
;
1274 if (WaitStatus
== STATUS_USER_APC
) break;
1276 while (co_MsqDispatchOneSentMessage(pti
))
1278 } while (WaitStatus
== STATUS_WAIT_1
);
1281 if (WaitStatus
== STATUS_USER_APC
)
1283 // The current thread is dying!
1284 TRACE("User APC\n");
1285 co_IntDeliverUserAPC();
1286 ERR("User APC Returned\n"); // Should not see this message.
1289 if (WaitStatus
!= STATUS_TIMEOUT
)
1293 *uResult
= (STATUS_WAIT_0
== WaitStatus
? Result
: -1);
1301 MsqPostMessage(PTHREADINFO pti
,
1303 BOOLEAN HardwareMessage
,
1308 PUSER_MESSAGE Message
;
1309 PUSER_MESSAGE_QUEUE MessageQueue
;
1311 if ( pti
->TIF_flags
& TIF_INCLEANUP
|| pti
->MessageQueue
->QF_flags
& QF_INDESTROY
)
1313 ERR("Post Msg; Thread or Q is Dead!\n");
1317 if(!(Message
= MsqCreateMessage(Msg
)))
1322 MessageQueue
= pti
->MessageQueue
;
1324 if (!HardwareMessage
)
1326 InsertTailList(&pti
->PostedMessagesListHead
, &Message
->ListEntry
);
1330 InsertTailList(&MessageQueue
->HardwareMessagesListHead
, &Message
->ListEntry
);
1333 if (Msg
->message
== WM_HOTKEY
) MessageBits
|= QS_HOTKEY
; // Justin Case, just set it.
1334 Message
->dwQEvent
= dwQEvent
;
1335 Message
->ExtraInfo
= ExtraInfo
;
1336 Message
->QS_Flags
= MessageBits
;
1338 MsqWakeQueue(pti
, MessageBits
, TRUE
);
1342 MsqPostQuitMessage(PTHREADINFO pti
, ULONG ExitCode
)
1344 pti
->QuitPosted
= TRUE
;
1345 pti
->exitCode
= ExitCode
;
1346 MsqWakeQueue(pti
, QS_POSTMESSAGE
|QS_ALLPOSTMESSAGE
, TRUE
);
1349 /***********************************************************************
1350 * MsqSendParentNotify
1352 * Send a WM_PARENTNOTIFY to all ancestors of the given window, unless
1353 * the window has the WS_EX_NOPARENTNOTIFY style.
1355 static void MsqSendParentNotify( PWND pwnd
, WORD event
, WORD idChild
, POINT pt
)
1357 PWND pwndDesktop
= UserGetDesktopWindow();
1359 /* pt has to be in the client coordinates of the parent window */
1360 pt
.x
+= pwndDesktop
->rcClient
.left
- pwnd
->rcClient
.left
;
1361 pt
.y
+= pwndDesktop
->rcClient
.top
- pwnd
->rcClient
.top
;
1367 if (!(pwnd
->style
& WS_CHILD
)) break;
1368 if (pwnd
->ExStyle
& WS_EX_NOPARENTNOTIFY
) break;
1369 if (!(pwndParent
= IntGetParent(pwnd
))) break;
1370 if (pwndParent
== pwndDesktop
) break;
1371 pt
.x
+= pwnd
->rcClient
.left
- pwndParent
->rcClient
.left
;
1372 pt
.y
+= pwnd
->rcClient
.top
- pwndParent
->rcClient
.top
;
1375 co_IntSendMessage( UserHMGetHandle(pwnd
), WM_PARENTNOTIFY
,
1376 MAKEWPARAM( event
, idChild
), MAKELPARAM( pt
.x
, pt
.y
) );
1382 IntTrackMouseMove(PWND pwndTrack
, PDESKTOP pDesk
, PMSG msg
, USHORT hittest
)
1384 // PWND pwndTrack = IntChildrenWindowFromPoint(pwndMsg, msg->pt.x, msg->pt.y);
1385 // hittest = (USHORT)GetNCHitEx(pwndTrack, msg->pt); /// @todo WTF is this???
1387 if ( pDesk
->spwndTrack
!= pwndTrack
|| // Change with tracking window or
1388 msg
->message
!= WM_MOUSEMOVE
|| // Mouse click changes or
1389 pDesk
->htEx
!= hittest
) // Change in current hit test states.
1391 TRACE("ITMM: Track Mouse Move!\n");
1393 /* Handle only the changing window track and mouse move across a border. */
1394 if ( pDesk
->spwndTrack
!= pwndTrack
||
1395 (pDesk
->htEx
== HTCLIENT
) ^ (hittest
== HTCLIENT
) )
1397 TRACE("ITMM: Another Wnd %d or Across Border %d\n",
1398 pDesk
->spwndTrack
!= pwndTrack
,(pDesk
->htEx
== HTCLIENT
) ^ (hittest
== HTCLIENT
));
1400 if ( pDesk
->dwDTFlags
& DF_TME_LEAVE
)
1401 UserPostMessage( UserHMGetHandle(pDesk
->spwndTrack
),
1402 (pDesk
->htEx
!= HTCLIENT
) ? WM_NCMOUSELEAVE
: WM_MOUSELEAVE
,
1405 if ( pDesk
->dwDTFlags
& DF_TME_HOVER
)
1406 IntKillTimer(pDesk
->spwndTrack
, ID_EVENT_SYSTIMER_MOUSEHOVER
, TRUE
);
1408 /* Clear the flags to sign a change. */
1409 pDesk
->dwDTFlags
&= ~(DF_TME_LEAVE
|DF_TME_HOVER
);
1411 /* Set the Track window and hit test. */
1412 pDesk
->spwndTrack
= pwndTrack
;
1413 pDesk
->htEx
= hittest
;
1416 /* Reset, Same Track window, Hover set and Mouse Clicks or Clobbered Hover box. */
1417 if ( pDesk
->spwndTrack
== pwndTrack
&&
1418 ( msg
->message
!= WM_MOUSEMOVE
|| !RECTL_bPointInRect(&pDesk
->rcMouseHover
, msg
->pt
.x
, msg
->pt
.y
)) &&
1419 pDesk
->dwDTFlags
& DF_TME_HOVER
)
1421 TRACE("ITMM: Reset Hover points!\n");
1422 // Restart timer for the hover period.
1423 IntSetTimer(pDesk
->spwndTrack
, ID_EVENT_SYSTIMER_MOUSEHOVER
, pDesk
->dwMouseHoverTime
, SystemTimerProc
, TMRF_SYSTEM
);
1424 // Reset desktop mouse hover from the system default hover rectangle.
1425 RECTL_vSetRect(&pDesk
->rcMouseHover
,
1426 msg
->pt
.x
- gspv
.iMouseHoverWidth
/ 2,
1427 msg
->pt
.y
- gspv
.iMouseHoverHeight
/ 2,
1428 msg
->pt
.x
+ gspv
.iMouseHoverWidth
/ 2,
1429 msg
->pt
.y
+ gspv
.iMouseHoverHeight
/ 2);
1433 BOOL
co_IntProcessMouseMessage(MSG
* msg
, BOOL
* RemoveMessages
, UINT first
, UINT last
)
1440 MOUSEHOOKSTRUCT hook
;
1441 BOOL eatMsg
= FALSE
;
1443 PWND pwndMsg
, pwndDesktop
;
1444 PUSER_MESSAGE_QUEUE MessageQueue
;
1446 PSYSTEM_CURSORINFO CurInfo
;
1449 pti
= PsGetCurrentThreadWin32Thread();
1450 pwndDesktop
= UserGetDesktopWindow();
1451 MessageQueue
= pti
->MessageQueue
;
1452 CurInfo
= IntGetSysCursorInfo();
1453 pwndMsg
= ValidateHwndNoErr(msg
->hwnd
);
1454 clk_msg
= MessageQueue
->msgDblClk
;
1455 pDesk
= pwndDesktop
->head
.rpdesk
;
1457 /* find the window to dispatch this mouse message to */
1458 if (MessageQueue
->spwndCapture
)
1461 pwndMsg
= MessageQueue
->spwndCapture
;
1466 Start with null window. See wine win.c:test_mouse_input:WM_COMMAND tests.
1468 pwndMsg
= co_WinPosWindowFromPoint( NULL
, &msg
->pt
, &hittest
, FALSE
);
1471 TRACE("Got mouse message for %p, hittest: 0x%x\n", msg
->hwnd
, hittest
);
1473 // Null window or not the same "Hardware" message queue.
1474 if (pwndMsg
== NULL
|| pwndMsg
->head
.pti
->MessageQueue
!= pti
->MessageQueue
)
1476 // Crossing a boundary, so set cursor. See default message queue cursor.
1477 UserSetCursor(SYSTEMCUR(ARROW
), FALSE
);
1478 /* Remove and ignore the message */
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
, UINT first
, UINT last
)
1817 if ( IS_MOUSE_MESSAGE(Msg
->message
))
1819 return co_IntProcessMouseMessage(Msg
, RemoveMessages
, 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
,
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("MsqPeekHardwareMessage: Thread Q is locked to another pti!\n");
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
;
1911 UpdateKeyStateFromMsg(MessageQueue
, &msg
);
1912 AcceptMessage
= co_IntProcessHardwareMessage(&msg
, &Remove
, MsgFilterLow
, MsgFilterHigh
);
1916 if (CurrentMessage
->pti
!= NULL
)
1918 RemoveEntryList(&CurrentMessage
->ListEntry
);
1919 MsqDestroyMessage(CurrentMessage
);
1921 ClearMsgBitsMask(pti
, QS_Flags
);
1924 MessageQueue
->idSysPeek
= idSave
;
1935 MessageQueue
->ptiSysLock
= NULL
;
1936 pti
->pcti
->CTI_flags
&= ~CTI_THREADSYSLOCK
;
1941 MsqPeekMessage(IN PTHREADINFO pti
,
1944 IN UINT MsgFilterLow
,
1945 IN UINT MsgFilterHigh
,
1947 OUT LONG_PTR
*ExtraInfo
,
1950 PUSER_MESSAGE CurrentMessage
;
1951 PLIST_ENTRY ListHead
;
1955 ListHead
= pti
->PostedMessagesListHead
.Flink
;
1957 if (IsListEmpty(ListHead
)) return FALSE
;
1959 while(ListHead
!= &pti
->PostedMessagesListHead
)
1961 CurrentMessage
= CONTAINING_RECORD(ListHead
, USER_MESSAGE
, ListEntry
);
1962 ListHead
= ListHead
->Flink
;
1965 1: any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL.
1966 2: retrieves only messages on the current thread's message queue whose hwnd value is NULL.
1967 3: handle to the window whose messages are to be retrieved.
1969 if ( ( !Window
|| // 1
1970 ( Window
== PWND_BOTTOM
&& CurrentMessage
->Msg
.hwnd
== NULL
) || // 2
1971 ( Window
!= PWND_BOTTOM
&& Window
->head
.h
== CurrentMessage
->Msg
.hwnd
) ) && // 3
1972 ( ( ( MsgFilterLow
== 0 && MsgFilterHigh
== 0 ) && CurrentMessage
->QS_Flags
& QSflags
) ||
1973 ( MsgFilterLow
<= CurrentMessage
->Msg
.message
&& MsgFilterHigh
>= CurrentMessage
->Msg
.message
) ) )
1975 *Message
= CurrentMessage
->Msg
;
1976 *ExtraInfo
= CurrentMessage
->ExtraInfo
;
1977 QS_Flags
= CurrentMessage
->QS_Flags
;
1981 if (CurrentMessage
->pti
!= NULL
)
1983 RemoveEntryList(&CurrentMessage
->ListEntry
);
1984 MsqDestroyMessage(CurrentMessage
);
1986 ClearMsgBitsMask(pti
, QS_Flags
);
1997 co_MsqWaitForNewMessages(PTHREADINFO pti
, PWND WndFilter
,
1998 UINT MsgFilterMin
, UINT MsgFilterMax
)
2002 ret
= KeWaitForSingleObject( pti
->pEventQueueServer
,
2008 if ( ret
== STATUS_USER_APC
)
2010 TRACE("MWFNW User APC\n");
2011 co_IntDeliverUserAPC();
2017 MsqIsHung(PTHREADINFO pti
)
2019 LARGE_INTEGER LargeTickCount
;
2021 KeQueryTickCount(&LargeTickCount
);
2022 return ((LargeTickCount
.u
.LowPart
- pti
->timeLast
) > MSQ_HUNG
);
2027 HungAppSysTimerProc(HWND hwnd
, UINT uMsg
, UINT_PTR idEvent
, DWORD dwTime
)
2030 TRACE("HungAppSysTimerProc\n");
2031 // Process list of windows that are hung and waiting.
2035 MsqInitializeMessageQueue(PTHREADINFO pti
, PUSER_MESSAGE_QUEUE MessageQueue
)
2037 MessageQueue
->CaretInfo
= (PTHRDCARETINFO
)(MessageQueue
+ 1);
2038 InitializeListHead(&MessageQueue
->HardwareMessagesListHead
); // Keep here!
2039 MessageQueue
->spwndFocus
= NULL
;
2040 MessageQueue
->iCursorLevel
= 0;
2041 MessageQueue
->CursorObject
= SYSTEMCUR(WAIT
); // See test_initial_cursor.
2042 if (MessageQueue
->CursorObject
)
2044 TRACE("Default cursor hcur %p\n",UserHMGetHandle(MessageQueue
->CursorObject
));
2045 UserReferenceObject(MessageQueue
->CursorObject
);
2047 RtlCopyMemory(MessageQueue
->afKeyState
, gafAsyncKeyState
, sizeof(gafAsyncKeyState
));
2048 MessageQueue
->ptiMouse
= pti
;
2049 MessageQueue
->ptiKeyboard
= pti
;
2050 MessageQueue
->cThreads
++;
2056 MsqCleanupThreadMsgs(PTHREADINFO pti
)
2058 PLIST_ENTRY CurrentEntry
;
2059 PUSER_MESSAGE CurrentMessage
;
2060 PUSER_SENT_MESSAGE CurrentSentMessage
;
2062 /* cleanup posted messages */
2063 while (!IsListEmpty(&pti
->PostedMessagesListHead
))
2065 CurrentEntry
= RemoveHeadList(&pti
->PostedMessagesListHead
);
2066 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
, ListEntry
);
2067 if (CurrentMessage
->dwQEvent
)
2069 if (CurrentMessage
->dwQEvent
== POSTEVENT_NWE
)
2071 ExFreePoolWithTag( (PVOID
)CurrentMessage
->ExtraInfo
, TAG_HOOK
);
2074 MsqDestroyMessage(CurrentMessage
);
2077 /* remove the messages that have not yet been dispatched */
2078 while (!IsListEmpty(&pti
->SentMessagesListHead
))
2080 CurrentEntry
= RemoveHeadList(&pti
->SentMessagesListHead
);
2081 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
, ListEntry
);
2083 TRACE("Notify the sender and remove a message from the queue that had not been dispatched\n");
2084 /* Only if the message has a sender was the message in the DispatchingList */
2085 if ((CurrentSentMessage
->ptiSender
)
2086 && (CurrentSentMessage
->DispatchingListEntry
.Flink
!= NULL
))
2088 RemoveEntryList(&CurrentSentMessage
->DispatchingListEntry
);
2091 /* wake the sender's thread */
2092 if (CurrentSentMessage
->CompletionEvent
!= NULL
)
2094 KeSetEvent(CurrentSentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
2097 if (CurrentSentMessage
->HasPackedLParam
)
2099 if (CurrentSentMessage
->Msg
.lParam
)
2100 ExFreePool((PVOID
)CurrentSentMessage
->Msg
.lParam
);
2103 /* free the message */
2104 ExFreePool(CurrentSentMessage
);
2107 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
2108 ExitThread() was called in a SendMessage() umode callback */
2109 while (!IsListEmpty(&pti
->LocalDispatchingMessagesHead
))
2111 CurrentEntry
= RemoveHeadList(&pti
->LocalDispatchingMessagesHead
);
2112 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
, ListEntry
);
2114 /* remove the message from the dispatching list */
2115 if(CurrentSentMessage
->DispatchingListEntry
.Flink
!= NULL
)
2117 RemoveEntryList(&CurrentSentMessage
->DispatchingListEntry
);
2120 TRACE("Notify the sender, the thread has been terminated while dispatching a message!\n");
2122 /* wake the sender's thread */
2123 if (CurrentSentMessage
->CompletionEvent
!= NULL
)
2125 KeSetEvent(CurrentSentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
2128 if (CurrentSentMessage
->HasPackedLParam
)
2130 if (CurrentSentMessage
->Msg
.lParam
)
2131 ExFreePool((PVOID
)CurrentSentMessage
->Msg
.lParam
);
2134 /* free the message */
2135 ExFreePool(CurrentSentMessage
);
2138 /* tell other threads not to bother returning any info to us */
2139 while (! IsListEmpty(&pti
->DispatchingMessagesHead
))
2141 CurrentEntry
= RemoveHeadList(&pti
->DispatchingMessagesHead
);
2142 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
, DispatchingListEntry
);
2143 CurrentSentMessage
->CompletionEvent
= NULL
;
2144 CurrentSentMessage
->Result
= NULL
;
2146 /* do NOT dereference our message queue as it might get attempted to be
2150 // Clear it all out.
2153 pti
->pcti
->fsWakeBits
= 0;
2154 pti
->pcti
->fsChangeBits
= 0;
2157 pti
->nCntsQBits
[QSRosKey
] = 0;
2158 pti
->nCntsQBits
[QSRosMouseMove
] = 0;
2159 pti
->nCntsQBits
[QSRosMouseButton
] = 0;
2160 pti
->nCntsQBits
[QSRosPostMessage
] = 0;
2161 pti
->nCntsQBits
[QSRosSendMessage
] = 0;
2162 pti
->nCntsQBits
[QSRosHotKey
] = 0;
2163 pti
->nCntsQBits
[QSRosEvent
] = 0;
2167 MsqCleanupMessageQueue(PTHREADINFO pti
)
2169 PUSER_MESSAGE_QUEUE MessageQueue
;
2171 MessageQueue
= pti
->MessageQueue
;
2172 MessageQueue
->cThreads
--;
2174 if (MessageQueue
->cThreads
)
2176 if (MessageQueue
->ptiSysLock
== pti
) MessageQueue
->ptiSysLock
= NULL
;
2179 if (MessageQueue
->CursorObject
)
2181 PCURICON_OBJECT pCursor
= MessageQueue
->CursorObject
;
2183 /* Change to another cursor if we going to dereference current one
2184 Note: we can't use UserSetCursor because it uses current thread
2185 message queue instead of queue given for cleanup */
2186 if (IntGetSysCursorInfo()->CurrentCursorObject
== pCursor
)
2190 /* Get the screen DC */
2191 hdcScreen
= IntGetScreenDC();
2193 GreMovePointer(hdcScreen
, -1, -1);
2194 IntGetSysCursorInfo()->CurrentCursorObject
= NULL
;
2197 TRACE("DereferenceObject pCursor\n");
2198 UserDereferenceObject(pCursor
);
2201 if (gpqForeground
== MessageQueue
)
2203 IntSetFocusMessageQueue(NULL
);
2205 if (gpqForegroundPrev
== MessageQueue
)
2207 gpqForegroundPrev
= NULL
;
2209 if (gpqCursor
== MessageQueue
)
2215 PUSER_MESSAGE_QUEUE FASTCALL
2216 MsqCreateMessageQueue(PTHREADINFO pti
)
2218 PUSER_MESSAGE_QUEUE MessageQueue
;
2220 MessageQueue
= (PUSER_MESSAGE_QUEUE
)ExAllocatePoolWithTag(NonPagedPool
,
2221 sizeof(USER_MESSAGE_QUEUE
) + sizeof(THRDCARETINFO
),
2229 RtlZeroMemory(MessageQueue
, sizeof(USER_MESSAGE_QUEUE
) + sizeof(THRDCARETINFO
));
2230 /* hold at least one reference until it'll be destroyed */
2231 IntReferenceMessageQueue(MessageQueue
);
2232 /* initialize the queue */
2233 if (!MsqInitializeMessageQueue(pti
, MessageQueue
))
2235 IntDereferenceMessageQueue(MessageQueue
);
2239 return MessageQueue
;
2243 MsqDestroyMessageQueue(PTHREADINFO pti
)
2246 PUSER_MESSAGE_QUEUE MessageQueue
= pti
->MessageQueue
;
2248 MessageQueue
->QF_flags
|= QF_INDESTROY
;
2250 /* remove the message queue from any desktops */
2251 if ((desk
= InterlockedExchangePointer((PVOID
*)&MessageQueue
->Desktop
, 0)))
2253 (void)InterlockedExchangePointer((PVOID
*)&desk
->ActiveMessageQueue
, 0);
2254 IntDereferenceMessageQueue(MessageQueue
);
2258 MsqCleanupMessageQueue(pti
);
2260 /* decrease the reference counter, if it hits zero, the queue will be freed */
2261 IntDereferenceMessageQueue(MessageQueue
);
2265 MsqSetMessageExtraInfo(LPARAM lParam
)
2269 PUSER_MESSAGE_QUEUE MessageQueue
;
2271 pti
= PsGetCurrentThreadWin32Thread();
2272 MessageQueue
= pti
->MessageQueue
;
2278 Ret
= MessageQueue
->ExtraInfo
;
2279 MessageQueue
->ExtraInfo
= lParam
;
2285 MsqGetMessageExtraInfo(VOID
)
2288 PUSER_MESSAGE_QUEUE MessageQueue
;
2290 pti
= PsGetCurrentThreadWin32Thread();
2291 MessageQueue
= pti
->MessageQueue
;
2297 return MessageQueue
->ExtraInfo
;
2300 // ReplyMessage is called by the thread receiving the window message.
2302 co_MsqReplyMessage( LRESULT lResult
)
2304 PUSER_SENT_MESSAGE Message
;
2307 pti
= PsGetCurrentThreadWin32Thread();
2308 Message
= pti
->pusmCurrent
;
2310 if (!Message
) return FALSE
;
2312 if (Message
->QS_Flags
& QS_SMRESULT
) return FALSE
;
2314 // SendMessageXxx || Callback msg and not a notify msg
2315 if (Message
->ptiSender
|| Message
->CompletionCallback
)
2317 Message
->lResult
= lResult
;
2318 Message
->QS_Flags
|= QS_SMRESULT
;
2319 // See co_MsqDispatchOneSentMessage, change bits already accounted for and cleared and this msg is going away..
2325 MsqSetStateWindow(PTHREADINFO pti
, ULONG Type
, HWND hWnd
)
2328 PUSER_MESSAGE_QUEUE MessageQueue
;
2330 MessageQueue
= pti
->MessageQueue
;
2334 case MSQ_STATE_CAPTURE
:
2335 Prev
= MessageQueue
->spwndCapture
? UserHMGetHandle(MessageQueue
->spwndCapture
) : 0;
2336 MessageQueue
->spwndCapture
= ValidateHwndNoErr(hWnd
);
2338 case MSQ_STATE_ACTIVE
:
2339 Prev
= MessageQueue
->spwndActive
? UserHMGetHandle(MessageQueue
->spwndActive
) : 0;
2340 MessageQueue
->spwndActive
= ValidateHwndNoErr(hWnd
);
2342 case MSQ_STATE_FOCUS
:
2343 Prev
= MessageQueue
->spwndFocus
? UserHMGetHandle(MessageQueue
->spwndFocus
) : 0;
2344 MessageQueue
->spwndFocus
= ValidateHwndNoErr(hWnd
);
2346 case MSQ_STATE_MENUOWNER
:
2347 Prev
= MessageQueue
->MenuOwner
;
2348 MessageQueue
->MenuOwner
= hWnd
;
2350 case MSQ_STATE_MOVESIZE
:
2351 Prev
= MessageQueue
->MoveSize
;
2352 MessageQueue
->MoveSize
= hWnd
;
2354 case MSQ_STATE_CARET
:
2355 ASSERT(MessageQueue
->CaretInfo
);
2356 Prev
= MessageQueue
->CaretInfo
->hWnd
;
2357 MessageQueue
->CaretInfo
->hWnd
= hWnd
;
2366 NtUserGetKeyState(INT key
)
2372 Ret
= UserGetKeyState(key
);
2382 NtUserGetKeyboardState(LPBYTE lpKeyState
)
2384 DWORD i
, ret
= TRUE
;
2386 PUSER_MESSAGE_QUEUE MessageQueue
;
2390 pti
= PsGetCurrentThreadWin32Thread();
2391 MessageQueue
= pti
->MessageQueue
;
2395 /* Probe and copy key state to an array */
2396 ProbeForWrite(lpKeyState
, 256 * sizeof(BYTE
), 1);
2397 for (i
= 0; i
< 256; ++i
)
2400 if (IS_KEY_DOWN(MessageQueue
->afKeyState
, i
))
2401 lpKeyState
[i
] |= KS_DOWN_BIT
;
2402 if (IS_KEY_LOCKED(MessageQueue
->afKeyState
, i
))
2403 lpKeyState
[i
] |= KS_LOCK_BIT
;
2406 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2408 SetLastNtError(_SEH2_GetExceptionCode());
2420 NtUserSetKeyboardState(LPBYTE pKeyState
)
2425 PUSER_MESSAGE_QUEUE MessageQueue
;
2427 UserEnterExclusive();
2429 pti
= PsGetCurrentThreadWin32Thread();
2430 MessageQueue
= pti
->MessageQueue
;
2434 ProbeForRead(pKeyState
, 256 * sizeof(BYTE
), 1);
2435 for (i
= 0; i
< 256; ++i
)
2437 SET_KEY_DOWN(MessageQueue
->afKeyState
, i
, pKeyState
[i
] & KS_DOWN_BIT
);
2438 SET_KEY_LOCKED(MessageQueue
->afKeyState
, i
, pKeyState
[i
] & KS_LOCK_BIT
);
2441 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2443 SetLastNtError(_SEH2_GetExceptionCode());