664e347b031319235bf52885078d00038420efa0
[reactos.git] / subsys / win32k / ntuser / msgqueue.c
1 /* $Id$
2  *
3  * COPYRIGHT:        See COPYING in the top level directory
4  * PROJECT:          ReactOS kernel
5  * PURPOSE:          Message queues
6  * FILE:             subsys/win32k/ntuser/msgqueue.c
7  * PROGRAMER:        Casper S. Hornstrup (chorns@users.sourceforge.net)
8  * REVISION HISTORY:
9  *       06-06-2001  CSH  Created
10  */
11
12 /* INCLUDES ******************************************************************/
13
14 #include <ddk/ntddk.h>
15 #include <win32k/win32k.h>
16 #include <include/msgqueue.h>
17 #include <include/callback.h>
18 #include <include/window.h>
19 #include <include/winpos.h>
20 #include <include/class.h>
21
22 #define NDEBUG
23 #include <debug.h>
24
25 /* GLOBALS *******************************************************************/
26
27 #define SYSTEM_MESSAGE_QUEUE_SIZE           (256)
28
29 static MSG SystemMessageQueue[SYSTEM_MESSAGE_QUEUE_SIZE];
30 static ULONG SystemMessageQueueHead = 0;
31 static ULONG SystemMessageQueueTail = 0;
32 static ULONG SystemMessageQueueCount = 0;
33 static KSPIN_LOCK SystemMessageQueueLock;
34
35 static ULONG HardwareMessageQueueStamp = 0;
36 static LIST_ENTRY HardwareMessageQueueHead;
37 static FAST_MUTEX HardwareMessageQueueLock;
38
39 static KEVENT HardwareMessageEvent;
40
41 /* FUNCTIONS *****************************************************************/
42
43 VOID
44 MsqIncPaintCountQueue(PUSER_MESSAGE_QUEUE Queue)
45 {
46   ExAcquireFastMutex(&Queue->Lock);
47   Queue->PaintCount++;
48   Queue->PaintPosted = TRUE;
49   ExReleaseFastMutex(&Queue->Lock);
50 }
51
52 VOID
53 MsqDecPaintCountQueue(PUSER_MESSAGE_QUEUE Queue)
54 {
55   ExAcquireFastMutex(&Queue->Lock);
56   Queue->PaintCount--;
57   if (Queue->PaintCount == 0)
58     {
59       Queue->PaintPosted = FALSE;
60     }
61   ExReleaseFastMutex(&Queue->Lock);
62 }
63
64
65 NTSTATUS
66 MsqInitializeImpl(VOID)
67 {
68   /*CurrentFocusMessageQueue = NULL;*/
69   InitializeListHead(&HardwareMessageQueueHead);
70   KeInitializeEvent(&HardwareMessageEvent, NotificationEvent, 0);
71   KeInitializeSpinLock(&SystemMessageQueueLock);
72   ExInitializeFastMutex(&HardwareMessageQueueLock);
73   return(STATUS_SUCCESS);
74 }
75
76 VOID
77 MsqInsertSystemMessage(MSG* Msg)
78 {
79   KIRQL OldIrql;
80
81   KeAcquireSpinLock(&SystemMessageQueueLock, &OldIrql);
82   if (SystemMessageQueueCount == SYSTEM_MESSAGE_QUEUE_SIZE)
83     {
84       KeReleaseSpinLock(&SystemMessageQueueLock, OldIrql);
85       return;
86     }
87   SystemMessageQueue[SystemMessageQueueTail] = *Msg;
88   SystemMessageQueueTail = 
89     (SystemMessageQueueTail + 1) % SYSTEM_MESSAGE_QUEUE_SIZE;
90   SystemMessageQueueCount++;
91   KeReleaseSpinLock(&SystemMessageQueueLock, OldIrql);
92   KeSetEvent(&HardwareMessageEvent, IO_NO_INCREMENT, FALSE);
93 }
94
95 BOOL STATIC
96 MsqTranslateMouseMessage(HWND hWnd, UINT FilterLow, UINT FilterHigh,
97                          PUSER_MESSAGE Message, BOOL Remove, 
98                          PWINDOW_OBJECT ScopeWin, PUSHORT HitTest,
99                          PPOINT ScreenPoint, PBOOL MouseClick)
100 {
101   static ULONG ClkTime = 0;
102   static USHORT ClkMessage = 0;
103   static HWND ClkWnd = 0;
104   static POINT ClkPos = {0, 0};
105
106   USHORT Msg = Message->Msg.message;
107   PWINDOW_OBJECT Window;
108   POINT Point;
109   ULONG Click = 0;
110
111   /* FIXME: Handle window capture. */
112
113   *HitTest = WinPosWindowFromPoint(ScopeWin, Message->Msg.pt, &Window);
114   
115   if (Window == NULL)
116     {
117       ExFreePool(Message);
118       return(FALSE);
119     }
120   if (Window->MessageQueue != PsGetWin32Thread()->MessageQueue)
121     {      
122       ExAcquireFastMutex(&Window->MessageQueue->Lock);
123       InsertTailList(&Window->MessageQueue->HardwareMessagesListHead,
124                      &Message->ListEntry);
125       ExReleaseFastMutex(&Window->MessageQueue->Lock);
126       KeSetEvent(&Window->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
127       return(FALSE);
128     }
129
130   if (hWnd != NULL && Window->Self != hWnd && 
131       !W32kIsChildWindow(hWnd, Window->Self))
132     {
133       return(FALSE);
134     }
135
136   if (Msg == WM_LBUTTONDOWN || Msg == WM_RBUTTONDOWN || Msg == WM_MBUTTONDOWN)
137     {
138       (*MouseClick) = Click = 1;
139     }
140   if (Click)
141     {
142       if (W32kGetClassLong(Window, GCL_STYLE) & CS_DBLCLKS ||
143           (*HitTest) != HTCLIENT)
144         {
145           if (Msg == ClkMessage &&
146               Window->Self == ClkWnd &&
147               (Message->Msg.time - ClkTime) < 452 &&
148               abs(Message->Msg.pt.x - ClkPos.x) < 2 &&
149               abs(Message->Msg.pt.y - ClkPos.y) < 2)
150             {
151               Msg += (WM_LBUTTONDBLCLK - WM_LBUTTONDOWN);
152               Click++;
153             }
154         }
155     }
156
157   *ScreenPoint = Message->Msg.pt;
158
159   if ((*HitTest) != HTCLIENT)
160     {
161       Msg += WM_NCMOUSEMOVE - WM_MOUSEMOVE;
162       Message->Msg.wParam = *HitTest;
163     }
164   else
165     {
166       Point = Message->Msg.pt;
167
168       Point.x -= Window->ClientRect.left;
169       Point.y -= Window->ClientRect.top;
170     }
171
172   /* FIXME: Check message filter. */
173
174   if (Remove && Click)
175     {
176       if (Click == 1)
177         {
178           ClkTime = Message->Msg.time;
179           ClkMessage = Msg;
180           ClkWnd = Window->Self;
181           ClkPos = (*ScreenPoint);
182         }
183       else
184         {
185           ClkTime = 0;
186           ClkWnd = NULL;
187         }
188     }
189
190   Message->Msg.hwnd = Window->Self;
191   Message->Msg.message = Msg;
192   Message->Msg.lParam = MAKELONG(Point.x, Point.y);
193
194   return(TRUE);
195 }
196
197 BOOL
198 MsqPeekHardwareMessage(PUSER_MESSAGE_QUEUE MessageQueue, HWND hWnd, 
199                        UINT FilterLow, UINT FilterHigh, BOOL Remove,
200                        PUSER_MESSAGE* Message)
201 {
202   KIRQL OldIrql;
203   USHORT HitTest;
204   POINT ScreenPoint;
205   BOOL Accept;
206   BOOL MouseClick;
207   PLIST_ENTRY CurrentEntry;
208   ULONG ActiveStamp;
209   PWINDOW_OBJECT DesktopWindow;
210
211   DesktopWindow = W32kGetWindowObject(W32kGetDesktopWindow());
212
213   /* Process messages in the message queue itself. */
214   ExAcquireFastMutex(&MessageQueue->Lock);
215   CurrentEntry = MessageQueue->HardwareMessagesListHead.Flink;
216   while (CurrentEntry != &MessageQueue->HardwareMessagesListHead)
217     {
218       PUSER_MESSAGE Current = 
219         CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, ListEntry);
220       CurrentEntry = CurrentEntry->Flink;
221       RemoveEntryList(&Current->ListEntry);
222       if (Current->Msg.message >= WM_MOUSEFIRST && 
223           Current->Msg.message <= WM_MOUSELAST)
224         {
225           Accept = MsqTranslateMouseMessage(hWnd, FilterLow, FilterHigh,
226                                             Current, Remove, 
227                                             DesktopWindow, &HitTest,
228                                             &ScreenPoint, &MouseClick);
229           if (Accept)
230             {
231               RemoveEntryList(&Current->ListEntry);
232               ExReleaseFastMutex(&MessageQueue->Lock);
233               *Message = Current;
234               W32kReleaseWindowObject(DesktopWindow);
235               return(TRUE);
236             }
237         }
238       CurrentEntry = CurrentEntry->Flink;
239     }
240   ExReleaseFastMutex(&MessageQueue->Lock);
241
242   /* Now try the global queue. */
243   ExAcquireFastMutex(&HardwareMessageQueueLock);
244   /* Transfer all messages from the DPC accessible queue to the main queue. */
245   KeAcquireSpinLock(&SystemMessageQueueLock, &OldIrql);
246   while (SystemMessageQueueCount > 0)
247     {      
248       PUSER_MESSAGE UserMsg;
249       MSG Msg;
250
251       Msg = SystemMessageQueue[SystemMessageQueueHead];
252       SystemMessageQueueHead = 
253         (SystemMessageQueueHead + 1) % SYSTEM_MESSAGE_QUEUE_SIZE;
254       SystemMessageQueueCount--;
255       KeReleaseSpinLock(&SystemMessageQueueLock, OldIrql);
256       UserMsg = ExAllocatePool(NonPagedPool, sizeof(USER_MESSAGE));
257       UserMsg->Msg = Msg;
258       InsertTailList(&HardwareMessageQueueHead, &UserMsg->ListEntry);
259       KeAcquireSpinLock(&SystemMessageQueueLock, &OldIrql);
260     }
261   KeReleaseSpinLock(&SystemMessageQueueLock, OldIrql);
262   HardwareMessageQueueStamp++;
263
264   /* Process messages in the queue until we find one to return. */
265   CurrentEntry = HardwareMessageQueueHead.Flink;
266   while (CurrentEntry != &HardwareMessageQueueHead)
267     {
268       PUSER_MESSAGE Current = 
269         CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, ListEntry);
270       CurrentEntry = CurrentEntry->Flink;
271       RemoveEntryList(&Current->ListEntry);
272       if (Current->Msg.message >= WM_MOUSEFIRST && 
273           Current->Msg.message <= WM_MOUSELAST)
274         {
275           ActiveStamp = HardwareMessageQueueStamp;
276           ExReleaseFastMutex(&HardwareMessageQueueLock);
277           /* Translate the message. */
278           Accept = MsqTranslateMouseMessage(hWnd, FilterLow, FilterHigh,
279                                             Current, Remove,
280                                             DesktopWindow, &HitTest,
281                                             &ScreenPoint, &MouseClick);
282           ExAcquireFastMutex(&HardwareMessageQueueLock);
283           if (Accept)
284             {
285               /* Check for no more messages in the system queue. */
286               KeAcquireSpinLock(&SystemMessageQueueLock, &OldIrql);
287               if (SystemMessageQueueCount == 0 &&
288                   IsListEmpty(&HardwareMessageQueueHead))
289                 {
290                   KeClearEvent(&HardwareMessageEvent);
291                 }
292               KeReleaseSpinLock(&SystemMessageQueueLock, OldIrql);
293
294               /* 
295                  If we aren't removing the message then add it to the private
296                  queue.
297               */
298               if (!Remove)
299                 {
300                   InsertTailList(&MessageQueue->HardwareMessagesListHead,
301                                  &Current->ListEntry);
302                 }
303               ExReleaseFastMutex(&HardwareMessageQueueLock);
304               *Message = Current;            
305               W32kReleaseWindowObject(DesktopWindow);
306               return(TRUE);
307             }
308           /* If the contents of the queue changed then restart processing. */
309           if (HardwareMessageQueueStamp != ActiveStamp)
310             {
311               CurrentEntry = HardwareMessageQueueHead.Flink;
312               continue;
313             }
314         }      
315     }
316   /* Check if the system message queue is now empty. */
317   KeAcquireSpinLock(&SystemMessageQueueLock, &OldIrql);
318   if (SystemMessageQueueCount == 0 && IsListEmpty(&HardwareMessageQueueHead))
319     {
320       KeClearEvent(&HardwareMessageEvent);
321     }
322   KeReleaseSpinLock(&SystemMessageQueueLock, OldIrql);
323   ExReleaseFastMutex(&HardwareMessageQueueLock);
324
325   return(FALSE);
326 }
327
328 VOID
329 MsqPostKeyboardMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
330 {
331 #if 0
332   MSG Msg;
333   PUSER_MESSAGE Message;
334
335   if (CurrentFocusMessageQueue == NULL)
336     {
337       return;
338     }
339
340   Msg.hwnd = CurrentFocusMessageQueue->FocusWindow;
341   Msg.message = uMsg;
342   Msg.wParam = wParam;
343   Msg.lParam = lParam;
344   /* FIXME: Initialize time and point. */
345
346   Message = MsqCreateMessage(&Msg);
347   MsqPostMessage(CurrentFocusMessageQueue, Message, TRUE);
348 #endif
349 }
350
351 VOID
352 MsqInitializeMessage(PUSER_MESSAGE Message,
353                      LPMSG Msg)
354 {
355   RtlMoveMemory(&Message->Msg, Msg, sizeof(MSG));
356 }
357
358 PUSER_MESSAGE
359 MsqCreateMessage(LPMSG Msg)
360 {
361   PUSER_MESSAGE Message;
362   
363   Message = (PUSER_MESSAGE)ExAllocatePool(PagedPool, sizeof(USER_MESSAGE));
364   if (!Message)
365     {
366       return NULL;
367     }
368   
369   MsqInitializeMessage(Message, Msg);
370   
371   return Message;
372 }
373
374 VOID
375 MsqDestroyMessage(PUSER_MESSAGE Message)
376 {
377   ExFreePool(Message);
378 }
379
380 VOID
381 MsqDispatchSentNotifyMessages(PUSER_MESSAGE_QUEUE MessageQueue)
382 {
383   PLIST_ENTRY ListEntry;
384   PUSER_SENT_MESSAGE_NOTIFY Message;
385
386   while (!IsListEmpty(&MessageQueue->SentMessagesListHead))
387   {
388     ExAcquireFastMutex(&MessageQueue->Lock);
389     ListEntry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
390     Message = CONTAINING_RECORD(ListEntry, USER_SENT_MESSAGE_NOTIFY, 
391                                 ListEntry);
392     ExReleaseFastMutex(&MessageQueue->Lock);
393
394     W32kCallSentMessageCallback(Message->CompletionCallback,
395                                 Message->hWnd,
396                                 Message->Msg,
397                                 Message->CompletionCallbackContext,
398                                 Message->Result);
399   }
400 }
401
402 BOOLEAN
403 MsqPeekSentMessages(PUSER_MESSAGE_QUEUE MessageQueue)
404 {
405   return(!IsListEmpty(&MessageQueue->SentMessagesListHead));
406 }
407
408 BOOLEAN
409 MsqDispatchOneSentMessage(PUSER_MESSAGE_QUEUE MessageQueue)
410 {
411   PUSER_SENT_MESSAGE Message;
412   PLIST_ENTRY Entry;
413   LRESULT Result;
414   PUSER_SENT_MESSAGE_NOTIFY NotifyMessage;
415
416   ExAcquireFastMutex(&MessageQueue->Lock);
417   if (IsListEmpty(&MessageQueue->SentMessagesListHead))
418     {
419       ExReleaseFastMutex(&MessageQueue->Lock);
420       return(FALSE);
421     }
422   Entry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
423   Message = CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry);
424   ExReleaseFastMutex(&MessageQueue->Lock);
425
426   /* Call the window procedure. */
427   Result = W32kCallWindowProc(NULL,
428                               Message->Msg.hwnd,
429                               Message->Msg.message,
430                               Message->Msg.wParam,
431                               Message->Msg.lParam);
432
433   /* Let the sender know the result. */
434   if (Message->Result != NULL)
435     {
436       *Message->Result = Result;
437     }
438
439   /* Notify the sender. */
440   if (Message->CompletionEvent != NULL)
441     {
442       KeSetEvent(Message->CompletionEvent, IO_NO_INCREMENT, FALSE);
443     }
444
445   /* Notify the sender if they specified a callback. */
446   if (Message->CompletionCallback != NULL)
447     {
448       NotifyMessage = ExAllocatePool(NonPagedPool, 
449                                      sizeof(USER_SENT_MESSAGE_NOTIFY));
450       NotifyMessage->CompletionCallback = 
451         Message->CompletionCallback;
452       NotifyMessage->CompletionCallbackContext =
453         Message->CompletionCallbackContext;
454       NotifyMessage->Result = Result;
455       NotifyMessage->hWnd = Message->Msg.hwnd;
456       NotifyMessage->Msg = Message->Msg.message;
457       MsqSendNotifyMessage(Message->CompletionQueue, NotifyMessage);
458     }
459
460   ExFreePool(Message);
461   return(TRUE);
462 }
463
464 VOID
465 MsqSendNotifyMessage(PUSER_MESSAGE_QUEUE MessageQueue,
466                      PUSER_SENT_MESSAGE_NOTIFY NotifyMessage)
467 {
468   ExAcquireFastMutex(&MessageQueue->Lock);
469   InsertTailList(&MessageQueue->NotifyMessagesListHead, 
470                  &NotifyMessage->ListEntry);
471   ExReleaseFastMutex(&MessageQueue->Lock);
472 }
473
474 VOID
475 MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue,
476                PUSER_SENT_MESSAGE Message)
477 {
478   ExAcquireFastMutex(&MessageQueue->Lock);
479   InsertTailList(&MessageQueue->SentMessagesListHead, &Message->ListEntry);
480   ExReleaseFastMutex(&MessageQueue->Lock);
481 }
482
483 VOID
484 MsqPostMessage(PUSER_MESSAGE_QUEUE MessageQueue, PUSER_MESSAGE Message)
485 {
486   ExAcquireFastMutex(&MessageQueue->Lock);
487   InsertTailList(&MessageQueue->PostedMessagesListHead,
488                  &Message->ListEntry);
489   KeSetEvent(&MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
490   ExReleaseFastMutex(&MessageQueue->Lock);
491 }
492
493 BOOLEAN
494 MsqFindMessage(IN PUSER_MESSAGE_QUEUE MessageQueue,
495                IN BOOLEAN Hardware,
496                IN BOOLEAN Remove,
497                IN HWND Wnd,
498                IN UINT MsgFilterLow,
499                IN UINT MsgFilterHigh,
500                OUT PUSER_MESSAGE* Message)
501 {
502   PLIST_ENTRY CurrentEntry;
503   PUSER_MESSAGE CurrentMessage;
504   PLIST_ENTRY ListHead;
505
506   if (Hardware)
507     {
508       return(MsqPeekHardwareMessage(MessageQueue, Wnd,
509                                     MsgFilterLow, MsgFilterHigh,
510                                     Remove, Message));
511     }
512   
513   ExAcquireFastMutex(&MessageQueue->Lock);
514   CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
515   ListHead = &MessageQueue->PostedMessagesListHead;
516   while (CurrentEntry != ListHead)
517     {
518       CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, 
519                                          ListEntry);
520       if ((Wnd == 0 || Wnd == CurrentMessage->Msg.hwnd) &&
521           ((MsgFilterLow == 0 && MsgFilterHigh == 0) ||
522            (MsgFilterLow <= CurrentMessage->Msg.message &&
523             MsgFilterHigh >= CurrentMessage->Msg.message)))
524         {
525           if (Remove)
526             {
527               RemoveEntryList(&CurrentMessage->ListEntry);
528             }
529           ExReleaseFastMutex(&MessageQueue->Lock);
530           *Message = CurrentMessage;
531           return(TRUE);
532         }
533       CurrentEntry = CurrentEntry->Flink;
534     }
535   ExReleaseFastMutex(&MessageQueue->Lock);
536   return(FALSE);
537 }
538
539 NTSTATUS
540 MsqWaitForNewMessages(PUSER_MESSAGE_QUEUE MessageQueue)
541 {
542   PVOID WaitObjects[2] = {&MessageQueue->NewMessages, &HardwareMessageEvent};
543   return(KeWaitForMultipleObjects(2,
544                                   WaitObjects,
545                                   WaitAny,
546                                   Executive,
547                                   UserMode,
548                                   TRUE,
549                                   NULL,
550                                   NULL));
551 }
552
553 VOID
554 MsqInitializeMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
555 {
556   InitializeListHead(&MessageQueue->PostedMessagesListHead);
557   InitializeListHead(&MessageQueue->SentMessagesListHead);
558   InitializeListHead(&MessageQueue->HardwareMessagesListHead);
559   ExInitializeFastMutex(&MessageQueue->Lock);
560   MessageQueue->QuitPosted = FALSE;
561   MessageQueue->QuitExitCode = 0;
562   KeInitializeEvent(&MessageQueue->NewMessages, NotificationEvent, FALSE);
563   MessageQueue->QueueStatus = 0;
564   MessageQueue->FocusWindow = NULL;
565 }
566
567 VOID
568 MsqFreeMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
569 {
570   PLIST_ENTRY CurrentEntry;
571   PUSER_MESSAGE CurrentMessage;
572   
573   CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
574   while (CurrentEntry != &MessageQueue->PostedMessagesListHead)
575     {
576       CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, 
577                                          ListEntry);
578       CurrentEntry = CurrentEntry->Flink;
579       ExFreePool(CurrentMessage);
580     }
581 }
582
583 PUSER_MESSAGE_QUEUE
584 MsqCreateMessageQueue(VOID)
585 {
586   PUSER_MESSAGE_QUEUE MessageQueue;
587
588   MessageQueue = (PUSER_MESSAGE_QUEUE)ExAllocatePool(PagedPool, 
589                                    sizeof(USER_MESSAGE_QUEUE));
590   if (!MessageQueue)
591     {
592       return NULL;
593     }
594   
595   MsqInitializeMessageQueue(MessageQueue);
596   
597   return MessageQueue;
598 }
599
600 VOID
601 MsqDestroyMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
602 {
603   MsqFreeMessageQueue(MessageQueue);
604   ExFreePool(MessageQueue);
605 }
606
607 /* EOF */