2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 /* $Id: message.c,v 1.39 2003/12/20 21:45:14 weiden Exp $
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS kernel
24 * FILE: subsys/win32k/ntuser/message.c
25 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
27 * 06-06-2001 CSH Created
30 /* INCLUDES ******************************************************************/
32 #include <ddk/ntddk.h>
33 #include <win32k/win32k.h>
34 #include <include/msgqueue.h>
35 #include <include/window.h>
36 #include <include/class.h>
37 #include <include/error.h>
38 #include <include/object.h>
39 #include <include/winsta.h>
40 #include <include/callback.h>
41 #include <include/painting.h>
42 #include <include/input.h>
43 #include <internal/safe.h>
48 /* FUNCTIONS *****************************************************************/
51 IntInitMessageImpl(VOID
)
53 return STATUS_SUCCESS
;
57 IntCleanupMessageImpl(VOID
)
59 return STATUS_SUCCESS
;
64 NtUserDispatchMessage(CONST MSG
* UnsafeMsg
)
67 PWINDOW_OBJECT WindowObject
;
71 Status
= MmCopyFromCaller(&Msg
, (PVOID
) UnsafeMsg
, sizeof(MSG
));
72 if (! NT_SUCCESS(Status
))
74 SetLastNtError(Status
);
78 /* Process timer messages. */
79 if (Msg
.message
== WM_TIMER
)
83 /* FIXME: Call hooks. */
85 /* FIXME: Check for continuing validity of timer. */
87 return IntCallWindowProc((WNDPROC
)Msg
.lParam
,
91 0 /* GetTickCount() */);
95 if( Msg
.hwnd
== 0 ) return 0;
97 /* Get the window object. */
98 WindowObject
= IntGetWindowObject(Msg
.hwnd
);
101 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE
);
104 if(WindowObject
->OwnerThread
!= PsGetCurrentThread())
106 IntReleaseWindowObject(WindowObject
);
107 DPRINT1("Window doesn't belong to the calling thread!\n");
110 /* FIXME: Call hook procedures. */
112 /* Call the window procedure. */
113 Result
= IntCallWindowProc(WindowObject
->WndProcW
,
119 IntReleaseWindowObject(WindowObject
);
125 IntSendSpecialMessages(PUSER_MESSAGE_QUEUE ThreadQueue
, LPMSG Msg
)
127 if(!Msg
->hwnd
|| ThreadQueue
->CaptureWindow
)
136 IntSendMessage(Msg
->hwnd
, WM_SETCURSOR
, (WPARAM
)Msg
->hwnd
, MAKELPARAM(HTCLIENT
, Msg
->message
), TRUE
);
141 IntSendMessage(Msg
->hwnd
, WM_SETCURSOR
, (WPARAM
)Msg
->hwnd
, MAKELPARAM(Msg
->wParam
, Msg
->message
), TRUE
);
148 case WM_LBUTTONDBLCLK
:
149 case WM_MBUTTONDBLCLK
:
150 case WM_RBUTTONDBLCLK
:
151 case WM_XBUTTONDBLCLK
:
155 if(!IntGetWindowStationObject(InputWindowStation
))
159 wParam
= (WPARAM
)InputWindowStation
->SystemCursor
.ButtonsDown
;
160 ObDereferenceObject(InputWindowStation
);
162 IntSendMessage(Msg
->hwnd
, WM_MOUSEMOVE
, wParam
, Msg
->lParam
, TRUE
);
163 IntSendMessage(Msg
->hwnd
, WM_SETCURSOR
, (WPARAM
)Msg
->hwnd
, MAKELPARAM(HTCLIENT
, Msg
->message
), TRUE
);
166 case WM_NCLBUTTONDOWN
:
167 case WM_NCMBUTTONDOWN
:
168 case WM_NCRBUTTONDOWN
:
169 case WM_NCXBUTTONDOWN
:
170 case WM_NCLBUTTONDBLCLK
:
171 case WM_NCMBUTTONDBLCLK
:
172 case WM_NCRBUTTONDBLCLK
:
173 case WM_NCXBUTTONDBLCLK
:
175 IntSendMessage(Msg
->hwnd
, WM_NCMOUSEMOVE
, (WPARAM
)Msg
->wParam
, Msg
->lParam
, TRUE
);
176 IntSendMessage(Msg
->hwnd
, WM_SETCURSOR
, (WPARAM
)Msg
->hwnd
, MAKELPARAM(Msg
->wParam
, Msg
->message
), TRUE
);
183 * Internal version of PeekMessage() doing all the work
186 IntPeekMessage(LPMSG Msg
,
192 PUSER_MESSAGE_QUEUE ThreadQueue
;
194 PUSER_MESSAGE Message
;
195 BOOLEAN RemoveMessages
;
197 /* The queues and order in which they are checked are documented in the MSDN
198 article on GetMessage() */
200 ThreadQueue
= (PUSER_MESSAGE_QUEUE
)PsGetWin32Thread()->MessageQueue
;
202 /* Inspect RemoveMsg flags */
203 /* FIXME: The only flag we process is PM_REMOVE - processing of others must still be implemented */
204 RemoveMessages
= RemoveMsg
& PM_REMOVE
;
206 /* Dispatch sent messages here. */
207 while (MsqDispatchOneSentMessage(ThreadQueue
))
210 /* Now look for a quit message. */
211 /* FIXME: WINE checks the message number filter here. */
212 if (ThreadQueue
->QuitPosted
)
215 Msg
->message
= WM_QUIT
;
216 Msg
->wParam
= ThreadQueue
->QuitExitCode
;
220 ThreadQueue
->QuitPosted
= FALSE
;
225 /* Now check for normal messages. */
226 Present
= MsqFindMessage(ThreadQueue
,
235 RtlCopyMemory(Msg
, &Message
->Msg
, sizeof(MSG
));
238 MsqDestroyMessage(Message
);
239 IntSendSpecialMessages(ThreadQueue
, Msg
);
244 /* Check for hardware events. */
245 Present
= MsqFindMessage(ThreadQueue
,
254 RtlCopyMemory(Msg
, &Message
->Msg
, sizeof(MSG
));
257 MsqDestroyMessage(Message
);
258 IntSendSpecialMessages(ThreadQueue
, Msg
);
263 /* Check for sent messages again. */
264 while (MsqDispatchOneSentMessage(ThreadQueue
))
267 /* Check for paint messages. */
268 if (IntGetPaintMessage(Wnd
, PsGetWin32Thread(), Msg
, RemoveMessages
))
277 NtUserPeekMessage(LPMSG UnsafeMsg
,
286 PWINDOW_OBJECT Window
;
291 Window
= IntGetWindowObject(Wnd
);
295 IntReleaseWindowObject(Window
);
297 if (MsgFilterMax
< MsgFilterMin
)
303 Present
= IntPeekMessage(&SafeMsg
, Wnd
, MsgFilterMin
, MsgFilterMax
, RemoveMsg
);
306 Status
= MmCopyToCaller(UnsafeMsg
, &SafeMsg
, sizeof(MSG
));
307 if (! NT_SUCCESS(Status
))
309 /* There is error return documented for PeekMessage().
310 Do the best we can */
311 SetLastNtError(Status
);
320 IntWaitMessage(HWND Wnd
,
324 PUSER_MESSAGE_QUEUE ThreadQueue
;
328 ThreadQueue
= (PUSER_MESSAGE_QUEUE
)PsGetWin32Thread()->MessageQueue
;
332 if (IntPeekMessage(&Msg
, Wnd
, MsgFilterMin
, MsgFilterMax
, PM_NOREMOVE
))
337 /* Nothing found. Wait for new messages. */
338 Status
= MsqWaitForNewMessages(ThreadQueue
);
340 while (STATUS_WAIT_0
<= STATUS_WAIT_0
&& Status
<= STATUS_WAIT_63
);
342 SetLastNtError(Status
);
348 NtUserGetMessage(LPMSG UnsafeMsg
,
353 * FUNCTION: Get a message from the calling thread's message queue.
355 * UnsafeMsg - Pointer to the structure which receives the returned message.
356 * Wnd - Window whose messages are to be retrieved.
357 * MsgFilterMin - Integer value of the lowest message value to be
359 * MsgFilterMax - Integer value of the highest message value to be
366 PWINDOW_OBJECT Window
;
371 Window
= IntGetWindowObject(Wnd
);
375 IntReleaseWindowObject(Window
);
377 if (MsgFilterMax
< MsgFilterMin
)
385 GotMessage
= IntPeekMessage(&SafeMsg
, Wnd
, MsgFilterMin
, MsgFilterMax
, PM_REMOVE
);
388 Status
= MmCopyToCaller(UnsafeMsg
, &SafeMsg
, sizeof(MSG
));
389 if (! NT_SUCCESS(Status
))
391 SetLastNtError(Status
);
397 IntWaitMessage(Wnd
, MsgFilterMin
, MsgFilterMax
);
400 while (! GotMessage
);
402 return WM_QUIT
!= SafeMsg
.message
;
422 NtUserPostMessage(HWND hWnd
,
427 PWINDOW_OBJECT Window
;
429 PUSER_MESSAGE Message
;
430 LARGE_INTEGER LargeTickCount
;
434 MsqPostQuitMessage(PsGetWin32Thread()->MessageQueue
, wParam
);
438 Window
= IntGetWindowObject(hWnd
);
441 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE
);
446 Mesg
.wParam
= wParam
;
447 Mesg
.lParam
= lParam
;
448 Mesg
.pt
.x
= PsGetWin32Process()->WindowStation
->SystemCursor
.x
;
449 Mesg
.pt
.y
= PsGetWin32Process()->WindowStation
->SystemCursor
.y
;
450 KeQueryTickCount(&LargeTickCount
);
451 Mesg
.time
= LargeTickCount
.u
.LowPart
;
452 Message
= MsqCreateMessage(&Mesg
);
453 MsqPostMessage(Window
->MessageQueue
, Message
);
454 IntReleaseWindowObject(Window
);
461 NtUserPostThreadMessage(DWORD idThread
,
468 PUSER_MESSAGE Message
;
473 Status
= PsLookupThreadByThreadId((void *)idThread
,&peThread
);
475 if( Status
== STATUS_SUCCESS
) {
476 pThread
= peThread
->Win32Thread
;
477 if( !pThread
|| !pThread
->MessageQueue
)
479 ObDereferenceObject( peThread
);
484 Mesg
.wParam
= wParam
;
485 Mesg
.lParam
= lParam
;
486 Message
= MsqCreateMessage(&Mesg
);
487 MsqPostMessage(pThread
->MessageQueue
, Message
);
488 ObDereferenceObject( peThread
);
491 SetLastNtError( Status
);
497 NtUserQuerySendMessage(DWORD Unknown0
)
505 IntSendMessage(HWND hWnd
,
513 PWINDOW_OBJECT Window
;
515 /* FIXME: Check for a broadcast or topmost destination. */
517 /* FIXME: Call hooks. */
518 Window
= IntGetWindowObject(hWnd
);
521 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE
);
525 /* FIXME: Check for an exiting window. */
527 if (NULL
!= PsGetWin32Thread() &&
528 Window
->MessageQueue
== PsGetWin32Thread()->MessageQueue
)
532 Result
= IntCallTrampolineWindowProc(NULL
, hWnd
, Msg
, wParam
,
534 IntReleaseWindowObject(Window
);
539 Result
= IntCallWindowProc(Window
->WndProcW
, hWnd
, Msg
, wParam
, lParam
);
540 IntReleaseWindowObject(Window
);
546 PUSER_SENT_MESSAGE Message
;
547 PKEVENT CompletionEvent
;
549 CompletionEvent
= ExAllocatePool(NonPagedPool
, sizeof(KEVENT
));
550 KeInitializeEvent(CompletionEvent
, NotificationEvent
, FALSE
);
552 Message
= ExAllocatePool(NonPagedPool
, sizeof(USER_SENT_MESSAGE
));
553 Message
->Msg
.hwnd
= hWnd
;
554 Message
->Msg
.message
= Msg
;
555 Message
->Msg
.wParam
= wParam
;
556 Message
->Msg
.lParam
= lParam
;
557 Message
->CompletionEvent
= CompletionEvent
;
558 Message
->Result
= &Result
;
559 Message
->CompletionQueue
= NULL
;
560 Message
->CompletionCallback
= NULL
;
561 MsqSendMessage(Window
->MessageQueue
, Message
);
563 IntReleaseWindowObject(Window
);
564 Status
= KeWaitForSingleObject(CompletionEvent
,
569 if (Status
== STATUS_WAIT_0
)
581 NtUserSendMessage(HWND Wnd
,
585 PNTUSERSENDMESSAGEINFO UnsafeInfo
)
589 PWINDOW_OBJECT Window
;
590 NTUSERSENDMESSAGEINFO Info
;
592 /* FIXME: Check for a broadcast or topmost destination. */
594 /* FIXME: Call hooks. */
595 Window
= IntGetWindowObject(Wnd
);
598 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE
);
602 /* FIXME: Check for an exiting window. */
604 /* See if the current thread can handle the message */
605 if (NULL
!= PsGetWin32Thread() &&
606 Window
->MessageQueue
== PsGetWin32Thread()->MessageQueue
)
608 /* Gather the information usermode needs to call the window proc directly */
609 Info
.HandledByKernel
= FALSE
;
610 if (0xFFFF0000 != ((DWORD
) Window
->WndProcW
& 0xFFFF0000))
612 if (0xFFFF0000 != ((DWORD
) Window
->WndProcA
& 0xFFFF0000))
614 /* Both Unicode and Ansi winprocs are real */
615 Info
.Ansi
= ! Window
->Unicode
;
616 Info
.Proc
= (Window
->Unicode
? Window
->WndProcW
: Window
->WndProcA
);
620 /* Real Unicode winproc */
622 Info
.Proc
= Window
->WndProcW
;
627 /* Must have real Ansi winproc */
629 Info
.Proc
= Window
->WndProcA
;
631 IntReleaseWindowObject(Window
);
635 /* Must be handled by other thread */
636 IntReleaseWindowObject(Window
);
637 Info
.HandledByKernel
= TRUE
;
638 Result
= IntSendMessage(Wnd
, Msg
, wParam
, lParam
, FALSE
);
641 Status
= MmCopyToCaller(UnsafeInfo
, &Info
, sizeof(NTUSERSENDMESSAGEINFO
));
642 if (! NT_SUCCESS(Status
))
644 SetLastWin32Error(ERROR_INVALID_PARAMETER
);
651 NtUserSendMessageCallback(HWND hWnd
,
655 SENDASYNCPROC lpCallBack
,
664 NtUserSendNotifyMessage(HWND hWnd
,
675 NtUserWaitMessage(VOID
)
678 return IntWaitMessage(NULL
, 0, 0);
682 NtUserGetQueueStatus(BOOL ClearChanges
)
684 PUSER_MESSAGE_QUEUE Queue
;
687 Queue
= PsGetWin32Thread()->MessageQueue
;
689 ExAcquireFastMutex(&Queue
->Lock
);
691 Result
= MAKELONG(Queue
->ChangedBits
, Queue
->WakeBits
);
694 Queue
->ChangedBits
= 0;
697 ExReleaseFastMutex(&Queue
->Lock
);
703 IntInitMessagePumpHook()
705 PsGetCurrentThread()->Win32Thread
->MessagePumpHookValue
++;
710 IntUninitMessagePumpHook()
712 if (PsGetCurrentThread()->Win32Thread
->MessagePumpHookValue
<= 0)
716 PsGetCurrentThread()->Win32Thread
->MessagePumpHookValue
--;