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