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.
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS kernel
23 * PURPOSE: Message queues
24 * FILE: subsys/win32k/ntuser/msgqueue.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/callback.h>
36 #include <include/window.h>
37 #include <include/winpos.h>
38 #include <include/class.h>
43 /* GLOBALS *******************************************************************/
45 #define SYSTEM_MESSAGE_QUEUE_SIZE (256)
47 static MSG SystemMessageQueue[SYSTEM_MESSAGE_QUEUE_SIZE];
48 static ULONG SystemMessageQueueHead = 0;
49 static ULONG SystemMessageQueueTail = 0;
50 static ULONG SystemMessageQueueCount = 0;
51 static ULONG SystemMessageQueueMouseMove = -1;
52 static KSPIN_LOCK SystemMessageQueueLock;
54 static ULONG HardwareMessageQueueStamp = 0;
55 static LIST_ENTRY HardwareMessageQueueHead;
56 static FAST_MUTEX HardwareMessageQueueLock;
58 static KEVENT HardwareMessageEvent;
60 static PAGED_LOOKASIDE_LIST MessageLookasideList;
62 /* FUNCTIONS *****************************************************************/
64 /* check the queue status */
65 inline BOOL MsqIsSignaled( PUSER_MESSAGE_QUEUE Queue )
67 return ((Queue->WakeBits & Queue->WakeMask) || (Queue->ChangedBits & Queue->ChangedMask));
70 /* set some queue bits */
71 inline VOID MsqSetQueueBits( PUSER_MESSAGE_QUEUE Queue, WORD Bits )
73 Queue->WakeBits |= Bits;
74 Queue->ChangedBits |= Bits;
75 if (MsqIsSignaled( Queue )) KeSetEvent(&Queue->NewMessages, IO_NO_INCREMENT, FALSE);
78 /* clear some queue bits */
79 inline VOID MsqClearQueueBits( PUSER_MESSAGE_QUEUE Queue, WORD Bits )
81 Queue->WakeBits &= ~Bits;
82 Queue->ChangedBits &= ~Bits;
86 MsqIncPaintCountQueue(PUSER_MESSAGE_QUEUE Queue)
88 ExAcquireFastMutex(&Queue->Lock);
90 Queue->PaintPosted = TRUE;
91 KeSetEvent(&Queue->NewMessages, IO_NO_INCREMENT, FALSE);
92 ExReleaseFastMutex(&Queue->Lock);
96 MsqDecPaintCountQueue(PUSER_MESSAGE_QUEUE Queue)
98 ExAcquireFastMutex(&Queue->Lock);
100 if (Queue->PaintCount == 0)
102 Queue->PaintPosted = FALSE;
104 ExReleaseFastMutex(&Queue->Lock);
109 MsqInitializeImpl(VOID)
111 /*CurrentFocusMessageQueue = NULL;*/
112 InitializeListHead(&HardwareMessageQueueHead);
113 KeInitializeEvent(&HardwareMessageEvent, NotificationEvent, 0);
114 KeInitializeSpinLock(&SystemMessageQueueLock);
115 ExInitializeFastMutex(&HardwareMessageQueueLock);
117 ExInitializePagedLookasideList(&MessageLookasideList,
121 sizeof(USER_MESSAGE),
125 return(STATUS_SUCCESS);
129 MsqInsertSystemMessage(MSG* Msg, BOOL RemMouseMoveMsg)
132 ULONG mmov = (ULONG)-1;
134 KeAcquireSpinLock(&SystemMessageQueueLock, &OldIrql);
136 /* only insert WM_MOUSEMOVE messages if not already in system message queue */
137 if((Msg->message == WM_MOUSEMOVE) && RemMouseMoveMsg)
138 mmov = SystemMessageQueueMouseMove;
140 if(mmov != (ULONG)-1)
142 /* insert message at the queue head */
143 while (mmov != SystemMessageQueueHead )
145 ULONG prev = mmov ? mmov - 1 : SYSTEM_MESSAGE_QUEUE_SIZE - 1;
146 SystemMessageQueue[mmov] = SystemMessageQueue[prev];
149 SystemMessageQueue[SystemMessageQueueHead] = *Msg;
153 if (SystemMessageQueueCount == SYSTEM_MESSAGE_QUEUE_SIZE)
155 KeReleaseSpinLock(&SystemMessageQueueLock, OldIrql);
158 SystemMessageQueue[SystemMessageQueueTail] = *Msg;
159 if(Msg->message == WM_MOUSEMOVE)
160 SystemMessageQueueMouseMove = SystemMessageQueueTail;
161 SystemMessageQueueTail =
162 (SystemMessageQueueTail + 1) % SYSTEM_MESSAGE_QUEUE_SIZE;
163 SystemMessageQueueCount++;
165 KeReleaseSpinLock(&SystemMessageQueueLock, OldIrql);
166 KeSetEvent(&HardwareMessageEvent, IO_NO_INCREMENT, FALSE);
170 MsqTranslateMouseMessage(HWND hWnd, UINT FilterLow, UINT FilterHigh,
171 PUSER_MESSAGE Message, BOOL Remove,
172 PWINDOW_OBJECT ScopeWin, PUSHORT HitTest,
173 PPOINT ScreenPoint, PBOOL MouseClick)
175 USHORT Msg = Message->Msg.message;
176 PWINDOW_OBJECT Window;
179 if ((Window = IntGetCaptureWindow()) == NULL)
181 *HitTest = WinPosWindowFromPoint(ScopeWin, Message->Msg.pt, &Window);
193 if (Window->MessageQueue != PsGetWin32Thread()->MessageQueue)
195 ExAcquireFastMutex(&Window->MessageQueue->Lock);
196 InsertTailList(&Window->MessageQueue->HardwareMessagesListHead,
197 &Message->ListEntry);
198 ExReleaseFastMutex(&Window->MessageQueue->Lock);
199 KeSetEvent(&Window->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
203 if (hWnd != NULL && Window->Self != hWnd &&
204 !IntIsChildWindow(hWnd, Window->Self))
209 if (Msg == WM_LBUTTONDBLCLK || Msg == WM_RBUTTONDBLCLK || Msg == WM_MBUTTONDBLCLK)
211 if (((*HitTest) != HTCLIENT) || !(IntGetClassLong(Window, GCL_STYLE, FALSE) & CS_DBLCLKS))
213 Msg -= (WM_LBUTTONDBLCLK - WM_LBUTTONDOWN);
214 /* FIXME set WindowStation's system cursor variables:
215 LastBtnDown to Msg.time
220 /* FIXME check if the dblclick was made in the same window, if
221 not, change it to a normal click message */
225 *ScreenPoint = Message->Msg.pt;
226 Point = Message->Msg.pt;
228 if ((*HitTest) != HTCLIENT)
230 Msg += WM_NCMOUSEMOVE - WM_MOUSEMOVE;
231 Message->Msg.wParam = *HitTest;
235 Point.x -= Window->ClientRect.left;
236 Point.y -= Window->ClientRect.top;
239 /* FIXME: Check message filter. */
243 Message->Msg.hwnd = Window->Self;
244 Message->Msg.message = Msg;
245 Message->Msg.lParam = MAKELONG(Point.x, Point.y);
252 MsqPeekHardwareMessage(PUSER_MESSAGE_QUEUE MessageQueue, HWND hWnd,
253 UINT FilterLow, UINT FilterHigh, BOOL Remove,
254 PUSER_MESSAGE* Message)
261 PLIST_ENTRY CurrentEntry;
263 PWINDOW_OBJECT DesktopWindow;
265 DesktopWindow = IntGetWindowObject(IntGetDesktopWindow());
267 /* Process messages in the message queue itself. */
268 ExAcquireFastMutex(&MessageQueue->Lock);
269 CurrentEntry = MessageQueue->HardwareMessagesListHead.Flink;
270 while (CurrentEntry != &MessageQueue->HardwareMessagesListHead)
272 PUSER_MESSAGE Current =
273 CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, ListEntry);
274 CurrentEntry = CurrentEntry->Flink;
275 if (Current->Msg.message >= WM_MOUSEFIRST &&
276 Current->Msg.message <= WM_MOUSELAST)
278 Accept = MsqTranslateMouseMessage(hWnd, FilterLow, FilterHigh,
280 DesktopWindow, &HitTest,
281 &ScreenPoint, &MouseClick);
286 RemoveEntryList(&Current->ListEntry);
288 ExReleaseFastMutex(&MessageQueue->Lock);
290 IntReleaseWindowObject(DesktopWindow);
295 ExReleaseFastMutex(&MessageQueue->Lock);
297 /* Now try the global queue. */
298 ExAcquireFastMutex(&HardwareMessageQueueLock);
299 /* Transfer all messages from the DPC accessible queue to the main queue. */
300 KeAcquireSpinLock(&SystemMessageQueueLock, &OldIrql);
301 while (SystemMessageQueueCount > 0)
303 PUSER_MESSAGE UserMsg;
306 Msg = SystemMessageQueue[SystemMessageQueueHead];
307 SystemMessageQueueHead =
308 (SystemMessageQueueHead + 1) % SYSTEM_MESSAGE_QUEUE_SIZE;
309 SystemMessageQueueCount--;
310 KeReleaseSpinLock(&SystemMessageQueueLock, OldIrql);
311 UserMsg = ExAllocateFromPagedLookasideList(&MessageLookasideList);
313 InsertTailList(&HardwareMessageQueueHead, &UserMsg->ListEntry);
314 KeAcquireSpinLock(&SystemMessageQueueLock, &OldIrql);
317 * we could set this to -1 conditionally if we find one, but
318 * this is more efficient and just as effective.
320 SystemMessageQueueMouseMove = -1;
321 KeReleaseSpinLock(&SystemMessageQueueLock, OldIrql);
322 HardwareMessageQueueStamp++;
324 /* Process messages in the queue until we find one to return. */
325 CurrentEntry = HardwareMessageQueueHead.Flink;
326 while (CurrentEntry != &HardwareMessageQueueHead)
328 PUSER_MESSAGE Current =
329 CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, ListEntry);
330 CurrentEntry = CurrentEntry->Flink;
331 RemoveEntryList(&Current->ListEntry);
332 if (Current->Msg.message >= WM_MOUSEFIRST &&
333 Current->Msg.message <= WM_MOUSELAST)
335 ActiveStamp = HardwareMessageQueueStamp;
336 ExReleaseFastMutex(&HardwareMessageQueueLock);
337 /* Translate the message. */
338 Accept = MsqTranslateMouseMessage(hWnd, FilterLow, FilterHigh,
340 DesktopWindow, &HitTest,
341 &ScreenPoint, &MouseClick);
342 ExAcquireFastMutex(&HardwareMessageQueueLock);
345 /* Check for no more messages in the system queue. */
346 KeAcquireSpinLock(&SystemMessageQueueLock, &OldIrql);
347 if (SystemMessageQueueCount == 0 &&
348 IsListEmpty(&HardwareMessageQueueHead))
350 KeClearEvent(&HardwareMessageEvent);
352 KeReleaseSpinLock(&SystemMessageQueueLock, OldIrql);
355 If we aren't removing the message then add it to the private
360 InsertTailList(&MessageQueue->HardwareMessagesListHead,
361 &Current->ListEntry);
363 ExReleaseFastMutex(&HardwareMessageQueueLock);
365 IntReleaseWindowObject(DesktopWindow);
368 /* If the contents of the queue changed then restart processing. */
369 if (HardwareMessageQueueStamp != ActiveStamp)
371 CurrentEntry = HardwareMessageQueueHead.Flink;
376 /* Check if the system message queue is now empty. */
377 KeAcquireSpinLock(&SystemMessageQueueLock, &OldIrql);
378 if (SystemMessageQueueCount == 0 && IsListEmpty(&HardwareMessageQueueHead))
380 KeClearEvent(&HardwareMessageEvent);
382 KeReleaseSpinLock(&SystemMessageQueueLock, OldIrql);
383 ExReleaseFastMutex(&HardwareMessageQueueLock);
389 MsqPostKeyboardMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
391 PUSER_MESSAGE_QUEUE FocusMessageQueue;
392 PUSER_MESSAGE Message;
395 DPRINT("MsqPostKeyboardMessage(uMsg 0x%x, wParam 0x%x, lParam 0x%x)\n",
396 uMsg, wParam, lParam);
398 FocusMessageQueue = IntGetFocusMessageQueue();
399 if (FocusMessageQueue == NULL)
401 DPRINT("No focus message queue\n");
405 if (FocusMessageQueue->FocusWindow != (HWND)0)
407 Msg.hwnd = FocusMessageQueue->FocusWindow;
411 /* FIXME: Initialize time and point. */
413 Message = MsqCreateMessage(&Msg);
414 MsqPostMessage(FocusMessageQueue, Message);
418 DPRINT("Invalid focus window handle\n");
423 MsqInitializeMessage(PUSER_MESSAGE Message,
426 RtlMoveMemory(&Message->Msg, Msg, sizeof(MSG));
429 PUSER_MESSAGE FASTCALL
430 MsqCreateMessage(LPMSG Msg)
432 PUSER_MESSAGE Message;
434 Message = ExAllocateFromPagedLookasideList(&MessageLookasideList);
440 MsqInitializeMessage(Message, Msg);
446 MsqDestroyMessage(PUSER_MESSAGE Message)
448 ExFreeToPagedLookasideList(&MessageLookasideList, Message);
452 MsqDispatchSentNotifyMessages(PUSER_MESSAGE_QUEUE MessageQueue)
454 PLIST_ENTRY ListEntry;
455 PUSER_SENT_MESSAGE_NOTIFY Message;
457 while (!IsListEmpty(&MessageQueue->SentMessagesListHead))
459 ExAcquireFastMutex(&MessageQueue->Lock);
460 ListEntry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
461 Message = CONTAINING_RECORD(ListEntry, USER_SENT_MESSAGE_NOTIFY,
463 ExReleaseFastMutex(&MessageQueue->Lock);
465 IntCallSentMessageCallback(Message->CompletionCallback,
468 Message->CompletionCallbackContext,
474 MsqPeekSentMessages(PUSER_MESSAGE_QUEUE MessageQueue)
476 return(!IsListEmpty(&MessageQueue->SentMessagesListHead));
480 MsqDispatchOneSentMessage(PUSER_MESSAGE_QUEUE MessageQueue)
482 PUSER_SENT_MESSAGE Message;
485 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage;
487 ExAcquireFastMutex(&MessageQueue->Lock);
488 if (IsListEmpty(&MessageQueue->SentMessagesListHead))
490 ExReleaseFastMutex(&MessageQueue->Lock);
493 Entry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
494 Message = CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry);
495 ExReleaseFastMutex(&MessageQueue->Lock);
497 /* Call the window procedure. */
498 Result = IntCallWindowProc(NULL,
500 Message->Msg.message,
502 Message->Msg.lParam);
504 /* Let the sender know the result. */
505 if (Message->Result != NULL)
507 *Message->Result = Result;
510 /* Notify the sender. */
511 if (Message->CompletionEvent != NULL)
513 KeSetEvent(Message->CompletionEvent, IO_NO_INCREMENT, FALSE);
516 /* Notify the sender if they specified a callback. */
517 if (Message->CompletionCallback != NULL)
519 NotifyMessage = ExAllocatePool(NonPagedPool,
520 sizeof(USER_SENT_MESSAGE_NOTIFY));
521 NotifyMessage->CompletionCallback =
522 Message->CompletionCallback;
523 NotifyMessage->CompletionCallbackContext =
524 Message->CompletionCallbackContext;
525 NotifyMessage->Result = Result;
526 NotifyMessage->hWnd = Message->Msg.hwnd;
527 NotifyMessage->Msg = Message->Msg.message;
528 MsqSendNotifyMessage(Message->CompletionQueue, NotifyMessage);
536 MsqSendNotifyMessage(PUSER_MESSAGE_QUEUE MessageQueue,
537 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage)
539 ExAcquireFastMutex(&MessageQueue->Lock);
540 InsertTailList(&MessageQueue->NotifyMessagesListHead,
541 &NotifyMessage->ListEntry);
542 KeSetEvent(&MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
543 ExReleaseFastMutex(&MessageQueue->Lock);
547 MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue,
548 PUSER_SENT_MESSAGE Message)
550 ExAcquireFastMutex(&MessageQueue->Lock);
551 InsertTailList(&MessageQueue->SentMessagesListHead, &Message->ListEntry);
552 KeSetEvent(&MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
553 ExReleaseFastMutex(&MessageQueue->Lock);
557 MsqPostMessage(PUSER_MESSAGE_QUEUE MessageQueue, PUSER_MESSAGE Message)
559 ExAcquireFastMutex(&MessageQueue->Lock);
560 InsertTailList(&MessageQueue->PostedMessagesListHead,
561 &Message->ListEntry);
562 KeSetEvent(&MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
563 ExReleaseFastMutex(&MessageQueue->Lock);
567 MsqPostQuitMessage(PUSER_MESSAGE_QUEUE MessageQueue, ULONG ExitCode)
569 ExAcquireFastMutex(&MessageQueue->Lock);
570 MessageQueue->QuitPosted = TRUE;
571 MessageQueue->QuitExitCode = ExitCode;
572 KeSetEvent(&MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
573 ExReleaseFastMutex(&MessageQueue->Lock);
577 MsqFindMessage(IN PUSER_MESSAGE_QUEUE MessageQueue,
581 IN UINT MsgFilterLow,
582 IN UINT MsgFilterHigh,
583 OUT PUSER_MESSAGE* Message)
585 PLIST_ENTRY CurrentEntry;
586 PUSER_MESSAGE CurrentMessage;
587 PLIST_ENTRY ListHead;
591 return(MsqPeekHardwareMessage(MessageQueue, Wnd,
592 MsgFilterLow, MsgFilterHigh,
596 ExAcquireFastMutex(&MessageQueue->Lock);
597 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
598 ListHead = &MessageQueue->PostedMessagesListHead;
599 while (CurrentEntry != ListHead)
601 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
603 if ((Wnd == 0 || Wnd == CurrentMessage->Msg.hwnd) &&
604 ((MsgFilterLow == 0 && MsgFilterHigh == 0) ||
605 (MsgFilterLow <= CurrentMessage->Msg.message &&
606 MsgFilterHigh >= CurrentMessage->Msg.message)))
610 RemoveEntryList(&CurrentMessage->ListEntry);
612 ExReleaseFastMutex(&MessageQueue->Lock);
613 *Message = CurrentMessage;
616 CurrentEntry = CurrentEntry->Flink;
618 ExReleaseFastMutex(&MessageQueue->Lock);
623 MsqWaitForNewMessages(PUSER_MESSAGE_QUEUE MessageQueue)
625 PVOID WaitObjects[2] = {&MessageQueue->NewMessages, &HardwareMessageEvent};
626 return(KeWaitForMultipleObjects(2,
637 MsqInitializeMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
639 InitializeListHead(&MessageQueue->PostedMessagesListHead);
640 InitializeListHead(&MessageQueue->SentMessagesListHead);
641 InitializeListHead(&MessageQueue->HardwareMessagesListHead);
642 ExInitializeFastMutex(&MessageQueue->Lock);
643 MessageQueue->QuitPosted = FALSE;
644 MessageQueue->QuitExitCode = 0;
645 KeInitializeEvent(&MessageQueue->NewMessages, SynchronizationEvent, FALSE);
646 MessageQueue->QueueStatus = 0;
647 MessageQueue->FocusWindow = NULL;
651 MsqFreeMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
653 PLIST_ENTRY CurrentEntry;
654 PUSER_MESSAGE CurrentMessage;
656 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
657 while (CurrentEntry != &MessageQueue->PostedMessagesListHead)
659 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
661 CurrentEntry = CurrentEntry->Flink;
662 MsqDestroyMessage(CurrentMessage);
666 PUSER_MESSAGE_QUEUE FASTCALL
667 MsqCreateMessageQueue(VOID)
669 PUSER_MESSAGE_QUEUE MessageQueue;
671 MessageQueue = (PUSER_MESSAGE_QUEUE)ExAllocatePool(PagedPool,
672 sizeof(USER_MESSAGE_QUEUE));
678 MsqInitializeMessageQueue(MessageQueue);
684 MsqDestroyMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
686 MsqFreeMessageQueue(MessageQueue);
687 ExFreePool(MessageQueue);