:pserver:cvsanon@mok.lvcm.com:/CVS/ReactOS reactos
[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->MessageQueue != PsGetWin32Thread()->MessageQueue)
116     {      
117       ExAcquireFastMutex(&Window->MessageQueue->Lock);
118       InsertTailList(&Window->MessageQueue->HardwareMessagesListHead,
119                      &Message->ListEntry);
120       ExReleaseFastMutex(&Window->MessageQueue->Lock);
121       KeSetEvent(&Window->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
122       return(FALSE);
123     }
124
125   if (hWnd != NULL && Window->Self != hWnd && 
126       !W32kIsChildWindow(hWnd, Window->Self))
127     {
128       return(FALSE);
129     }
130
131   if (Msg == WM_LBUTTONDOWN || Msg == WM_RBUTTONDOWN || Msg == WM_MBUTTONDOWN)
132     {
133       (*MouseClick) = Click = 1;
134     }
135   if (Click)
136     {
137       if (W32kGetClassLong(Window, GCL_STYLE) & CS_DBLCLKS ||
138           (*HitTest) != HTCLIENT)
139         {
140           if (Msg == ClkMessage &&
141               Window->Self == ClkWnd &&
142               (Message->Msg.time - ClkTime) < 452 &&
143               abs(Message->Msg.pt.x - ClkPos.x) < 2 &&
144               abs(Message->Msg.pt.y - ClkPos.y) < 2)
145             {
146               Msg += (WM_LBUTTONDBLCLK - WM_LBUTTONDOWN);
147               Click++;
148             }
149         }
150     }
151
152   *ScreenPoint = Message->Msg.pt;
153
154   if ((*HitTest) != HTCLIENT)
155     {
156       Msg += WM_NCMOUSEMOVE - WM_MOUSEMOVE;
157       Message->Msg.wParam = *HitTest;
158     }
159   else
160     {
161       Point = Message->Msg.pt;
162
163       Point.x -= Window->ClientRect.left;
164       Point.y -= Window->ClientRect.top;
165     }
166
167   /* FIXME: Check message filter. */
168
169   if (Remove && Click)
170     {
171       if (Click == 1)
172         {
173           ClkTime = Message->Msg.time;
174           ClkMessage = Msg;
175           ClkWnd = Window->Self;
176           ClkPos = (*ScreenPoint);
177         }
178       else
179         {
180           ClkTime = 0;
181           ClkWnd = NULL;
182         }
183     }
184
185   Message->Msg.hwnd = Window->Self;
186   Message->Msg.message = Msg;
187   Message->Msg.lParam = MAKELONG(Point.x, Point.y);
188
189   return(TRUE);
190 }
191
192 BOOL
193 MsqPeekHardwareMessage(PUSER_MESSAGE_QUEUE MessageQueue, HWND hWnd, 
194                        UINT FilterLow, UINT FilterHigh, BOOL Remove,
195                        PUSER_MESSAGE* Message)
196 {
197   KIRQL OldIrql;
198   USHORT HitTest;
199   POINT ScreenPoint;
200   BOOL Accept;
201   BOOL MouseClick;
202   PLIST_ENTRY CurrentEntry;
203   ULONG ActiveStamp;
204   PWINDOW_OBJECT DesktopWindow;
205
206   DesktopWindow = W32kGetWindowObject(W32kGetDesktopWindow());
207
208   /* Process messages in the message queue itself. */
209   ExAcquireFastMutex(&MessageQueue->Lock);
210   CurrentEntry = MessageQueue->HardwareMessagesListHead.Flink;
211   while (CurrentEntry != &MessageQueue->HardwareMessagesListHead)
212     {
213       PUSER_MESSAGE Current = 
214         CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, ListEntry);
215       CurrentEntry = CurrentEntry->Flink;
216       RemoveEntryList(&Current->ListEntry);
217       if (Current->Msg.message >= WM_MOUSEFIRST && 
218           Current->Msg.message <= WM_MOUSELAST)
219         {
220           Accept = MsqTranslateMouseMessage(hWnd, FilterLow, FilterHigh,
221                                             Current, Remove, 
222                                             DesktopWindow, &HitTest,
223                                             &ScreenPoint, &MouseClick);
224           if (Accept)
225             {
226               RemoveEntryList(&Current->ListEntry);
227               ExReleaseFastMutex(&MessageQueue->Lock);
228               *Message = Current;
229               W32kReleaseWindowObject(DesktopWindow);
230               return(TRUE);
231             }
232         }
233       CurrentEntry = CurrentEntry->Flink;
234     }
235   ExReleaseFastMutex(&MessageQueue->Lock);
236
237   /* Now try the global queue. */
238   ExAcquireFastMutex(&HardwareMessageQueueLock);
239   /* Transfer all messages from the DPC accessible queue to the main queue. */
240   KeAcquireSpinLock(&SystemMessageQueueLock, &OldIrql);
241   while (SystemMessageQueueCount > 0)
242     {      
243       PUSER_MESSAGE UserMsg;
244       MSG Msg;
245
246       Msg = SystemMessageQueue[SystemMessageQueueHead];
247       SystemMessageQueueHead = 
248         (SystemMessageQueueHead + 1) % SYSTEM_MESSAGE_QUEUE_SIZE;
249       SystemMessageQueueCount--;
250       KeReleaseSpinLock(&SystemMessageQueueLock, OldIrql);
251       UserMsg = ExAllocatePool(NonPagedPool, sizeof(USER_MESSAGE));
252       UserMsg->Msg = Msg;
253       InsertTailList(&HardwareMessageQueueHead, &UserMsg->ListEntry);
254       KeAcquireSpinLock(&SystemMessageQueueLock, &OldIrql);
255     }
256   KeReleaseSpinLock(&SystemMessageQueueLock, OldIrql);
257   HardwareMessageQueueStamp++;
258
259   /* Process messages in the queue until we find one to return. */
260   CurrentEntry = HardwareMessageQueueHead.Flink;
261   while (CurrentEntry != &HardwareMessageQueueHead)
262     {
263       PUSER_MESSAGE Current = 
264         CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, ListEntry);
265       CurrentEntry = CurrentEntry->Flink;
266       RemoveEntryList(&Current->ListEntry);
267       if (Current->Msg.message >= WM_MOUSEFIRST && 
268           Current->Msg.message <= WM_MOUSELAST)
269         {
270           ActiveStamp = HardwareMessageQueueStamp;
271           ExReleaseFastMutex(&HardwareMessageQueueLock);
272           /* Translate the message. */
273           Accept = MsqTranslateMouseMessage(hWnd, FilterLow, FilterHigh,
274                                             Current, Remove,
275                                             DesktopWindow, &HitTest,
276                                             &ScreenPoint, &MouseClick);
277           ExAcquireFastMutex(&HardwareMessageQueueLock);
278           if (Accept)
279             {
280               /* Check for no more messages in the system queue. */
281               KeAcquireSpinLock(&SystemMessageQueueLock, &OldIrql);
282               if (SystemMessageQueueCount == 0 &&
283                   IsListEmpty(&HardwareMessageQueueHead))
284                 {
285                   KeClearEvent(&HardwareMessageEvent);
286                 }
287               KeReleaseSpinLock(&SystemMessageQueueLock, OldIrql);
288
289               /* 
290                  If we aren't removing the message then add it to the private
291                  queue.
292               */
293               if (!Remove)
294                 {
295                   InsertTailList(&MessageQueue->HardwareMessagesListHead,
296                                  &Current->ListEntry);
297                 }
298               ExReleaseFastMutex(&HardwareMessageQueueLock);
299               *Message = Current;            
300               W32kReleaseWindowObject(DesktopWindow);
301               return(TRUE);
302             }
303           /* If the contents of the queue changed then restart processing. */
304           if (HardwareMessageQueueStamp != ActiveStamp)
305             {
306               CurrentEntry = HardwareMessageQueueHead.Flink;
307               continue;
308             }
309         }      
310     }
311   /* Check if the system message queue is now empty. */
312   KeAcquireSpinLock(&SystemMessageQueueLock, &OldIrql);
313   if (SystemMessageQueueCount == 0 && IsListEmpty(&HardwareMessageQueueHead))
314     {
315       KeClearEvent(&HardwareMessageEvent);
316     }
317   KeReleaseSpinLock(&SystemMessageQueueLock, OldIrql);
318   ExReleaseFastMutex(&HardwareMessageQueueLock);
319
320   return(FALSE);
321 }
322
323 VOID
324 MsqPostKeyboardMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
325 {
326 #if 0
327   MSG Msg;
328   PUSER_MESSAGE Message;
329
330   if (CurrentFocusMessageQueue == NULL)
331     {
332       return;
333     }
334
335   Msg.hwnd = CurrentFocusMessageQueue->FocusWindow;
336   Msg.message = uMsg;
337   Msg.wParam = wParam;
338   Msg.lParam = lParam;
339   /* FIXME: Initialize time and point. */
340
341   Message = MsqCreateMessage(&Msg);
342   MsqPostMessage(CurrentFocusMessageQueue, Message, TRUE);
343 #endif
344 }
345
346 VOID
347 MsqInitializeMessage(PUSER_MESSAGE Message,
348                      LPMSG Msg)
349 {
350   RtlMoveMemory(&Message->Msg, Msg, sizeof(MSG));
351 }
352
353 PUSER_MESSAGE
354 MsqCreateMessage(LPMSG Msg)
355 {
356   PUSER_MESSAGE Message;
357   
358   Message = (PUSER_MESSAGE)ExAllocatePool(PagedPool, sizeof(USER_MESSAGE));
359   if (!Message)
360     {
361       return NULL;
362     }
363   
364   MsqInitializeMessage(Message, Msg);
365   
366   return Message;
367 }
368
369 VOID
370 MsqDestroyMessage(PUSER_MESSAGE Message)
371 {
372   ExFreePool(Message);
373 }
374
375 VOID
376 MsqDispatchSentNotifyMessages(PUSER_MESSAGE_QUEUE MessageQueue)
377 {
378   PLIST_ENTRY ListEntry;
379   PUSER_SENT_MESSAGE_NOTIFY Message;
380
381   while (!IsListEmpty(&MessageQueue->SentMessagesListHead))
382   {
383     ExAcquireFastMutex(&MessageQueue->Lock);
384     ListEntry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
385     Message = CONTAINING_RECORD(ListEntry, USER_SENT_MESSAGE_NOTIFY, 
386                                 ListEntry);
387     ExReleaseFastMutex(&MessageQueue->Lock);
388
389     W32kCallSentMessageCallback(Message->CompletionCallback,
390                                 Message->hWnd,
391                                 Message->Msg,
392                                 Message->CompletionCallbackContext,
393                                 Message->Result);
394   }
395 }
396
397 BOOLEAN
398 MsqPeekSentMessages(PUSER_MESSAGE_QUEUE MessageQueue)
399 {
400   return(!IsListEmpty(&MessageQueue->SentMessagesListHead));
401 }
402
403 BOOLEAN
404 MsqDispatchOneSentMessage(PUSER_MESSAGE_QUEUE MessageQueue)
405 {
406   PUSER_SENT_MESSAGE Message;
407   PLIST_ENTRY Entry;
408   LRESULT Result;
409   PUSER_SENT_MESSAGE_NOTIFY NotifyMessage;
410
411   ExAcquireFastMutex(&MessageQueue->Lock);
412   if (IsListEmpty(&MessageQueue->SentMessagesListHead))
413     {
414       ExReleaseFastMutex(&MessageQueue->Lock);
415       return(FALSE);
416     }
417   Entry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
418   Message = CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry);
419   ExReleaseFastMutex(&MessageQueue->Lock);
420
421   /* Call the window procedure. */
422   Result = W32kCallWindowProc(NULL,
423                               Message->Msg.hwnd,
424                               Message->Msg.message,
425                               Message->Msg.wParam,
426                               Message->Msg.lParam);
427
428   /* Let the sender know the result. */
429   if (Message->Result != NULL)
430     {
431       *Message->Result = Result;
432     }
433
434   /* Notify the sender. */
435   if (Message->CompletionEvent != NULL)
436     {
437       KeSetEvent(Message->CompletionEvent, IO_NO_INCREMENT, FALSE);
438     }
439
440   /* Notify the sender if they specified a callback. */
441   if (Message->CompletionCallback != NULL)
442     {
443       NotifyMessage = ExAllocatePool(NonPagedPool, 
444                                      sizeof(USER_SENT_MESSAGE_NOTIFY));
445       NotifyMessage->CompletionCallback = 
446         Message->CompletionCallback;
447       NotifyMessage->CompletionCallbackContext =
448         Message->CompletionCallbackContext;
449       NotifyMessage->Result = Result;
450       NotifyMessage->hWnd = Message->Msg.hwnd;
451       NotifyMessage->Msg = Message->Msg.message;
452       MsqSendNotifyMessage(Message->CompletionQueue, NotifyMessage);
453     }
454
455   ExFreePool(Message);
456   return(TRUE);
457 }
458
459 VOID
460 MsqSendNotifyMessage(PUSER_MESSAGE_QUEUE MessageQueue,
461                      PUSER_SENT_MESSAGE_NOTIFY NotifyMessage)
462 {
463   ExAcquireFastMutex(&MessageQueue->Lock);
464   InsertTailList(&MessageQueue->NotifyMessagesListHead, 
465                  &NotifyMessage->ListEntry);
466   ExReleaseFastMutex(&MessageQueue->Lock);
467 }
468
469 VOID
470 MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue,
471                PUSER_SENT_MESSAGE Message)
472 {
473   ExAcquireFastMutex(&MessageQueue->Lock);
474   InsertTailList(&MessageQueue->SentMessagesListHead, &Message->ListEntry);
475   ExReleaseFastMutex(&MessageQueue->Lock);
476 }
477
478 VOID
479 MsqPostMessage(PUSER_MESSAGE_QUEUE MessageQueue, PUSER_MESSAGE Message)
480 {
481   ExAcquireFastMutex(&MessageQueue->Lock);
482   InsertTailList(&MessageQueue->PostedMessagesListHead,
483                  &Message->ListEntry);
484   KeSetEvent(&MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
485   ExReleaseFastMutex(&MessageQueue->Lock);
486 }
487
488 BOOLEAN
489 MsqFindMessage(IN PUSER_MESSAGE_QUEUE MessageQueue,
490                IN BOOLEAN Hardware,
491                IN BOOLEAN Remove,
492                IN HWND Wnd,
493                IN UINT MsgFilterLow,
494                IN UINT MsgFilterHigh,
495                OUT PUSER_MESSAGE* Message)
496 {
497   PLIST_ENTRY CurrentEntry;
498   PUSER_MESSAGE CurrentMessage;
499   PLIST_ENTRY ListHead;
500
501   if (Hardware)
502     {
503       return(MsqPeekHardwareMessage(MessageQueue, Wnd,
504                                     MsgFilterLow, MsgFilterHigh,
505                                     Remove, Message));
506     }
507   
508   ExAcquireFastMutex(&MessageQueue->Lock);
509   CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
510   ListHead = &MessageQueue->PostedMessagesListHead;
511   while (CurrentEntry != ListHead)
512     {
513       CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, 
514                                          ListEntry);
515       if ((Wnd == 0 || Wnd == CurrentMessage->Msg.hwnd) &&
516           ((MsgFilterLow == 0 && MsgFilterHigh == 0) ||
517            (MsgFilterLow <= CurrentMessage->Msg.message &&
518             MsgFilterHigh >= CurrentMessage->Msg.message)))
519         {
520           if (Remove)
521             {
522               RemoveEntryList(&CurrentMessage->ListEntry);
523             }
524           ExReleaseFastMutex(&MessageQueue->Lock);
525           *Message = CurrentMessage;
526           return(TRUE);
527         }
528       CurrentEntry = CurrentEntry->Flink;
529     }
530   ExReleaseFastMutex(&MessageQueue->Lock);
531   return(FALSE);
532 }
533
534 NTSTATUS
535 MsqWaitForNewMessages(PUSER_MESSAGE_QUEUE MessageQueue)
536 {
537   PVOID WaitObjects[2] = {&MessageQueue->NewMessages, &HardwareMessageEvent};
538   return(KeWaitForMultipleObjects(2,
539                                   WaitObjects,
540                                   WaitAny,
541                                   Executive,
542                                   UserMode,
543                                   TRUE,
544                                   NULL,
545                                   NULL));
546 }
547
548 VOID
549 MsqInitializeMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
550 {
551   InitializeListHead(&MessageQueue->PostedMessagesListHead);
552   InitializeListHead(&MessageQueue->SentMessagesListHead);
553   InitializeListHead(&MessageQueue->HardwareMessagesListHead);
554   ExInitializeFastMutex(&MessageQueue->Lock);
555   MessageQueue->QuitPosted = FALSE;
556   MessageQueue->QuitExitCode = 0;
557   KeInitializeEvent(&MessageQueue->NewMessages, NotificationEvent, FALSE);
558   MessageQueue->QueueStatus = 0;
559   MessageQueue->FocusWindow = NULL;
560 }
561
562 VOID
563 MsqFreeMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
564 {
565   PLIST_ENTRY CurrentEntry;
566   PUSER_MESSAGE CurrentMessage;
567   
568   CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
569   while (CurrentEntry != &MessageQueue->PostedMessagesListHead)
570     {
571       CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, 
572                                          ListEntry);
573       CurrentEntry = CurrentEntry->Flink;
574       ExFreePool(CurrentMessage);
575     }
576 }
577
578 PUSER_MESSAGE_QUEUE
579 MsqCreateMessageQueue(VOID)
580 {
581   PUSER_MESSAGE_QUEUE MessageQueue;
582
583   MessageQueue = (PUSER_MESSAGE_QUEUE)ExAllocatePool(PagedPool, 
584                                    sizeof(USER_MESSAGE_QUEUE));
585   if (!MessageQueue)
586     {
587       return NULL;
588     }
589   
590   MsqInitializeMessageQueue(MessageQueue);
591   
592   return MessageQueue;
593 }
594
595 VOID
596 MsqDestroyMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
597 {
598   MsqFreeMessageQueue(MessageQueue);
599   ExFreePool(MessageQueue);
600 }
601
602 /* EOF */