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