[Wine32k|User32]
[reactos.git] / reactos / subsystems / win32 / win32k / ntuser / msgqueue.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 along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 /*
20 * COPYRIGHT: See COPYING in the top level directory
21 * PROJECT: ReactOS kernel
22 * PURPOSE: Message queues
23 * FILE: subsystems/win32/win32k/ntuser/msgqueue.c
24 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
25 * REVISION HISTORY:
26 * 06-06-2001 CSH Created
27 */
28
29 /* INCLUDES ******************************************************************/
30
31 #include <win32k.h>
32
33 #define NDEBUG
34 #include <debug.h>
35
36 /* GLOBALS *******************************************************************/
37
38 #define SYSTEM_MESSAGE_QUEUE_SIZE (256)
39
40 static MSG SystemMessageQueue[SYSTEM_MESSAGE_QUEUE_SIZE];
41 static ULONG SystemMessageQueueHead = 0;
42 static ULONG SystemMessageQueueTail = 0;
43 static ULONG SystemMessageQueueCount = 0;
44 static KSPIN_LOCK SystemMessageQueueLock;
45
46 static ULONG volatile HardwareMessageQueueStamp = 0;
47 static LIST_ENTRY HardwareMessageQueueHead;
48 static KMUTANT HardwareMessageQueueLock;
49
50 static KEVENT HardwareMessageEvent;
51
52 static PAGED_LOOKASIDE_LIST MessageLookasideList;
53
54 #define IntLockSystemMessageQueue(OldIrql) \
55 KeAcquireSpinLock(&SystemMessageQueueLock, &OldIrql)
56
57 #define IntUnLockSystemMessageQueue(OldIrql) \
58 KeReleaseSpinLock(&SystemMessageQueueLock, OldIrql)
59
60 #define IntUnLockSystemHardwareMessageQueueLock(Wait) \
61 KeReleaseMutant(&HardwareMessageQueueLock, IO_NO_INCREMENT, FALSE, Wait)
62
63 /* FUNCTIONS *****************************************************************/
64
65 HANDLE FASTCALL
66 IntMsqSetWakeMask(DWORD WakeMask)
67 {
68 PTHREADINFO Win32Thread;
69 PUSER_MESSAGE_QUEUE MessageQueue;
70 HANDLE MessageEventHandle;
71
72 Win32Thread = PsGetCurrentThreadWin32Thread();
73 if (Win32Thread == NULL || Win32Thread->MessageQueue == NULL)
74 return 0;
75
76 MessageQueue = Win32Thread->MessageQueue;
77 MessageQueue->WakeMask = WakeMask;
78 MessageEventHandle = MessageQueue->NewMessagesHandle;
79
80 if (Win32Thread->pcti)
81 Win32Thread->pcti->fsWakeMask = WakeMask;
82
83 IdlePing();
84
85 return MessageEventHandle;
86 }
87
88 BOOL FASTCALL
89 IntMsqClearWakeMask(VOID)
90 {
91 PTHREADINFO Win32Thread;
92 PUSER_MESSAGE_QUEUE MessageQueue;
93
94 Win32Thread = PsGetCurrentThreadWin32Thread();
95 if (Win32Thread == NULL || Win32Thread->MessageQueue == NULL)
96 return FALSE;
97
98 MessageQueue = Win32Thread->MessageQueue;
99 // HACK!!!!!!! Newbies that wrote this should hold your head down in shame! (jt)
100 MessageQueue->WakeMask = ~0;
101
102 if (Win32Thread->pcti)
103 Win32Thread->pcti->fsWakeMask = 0;
104
105 IdlePong();
106
107 return TRUE;
108 }
109
110 VOID FASTCALL
111 MsqWakeQueue(PUSER_MESSAGE_QUEUE Queue, DWORD MessageBits)
112 {
113 Queue->QueueBits |= MessageBits;
114 Queue->ChangedBits |= MessageBits;
115 if (Queue->WakeMask & MessageBits)
116 KeSetEvent(Queue->NewMessages, IO_NO_INCREMENT, FALSE);
117 }
118
119 VOID FASTCALL
120 MsqIncPaintCountQueue(PUSER_MESSAGE_QUEUE Queue)
121 {
122 Queue->PaintCount++;
123 MsqWakeQueue(Queue, QS_PAINT);
124 }
125
126 VOID FASTCALL
127 MsqDecPaintCountQueue(PUSER_MESSAGE_QUEUE Queue)
128 {
129 Queue->PaintCount--;
130 }
131
132
133 INIT_FUNCTION
134 NTSTATUS
135 NTAPI
136 MsqInitializeImpl(VOID)
137 {
138 /*CurrentFocusMessageQueue = NULL;*/
139 InitializeListHead(&HardwareMessageQueueHead);
140 KeInitializeEvent(&HardwareMessageEvent, NotificationEvent, 0);
141 KeInitializeSpinLock(&SystemMessageQueueLock);
142 KeInitializeMutant(&HardwareMessageQueueLock, 0);
143
144 ExInitializePagedLookasideList(&MessageLookasideList,
145 NULL,
146 NULL,
147 0,
148 sizeof(USER_MESSAGE),
149 TAG_USRMSG,
150 256);
151
152 return(STATUS_SUCCESS);
153 }
154
155 VOID FASTCALL
156 co_MsqInsertMouseMessage(MSG* Msg)
157 {
158 LARGE_INTEGER LargeTickCount;
159 KIRQL OldIrql;
160 ULONG Prev;
161 MSLLHOOKSTRUCT MouseHookData;
162
163 KeQueryTickCount(&LargeTickCount);
164 Msg->time = MsqCalculateMessageTime(&LargeTickCount);
165
166 MouseHookData.pt.x = LOWORD(Msg->lParam);
167 MouseHookData.pt.y = HIWORD(Msg->lParam);
168 switch(Msg->message)
169 {
170 case WM_MOUSEWHEEL:
171 MouseHookData.mouseData = MAKELONG(0, GET_WHEEL_DELTA_WPARAM(Msg->wParam));
172 break;
173 case WM_XBUTTONDOWN:
174 case WM_XBUTTONUP:
175 case WM_XBUTTONDBLCLK:
176 case WM_NCXBUTTONDOWN:
177 case WM_NCXBUTTONUP:
178 case WM_NCXBUTTONDBLCLK:
179 MouseHookData.mouseData = MAKELONG(0, HIWORD(Msg->wParam));
180 break;
181 default:
182 MouseHookData.mouseData = 0;
183 break;
184 }
185
186 MouseHookData.flags = 0;
187 MouseHookData.time = Msg->time;
188 MouseHookData.dwExtraInfo = 0;
189
190 /* If the hook procedure returned non zero, dont send the message */
191 if (co_HOOK_CallHooks(WH_MOUSE_LL, HC_ACTION, Msg->message, (LPARAM) &MouseHookData))
192 return;
193
194 /*
195 * If we got WM_MOUSEMOVE and there are already messages in the
196 * system message queue, check if the last message is mouse move
197 * and if it is then just overwrite it.
198 */
199 IntLockSystemMessageQueue(OldIrql);
200
201 /*
202 * Bail out if the queue is full. FIXME: We should handle this case
203 * more gracefully.
204 */
205
206 if (SystemMessageQueueCount == SYSTEM_MESSAGE_QUEUE_SIZE)
207 {
208 IntUnLockSystemMessageQueue(OldIrql);
209 return;
210 }
211
212 if (Msg->message == WM_MOUSEMOVE && SystemMessageQueueCount)
213 {
214 if (SystemMessageQueueTail == 0)
215 Prev = SYSTEM_MESSAGE_QUEUE_SIZE - 1;
216 else
217 Prev = SystemMessageQueueTail - 1;
218 if (SystemMessageQueue[Prev].message == WM_MOUSEMOVE)
219 {
220 SystemMessageQueueTail = Prev;
221 SystemMessageQueueCount--;
222 }
223 }
224
225 /*
226 * Actually insert the message into the system message queue.
227 */
228
229 SystemMessageQueue[SystemMessageQueueTail] = *Msg;
230 SystemMessageQueueTail =
231 (SystemMessageQueueTail + 1) % SYSTEM_MESSAGE_QUEUE_SIZE;
232 SystemMessageQueueCount++;
233
234 IntUnLockSystemMessageQueue(OldIrql);
235
236 KeSetEvent(&HardwareMessageEvent, IO_NO_INCREMENT, FALSE);
237 }
238
239 BOOL FASTCALL
240 MsqIsClkLck(LPMSG Msg, BOOL Remove)
241 {
242 PTHREADINFO pti;
243 PSYSTEM_CURSORINFO CurInfo;
244 BOOL Res = FALSE;
245
246 pti = PsGetCurrentThreadWin32Thread();
247 if (pti->rpdesk == NULL)
248 {
249 return FALSE;
250 }
251
252 CurInfo = IntGetSysCursorInfo();
253
254 switch (Msg->message)
255 {
256 case WM_LBUTTONUP:
257 Res = ((Msg->time - CurInfo->ClickLockTime) >= gspv.dwMouseClickLockTime);
258 if (Res && (!CurInfo->ClickLockActive))
259 {
260 CurInfo->ClickLockActive = TRUE;
261 }
262 break;
263 case WM_LBUTTONDOWN:
264 if (CurInfo->ClickLockActive)
265 {
266 Res = TRUE;
267 CurInfo->ClickLockActive = FALSE;
268 CurInfo->ClickLockTime = 0;
269 }
270 else
271 {
272 CurInfo->ClickLockTime = Msg->time;
273 }
274 break;
275 }
276 return Res;
277 }
278
279 BOOL FASTCALL
280 MsqIsDblClk(LPMSG Msg, BOOL Remove)
281 {
282 PTHREADINFO pti;
283 PSYSTEM_CURSORINFO CurInfo;
284 LONG dX, dY;
285 BOOL Res;
286
287 pti = PsGetCurrentThreadWin32Thread();
288 if (pti->rpdesk == NULL)
289 {
290 return FALSE;
291 }
292
293 CurInfo = IntGetSysCursorInfo();
294 Res = (Msg->hwnd == (HWND)CurInfo->LastClkWnd) &&
295 ((Msg->time - CurInfo->LastBtnDown) < gspv.iDblClickTime);
296 if(Res)
297 {
298
299 dX = CurInfo->LastBtnDownX - Msg->pt.x;
300 dY = CurInfo->LastBtnDownY - Msg->pt.y;
301 if(dX < 0)
302 dX = -dX;
303 if(dY < 0)
304 dY = -dY;
305
306 Res = (dX <= gspv.iDblClickWidth) &&
307 (dY <= gspv.iDblClickHeight);
308
309 if(Res)
310 {
311 if(CurInfo->ButtonsDown)
312 Res = (CurInfo->ButtonsDown == Msg->message);
313 }
314 }
315
316 if(Remove)
317 {
318 CurInfo->LastBtnDownX = Msg->pt.x;
319 CurInfo->LastBtnDownY = Msg->pt.y;
320 CurInfo->ButtonsDown = Msg->message;
321 if (Res)
322 {
323 CurInfo->LastBtnDown = 0;
324 CurInfo->LastClkWnd = NULL;
325 }
326 else
327 {
328 CurInfo->LastClkWnd = (HANDLE)Msg->hwnd;
329 CurInfo->LastBtnDown = Msg->time;
330 }
331 }
332
333 return Res;
334 }
335
336 static BOOL APIENTRY
337 co_MsqTranslateMouseMessage(PUSER_MESSAGE_QUEUE MessageQueue, PWND Window, UINT FilterLow, UINT FilterHigh,
338 PUSER_MESSAGE Message, BOOL Remove, PBOOL Freed,
339 PWND ScopeWin, PPOINT ScreenPoint, BOOL FromGlobalQueue, PLIST_ENTRY *Next)
340 {
341 USHORT Msg = Message->Msg.message;
342 PWND CaptureWindow = NULL;
343 HWND hCaptureWin;
344
345 /* FIXME: Mouse message can be sent before the Desktop is up and running in which case ScopeWin (Desktop) is 0.
346 Is this the best fix? */
347 if (ScopeWin == 0) return FALSE;
348
349 ASSERT_REFS_CO(ScopeWin);
350
351 /*
352 co_WinPosWindowFromPoint can return a Window, and in that case
353 that window has a ref that we need to deref. Thats why we add "dummy"
354 refs in all other cases.
355 */
356
357 hCaptureWin = IntGetCaptureWindow();
358 if (hCaptureWin == NULL)
359 {
360 if (Msg == WM_MOUSEWHEEL)
361 {
362 CaptureWindow = UserGetWindowObject(IntGetFocusWindow());
363 if (CaptureWindow) UserReferenceObject(CaptureWindow);
364 }
365 else
366 {
367 co_WinPosWindowFromPoint(ScopeWin, NULL, &Message->Msg.pt, &CaptureWindow);
368 if(CaptureWindow == NULL)
369 {
370 CaptureWindow = ScopeWin;
371 if (CaptureWindow) UserReferenceObject(CaptureWindow);
372 }
373 else
374 {
375 /* this is the one case where we dont add a ref, since the returned
376 window is already referenced */
377 }
378 }
379 }
380 else
381 {
382 /* FIXME - window messages should go to the right window if no buttons are
383 pressed */
384 CaptureWindow = UserGetWindowObject(hCaptureWin);
385 if (CaptureWindow) UserReferenceObject(CaptureWindow);
386 }
387
388
389
390 if (CaptureWindow == NULL)
391 {
392 if(!FromGlobalQueue)
393 {
394 RemoveEntryList(&Message->ListEntry);
395 if(MessageQueue->MouseMoveMsg == Message)
396 {
397 MessageQueue->MouseMoveMsg = NULL;
398 }
399 }
400 // when FromGlobalQueue is true, the caller has already removed the Message
401 ExFreePool(Message);
402 *Freed = TRUE;
403 return(FALSE);
404 }
405
406 if (CaptureWindow->head.pti->MessageQueue != MessageQueue)
407 {
408 if (! FromGlobalQueue)
409 {
410 DPRINT("Moving msg between private queues\n");
411 /* This message is already queued in a private queue, but we need
412 * to move it to a different queue, perhaps because a new window
413 * was created which now covers the screen area previously taken
414 * by another window. To move it, we need to take it out of the
415 * old queue. Note that we're already holding the lock mutexes of the
416 * old queue */
417 RemoveEntryList(&Message->ListEntry);
418
419 /* remove the pointer for the current WM_MOUSEMOVE message in case we
420 just removed it */
421 if(MessageQueue->MouseMoveMsg == Message)
422 {
423 MessageQueue->MouseMoveMsg = NULL;
424 }
425 }
426
427 /* lock the destination message queue, so we don't get in trouble with other
428 threads, messing with it at the same time */
429 IntLockHardwareMessageQueue(CaptureWindow->head.pti->MessageQueue);
430 InsertTailList(&CaptureWindow->head.pti->MessageQueue->HardwareMessagesListHead,
431 &Message->ListEntry);
432 if(Message->Msg.message == WM_MOUSEMOVE)
433 {
434 if(CaptureWindow->head.pti->MessageQueue->MouseMoveMsg)
435 {
436 /* remove the old WM_MOUSEMOVE message, we're processing a more recent
437 one */
438 RemoveEntryList(&CaptureWindow->head.pti->MessageQueue->MouseMoveMsg->ListEntry);
439 ExFreePool(CaptureWindow->head.pti->MessageQueue->MouseMoveMsg);
440 }
441 /* save the pointer to the WM_MOUSEMOVE message in the new queue */
442 CaptureWindow->head.pti->MessageQueue->MouseMoveMsg = Message;
443
444 MsqWakeQueue(CaptureWindow->head.pti->MessageQueue, QS_MOUSEMOVE);
445 }
446 else
447 {
448 MsqWakeQueue(CaptureWindow->head.pti->MessageQueue, QS_MOUSEBUTTON);
449 }
450 IntUnLockHardwareMessageQueue(CaptureWindow->head.pti->MessageQueue);
451
452 *Freed = FALSE;
453 UserDereferenceObject(CaptureWindow);
454 return(FALSE);
455 }
456
457 /* From here on, we're in the same message queue as the caller! */
458
459 *ScreenPoint = Message->Msg.pt;
460
461 if((Window != NULL && PtrToInt(Window) != 1 && CaptureWindow->head.h != Window->head.h) ||
462 ((FilterLow != 0 || FilterHigh != 0) && (Msg < FilterLow || Msg > FilterHigh)))
463 {
464 /* Reject the message because it doesn't match the filter */
465
466 if(FromGlobalQueue)
467 {
468 /* Lock the message queue so no other thread can mess with it.
469 Our own message queue is not locked while fetching from the global
470 queue, so we have to make sure nothing interferes! */
471 IntLockHardwareMessageQueue(CaptureWindow->head.pti->MessageQueue);
472 /* if we're from the global queue, we need to add our message to our
473 private queue so we don't loose it! */
474 InsertTailList(&CaptureWindow->head.pti->MessageQueue->HardwareMessagesListHead,
475 &Message->ListEntry);
476 }
477
478 if (Message->Msg.message == WM_MOUSEMOVE)
479 {
480 if(CaptureWindow->head.pti->MessageQueue->MouseMoveMsg &&
481 (CaptureWindow->head.pti->MessageQueue->MouseMoveMsg != Message))
482 {
483 /* delete the old message */
484 RemoveEntryList(&CaptureWindow->head.pti->MessageQueue->MouseMoveMsg->ListEntry);
485 ExFreePool(CaptureWindow->head.pti->MessageQueue->MouseMoveMsg);
486 if (!FromGlobalQueue)
487 {
488 // We might have deleted the next one in our queue, so fix next
489 *Next = Message->ListEntry.Flink;
490 }
491 }
492 /* always save a pointer to this WM_MOUSEMOVE message here because we're
493 sure that the message is in the private queue */
494 CaptureWindow->head.pti->MessageQueue->MouseMoveMsg = Message;
495 }
496 if(FromGlobalQueue)
497 {
498 IntUnLockHardwareMessageQueue(CaptureWindow->head.pti->MessageQueue);
499 }
500
501 UserDereferenceObject(CaptureWindow);
502 *Freed = FALSE;
503 return(FALSE);
504 }
505
506 /* FIXME - only assign if removing? */
507 Message->Msg.hwnd = CaptureWindow->head.h;
508 Message->Msg.message = Msg;
509 Message->Msg.lParam = MAKELONG(Message->Msg.pt.x, Message->Msg.pt.y);
510
511 /* remove the reference to the current WM_(NC)MOUSEMOVE message, if this message
512 is it */
513 if (Message->Msg.message == WM_MOUSEMOVE ||
514 Message->Msg.message == WM_NCMOUSEMOVE)
515 {
516 if(FromGlobalQueue)
517 {
518 /* Lock the message queue so no other thread can mess with it.
519 Our own message queue is not locked while fetching from the global
520 queue, so we have to make sure nothing interferes! */
521 IntLockHardwareMessageQueue(CaptureWindow->head.pti->MessageQueue);
522 if(CaptureWindow->head.pti->MessageQueue->MouseMoveMsg)
523 {
524 /* delete the WM_(NC)MOUSEMOVE message in the private queue, we're dealing
525 with one that's been sent later */
526 RemoveEntryList(&CaptureWindow->head.pti->MessageQueue->MouseMoveMsg->ListEntry);
527 ExFreePool(CaptureWindow->head.pti->MessageQueue->MouseMoveMsg);
528 /* our message is not in the private queue so we can remove the pointer
529 instead of setting it to the current message we're processing */
530 CaptureWindow->head.pti->MessageQueue->MouseMoveMsg = NULL;
531 }
532 IntUnLockHardwareMessageQueue(CaptureWindow->head.pti->MessageQueue);
533 }
534 else if (CaptureWindow->head.pti->MessageQueue->MouseMoveMsg == Message)
535 {
536 CaptureWindow->head.pti->MessageQueue->MouseMoveMsg = NULL;
537 }
538 }
539
540 UserDereferenceObject(CaptureWindow);
541 *Freed = FALSE;
542 return(TRUE);
543 }
544
545 BOOL APIENTRY
546 co_MsqPeekHardwareMessage(IN PUSER_MESSAGE_QUEUE MessageQueue,
547 IN BOOL Remove,
548 IN PWND Window,
549 IN UINT FilterLow,
550 IN UINT FilterHigh,
551 OUT PMSG Message)
552 {
553 KIRQL OldIrql;
554 POINT ScreenPoint;
555 BOOL Accept, Freed;
556 PLIST_ENTRY CurrentEntry;
557 PWND DesktopWindow = NULL;
558 PVOID WaitObjects[2];
559 NTSTATUS WaitStatus;
560 DECLARE_RETURN(BOOL);
561 USER_REFERENCE_ENTRY Ref;
562 PDESKTOPINFO Desk = NULL;
563
564 WaitObjects[1] = MessageQueue->NewMessages;
565 WaitObjects[0] = &HardwareMessageQueueLock;
566 do
567 {
568 UserLeaveCo();
569
570 WaitStatus = KeWaitForMultipleObjects(2, WaitObjects, WaitAny, UserRequest,
571 UserMode, FALSE, NULL, NULL);
572
573 UserEnterCo();
574 }
575 while (NT_SUCCESS(WaitStatus) && STATUS_WAIT_0 != WaitStatus);
576
577 DesktopWindow = UserGetWindowObject(IntGetDesktopWindow());
578
579 if (DesktopWindow)
580 {
581 UserRefObjectCo(DesktopWindow, &Ref);
582 Desk = DesktopWindow->head.pti->pDeskInfo;
583 }
584
585 /* Process messages in the message queue itself. */
586 IntLockHardwareMessageQueue(MessageQueue);
587 CurrentEntry = MessageQueue->HardwareMessagesListHead.Flink;
588 while (CurrentEntry != &MessageQueue->HardwareMessagesListHead)
589 {
590 PUSER_MESSAGE Current =
591 CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, ListEntry);
592 CurrentEntry = CurrentEntry->Flink;
593 if (Current->Msg.message >= WM_MOUSEFIRST &&
594 Current->Msg.message <= WM_MOUSELAST)
595 {
596
597
598 Accept = co_MsqTranslateMouseMessage(MessageQueue, Window, FilterLow, FilterHigh,
599 Current, Remove, &Freed,
600 DesktopWindow, &ScreenPoint, FALSE, &CurrentEntry);
601 if (Accept)
602 {
603 *Message = Current->Msg;
604 if (Remove)
605 {
606 RemoveEntryList(&Current->ListEntry);
607 MsqDestroyMessage(Current);
608 }
609 IntUnLockHardwareMessageQueue(MessageQueue);
610 IntUnLockSystemHardwareMessageQueueLock(FALSE);
611
612 if (Desk)
613 Desk->LastInputWasKbd = FALSE;
614
615 RETURN(TRUE);
616 }
617
618 }
619 else
620 {
621 *Message = Current->Msg;
622 if (Remove)
623 {
624 RemoveEntryList(&Current->ListEntry);
625 MsqDestroyMessage(Current);
626 }
627 IntUnLockHardwareMessageQueue(MessageQueue);
628 IntUnLockSystemHardwareMessageQueueLock(FALSE);
629
630 RETURN(TRUE);
631 }
632 }
633 IntUnLockHardwareMessageQueue(MessageQueue);
634
635 /* Now try the global queue. */
636
637 /* Transfer all messages from the DPC accessible queue to the main queue. */
638 IntLockSystemMessageQueue(OldIrql);
639 while (SystemMessageQueueCount > 0)
640 {
641 PUSER_MESSAGE UserMsg;
642 MSG Msg;
643
644 ASSERT(SystemMessageQueueHead < SYSTEM_MESSAGE_QUEUE_SIZE);
645 Msg = SystemMessageQueue[SystemMessageQueueHead];
646 SystemMessageQueueHead =
647 (SystemMessageQueueHead + 1) % SYSTEM_MESSAGE_QUEUE_SIZE;
648 SystemMessageQueueCount--;
649 IntUnLockSystemMessageQueue(OldIrql);
650
651 UserMsg = ExAllocateFromPagedLookasideList(&MessageLookasideList);
652 /* What to do if out of memory? For now we just panic a bit in debug */
653 ASSERT(UserMsg);
654 UserMsg->Msg = Msg;
655 InsertTailList(&HardwareMessageQueueHead, &UserMsg->ListEntry);
656
657 IntLockSystemMessageQueue(OldIrql);
658 }
659 HardwareMessageQueueStamp++;
660 IntUnLockSystemMessageQueue(OldIrql);
661
662 /* Process messages in the queue until we find one to return. */
663 CurrentEntry = HardwareMessageQueueHead.Flink;
664 while (CurrentEntry != &HardwareMessageQueueHead)
665 {
666 PUSER_MESSAGE Current =
667 CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, ListEntry);
668 CurrentEntry = CurrentEntry->Flink;
669 RemoveEntryList(&Current->ListEntry);
670 HardwareMessageQueueStamp++;
671 if (Current->Msg.message >= WM_MOUSEFIRST &&
672 Current->Msg.message <= WM_MOUSELAST)
673 {
674 const ULONG ActiveStamp = HardwareMessageQueueStamp;
675 /* Translate the message. */
676 Accept = co_MsqTranslateMouseMessage(MessageQueue, Window, FilterLow, FilterHigh,
677 Current, Remove, &Freed,
678 DesktopWindow, &ScreenPoint, TRUE, NULL);
679 if (Accept)
680 {
681 /* Check for no more messages in the system queue. */
682 IntLockSystemMessageQueue(OldIrql);
683 if (SystemMessageQueueCount == 0 &&
684 IsListEmpty(&HardwareMessageQueueHead))
685 {
686 KeClearEvent(&HardwareMessageEvent);
687 }
688 IntUnLockSystemMessageQueue(OldIrql);
689
690 /*
691 If we aren't removing the message then add it to the private
692 queue.
693 */
694 if (!Remove)
695 {
696 IntLockHardwareMessageQueue(MessageQueue);
697 if(Current->Msg.message == WM_MOUSEMOVE)
698 {
699 if(MessageQueue->MouseMoveMsg)
700 {
701 RemoveEntryList(&MessageQueue->MouseMoveMsg->ListEntry);
702 ExFreePool(MessageQueue->MouseMoveMsg);
703 }
704 MessageQueue->MouseMoveMsg = Current;
705 }
706 InsertTailList(&MessageQueue->HardwareMessagesListHead,
707 &Current->ListEntry);
708 IntUnLockHardwareMessageQueue(MessageQueue);
709 }
710 IntUnLockSystemHardwareMessageQueueLock(FALSE);
711 *Message = Current->Msg;
712
713 if (Remove)
714 {
715 MsqDestroyMessage(Current);
716 }
717
718 RETURN(TRUE);
719 }
720 /* If the contents of the queue changed then restart processing. */
721 if (HardwareMessageQueueStamp != ActiveStamp)
722 {
723 CurrentEntry = HardwareMessageQueueHead.Flink;
724 continue;
725 }
726 }
727 }
728
729 /* Check if the system message queue is now empty. */
730 IntLockSystemMessageQueue(OldIrql);
731 if (SystemMessageQueueCount == 0 && IsListEmpty(&HardwareMessageQueueHead))
732 {
733 KeClearEvent(&HardwareMessageEvent);
734 }
735 IntUnLockSystemMessageQueue(OldIrql);
736 IntUnLockSystemHardwareMessageQueueLock(FALSE);
737
738 RETURN(FALSE);
739
740 CLEANUP:
741 if (DesktopWindow) UserDerefObjectCo(DesktopWindow);
742
743 END_CLEANUP;
744 }
745
746 //
747 // Note: Only called from input.c.
748 //
749 VOID FASTCALL
750 co_MsqPostKeyboardMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
751 {
752 PUSER_MESSAGE_QUEUE FocusMessageQueue;
753 MSG Msg;
754 LARGE_INTEGER LargeTickCount;
755 KBDLLHOOKSTRUCT KbdHookData;
756 BOOLEAN Entered = FALSE;
757
758 DPRINT("MsqPostKeyboardMessage(uMsg 0x%x, wParam 0x%x, lParam 0x%x)\n",
759 uMsg, wParam, lParam);
760
761 // Condition may arise when calling MsqPostMessage and waiting for an event.
762 if (!UserIsEntered())
763 {
764 // Fixme: Not sure ATM if this thread is locked.
765 UserEnterExclusive();
766 Entered = TRUE;
767 }
768
769 FocusMessageQueue = IntGetFocusMessageQueue();
770
771 Msg.hwnd = 0;
772
773 if (FocusMessageQueue && (FocusMessageQueue->FocusWindow != (HWND)0))
774 Msg.hwnd = FocusMessageQueue->FocusWindow;
775
776 Msg.message = uMsg;
777 Msg.wParam = wParam;
778 Msg.lParam = lParam;
779
780 KeQueryTickCount(&LargeTickCount);
781 Msg.time = MsqCalculateMessageTime(&LargeTickCount);
782
783 /* We can't get the Msg.pt point here since we don't know thread
784 (and thus the window station) the message will end up in yet. */
785
786 KbdHookData.vkCode = Msg.wParam;
787 KbdHookData.scanCode = (Msg.lParam >> 16) & 0xff;
788 KbdHookData.flags = (0 == (Msg.lParam & 0x01000000) ? 0 : LLKHF_EXTENDED) |
789 (0 == (Msg.lParam & 0x20000000) ? 0 : LLKHF_ALTDOWN) |
790 (0 == (Msg.lParam & 0x80000000) ? 0 : LLKHF_UP);
791 KbdHookData.time = Msg.time;
792 KbdHookData.dwExtraInfo = 0;
793 if (co_HOOK_CallHooks(WH_KEYBOARD_LL, HC_ACTION, Msg.message, (LPARAM) &KbdHookData))
794 {
795 DPRINT1("Kbd msg %d wParam %d lParam 0x%08x dropped by WH_KEYBOARD_LL hook\n",
796 Msg.message, Msg.wParam, Msg.lParam);
797 if (Entered) UserLeave();
798 return;
799 }
800
801 if (FocusMessageQueue == NULL)
802 {
803 DPRINT("No focus message queue\n");
804 if (Entered) UserLeave();
805 return;
806 }
807
808 if (FocusMessageQueue->FocusWindow != (HWND)0)
809 {
810 Msg.hwnd = FocusMessageQueue->FocusWindow;
811 DPRINT("Msg.hwnd = %x\n", Msg.hwnd);
812
813 FocusMessageQueue->Desktop->pDeskInfo->LastInputWasKbd = TRUE;
814
815 Msg.pt = gpsi->ptCursor;
816 MsqPostMessage(FocusMessageQueue, &Msg, TRUE, QS_KEY);
817 }
818 else
819 {
820 DPRINT("Invalid focus window handle\n");
821 }
822
823 if (Entered) UserLeave();
824 return;
825 }
826
827 VOID FASTCALL
828 MsqPostHotKeyMessage(PVOID Thread, HWND hWnd, WPARAM wParam, LPARAM lParam)
829 {
830 PWND Window;
831 PTHREADINFO Win32Thread;
832 MSG Mesg;
833 LARGE_INTEGER LargeTickCount;
834 NTSTATUS Status;
835
836 Status = ObReferenceObjectByPointer (Thread,
837 THREAD_ALL_ACCESS,
838 PsThreadType,
839 KernelMode);
840 if (!NT_SUCCESS(Status))
841 return;
842
843 Win32Thread = ((PETHREAD)Thread)->Tcb.Win32Thread;
844 if (Win32Thread == NULL || Win32Thread->MessageQueue == NULL)
845 {
846 ObDereferenceObject ((PETHREAD)Thread);
847 return;
848 }
849
850 Window = IntGetWindowObject(hWnd);
851 if (!Window)
852 {
853 ObDereferenceObject ((PETHREAD)Thread);
854 return;
855 }
856
857 Mesg.hwnd = hWnd;
858 Mesg.message = WM_HOTKEY;
859 Mesg.wParam = wParam;
860 Mesg.lParam = lParam;
861 KeQueryTickCount(&LargeTickCount);
862 Mesg.time = MsqCalculateMessageTime(&LargeTickCount);
863 Mesg.pt = gpsi->ptCursor;
864 MsqPostMessage(Window->head.pti->MessageQueue, &Mesg, FALSE, QS_HOTKEY);
865 UserDereferenceObject(Window);
866 ObDereferenceObject (Thread);
867
868 }
869
870 PUSER_MESSAGE FASTCALL
871 MsqCreateMessage(LPMSG Msg)
872 {
873 PUSER_MESSAGE Message;
874
875 Message = ExAllocateFromPagedLookasideList(&MessageLookasideList);
876 if (!Message)
877 {
878 return NULL;
879 }
880
881 RtlMoveMemory(&Message->Msg, Msg, sizeof(MSG));
882
883 return Message;
884 }
885
886 VOID FASTCALL
887 MsqDestroyMessage(PUSER_MESSAGE Message)
888 {
889 ExFreeToPagedLookasideList(&MessageLookasideList, Message);
890 }
891
892 VOID FASTCALL
893 co_MsqDispatchSentNotifyMessages(PUSER_MESSAGE_QUEUE MessageQueue)
894 {
895 PLIST_ENTRY ListEntry;
896 PUSER_SENT_MESSAGE_NOTIFY Message;
897
898 while (!IsListEmpty(&MessageQueue->SentMessagesListHead))
899 {
900 ListEntry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
901 Message = CONTAINING_RECORD(ListEntry, USER_SENT_MESSAGE_NOTIFY,
902 ListEntry);
903
904 co_IntCallSentMessageCallback(Message->CompletionCallback,
905 Message->hWnd,
906 Message->Msg,
907 Message->CompletionCallbackContext,
908 Message->Result);
909
910 }
911
912 }
913
914 BOOLEAN FASTCALL
915 MsqPeekSentMessages(PUSER_MESSAGE_QUEUE MessageQueue)
916 {
917 return(!IsListEmpty(&MessageQueue->SentMessagesListHead));
918 }
919
920 BOOLEAN FASTCALL
921 co_MsqDispatchOneSentMessage(PUSER_MESSAGE_QUEUE MessageQueue)
922 {
923 PUSER_SENT_MESSAGE Message;
924 PLIST_ENTRY Entry;
925 LRESULT Result;
926
927 if (IsListEmpty(&MessageQueue->SentMessagesListHead))
928 {
929 return(FALSE);
930 }
931
932 /* remove it from the list of pending messages */
933 Entry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
934 Message = CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry);
935
936 /* insert it to the list of messages that are currently dispatched by this
937 message queue */
938 InsertTailList(&MessageQueue->LocalDispatchingMessagesHead,
939 &Message->ListEntry);
940
941 if (Message->HookMessage == MSQ_ISHOOK)
942 { // Direct Hook Call processor
943 Result = co_CallHook( Message->Msg.message, // HookId
944 (INT)(INT_PTR)Message->Msg.hwnd, // Code
945 Message->Msg.wParam,
946 Message->Msg.lParam);
947 }
948 else if (Message->HookMessage == MSQ_ISEVENT)
949 { // Direct Event Call processor
950 Result = co_EVENT_CallEvents( Message->Msg.message,
951 Message->Msg.hwnd,
952 Message->Msg.wParam,
953 Message->Msg.lParam);
954 }
955 else
956 { /* Call the window procedure. */
957 Result = co_IntSendMessage( Message->Msg.hwnd,
958 Message->Msg.message,
959 Message->Msg.wParam,
960 Message->Msg.lParam);
961 }
962
963 /* remove the message from the local dispatching list, because it doesn't need
964 to be cleaned up on thread termination anymore */
965 RemoveEntryList(&Message->ListEntry);
966
967 /* remove the message from the dispatching list if needed, so lock the sender's message queue */
968 if (!(Message->HookMessage & MSQ_SENTNOWAIT))
969 {
970 if (Message->DispatchingListEntry.Flink != NULL)
971 {
972 /* only remove it from the dispatching list if not already removed by a timeout */
973 RemoveEntryList(&Message->DispatchingListEntry);
974 }
975 }
976 /* still keep the sender's message queue locked, so the sender can't exit the
977 MsqSendMessage() function (if timed out) */
978
979 /* Let the sender know the result. */
980 if (Message->Result != NULL)
981 {
982 *Message->Result = Result;
983 }
984
985 if (Message->HasPackedLParam == TRUE)
986 {
987 if (Message->Msg.lParam)
988 ExFreePool((PVOID)Message->Msg.lParam);
989 }
990
991 /* Notify the sender. */
992 if (Message->CompletionEvent != NULL)
993 {
994 KeSetEvent(Message->CompletionEvent, IO_NO_INCREMENT, FALSE);
995 }
996
997 /* Call the callback if the message was sent with SendMessageCallback */
998 if (Message->CompletionCallback != NULL)
999 {
1000 co_IntCallSentMessageCallback(Message->CompletionCallback,
1001 Message->Msg.hwnd,
1002 Message->Msg.message,
1003 Message->CompletionCallbackContext,
1004 Result);
1005 }
1006
1007 /* Only if it is not a no wait message */
1008 if (!(Message->HookMessage & MSQ_SENTNOWAIT))
1009 {
1010 IntDereferenceMessageQueue(Message->SenderQueue);
1011 IntDereferenceMessageQueue(MessageQueue);
1012 }
1013
1014 /* free the message */
1015 ExFreePoolWithTag(Message, TAG_USRMSG);
1016 return(TRUE);
1017 }
1018
1019 VOID APIENTRY
1020 MsqRemoveWindowMessagesFromQueue(PVOID pWindow)
1021 {
1022 PUSER_SENT_MESSAGE SentMessage;
1023 PUSER_MESSAGE PostedMessage;
1024 PUSER_MESSAGE_QUEUE MessageQueue;
1025 PLIST_ENTRY CurrentEntry, ListHead;
1026 PWND Window = pWindow;
1027
1028 ASSERT(Window);
1029
1030 MessageQueue = Window->head.pti->MessageQueue;
1031 ASSERT(MessageQueue);
1032
1033 /* remove the posted messages for this window */
1034 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
1035 ListHead = &MessageQueue->PostedMessagesListHead;
1036 while (CurrentEntry != ListHead)
1037 {
1038 PostedMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1039 ListEntry);
1040 if (PostedMessage->Msg.hwnd == Window->head.h)
1041 {
1042 RemoveEntryList(&PostedMessage->ListEntry);
1043 MsqDestroyMessage(PostedMessage);
1044 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
1045 }
1046 else
1047 {
1048 CurrentEntry = CurrentEntry->Flink;
1049 }
1050 }
1051
1052 /* remove the sent messages for this window */
1053 CurrentEntry = MessageQueue->SentMessagesListHead.Flink;
1054 ListHead = &MessageQueue->SentMessagesListHead;
1055 while (CurrentEntry != ListHead)
1056 {
1057 SentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1058 ListEntry);
1059 if(SentMessage->Msg.hwnd == Window->head.h)
1060 {
1061 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1062
1063 RemoveEntryList(&SentMessage->ListEntry);
1064
1065 /* remove the message from the dispatching list if neede */
1066 if ((!(SentMessage->HookMessage & MSQ_SENTNOWAIT))
1067 && (SentMessage->DispatchingListEntry.Flink != NULL))
1068 {
1069 RemoveEntryList(&SentMessage->DispatchingListEntry);
1070 }
1071
1072 /* wake the sender's thread */
1073 if (SentMessage->CompletionEvent != NULL)
1074 {
1075 KeSetEvent(SentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1076 }
1077
1078 if (SentMessage->HasPackedLParam == TRUE)
1079 {
1080 if (SentMessage->Msg.lParam)
1081 ExFreePool((PVOID)SentMessage->Msg.lParam);
1082 }
1083
1084 /* Only if it is not a no wait message */
1085 if (!(SentMessage->HookMessage & MSQ_SENTNOWAIT))
1086 {
1087 /* dereference our and the sender's message queue */
1088 IntDereferenceMessageQueue(MessageQueue);
1089 IntDereferenceMessageQueue(SentMessage->SenderQueue);
1090 }
1091
1092 /* free the message */
1093 ExFreePoolWithTag(SentMessage, TAG_USRMSG);
1094
1095 CurrentEntry = MessageQueue->SentMessagesListHead.Flink;
1096 }
1097 else
1098 {
1099 CurrentEntry = CurrentEntry->Flink;
1100 }
1101 }
1102 }
1103
1104 VOID FASTCALL
1105 MsqSendNotifyMessage(PUSER_MESSAGE_QUEUE MessageQueue,
1106 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage)
1107 {
1108 InsertTailList(&MessageQueue->NotifyMessagesListHead,
1109 &NotifyMessage->ListEntry);
1110 MsqWakeQueue(MessageQueue, QS_SENDMESSAGE);
1111 }
1112
1113 NTSTATUS FASTCALL
1114 co_MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue,
1115 HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam,
1116 UINT uTimeout, BOOL Block, INT HookMessage,
1117 ULONG_PTR *uResult)
1118 {
1119 PTHREADINFO pti;
1120 PUSER_SENT_MESSAGE Message;
1121 KEVENT CompletionEvent;
1122 NTSTATUS WaitStatus;
1123 PUSER_MESSAGE_QUEUE ThreadQueue;
1124 LARGE_INTEGER Timeout;
1125 PLIST_ENTRY Entry;
1126 LRESULT Result = 0; //// Result could be trashed. ////
1127
1128 if(!(Message = ExAllocatePoolWithTag(PagedPool, sizeof(USER_SENT_MESSAGE), TAG_USRMSG)))
1129 {
1130 DPRINT1("MsqSendMessage(): Not enough memory to allocate a message");
1131 return STATUS_INSUFFICIENT_RESOURCES;
1132 }
1133
1134 KeInitializeEvent(&CompletionEvent, NotificationEvent, FALSE);
1135
1136 pti = PsGetCurrentThreadWin32Thread();
1137 ThreadQueue = pti->MessageQueue;
1138 ASSERT(ThreadQueue != MessageQueue);
1139
1140 Timeout.QuadPart = (LONGLONG) uTimeout * (LONGLONG) -10000;
1141
1142 /* FIXME - increase reference counter of sender's message queue here */
1143
1144 Message->Msg.hwnd = Wnd;
1145 Message->Msg.message = Msg;
1146 Message->Msg.wParam = wParam;
1147 Message->Msg.lParam = lParam;
1148 Message->CompletionEvent = &CompletionEvent;
1149 Message->Result = &Result;
1150 Message->SenderQueue = ThreadQueue;
1151 IntReferenceMessageQueue(ThreadQueue);
1152 Message->CompletionCallback = NULL;
1153 Message->CompletionCallbackContext = 0;
1154 Message->HookMessage = HookMessage;
1155 Message->HasPackedLParam = FALSE;
1156
1157 IntReferenceMessageQueue(MessageQueue);
1158
1159 /* add it to the list of pending messages */
1160 InsertTailList(&ThreadQueue->DispatchingMessagesHead, &Message->DispatchingListEntry);
1161
1162 /* queue it in the destination's message queue */
1163 InsertTailList(&MessageQueue->SentMessagesListHead, &Message->ListEntry);
1164
1165 MsqWakeQueue(MessageQueue, QS_SENDMESSAGE);
1166
1167 /* we can't access the Message anymore since it could have already been deleted! */
1168
1169 if(Block)
1170 {
1171 UserLeaveCo();
1172
1173 /* don't process messages sent to the thread */
1174 WaitStatus = KeWaitForSingleObject(&CompletionEvent, UserRequest, UserMode,
1175 FALSE, (uTimeout ? &Timeout : NULL));
1176
1177 UserEnterCo();
1178
1179 if(WaitStatus == STATUS_TIMEOUT)
1180 {
1181 /* look up if the message has not yet dispatched, if so
1182 make sure it can't pass a result and it must not set the completion event anymore */
1183 Entry = MessageQueue->SentMessagesListHead.Flink;
1184 while (Entry != &MessageQueue->SentMessagesListHead)
1185 {
1186 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry)
1187 == Message)
1188 {
1189 /* we can access Message here, it's secure because the message queue is locked
1190 and the message is still hasn't been dispatched */
1191 Message->CompletionEvent = NULL;
1192 Message->Result = NULL;
1193 break;
1194 }
1195 Entry = Entry->Flink;
1196 }
1197
1198 /* remove from the local dispatching list so the other thread knows,
1199 it can't pass a result and it must not set the completion event anymore */
1200 Entry = ThreadQueue->DispatchingMessagesHead.Flink;
1201 while (Entry != &ThreadQueue->DispatchingMessagesHead)
1202 {
1203 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, DispatchingListEntry)
1204 == Message)
1205 {
1206 /* we can access Message here, it's secure because the sender's message is locked
1207 and the message has definitely not yet been destroyed, otherwise it would
1208 have been removed from this list by the dispatching routine right after
1209 dispatching the message */
1210 Message->CompletionEvent = NULL;
1211 Message->Result = NULL;
1212 RemoveEntryList(&Message->DispatchingListEntry);
1213 Message->DispatchingListEntry.Flink = NULL;
1214 break;
1215 }
1216 Entry = Entry->Flink;
1217 }
1218
1219 DPRINT("MsqSendMessage (blocked) timed out\n");
1220 }
1221 while (co_MsqDispatchOneSentMessage(ThreadQueue))
1222 ;
1223 }
1224 else
1225 {
1226 PVOID WaitObjects[2];
1227
1228 WaitObjects[0] = &CompletionEvent;
1229 WaitObjects[1] = ThreadQueue->NewMessages;
1230 do
1231 {
1232 UserLeaveCo();
1233
1234 WaitStatus = KeWaitForMultipleObjects(2, WaitObjects, WaitAny, UserRequest,
1235 UserMode, FALSE, (uTimeout ? &Timeout : NULL), NULL);
1236
1237 UserEnterCo();
1238
1239 if(WaitStatus == STATUS_TIMEOUT)
1240 {
1241 /* look up if the message has not yet been dispatched, if so
1242 make sure it can't pass a result and it must not set the completion event anymore */
1243 Entry = MessageQueue->SentMessagesListHead.Flink;
1244 while (Entry != &MessageQueue->SentMessagesListHead)
1245 {
1246 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry)
1247 == Message)
1248 {
1249 /* we can access Message here, it's secure because the message queue is locked
1250 and the message is still hasn't been dispatched */
1251 Message->CompletionEvent = NULL;
1252 Message->Result = NULL;
1253 break;
1254 }
1255 Entry = Entry->Flink;
1256 }
1257
1258 /* remove from the local dispatching list so the other thread knows,
1259 it can't pass a result and it must not set the completion event anymore */
1260 Entry = ThreadQueue->DispatchingMessagesHead.Flink;
1261 while (Entry != &ThreadQueue->DispatchingMessagesHead)
1262 {
1263 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, DispatchingListEntry)
1264 == Message)
1265 {
1266 /* we can access Message here, it's secure because the sender's message is locked
1267 and the message has definitely not yet been destroyed, otherwise it would
1268 have been removed from this list by the dispatching routine right after
1269 dispatching the message */
1270 Message->CompletionEvent = NULL;
1271 Message->Result = NULL;
1272 RemoveEntryList(&Message->DispatchingListEntry);
1273 Message->DispatchingListEntry.Flink = NULL;
1274 break;
1275 }
1276 Entry = Entry->Flink;
1277 }
1278
1279 DPRINT("MsqSendMessage timed out\n");
1280 break;
1281 }
1282 while (co_MsqDispatchOneSentMessage(ThreadQueue))
1283 ;
1284 }
1285 while (NT_SUCCESS(WaitStatus) && STATUS_WAIT_0 != WaitStatus);
1286 }
1287
1288 if(WaitStatus != STATUS_TIMEOUT)
1289 *uResult = (STATUS_WAIT_0 == WaitStatus ? Result : -1);
1290
1291 return WaitStatus;
1292 }
1293
1294 VOID FASTCALL
1295 MsqPostMessage(PUSER_MESSAGE_QUEUE MessageQueue, MSG* Msg, BOOLEAN HardwareMessage,
1296 DWORD MessageBits)
1297 {
1298 PUSER_MESSAGE Message;
1299
1300 if(!(Message = MsqCreateMessage(Msg)))
1301 {
1302 return;
1303 }
1304
1305 if(!HardwareMessage)
1306 {
1307 InsertTailList(&MessageQueue->PostedMessagesListHead,
1308 &Message->ListEntry);
1309 }
1310 else
1311 {
1312 InsertTailList(&MessageQueue->HardwareMessagesListHead,
1313 &Message->ListEntry);
1314 }
1315 MsqWakeQueue(MessageQueue, MessageBits);
1316 }
1317
1318 VOID FASTCALL
1319 MsqPostQuitMessage(PUSER_MESSAGE_QUEUE MessageQueue, ULONG ExitCode)
1320 {
1321 MessageQueue->QuitPosted = TRUE;
1322 MessageQueue->QuitExitCode = ExitCode;
1323 MsqWakeQueue(MessageQueue, QS_POSTMESSAGE);
1324 }
1325
1326 BOOLEAN APIENTRY
1327 MsqPeekMessage(IN PUSER_MESSAGE_QUEUE MessageQueue,
1328 IN BOOLEAN Remove,
1329 IN PWND Window,
1330 IN UINT MsgFilterLow,
1331 IN UINT MsgFilterHigh,
1332 OUT PMSG Message)
1333 {
1334 PLIST_ENTRY CurrentEntry;
1335 PUSER_MESSAGE CurrentMessage;
1336 PLIST_ENTRY ListHead;
1337
1338 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
1339 ListHead = &MessageQueue->PostedMessagesListHead;
1340 while (CurrentEntry != ListHead)
1341 {
1342 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1343 ListEntry);
1344 if ( ( !Window ||
1345 PtrToInt(Window) == 1 ||
1346 Window->head.h == CurrentMessage->Msg.hwnd ) &&
1347 ( (MsgFilterLow == 0 && MsgFilterHigh == 0) ||
1348 ( MsgFilterLow <= CurrentMessage->Msg.message &&
1349 MsgFilterHigh >= CurrentMessage->Msg.message ) ) )
1350 {
1351 *Message= CurrentMessage->Msg;
1352
1353 if (Remove)
1354 {
1355 RemoveEntryList(&CurrentMessage->ListEntry);
1356 MsqDestroyMessage(CurrentMessage);
1357 }
1358
1359 return(TRUE);
1360 }
1361 CurrentEntry = CurrentEntry->Flink;
1362 }
1363
1364 return(FALSE);
1365 }
1366
1367 NTSTATUS FASTCALL
1368 co_MsqWaitForNewMessages(PUSER_MESSAGE_QUEUE MessageQueue, PWND WndFilter,
1369 UINT MsgFilterMin, UINT MsgFilterMax)
1370 {
1371 PVOID WaitObjects[2] = {MessageQueue->NewMessages, &HardwareMessageEvent};
1372 NTSTATUS ret;
1373
1374 UserLeaveCo();
1375
1376 ret = KeWaitForMultipleObjects(2,
1377 WaitObjects,
1378 WaitAny,
1379 Executive,
1380 UserMode,
1381 FALSE,
1382 NULL,
1383 NULL);
1384 UserEnterCo();
1385 return ret;
1386 }
1387
1388 BOOL FASTCALL
1389 MsqIsHung(PUSER_MESSAGE_QUEUE MessageQueue)
1390 {
1391 LARGE_INTEGER LargeTickCount;
1392
1393 KeQueryTickCount(&LargeTickCount);
1394 return ((LargeTickCount.u.LowPart - MessageQueue->LastMsgRead) > MSQ_HUNG);
1395 }
1396
1397 BOOLEAN FASTCALL
1398 MsqInitializeMessageQueue(struct _ETHREAD *Thread, PUSER_MESSAGE_QUEUE MessageQueue)
1399 {
1400 LARGE_INTEGER LargeTickCount;
1401 NTSTATUS Status;
1402
1403 MessageQueue->Thread = Thread;
1404 MessageQueue->CaretInfo = (PTHRDCARETINFO)(MessageQueue + 1);
1405 InitializeListHead(&MessageQueue->PostedMessagesListHead);
1406 InitializeListHead(&MessageQueue->SentMessagesListHead);
1407 InitializeListHead(&MessageQueue->HardwareMessagesListHead);
1408 InitializeListHead(&MessageQueue->DispatchingMessagesHead);
1409 InitializeListHead(&MessageQueue->LocalDispatchingMessagesHead);
1410 KeInitializeMutex(&MessageQueue->HardwareLock, 0);
1411 MessageQueue->QuitPosted = FALSE;
1412 MessageQueue->QuitExitCode = 0;
1413 KeQueryTickCount(&LargeTickCount);
1414 MessageQueue->LastMsgRead = LargeTickCount.u.LowPart;
1415 MessageQueue->FocusWindow = NULL;
1416 MessageQueue->PaintCount = 0;
1417 // HACK!!!!!!! Newbies that wrote this should hold your head down in shame! (jt)
1418 MessageQueue->WakeMask = ~0;
1419 MessageQueue->NewMessagesHandle = NULL;
1420
1421 Status = ZwCreateEvent(&MessageQueue->NewMessagesHandle, EVENT_ALL_ACCESS,
1422 NULL, SynchronizationEvent, FALSE);
1423 if (!NT_SUCCESS(Status))
1424 {
1425 return FALSE;
1426 }
1427
1428 Status = ObReferenceObjectByHandle(MessageQueue->NewMessagesHandle, 0,
1429 ExEventObjectType, KernelMode,
1430 (PVOID*)&MessageQueue->NewMessages, NULL);
1431 if (!NT_SUCCESS(Status))
1432 {
1433 ZwClose(MessageQueue->NewMessagesHandle);
1434 MessageQueue->NewMessagesHandle = NULL;
1435 return FALSE;
1436 }
1437
1438 return TRUE;
1439 }
1440
1441 VOID FASTCALL
1442 MsqCleanupMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
1443 {
1444 PLIST_ENTRY CurrentEntry;
1445 PUSER_MESSAGE CurrentMessage;
1446 PUSER_SENT_MESSAGE CurrentSentMessage;
1447
1448 /* cleanup posted messages */
1449 while (!IsListEmpty(&MessageQueue->PostedMessagesListHead))
1450 {
1451 CurrentEntry = RemoveHeadList(&MessageQueue->PostedMessagesListHead);
1452 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1453 ListEntry);
1454 MsqDestroyMessage(CurrentMessage);
1455 }
1456
1457 /* remove the messages that have not yet been dispatched */
1458 while (!IsListEmpty(&MessageQueue->SentMessagesListHead))
1459 {
1460 CurrentEntry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
1461 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1462 ListEntry);
1463
1464 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1465
1466 /* remove the message from the dispatching list if needed */
1467 if ((!(CurrentSentMessage->HookMessage & MSQ_SENTNOWAIT))
1468 && (CurrentSentMessage->DispatchingListEntry.Flink != NULL))
1469 {
1470 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
1471 }
1472
1473 /* wake the sender's thread */
1474 if (CurrentSentMessage->CompletionEvent != NULL)
1475 {
1476 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1477 }
1478
1479 if (CurrentSentMessage->HasPackedLParam == TRUE)
1480 {
1481 if (CurrentSentMessage->Msg.lParam)
1482 ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
1483 }
1484
1485 /* Only if it is not a no wait message */
1486 if (!(CurrentSentMessage->HookMessage & MSQ_SENTNOWAIT))
1487 {
1488 /* dereference our and the sender's message queue */
1489 IntDereferenceMessageQueue(MessageQueue);
1490 IntDereferenceMessageQueue(CurrentSentMessage->SenderQueue);
1491 }
1492
1493 /* free the message */
1494 ExFreePool(CurrentSentMessage);
1495 }
1496
1497 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
1498 ExitThread() was called in a SendMessage() umode callback */
1499 while (!IsListEmpty(&MessageQueue->LocalDispatchingMessagesHead))
1500 {
1501 CurrentEntry = RemoveHeadList(&MessageQueue->LocalDispatchingMessagesHead);
1502 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1503 ListEntry);
1504
1505 /* remove the message from the dispatching list */
1506 if(CurrentSentMessage->DispatchingListEntry.Flink != NULL)
1507 {
1508 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
1509 }
1510
1511 DPRINT("Notify the sender, the thread has been terminated while dispatching a message!\n");
1512
1513 /* wake the sender's thread */
1514 if (CurrentSentMessage->CompletionEvent != NULL)
1515 {
1516 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1517 }
1518
1519 if (CurrentSentMessage->HasPackedLParam == TRUE)
1520 {
1521 if (CurrentSentMessage->Msg.lParam)
1522 ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
1523 }
1524
1525 /* Only if it is not a no wait message */
1526 if (!(CurrentSentMessage->HookMessage & MSQ_SENTNOWAIT))
1527 {
1528 /* dereference our and the sender's message queue */
1529 IntDereferenceMessageQueue(MessageQueue);
1530 IntDereferenceMessageQueue(CurrentSentMessage->SenderQueue);
1531 }
1532
1533 /* free the message */
1534 ExFreePool(CurrentSentMessage);
1535 }
1536
1537 /* tell other threads not to bother returning any info to us */
1538 while (! IsListEmpty(&MessageQueue->DispatchingMessagesHead))
1539 {
1540 CurrentEntry = RemoveHeadList(&MessageQueue->DispatchingMessagesHead);
1541 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1542 DispatchingListEntry);
1543 CurrentSentMessage->CompletionEvent = NULL;
1544 CurrentSentMessage->Result = NULL;
1545
1546 /* do NOT dereference our message queue as it might get attempted to be
1547 locked later */
1548 }
1549
1550 }
1551
1552 PUSER_MESSAGE_QUEUE FASTCALL
1553 MsqCreateMessageQueue(struct _ETHREAD *Thread)
1554 {
1555 PUSER_MESSAGE_QUEUE MessageQueue;
1556
1557 MessageQueue = (PUSER_MESSAGE_QUEUE)ExAllocatePoolWithTag(NonPagedPool,
1558 sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO),
1559 TAG_MSGQ);
1560
1561 if (!MessageQueue)
1562 {
1563 return NULL;
1564 }
1565
1566 RtlZeroMemory(MessageQueue, sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO));
1567 /* hold at least one reference until it'll be destroyed */
1568 IntReferenceMessageQueue(MessageQueue);
1569 /* initialize the queue */
1570 if (!MsqInitializeMessageQueue(Thread, MessageQueue))
1571 {
1572 IntDereferenceMessageQueue(MessageQueue);
1573 return NULL;
1574 }
1575
1576 return MessageQueue;
1577 }
1578
1579 VOID FASTCALL
1580 MsqDestroyMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
1581 {
1582 PDESKTOP desk;
1583
1584 /* remove the message queue from any desktops */
1585 if ((desk = InterlockedExchangePointer((PVOID*)&MessageQueue->Desktop, 0)))
1586 {
1587 (void)InterlockedExchangePointer((PVOID*)&desk->ActiveMessageQueue, 0);
1588 IntDereferenceMessageQueue(MessageQueue);
1589 }
1590
1591 /* clean it up */
1592 MsqCleanupMessageQueue(MessageQueue);
1593
1594 /* decrease the reference counter, if it hits zero, the queue will be freed */
1595 IntDereferenceMessageQueue(MessageQueue);
1596 }
1597
1598 LPARAM FASTCALL
1599 MsqSetMessageExtraInfo(LPARAM lParam)
1600 {
1601 LPARAM Ret;
1602 PTHREADINFO pti;
1603 PUSER_MESSAGE_QUEUE MessageQueue;
1604
1605 pti = PsGetCurrentThreadWin32Thread();
1606 MessageQueue = pti->MessageQueue;
1607 if(!MessageQueue)
1608 {
1609 return 0;
1610 }
1611
1612 Ret = MessageQueue->ExtraInfo;
1613 MessageQueue->ExtraInfo = lParam;
1614
1615 return Ret;
1616 }
1617
1618 LPARAM FASTCALL
1619 MsqGetMessageExtraInfo(VOID)
1620 {
1621 PTHREADINFO pti;
1622 PUSER_MESSAGE_QUEUE MessageQueue;
1623
1624 pti = PsGetCurrentThreadWin32Thread();
1625 MessageQueue = pti->MessageQueue;
1626 if(!MessageQueue)
1627 {
1628 return 0;
1629 }
1630
1631 return MessageQueue->ExtraInfo;
1632 }
1633
1634 HWND FASTCALL
1635 MsqSetStateWindow(PUSER_MESSAGE_QUEUE MessageQueue, ULONG Type, HWND hWnd)
1636 {
1637 HWND Prev;
1638
1639 switch(Type)
1640 {
1641 case MSQ_STATE_CAPTURE:
1642 Prev = MessageQueue->CaptureWindow;
1643 MessageQueue->CaptureWindow = hWnd;
1644 return Prev;
1645 case MSQ_STATE_ACTIVE:
1646 Prev = MessageQueue->ActiveWindow;
1647 MessageQueue->ActiveWindow = hWnd;
1648 return Prev;
1649 case MSQ_STATE_FOCUS:
1650 Prev = MessageQueue->FocusWindow;
1651 MessageQueue->FocusWindow = hWnd;
1652 return Prev;
1653 case MSQ_STATE_MENUOWNER:
1654 Prev = MessageQueue->MenuOwner;
1655 MessageQueue->MenuOwner = hWnd;
1656 return Prev;
1657 case MSQ_STATE_MOVESIZE:
1658 Prev = MessageQueue->MoveSize;
1659 MessageQueue->MoveSize = hWnd;
1660 return Prev;
1661 case MSQ_STATE_CARET:
1662 ASSERT(MessageQueue->CaretInfo);
1663 Prev = MessageQueue->CaretInfo->hWnd;
1664 MessageQueue->CaretInfo->hWnd = hWnd;
1665 return Prev;
1666 }
1667
1668 return NULL;
1669 }
1670
1671 /* EOF */