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