partially implemented WM_SETCURSOR messages
[reactos.git] / reactos / subsys / win32k / ntuser / message.c
1 /*
2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
4 *
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.
9 *
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.
14 *
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.
18 */
19 /* $Id: message.c,v 1.39 2003/12/20 21:45:14 weiden Exp $
20 *
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS kernel
23 * PURPOSE: Messages
24 * FILE: subsys/win32k/ntuser/message.c
25 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
26 * REVISION HISTORY:
27 * 06-06-2001 CSH Created
28 */
29
30 /* INCLUDES ******************************************************************/
31
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>
44
45 #define NDEBUG
46 #include <debug.h>
47
48 /* FUNCTIONS *****************************************************************/
49
50 NTSTATUS FASTCALL
51 IntInitMessageImpl(VOID)
52 {
53 return STATUS_SUCCESS;
54 }
55
56 NTSTATUS FASTCALL
57 IntCleanupMessageImpl(VOID)
58 {
59 return STATUS_SUCCESS;
60 }
61
62
63 LRESULT STDCALL
64 NtUserDispatchMessage(CONST MSG* UnsafeMsg)
65 {
66 LRESULT Result;
67 PWINDOW_OBJECT WindowObject;
68 NTSTATUS Status;
69 MSG Msg;
70
71 Status = MmCopyFromCaller(&Msg, (PVOID) UnsafeMsg, sizeof(MSG));
72 if (! NT_SUCCESS(Status))
73 {
74 SetLastNtError(Status);
75 return 0;
76 }
77
78 /* Process timer messages. */
79 if (Msg.message == WM_TIMER)
80 {
81 if (Msg.lParam)
82 {
83 /* FIXME: Call hooks. */
84
85 /* FIXME: Check for continuing validity of timer. */
86
87 return IntCallWindowProc((WNDPROC)Msg.lParam,
88 Msg.hwnd,
89 Msg.message,
90 Msg.wParam,
91 0 /* GetTickCount() */);
92 }
93 }
94
95 if( Msg.hwnd == 0 ) return 0;
96
97 /* Get the window object. */
98 WindowObject = IntGetWindowObject(Msg.hwnd);
99 if(!WindowObject)
100 {
101 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
102 return 0;
103 }
104 if(WindowObject->OwnerThread != PsGetCurrentThread())
105 {
106 IntReleaseWindowObject(WindowObject);
107 DPRINT1("Window doesn't belong to the calling thread!\n");
108 return 0;
109 }
110 /* FIXME: Call hook procedures. */
111
112 /* Call the window procedure. */
113 Result = IntCallWindowProc(WindowObject->WndProcW,
114 Msg.hwnd,
115 Msg.message,
116 Msg.wParam,
117 Msg.lParam);
118
119 IntReleaseWindowObject(WindowObject);
120
121 return Result;
122 }
123
124 VOID FASTCALL
125 IntSendSpecialMessages(PUSER_MESSAGE_QUEUE ThreadQueue, LPMSG Msg)
126 {
127 if(!Msg->hwnd || ThreadQueue->CaptureWindow)
128 {
129 return;
130 }
131
132 switch(Msg->message)
133 {
134 case WM_MOUSEMOVE:
135 {
136 IntSendMessage(Msg->hwnd, WM_SETCURSOR, (WPARAM)Msg->hwnd, MAKELPARAM(HTCLIENT, Msg->message), TRUE);
137 break;
138 }
139 case WM_NCMOUSEMOVE:
140 {
141 IntSendMessage(Msg->hwnd, WM_SETCURSOR, (WPARAM)Msg->hwnd, MAKELPARAM(Msg->wParam, Msg->message), TRUE);
142 break;
143 }
144 case WM_LBUTTONDOWN:
145 case WM_MBUTTONDOWN:
146 case WM_RBUTTONDOWN:
147 case WM_XBUTTONDOWN:
148 case WM_LBUTTONDBLCLK:
149 case WM_MBUTTONDBLCLK:
150 case WM_RBUTTONDBLCLK:
151 case WM_XBUTTONDBLCLK:
152 {
153 WPARAM wParam;
154
155 if(!IntGetWindowStationObject(InputWindowStation))
156 {
157 break;
158 }
159 wParam = (WPARAM)InputWindowStation->SystemCursor.ButtonsDown;
160 ObDereferenceObject(InputWindowStation);
161
162 IntSendMessage(Msg->hwnd, WM_MOUSEMOVE, wParam, Msg->lParam, TRUE);
163 IntSendMessage(Msg->hwnd, WM_SETCURSOR, (WPARAM)Msg->hwnd, MAKELPARAM(HTCLIENT, Msg->message), TRUE);
164 break;
165 }
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:
174 {
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);
177 break;
178 }
179 }
180 }
181
182 /*
183 * Internal version of PeekMessage() doing all the work
184 */
185 BOOL STDCALL
186 IntPeekMessage(LPMSG Msg,
187 HWND Wnd,
188 UINT MsgFilterMin,
189 UINT MsgFilterMax,
190 UINT RemoveMsg)
191 {
192 PUSER_MESSAGE_QUEUE ThreadQueue;
193 BOOLEAN Present;
194 PUSER_MESSAGE Message;
195 BOOLEAN RemoveMessages;
196
197 /* The queues and order in which they are checked are documented in the MSDN
198 article on GetMessage() */
199
200 ThreadQueue = (PUSER_MESSAGE_QUEUE)PsGetWin32Thread()->MessageQueue;
201
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;
205
206 /* Dispatch sent messages here. */
207 while (MsqDispatchOneSentMessage(ThreadQueue))
208 ;
209
210 /* Now look for a quit message. */
211 /* FIXME: WINE checks the message number filter here. */
212 if (ThreadQueue->QuitPosted)
213 {
214 Msg->hwnd = Wnd;
215 Msg->message = WM_QUIT;
216 Msg->wParam = ThreadQueue->QuitExitCode;
217 Msg->lParam = 0;
218 if (RemoveMessages)
219 {
220 ThreadQueue->QuitPosted = FALSE;
221 }
222 return TRUE;
223 }
224
225 /* Now check for normal messages. */
226 Present = MsqFindMessage(ThreadQueue,
227 FALSE,
228 RemoveMessages,
229 Wnd,
230 MsgFilterMin,
231 MsgFilterMax,
232 &Message);
233 if (Present)
234 {
235 RtlCopyMemory(Msg, &Message->Msg, sizeof(MSG));
236 if (RemoveMessages)
237 {
238 MsqDestroyMessage(Message);
239 IntSendSpecialMessages(ThreadQueue, Msg);
240 }
241 return TRUE;
242 }
243
244 /* Check for hardware events. */
245 Present = MsqFindMessage(ThreadQueue,
246 TRUE,
247 RemoveMessages,
248 Wnd,
249 MsgFilterMin,
250 MsgFilterMax,
251 &Message);
252 if (Present)
253 {
254 RtlCopyMemory(Msg, &Message->Msg, sizeof(MSG));
255 if (RemoveMessages)
256 {
257 MsqDestroyMessage(Message);
258 IntSendSpecialMessages(ThreadQueue, Msg);
259 }
260 return TRUE;
261 }
262
263 /* Check for sent messages again. */
264 while (MsqDispatchOneSentMessage(ThreadQueue))
265 ;
266
267 /* Check for paint messages. */
268 if (IntGetPaintMessage(Wnd, PsGetWin32Thread(), Msg, RemoveMessages))
269 {
270 return TRUE;
271 }
272
273 return FALSE;
274 }
275
276 BOOL STDCALL
277 NtUserPeekMessage(LPMSG UnsafeMsg,
278 HWND Wnd,
279 UINT MsgFilterMin,
280 UINT MsgFilterMax,
281 UINT RemoveMsg)
282 {
283 MSG SafeMsg;
284 NTSTATUS Status;
285 BOOL Present;
286 PWINDOW_OBJECT Window;
287
288 /* Validate input */
289 if (NULL != Wnd)
290 {
291 Window = IntGetWindowObject(Wnd);
292 if(!Window)
293 Wnd = NULL;
294 else
295 IntReleaseWindowObject(Window);
296 }
297 if (MsgFilterMax < MsgFilterMin)
298 {
299 MsgFilterMin = 0;
300 MsgFilterMax = 0;
301 }
302
303 Present = IntPeekMessage(&SafeMsg, Wnd, MsgFilterMin, MsgFilterMax, RemoveMsg);
304 if (Present)
305 {
306 Status = MmCopyToCaller(UnsafeMsg, &SafeMsg, sizeof(MSG));
307 if (! NT_SUCCESS(Status))
308 {
309 /* There is error return documented for PeekMessage().
310 Do the best we can */
311 SetLastNtError(Status);
312 return FALSE;
313 }
314 }
315
316 return Present;
317 }
318
319 static BOOL STDCALL
320 IntWaitMessage(HWND Wnd,
321 UINT MsgFilterMin,
322 UINT MsgFilterMax)
323 {
324 PUSER_MESSAGE_QUEUE ThreadQueue;
325 NTSTATUS Status;
326 MSG Msg;
327
328 ThreadQueue = (PUSER_MESSAGE_QUEUE)PsGetWin32Thread()->MessageQueue;
329
330 do
331 {
332 if (IntPeekMessage(&Msg, Wnd, MsgFilterMin, MsgFilterMax, PM_NOREMOVE))
333 {
334 return TRUE;
335 }
336
337 /* Nothing found. Wait for new messages. */
338 Status = MsqWaitForNewMessages(ThreadQueue);
339 }
340 while (STATUS_WAIT_0 <= STATUS_WAIT_0 && Status <= STATUS_WAIT_63);
341
342 SetLastNtError(Status);
343
344 return FALSE;
345 }
346
347 BOOL STDCALL
348 NtUserGetMessage(LPMSG UnsafeMsg,
349 HWND Wnd,
350 UINT MsgFilterMin,
351 UINT MsgFilterMax)
352 /*
353 * FUNCTION: Get a message from the calling thread's message queue.
354 * ARGUMENTS:
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
358 * retrieved.
359 * MsgFilterMax - Integer value of the highest message value to be
360 * retrieved.
361 */
362 {
363 BOOL GotMessage;
364 MSG SafeMsg;
365 NTSTATUS Status;
366 PWINDOW_OBJECT Window;
367
368 /* Validate input */
369 if (NULL != Wnd)
370 {
371 Window = IntGetWindowObject(Wnd);
372 if(!Window)
373 Wnd = NULL;
374 else
375 IntReleaseWindowObject(Window);
376 }
377 if (MsgFilterMax < MsgFilterMin)
378 {
379 MsgFilterMin = 0;
380 MsgFilterMax = 0;
381 }
382
383 do
384 {
385 GotMessage = IntPeekMessage(&SafeMsg, Wnd, MsgFilterMin, MsgFilterMax, PM_REMOVE);
386 if (GotMessage)
387 {
388 Status = MmCopyToCaller(UnsafeMsg, &SafeMsg, sizeof(MSG));
389 if (! NT_SUCCESS(Status))
390 {
391 SetLastNtError(Status);
392 return (BOOL) -1;
393 }
394 }
395 else
396 {
397 IntWaitMessage(Wnd, MsgFilterMin, MsgFilterMax);
398 }
399 }
400 while (! GotMessage);
401
402 return WM_QUIT != SafeMsg.message;
403 }
404
405 DWORD
406 STDCALL
407 NtUserMessageCall(
408 DWORD Unknown0,
409 DWORD Unknown1,
410 DWORD Unknown2,
411 DWORD Unknown3,
412 DWORD Unknown4,
413 DWORD Unknown5,
414 DWORD Unknown6)
415 {
416 UNIMPLEMENTED
417
418 return 0;
419 }
420
421 BOOL STDCALL
422 NtUserPostMessage(HWND hWnd,
423 UINT Msg,
424 WPARAM wParam,
425 LPARAM lParam)
426 {
427 PWINDOW_OBJECT Window;
428 MSG Mesg;
429 PUSER_MESSAGE Message;
430 LARGE_INTEGER LargeTickCount;
431
432 if (WM_QUIT == Msg)
433 {
434 MsqPostQuitMessage(PsGetWin32Thread()->MessageQueue, wParam);
435 }
436 else
437 {
438 Window = IntGetWindowObject(hWnd);
439 if (!Window)
440 {
441 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
442 return FALSE;
443 }
444 Mesg.hwnd = hWnd;
445 Mesg.message = Msg;
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);
455 }
456
457 return TRUE;
458 }
459
460 BOOL STDCALL
461 NtUserPostThreadMessage(DWORD idThread,
462 UINT Msg,
463 WPARAM wParam,
464 LPARAM lParam)
465 {
466 MSG Mesg;
467
468 PUSER_MESSAGE Message;
469 PETHREAD peThread;
470 PW32THREAD pThread;
471 NTSTATUS Status;
472
473 Status = PsLookupThreadByThreadId((void *)idThread,&peThread);
474
475 if( Status == STATUS_SUCCESS ) {
476 pThread = peThread->Win32Thread;
477 if( !pThread || !pThread->MessageQueue )
478 {
479 ObDereferenceObject( peThread );
480 return FALSE;
481 }
482 Mesg.hwnd = 0;
483 Mesg.message = Msg;
484 Mesg.wParam = wParam;
485 Mesg.lParam = lParam;
486 Message = MsqCreateMessage(&Mesg);
487 MsqPostMessage(pThread->MessageQueue, Message);
488 ObDereferenceObject( peThread );
489 return TRUE;
490 } else {
491 SetLastNtError( Status );
492 return FALSE;
493 }
494 }
495
496 DWORD STDCALL
497 NtUserQuerySendMessage(DWORD Unknown0)
498 {
499 UNIMPLEMENTED;
500
501 return 0;
502 }
503
504 LRESULT STDCALL
505 IntSendMessage(HWND hWnd,
506 UINT Msg,
507 WPARAM wParam,
508 LPARAM lParam,
509 BOOL KernelMessage)
510 {
511 LRESULT Result;
512 NTSTATUS Status;
513 PWINDOW_OBJECT Window;
514
515 /* FIXME: Check for a broadcast or topmost destination. */
516
517 /* FIXME: Call hooks. */
518 Window = IntGetWindowObject(hWnd);
519 if (!Window)
520 {
521 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
522 return 0;
523 }
524
525 /* FIXME: Check for an exiting window. */
526
527 if (NULL != PsGetWin32Thread() &&
528 Window->MessageQueue == PsGetWin32Thread()->MessageQueue)
529 {
530 if (KernelMessage)
531 {
532 Result = IntCallTrampolineWindowProc(NULL, hWnd, Msg, wParam,
533 lParam);
534 IntReleaseWindowObject(Window);
535 return Result;
536 }
537 else
538 {
539 Result = IntCallWindowProc(Window->WndProcW, hWnd, Msg, wParam, lParam);
540 IntReleaseWindowObject(Window);
541 return Result;
542 }
543 }
544 else
545 {
546 PUSER_SENT_MESSAGE Message;
547 PKEVENT CompletionEvent;
548
549 CompletionEvent = ExAllocatePool(NonPagedPool, sizeof(KEVENT));
550 KeInitializeEvent(CompletionEvent, NotificationEvent, FALSE);
551
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);
562
563 IntReleaseWindowObject(Window);
564 Status = KeWaitForSingleObject(CompletionEvent,
565 UserRequest,
566 UserMode,
567 FALSE,
568 NULL);
569 if (Status == STATUS_WAIT_0)
570 {
571 return Result;
572 }
573 else
574 {
575 return FALSE;
576 }
577 }
578 }
579
580 LRESULT STDCALL
581 NtUserSendMessage(HWND Wnd,
582 UINT Msg,
583 WPARAM wParam,
584 LPARAM lParam,
585 PNTUSERSENDMESSAGEINFO UnsafeInfo)
586 {
587 LRESULT Result;
588 NTSTATUS Status;
589 PWINDOW_OBJECT Window;
590 NTUSERSENDMESSAGEINFO Info;
591
592 /* FIXME: Check for a broadcast or topmost destination. */
593
594 /* FIXME: Call hooks. */
595 Window = IntGetWindowObject(Wnd);
596 if (NULL == Window)
597 {
598 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
599 return 0;
600 }
601
602 /* FIXME: Check for an exiting window. */
603
604 /* See if the current thread can handle the message */
605 if (NULL != PsGetWin32Thread() &&
606 Window->MessageQueue == PsGetWin32Thread()->MessageQueue)
607 {
608 /* Gather the information usermode needs to call the window proc directly */
609 Info.HandledByKernel = FALSE;
610 if (0xFFFF0000 != ((DWORD) Window->WndProcW & 0xFFFF0000))
611 {
612 if (0xFFFF0000 != ((DWORD) Window->WndProcA & 0xFFFF0000))
613 {
614 /* Both Unicode and Ansi winprocs are real */
615 Info.Ansi = ! Window->Unicode;
616 Info.Proc = (Window->Unicode ? Window->WndProcW : Window->WndProcA);
617 }
618 else
619 {
620 /* Real Unicode winproc */
621 Info.Ansi = FALSE;
622 Info.Proc = Window->WndProcW;
623 }
624 }
625 else
626 {
627 /* Must have real Ansi winproc */
628 Info.Ansi = TRUE;
629 Info.Proc = Window->WndProcA;
630 }
631 IntReleaseWindowObject(Window);
632 }
633 else
634 {
635 /* Must be handled by other thread */
636 IntReleaseWindowObject(Window);
637 Info.HandledByKernel = TRUE;
638 Result = IntSendMessage(Wnd, Msg, wParam, lParam, FALSE);
639 }
640
641 Status = MmCopyToCaller(UnsafeInfo, &Info, sizeof(NTUSERSENDMESSAGEINFO));
642 if (! NT_SUCCESS(Status))
643 {
644 SetLastWin32Error(ERROR_INVALID_PARAMETER);
645 }
646
647 return Result;
648 }
649
650 BOOL STDCALL
651 NtUserSendMessageCallback(HWND hWnd,
652 UINT Msg,
653 WPARAM wParam,
654 LPARAM lParam,
655 SENDASYNCPROC lpCallBack,
656 ULONG_PTR dwData)
657 {
658 UNIMPLEMENTED;
659
660 return 0;
661 }
662
663 BOOL STDCALL
664 NtUserSendNotifyMessage(HWND hWnd,
665 UINT Msg,
666 WPARAM wParam,
667 LPARAM lParam)
668 {
669 UNIMPLEMENTED;
670
671 return 0;
672 }
673
674 BOOL STDCALL
675 NtUserWaitMessage(VOID)
676 {
677
678 return IntWaitMessage(NULL, 0, 0);
679 }
680
681 DWORD STDCALL
682 NtUserGetQueueStatus(BOOL ClearChanges)
683 {
684 PUSER_MESSAGE_QUEUE Queue;
685 DWORD Result;
686
687 Queue = PsGetWin32Thread()->MessageQueue;
688
689 ExAcquireFastMutex(&Queue->Lock);
690
691 Result = MAKELONG(Queue->ChangedBits, Queue->WakeBits);
692 if (ClearChanges)
693 {
694 Queue->ChangedBits = 0;
695 }
696
697 ExReleaseFastMutex(&Queue->Lock);
698
699 return Result;
700 }
701
702 BOOL STDCALL
703 IntInitMessagePumpHook()
704 {
705 PsGetCurrentThread()->Win32Thread->MessagePumpHookValue++;
706 return TRUE;
707 }
708
709 BOOL STDCALL
710 IntUninitMessagePumpHook()
711 {
712 if (PsGetCurrentThread()->Win32Thread->MessagePumpHookValue <= 0)
713 {
714 return FALSE;
715 }
716 PsGetCurrentThread()->Win32Thread->MessagePumpHookValue--;
717 return TRUE;
718 }
719
720 /* EOF */