update for HEAD-2003091401
[reactos.git] / 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$
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 <internal/safe.h>
43
44 #define NDEBUG
45 #include <debug.h>
46
47 /* FUNCTIONS *****************************************************************/
48
49 NTSTATUS FASTCALL
50 IntInitMessageImpl(VOID)
51 {
52   return STATUS_SUCCESS;
53 }
54
55 NTSTATUS FASTCALL
56 IntCleanupMessageImpl(VOID)
57 {
58   return STATUS_SUCCESS;
59 }
60
61
62 LRESULT STDCALL
63 NtUserDispatchMessage(CONST MSG* UnsafeMsg)
64 {
65   LRESULT Result;
66   PWINDOW_OBJECT WindowObject;
67   NTSTATUS Status;
68   MSG Msg;
69
70   Status = MmCopyFromCaller(&Msg, (PVOID) UnsafeMsg, sizeof(MSG));
71   if (! NT_SUCCESS(Status))
72     {
73     SetLastNtError(Status);
74     return 0;
75     }
76
77   /* Process timer messages. */
78   if (Msg.message == WM_TIMER)
79     {
80       if (Msg.lParam)
81         {
82           /* FIXME: Call hooks. */
83
84           /* FIXME: Check for continuing validity of timer. */
85
86           return IntCallWindowProc((WNDPROC)Msg.lParam,
87                                       Msg.hwnd,
88                                       Msg.message,
89                                       Msg.wParam,
90                                       0 /* GetTickCount() */);
91         }
92     }
93
94   /* Get the window object. */
95   Status = 
96     ObmReferenceObjectByHandle(PsGetWin32Process()->WindowStation->HandleTable,
97                                Msg.hwnd,
98                                otWindow,
99                                (PVOID*)&WindowObject);
100   if (!NT_SUCCESS(Status))
101     {
102       SetLastNtError(Status);
103       return 0;
104     }
105
106   /* FIXME: Call hook procedures. */
107
108   /* Call the window procedure. */
109   if (WindowObject->Unicode == TRUE)
110   {
111         Result = IntCallWindowProc(WindowObject->WndProcW,
112                                         Msg.hwnd,
113                                         Msg.message,
114                                         Msg.wParam,
115                                         Msg.lParam);
116   }
117   else
118   {
119         Result = IntCallWindowProc(WindowObject->WndProcA,
120                                         Msg.hwnd,
121                                         Msg.message,
122                                         Msg.wParam,
123                                         Msg.lParam);
124   }
125
126   return Result;
127 }
128
129 /*
130  * Internal version of PeekMessage() doing all the work
131  */
132 BOOL STDCALL
133 IntPeekMessage(LPMSG Msg,
134                 HWND Wnd,
135                 UINT MsgFilterMin,
136                 UINT MsgFilterMax,
137                 UINT RemoveMsg)
138 {
139   PUSER_MESSAGE_QUEUE ThreadQueue;
140   BOOLEAN Present;
141   PUSER_MESSAGE Message;
142   BOOLEAN RemoveMessages;
143
144   /* The queues and order in which they are checked are documented in the MSDN
145      article on GetMessage() */
146
147   ThreadQueue = (PUSER_MESSAGE_QUEUE)PsGetWin32Thread()->MessageQueue;
148
149   /* Inspect RemoveMsg flags */
150   /* FIXME: The only flag we process is PM_REMOVE - processing of others must still be implemented */
151   RemoveMessages = RemoveMsg & PM_REMOVE;
152
153   /* Dispatch sent messages here. */
154   while (MsqDispatchOneSentMessage(ThreadQueue))
155     ;
156       
157   /* Now look for a quit message. */
158   /* FIXME: WINE checks the message number filter here. */
159   if (ThreadQueue->QuitPosted)
160   {
161     Msg->hwnd = Wnd;
162     Msg->message = WM_QUIT;
163     Msg->wParam = ThreadQueue->QuitExitCode;
164     Msg->lParam = 0;
165     if (RemoveMessages)
166       {
167         ThreadQueue->QuitPosted = FALSE;
168       }
169     return TRUE;
170   }
171
172   /* Now check for normal messages. */
173   Present = MsqFindMessage(ThreadQueue,
174                            FALSE,
175                            RemoveMessages,
176                            Wnd,
177                            MsgFilterMin,
178                            MsgFilterMax,
179                            &Message);
180   if (Present)
181     {
182       RtlCopyMemory(Msg, &Message->Msg, sizeof(MSG));
183       if (RemoveMessages)
184         {
185           MsqDestroyMessage(Message);
186         }
187       return TRUE;
188     }
189
190   /* Check for hardware events. */
191   Present = MsqFindMessage(ThreadQueue,
192                            TRUE,
193                            RemoveMessages,
194                            Wnd,
195                            MsgFilterMin,
196                            MsgFilterMax,
197                            &Message);
198   if (Present)
199     {
200       RtlCopyMemory(Msg, &Message->Msg, sizeof(MSG));
201       if (RemoveMessages)
202         {
203           MsqDestroyMessage(Message);
204         }
205       return TRUE;
206     }
207
208   /* Check for sent messages again. */
209   while (MsqDispatchOneSentMessage(ThreadQueue))
210     ;
211
212   /* Check for paint messages. */
213   if (ThreadQueue->PaintPosted)
214     {
215       PWINDOW_OBJECT WindowObject;
216
217       Msg->hwnd = PaintingFindWinToRepaint(Wnd, PsGetWin32Thread());
218       Msg->message = WM_PAINT;
219       Msg->wParam = Msg->lParam = 0;
220
221       WindowObject = IntGetWindowObject(Msg->hwnd);
222       if (WindowObject != NULL)
223         {
224           if (WindowObject->Style & WS_MINIMIZE &&
225               (HICON)NtUserGetClassLong(Msg->hwnd, GCL_HICON, FALSE) != NULL)
226             {
227               Msg->message = WM_PAINTICON;
228               Msg->wParam = 1;
229             }
230
231           if (Msg->hwnd == NULL || Msg->hwnd == Wnd ||
232               IntIsChildWindow(Wnd, Msg->hwnd))
233             {
234               if (WindowObject->Flags & WINDOWOBJECT_NEED_INTERNALPAINT &&
235                   WindowObject->UpdateRegion == NULL)
236                 {
237                   WindowObject->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
238                   if (RemoveMessages)
239                     {
240                       MsqDecPaintCountQueue(WindowObject->MessageQueue);
241                     }
242                 }
243             }
244           IntReleaseWindowObject(WindowObject);
245         }
246
247       return TRUE;
248     }
249
250   return FALSE;
251 }
252
253 BOOL STDCALL
254 NtUserPeekMessage(LPMSG UnsafeMsg,
255                   HWND Wnd,
256                   UINT MsgFilterMin,
257                   UINT MsgFilterMax,
258                   UINT RemoveMsg)
259 {
260   MSG SafeMsg;
261   NTSTATUS Status;
262   BOOL Present;
263   PWINDOW_OBJECT Window;
264
265   /* Validate input */
266   if (NULL != Wnd)
267     {
268       Status = ObmReferenceObjectByHandle(PsGetWin32Process()->WindowStation->HandleTable,
269                                           Wnd, otWindow, (PVOID*)&Window);
270       if (!NT_SUCCESS(Status))
271         {
272           Wnd = NULL;
273         }
274       else
275         {
276           ObmDereferenceObject(Window);
277         }
278     }
279   if (MsgFilterMax < MsgFilterMin)
280     {
281       MsgFilterMin = 0;
282       MsgFilterMax = 0;
283     }
284
285   Present = IntPeekMessage(&SafeMsg, Wnd, MsgFilterMin, MsgFilterMax, RemoveMsg);
286   if (Present)
287     {
288       Status = MmCopyToCaller(UnsafeMsg, &SafeMsg, sizeof(MSG));
289       if (! NT_SUCCESS(Status))
290         {
291           /* There is error return documented for PeekMessage().
292              Do the best we can */
293           SetLastNtError(Status);
294           return FALSE;
295         }
296     }
297
298   return Present;
299 }
300
301 static BOOL STDCALL
302 IntWaitMessage(HWND Wnd,
303                 UINT MsgFilterMin,
304                 UINT MsgFilterMax)
305 {
306   PUSER_MESSAGE_QUEUE ThreadQueue;
307   NTSTATUS Status;
308   MSG Msg;
309
310   ThreadQueue = (PUSER_MESSAGE_QUEUE)PsGetWin32Thread()->MessageQueue;
311
312   do
313     {
314       if (IntPeekMessage(&Msg, Wnd, MsgFilterMin, MsgFilterMax, PM_NOREMOVE))
315         {
316           return TRUE;
317         }
318
319       /* Nothing found. Wait for new messages. */
320       Status = MsqWaitForNewMessages(ThreadQueue);
321     }
322   while (STATUS_WAIT_0 <= STATUS_WAIT_0 && Status <= STATUS_WAIT_63);
323
324   SetLastNtError(Status);
325
326   return FALSE;
327 }
328
329 BOOL STDCALL
330 NtUserGetMessage(LPMSG UnsafeMsg,
331                  HWND Wnd,
332                  UINT MsgFilterMin,
333                  UINT MsgFilterMax)
334 /*
335  * FUNCTION: Get a message from the calling thread's message queue.
336  * ARGUMENTS:
337  *      UnsafeMsg - Pointer to the structure which receives the returned message.
338  *      Wnd - Window whose messages are to be retrieved.
339  *      MsgFilterMin - Integer value of the lowest message value to be
340  *                     retrieved.
341  *      MsgFilterMax - Integer value of the highest message value to be
342  *                     retrieved.
343  */
344 {
345   BOOL GotMessage;
346   MSG SafeMsg;
347   NTSTATUS Status;
348   PWINDOW_OBJECT Window;
349
350   /* Validate input */
351   if (NULL != Wnd)
352     {
353       Status = ObmReferenceObjectByHandle(PsGetWin32Process()->WindowStation->HandleTable,
354                                           Wnd, otWindow, (PVOID*)&Window);
355       if (!NT_SUCCESS(Status))
356         {
357           Wnd = NULL;
358         }
359       else
360         {
361           ObmDereferenceObject(Window);
362         }
363     }
364   if (MsgFilterMax < MsgFilterMin)
365     {
366       MsgFilterMin = 0;
367       MsgFilterMax = 0;
368     }
369
370   do
371     {
372       GotMessage = IntPeekMessage(&SafeMsg, Wnd, MsgFilterMin, MsgFilterMax, PM_REMOVE);
373       if (GotMessage)
374         {
375           Status = MmCopyToCaller(UnsafeMsg, &SafeMsg, sizeof(MSG));
376           if (! NT_SUCCESS(Status))
377             {
378               SetLastNtError(Status);
379               return (BOOL) -1;
380             }
381         }
382       else
383         {
384           IntWaitMessage(Wnd, MsgFilterMin, MsgFilterMax);
385         }
386     }
387   while (! GotMessage);
388
389   return WM_QUIT != SafeMsg.message;
390 }
391
392 DWORD
393 STDCALL
394 NtUserMessageCall(
395   DWORD Unknown0,
396   DWORD Unknown1,
397   DWORD Unknown2,
398   DWORD Unknown3,
399   DWORD Unknown4,
400   DWORD Unknown5,
401   DWORD Unknown6)
402 {
403   UNIMPLEMENTED
404
405   return 0;
406 }
407
408 BOOL STDCALL
409 NtUserPostMessage(HWND hWnd,
410                   UINT Msg,
411                   WPARAM wParam,
412                   LPARAM lParam)
413 {
414   PWINDOW_OBJECT Window;
415   MSG Mesg;
416   PUSER_MESSAGE Message;
417   NTSTATUS Status;
418
419   if (WM_QUIT == Msg)
420     {
421       MsqPostQuitMessage(PsGetWin32Thread()->MessageQueue, wParam);
422     }
423   else
424     {
425       Status = ObmReferenceObjectByHandle(PsGetWin32Process()->WindowStation->HandleTable,
426                                           hWnd, otWindow, (PVOID*)&Window);
427       if (!NT_SUCCESS(Status))
428         {
429           SetLastNtError(Status);
430           return FALSE;
431         }
432       Mesg.hwnd = hWnd;
433       Mesg.message = Msg;
434       Mesg.wParam = wParam;
435       Mesg.lParam = lParam;
436       Message = MsqCreateMessage(&Mesg);
437       MsqPostMessage(Window->MessageQueue, Message);
438       ObmDereferenceObject(Window);
439     }
440
441   return TRUE;
442 }
443
444 BOOL STDCALL
445 NtUserPostThreadMessage(DWORD idThread,
446                         UINT Msg,
447                         WPARAM wParam,
448                         LPARAM lParam)
449 {
450   MSG Mesg;
451
452   PUSER_MESSAGE Message;
453   PETHREAD peThread;
454   PW32THREAD pThread;
455   NTSTATUS Status;
456
457   Status = PsLookupThreadByThreadId((void *)idThread,&peThread);
458   
459   if( Status == STATUS_SUCCESS ) {
460     pThread = peThread->Win32Thread;
461     if( !pThread || !pThread->MessageQueue )
462       {
463         ObDereferenceObject( peThread );
464         return FALSE;
465       }
466     Mesg.hwnd = 0;
467     Mesg.message = Msg;
468     Mesg.wParam = wParam;
469     Mesg.lParam = lParam;
470     Message = MsqCreateMessage(&Mesg);
471     MsqPostMessage(pThread->MessageQueue, Message);
472     ObDereferenceObject( peThread );
473     return TRUE;
474   } else {
475     SetLastNtError( Status );
476     return FALSE;
477   }
478 }
479
480 DWORD STDCALL
481 NtUserQuerySendMessage(DWORD Unknown0)
482 {
483   UNIMPLEMENTED;
484
485   return 0;
486 }
487
488 LRESULT STDCALL
489 IntSendMessage(HWND hWnd,
490                 UINT Msg,
491                 WPARAM wParam,
492                 LPARAM lParam,
493                 BOOL KernelMessage)
494 {
495   LRESULT Result;
496   NTSTATUS Status;
497   PWINDOW_OBJECT Window;
498
499   /* FIXME: Check for a broadcast or topmost destination. */
500
501   /* FIXME: Call hooks. */
502
503   Status = 
504     ObmReferenceObjectByHandle(PsGetWin32Process()->WindowStation->HandleTable,
505                                hWnd,
506                                otWindow,
507                                (PVOID*)&Window);
508   if (!NT_SUCCESS(Status))
509     {
510       return 0;
511     }
512
513   /* FIXME: Check for an exiting window. */
514
515   if (NULL != PsGetWin32Thread() &&
516       Window->MessageQueue == PsGetWin32Thread()->MessageQueue)
517     {
518       if (KernelMessage)
519         {
520           Result = IntCallTrampolineWindowProc(NULL, hWnd, Msg, wParam,
521                                                 lParam);
522           return Result;
523         }
524       else
525         {
526         if (Window->Unicode == TRUE)
527         {
528           Result = IntCallWindowProc(Window->WndProcW, hWnd, Msg, wParam, lParam);
529         }
530         else
531         {
532           Result = IntCallWindowProc(Window->WndProcA, hWnd, Msg, wParam, lParam);
533         }
534           return Result;
535         }
536     }
537   else
538     {
539       PUSER_SENT_MESSAGE Message;
540       PKEVENT CompletionEvent;
541
542       CompletionEvent = ExAllocatePool(NonPagedPool, sizeof(KEVENT));
543       KeInitializeEvent(CompletionEvent, NotificationEvent, FALSE);
544
545       Message = ExAllocatePool(NonPagedPool, sizeof(USER_SENT_MESSAGE));
546       Message->Msg.hwnd = hWnd;
547       Message->Msg.message = Msg;
548       Message->Msg.wParam = wParam;
549       Message->Msg.lParam = lParam;
550       Message->CompletionEvent = CompletionEvent;
551       Message->Result = &Result;
552       Message->CompletionQueue = NULL;
553       Message->CompletionCallback = NULL;
554       MsqSendMessage(Window->MessageQueue, Message);
555
556       ObmDereferenceObject(Window);
557       Status = KeWaitForSingleObject(CompletionEvent,
558                                      UserRequest,
559                                      UserMode,
560                                      FALSE,
561                                      NULL);
562       if (Status == STATUS_WAIT_0)
563         {
564           return Result;
565         }
566       else
567         {
568           return FALSE;
569         }
570     }
571 }
572
573 LRESULT STDCALL
574 NtUserSendMessage(HWND Wnd,
575                   UINT Msg,
576                   WPARAM wParam,
577                   LPARAM lParam)
578 {
579   return IntSendMessage(Wnd, Msg, wParam, lParam, FALSE);
580 }
581
582 BOOL STDCALL
583 NtUserSendMessageCallback(HWND hWnd,
584                           UINT Msg,
585                           WPARAM wParam,
586                           LPARAM lParam,
587                           SENDASYNCPROC lpCallBack,
588                           ULONG_PTR dwData)
589 {
590   UNIMPLEMENTED;
591
592   return 0;
593 }
594
595 BOOL STDCALL
596 NtUserSendNotifyMessage(HWND hWnd,
597                         UINT Msg,
598                         WPARAM wParam,
599                         LPARAM lParam)
600 {
601   UNIMPLEMENTED;
602
603   return 0;
604 }
605
606 BOOL STDCALL
607 NtUserWaitMessage(VOID)
608 {
609
610   return IntWaitMessage(NULL, 0, 0);
611 }
612
613
614 DWORD STDCALL
615 NtUserGetQueueStatus(BOOL ClearChanges)
616 {
617    PUSER_MESSAGE_QUEUE Queue;
618    DWORD Result;
619
620    Queue = PsGetWin32Thread()->MessageQueue;
621
622    ExAcquireFastMutex(&Queue->Lock);
623
624    Result = MAKELONG(Queue->ChangedBits, Queue->WakeBits);
625    if (ClearChanges)
626    {
627       Queue->ChangedBits = 0;
628    }
629
630    ExReleaseFastMutex(&Queue->Lock);
631
632    return Result;
633 }
634
635 /* EOF */