f79c19315feafb87f84388fb327a03603b042f9f
[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
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, 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 <w32k.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 static PAGED_LOOKASIDE_LIST TimerLookasideList;
54
55 #define IntLockSystemMessageQueue(OldIrql) \
56 KeAcquireSpinLock(&SystemMessageQueueLock, &OldIrql)
57
58 #define IntUnLockSystemMessageQueue(OldIrql) \
59 KeReleaseSpinLock(&SystemMessageQueueLock, OldIrql)
60
61 #define IntUnLockSystemHardwareMessageQueueLock(Wait) \
62 KeReleaseMutant(&HardwareMessageQueueLock, IO_NO_INCREMENT, FALSE, Wait)
63
64 /* FUNCTIONS *****************************************************************/
65
66 //
67 // Wakeup any thread/process waiting on idle input.
68 //
69 static VOID FASTCALL
70 IdlePing(VOID)
71 {
72 HWND hWnd;
73 PWINDOW_OBJECT Window;
74 PW32PROCESS W32d = PsGetCurrentProcessWin32Process();
75
76 hWnd = UserGetForegroundWindow();
77
78 Window = UserGetWindowObject(hWnd);
79
80 if (Window && Window->ti)
81 {
82 if (Window->ti->Hooks & HOOKID_TO_FLAG(WH_FOREGROUNDIDLE))
83 {
84 co_HOOK_CallHooks(WH_FOREGROUNDIDLE,HC_ACTION,0,0);
85 }
86 }
87
88 if (W32d && W32d->InputIdleEvent)
89 KePulseEvent( W32d->InputIdleEvent, EVENT_INCREMENT, TRUE);
90 }
91
92 HANDLE FASTCALL
93 IntMsqSetWakeMask(DWORD WakeMask)
94 {
95 PTHREADINFO Win32Thread;
96 PUSER_MESSAGE_QUEUE MessageQueue;
97 HANDLE MessageEventHandle;
98
99 Win32Thread = PsGetCurrentThreadWin32Thread();
100 if (Win32Thread == NULL || Win32Thread->MessageQueue == NULL)
101 return 0;
102
103 MessageQueue = Win32Thread->MessageQueue;
104 MessageQueue->WakeMask = WakeMask;
105 MessageEventHandle = MessageQueue->NewMessagesHandle;
106
107 return MessageEventHandle;
108 }
109
110 BOOL FASTCALL
111 IntMsqClearWakeMask(VOID)
112 {
113 PTHREADINFO Win32Thread;
114 PUSER_MESSAGE_QUEUE MessageQueue;
115
116 Win32Thread = PsGetCurrentThreadWin32Thread();
117 if (Win32Thread == NULL || Win32Thread->MessageQueue == NULL)
118 return FALSE;
119
120 MessageQueue = Win32Thread->MessageQueue;
121 MessageQueue->WakeMask = ~0;
122
123 return TRUE;
124 }
125
126 VOID FASTCALL
127 MsqIncPaintCountQueue(PUSER_MESSAGE_QUEUE Queue)
128 {
129 Queue->PaintCount++;
130 Queue->QueueBits |= QS_PAINT;
131 Queue->ChangedBits |= QS_PAINT;
132 if (Queue->WakeMask & QS_PAINT)
133 KeSetEvent(Queue->NewMessages, IO_NO_INCREMENT, FALSE);
134 }
135
136 VOID FASTCALL
137 MsqDecPaintCountQueue(PUSER_MESSAGE_QUEUE Queue)
138 {
139 Queue->PaintCount--;
140 }
141
142
143 NTSTATUS FASTCALL
144 MsqInitializeImpl(VOID)
145 {
146 /*CurrentFocusMessageQueue = NULL;*/
147 InitializeListHead(&HardwareMessageQueueHead);
148 KeInitializeEvent(&HardwareMessageEvent, NotificationEvent, 0);
149 KeInitializeSpinLock(&SystemMessageQueueLock);
150 KeInitializeMutant(&HardwareMessageQueueLock, 0);
151
152 ExInitializePagedLookasideList(&MessageLookasideList,
153 NULL,
154 NULL,
155 0,
156 sizeof(USER_MESSAGE),
157 TAG_USRMSG,
158 256);
159 ExInitializePagedLookasideList(&TimerLookasideList,
160 NULL,
161 NULL,
162 0,
163 sizeof(TIMER_ENTRY),
164 TAG_TIMER,
165 64);
166
167 return(STATUS_SUCCESS);
168 }
169
170 VOID FASTCALL
171 MsqInsertSystemMessage(MSG* Msg)
172 {
173 LARGE_INTEGER LargeTickCount;
174 KIRQL OldIrql;
175 ULONG Prev;
176 EVENTMSG Event;
177
178 IntLockSystemMessageQueue(OldIrql);
179
180 /*
181 * Bail out if the queue is full. FIXME: We should handle this case
182 * more gracefully.
183 */
184
185 if (SystemMessageQueueCount == SYSTEM_MESSAGE_QUEUE_SIZE)
186 {
187 IntUnLockSystemMessageQueue(OldIrql);
188 return;
189 }
190
191 KeQueryTickCount(&LargeTickCount);
192 Msg->time = MsqCalculateMessageTime(&LargeTickCount);
193
194 Event.message = Msg->message;
195 Event.time = Msg->time;
196 Event.hwnd = Msg->hwnd;
197 Event.paramL = Msg->pt.x;
198 Event.paramH = Msg->pt.y;
199 co_HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&Event);
200
201 /*
202 * If we got WM_MOUSEMOVE and there are already messages in the
203 * system message queue, check if the last message is mouse move
204 * and if it is then just overwrite it.
205 */
206
207 if (Msg->message == WM_MOUSEMOVE && SystemMessageQueueCount)
208 {
209 if (SystemMessageQueueTail == 0)
210 Prev = SYSTEM_MESSAGE_QUEUE_SIZE - 1;
211 else
212 Prev = SystemMessageQueueTail - 1;
213 if (SystemMessageQueue[Prev].message == WM_MOUSEMOVE)
214 {
215 SystemMessageQueueTail = Prev;
216 SystemMessageQueueCount--;
217 }
218 }
219
220 /*
221 * Actually insert the message into the system message queue.
222 */
223
224 SystemMessageQueue[SystemMessageQueueTail] = *Msg;
225 SystemMessageQueueTail =
226 (SystemMessageQueueTail + 1) % SYSTEM_MESSAGE_QUEUE_SIZE;
227 SystemMessageQueueCount++;
228
229 IntUnLockSystemMessageQueue(OldIrql);
230
231 KeSetEvent(&HardwareMessageEvent, IO_NO_INCREMENT, FALSE);
232 }
233
234 BOOL FASTCALL
235 MsqIsDblClk(LPMSG Msg, BOOL Remove)
236 {
237 PTHREADINFO pti;
238 PWINSTATION_OBJECT WinStaObject;
239 PSYSTEM_CURSORINFO CurInfo;
240 LONG dX, dY;
241 BOOL Res;
242
243 pti = PsGetCurrentThreadWin32Thread();
244 if (pti->Desktop == NULL)
245 {
246 return FALSE;
247 }
248
249 WinStaObject = pti->Desktop->WindowStation;
250
251 CurInfo = IntGetSysCursorInfo(WinStaObject);
252 Res = (Msg->hwnd == (HWND)CurInfo->LastClkWnd) &&
253 ((Msg->time - CurInfo->LastBtnDown) < CurInfo->DblClickSpeed);
254 if(Res)
255 {
256
257 dX = CurInfo->LastBtnDownX - Msg->pt.x;
258 dY = CurInfo->LastBtnDownY - Msg->pt.y;
259 if(dX < 0)
260 dX = -dX;
261 if(dY < 0)
262 dY = -dY;
263
264 Res = (dX <= CurInfo->DblClickWidth) &&
265 (dY <= CurInfo->DblClickHeight);
266
267 if(Res)
268 {
269 if(CurInfo->ButtonsDown)
270 Res = (CurInfo->ButtonsDown == Msg->message);
271 }
272 }
273
274 if(Remove)
275 {
276 if (Res)
277 {
278 CurInfo->LastBtnDown = 0;
279 CurInfo->LastBtnDownX = Msg->pt.x;
280 CurInfo->LastBtnDownY = Msg->pt.y;
281 CurInfo->LastClkWnd = NULL;
282 CurInfo->ButtonsDown = Msg->message;
283 }
284 else
285 {
286 CurInfo->LastBtnDownX = Msg->pt.x;
287 CurInfo->LastBtnDownY = Msg->pt.y;
288 CurInfo->LastClkWnd = (HANDLE)Msg->hwnd;
289 CurInfo->LastBtnDown = Msg->time;
290 CurInfo->ButtonsDown = Msg->message;
291 }
292 }
293
294 return Res;
295 }
296
297 BOOL static STDCALL
298 co_MsqTranslateMouseMessage(PUSER_MESSAGE_QUEUE MessageQueue, HWND hWnd, UINT FilterLow, UINT FilterHigh,
299 PUSER_MESSAGE Message, BOOL Remove, PBOOL Freed,
300 PWINDOW_OBJECT ScopeWin, PPOINT ScreenPoint, BOOL FromGlobalQueue, PLIST_ENTRY *Next)
301 {
302 USHORT Msg = Message->Msg.message;
303 PWINDOW_OBJECT Window = NULL;
304 HWND hCaptureWin;
305
306 ASSERT_REFS_CO(ScopeWin);
307
308 /*
309 co_WinPosWindowFromPoint can return a Window, and in that case
310 that window has a ref that we need to deref. Thats why we add "dummy"
311 refs in all other cases.
312 */
313
314 hCaptureWin = IntGetCaptureWindow();
315 if (hCaptureWin == NULL)
316 {
317 if(Msg == WM_MOUSEWHEEL)
318 {
319 Window = UserGetWindowObject(IntGetFocusWindow());
320 if (Window) UserReferenceObject(Window);
321 }
322 else
323 {
324 co_WinPosWindowFromPoint(ScopeWin, NULL, &Message->Msg.pt, &Window);
325 if(Window == NULL)
326 {
327 Window = ScopeWin;
328 if (Window) UserReferenceObject(Window);
329 }
330 else
331 {
332 /* this is the one case where we dont add a ref, since the returned
333 window is already referenced */
334 }
335 }
336 }
337 else
338 {
339 /* FIXME - window messages should go to the right window if no buttons are
340 pressed */
341 Window = UserGetWindowObject(hCaptureWin);
342 if (Window) UserReferenceObject(Window);
343 }
344
345
346
347 if (Window == NULL)
348 {
349 if(!FromGlobalQueue)
350 {
351 RemoveEntryList(&Message->ListEntry);
352 if(MessageQueue->MouseMoveMsg == Message)
353 {
354 MessageQueue->MouseMoveMsg = NULL;
355 }
356 }
357 // when FromGlobalQueue is true, the caller has already removed the Message
358 ExFreePool(Message);
359 *Freed = TRUE;
360 return(FALSE);
361 }
362
363 if (Window->MessageQueue != MessageQueue)
364 {
365 if (! FromGlobalQueue)
366 {
367 DPRINT("Moving msg between private queues\n");
368 /* This message is already queued in a private queue, but we need
369 * to move it to a different queue, perhaps because a new window
370 * was created which now covers the screen area previously taken
371 * by another window. To move it, we need to take it out of the
372 * old queue. Note that we're already holding the lock mutexes of the
373 * old queue */
374 RemoveEntryList(&Message->ListEntry);
375
376 /* remove the pointer for the current WM_MOUSEMOVE message in case we
377 just removed it */
378 if(MessageQueue->MouseMoveMsg == Message)
379 {
380 MessageQueue->MouseMoveMsg = NULL;
381 }
382 }
383
384 /* lock the destination message queue, so we don't get in trouble with other
385 threads, messing with it at the same time */
386 IntLockHardwareMessageQueue(Window->MessageQueue);
387 InsertTailList(&Window->MessageQueue->HardwareMessagesListHead,
388 &Message->ListEntry);
389 if(Message->Msg.message == WM_MOUSEMOVE)
390 {
391 if(Window->MessageQueue->MouseMoveMsg)
392 {
393 /* remove the old WM_MOUSEMOVE message, we're processing a more recent
394 one */
395 RemoveEntryList(&Window->MessageQueue->MouseMoveMsg->ListEntry);
396 ExFreePool(Window->MessageQueue->MouseMoveMsg);
397 }
398 /* save the pointer to the WM_MOUSEMOVE message in the new queue */
399 Window->MessageQueue->MouseMoveMsg = Message;
400
401 Window->MessageQueue->QueueBits |= QS_MOUSEMOVE;
402 Window->MessageQueue->ChangedBits |= QS_MOUSEMOVE;
403 if (Window->MessageQueue->WakeMask & QS_MOUSEMOVE)
404 KeSetEvent(Window->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
405 }
406 else
407 {
408 Window->MessageQueue->QueueBits |= QS_MOUSEBUTTON;
409 Window->MessageQueue->ChangedBits |= QS_MOUSEBUTTON;
410 if (Window->MessageQueue->WakeMask & QS_MOUSEBUTTON)
411 KeSetEvent(Window->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
412 }
413 IntUnLockHardwareMessageQueue(Window->MessageQueue);
414
415 *Freed = FALSE;
416 UserDereferenceObject(Window);
417 return(FALSE);
418 }
419
420 /* From here on, we're in the same message queue as the caller! */
421
422 *ScreenPoint = Message->Msg.pt;
423
424 if((hWnd != NULL && Window->hSelf != hWnd) ||
425 ((FilterLow != 0 || FilterHigh != 0) && (Msg < FilterLow || Msg > FilterHigh)))
426 {
427 /* Reject the message because it doesn't match the filter */
428
429 if(FromGlobalQueue)
430 {
431 /* Lock the message queue so no other thread can mess with it.
432 Our own message queue is not locked while fetching from the global
433 queue, so we have to make sure nothing interferes! */
434 IntLockHardwareMessageQueue(Window->MessageQueue);
435 /* if we're from the global queue, we need to add our message to our
436 private queue so we don't loose it! */
437 InsertTailList(&Window->MessageQueue->HardwareMessagesListHead,
438 &Message->ListEntry);
439 }
440
441 if (Message->Msg.message == WM_MOUSEMOVE)
442 {
443 if(Window->MessageQueue->MouseMoveMsg &&
444 (Window->MessageQueue->MouseMoveMsg != Message))
445 {
446 /* delete the old message */
447 RemoveEntryList(&Window->MessageQueue->MouseMoveMsg->ListEntry);
448 ExFreePool(Window->MessageQueue->MouseMoveMsg);
449 if (!FromGlobalQueue)
450 {
451 // We might have deleted the next one in our queue, so fix next
452 *Next = Message->ListEntry.Flink;
453 }
454 }
455 /* always save a pointer to this WM_MOUSEMOVE message here because we're
456 sure that the message is in the private queue */
457 Window->MessageQueue->MouseMoveMsg = Message;
458 }
459 if(FromGlobalQueue)
460 {
461 IntUnLockHardwareMessageQueue(Window->MessageQueue);
462 }
463
464 UserDereferenceObject(Window);
465 *Freed = FALSE;
466 return(FALSE);
467 }
468
469 /* FIXME - only assign if removing? */
470 Message->Msg.hwnd = Window->hSelf;
471 Message->Msg.message = Msg;
472 Message->Msg.lParam = MAKELONG(Message->Msg.pt.x, Message->Msg.pt.y);
473
474 /* remove the reference to the current WM_(NC)MOUSEMOVE message, if this message
475 is it */
476 if (Message->Msg.message == WM_MOUSEMOVE ||
477 Message->Msg.message == WM_NCMOUSEMOVE)
478 {
479 if(FromGlobalQueue)
480 {
481 /* Lock the message queue so no other thread can mess with it.
482 Our own message queue is not locked while fetching from the global
483 queue, so we have to make sure nothing interferes! */
484 IntLockHardwareMessageQueue(Window->MessageQueue);
485 if(Window->MessageQueue->MouseMoveMsg)
486 {
487 /* delete the WM_(NC)MOUSEMOVE message in the private queue, we're dealing
488 with one that's been sent later */
489 RemoveEntryList(&Window->MessageQueue->MouseMoveMsg->ListEntry);
490 ExFreePool(Window->MessageQueue->MouseMoveMsg);
491 /* our message is not in the private queue so we can remove the pointer
492 instead of setting it to the current message we're processing */
493 Window->MessageQueue->MouseMoveMsg = NULL;
494 }
495 IntUnLockHardwareMessageQueue(Window->MessageQueue);
496 }
497 else if(Window->MessageQueue->MouseMoveMsg == Message)
498 {
499 Window->MessageQueue->MouseMoveMsg = NULL;
500 }
501 }
502
503 UserDereferenceObject(Window);
504 *Freed = FALSE;
505 return(TRUE);
506 }
507
508 BOOL STDCALL
509 co_MsqPeekHardwareMessage(PUSER_MESSAGE_QUEUE MessageQueue, HWND hWnd,
510 UINT FilterLow, UINT FilterHigh, BOOL Remove,
511 PUSER_MESSAGE* Message)
512 {
513 KIRQL OldIrql;
514 POINT ScreenPoint;
515 BOOL Accept, Freed;
516 PLIST_ENTRY CurrentEntry;
517 PWINDOW_OBJECT DesktopWindow = NULL;
518 PVOID WaitObjects[2];
519 NTSTATUS WaitStatus;
520 DECLARE_RETURN(BOOL);
521 USER_REFERENCE_ENTRY Ref;
522 PDESKTOPINFO Desk = NULL;
523
524 WaitObjects[1] = MessageQueue->NewMessages;
525 WaitObjects[0] = &HardwareMessageQueueLock;
526 do
527 {
528 IdlePing();
529
530 UserLeaveCo();
531
532 WaitStatus = KeWaitForMultipleObjects(2, WaitObjects, WaitAny, UserRequest,
533 UserMode, FALSE, NULL, NULL);
534
535 UserEnterCo();
536
537 while (co_MsqDispatchOneSentMessage(MessageQueue))
538 {
539 ;
540 }
541 }
542 while (NT_SUCCESS(WaitStatus) && STATUS_WAIT_0 != WaitStatus);
543
544 DesktopWindow = UserGetWindowObject(IntGetDesktopWindow());
545
546 if (DesktopWindow)
547 {
548 UserRefObjectCo(DesktopWindow, &Ref);//can DesktopWindow be NULL?
549 Desk = DesktopWindow->ti->Desktop;
550 }
551
552 /* Process messages in the message queue itself. */
553 IntLockHardwareMessageQueue(MessageQueue);
554 CurrentEntry = MessageQueue->HardwareMessagesListHead.Flink;
555 while (CurrentEntry != &MessageQueue->HardwareMessagesListHead)
556 {
557 PUSER_MESSAGE Current =
558 CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, ListEntry);
559 CurrentEntry = CurrentEntry->Flink;
560 if (Current->Msg.message >= WM_MOUSEFIRST &&
561 Current->Msg.message <= WM_MOUSELAST)
562 {
563
564
565 Accept = co_MsqTranslateMouseMessage(MessageQueue, hWnd, FilterLow, FilterHigh,
566 Current, Remove, &Freed,
567 DesktopWindow, &ScreenPoint, FALSE, &CurrentEntry);
568 if (Accept)
569 {
570 if (Remove)
571 {
572 RemoveEntryList(&Current->ListEntry);
573 }
574 IntUnLockHardwareMessageQueue(MessageQueue);
575 IntUnLockSystemHardwareMessageQueueLock(FALSE);
576 *Message = Current;
577
578 if (Desk)
579 Desk->LastInputWasKbd = FALSE;
580
581 RETURN(TRUE);
582 }
583
584 }
585 }
586 IntUnLockHardwareMessageQueue(MessageQueue);
587
588 /* Now try the global queue. */
589
590 /* Transfer all messages from the DPC accessible queue to the main queue. */
591 IntLockSystemMessageQueue(OldIrql);
592 while (SystemMessageQueueCount > 0)
593 {
594 PUSER_MESSAGE UserMsg;
595 MSG Msg;
596 BOOL ProcessMessage;
597
598 ASSERT(SystemMessageQueueHead < SYSTEM_MESSAGE_QUEUE_SIZE);
599 Msg = SystemMessageQueue[SystemMessageQueueHead];
600 SystemMessageQueueHead =
601 (SystemMessageQueueHead + 1) % SYSTEM_MESSAGE_QUEUE_SIZE;
602 SystemMessageQueueCount--;
603 IntUnLockSystemMessageQueue(OldIrql);
604 if (WM_MOUSEFIRST <= Msg.message && Msg.message <= WM_MOUSELAST)
605 {
606 MSLLHOOKSTRUCT MouseHookData;
607
608 MouseHookData.pt.x = LOWORD(Msg.lParam);
609 MouseHookData.pt.y = HIWORD(Msg.lParam);
610 switch(Msg.message)
611 {
612 case WM_MOUSEWHEEL:
613 MouseHookData.mouseData = MAKELONG(0, GET_WHEEL_DELTA_WPARAM(Msg.wParam));
614 break;
615 case WM_XBUTTONDOWN:
616 case WM_XBUTTONUP:
617 case WM_XBUTTONDBLCLK:
618 case WM_NCXBUTTONDOWN:
619 case WM_NCXBUTTONUP:
620 case WM_NCXBUTTONDBLCLK:
621 MouseHookData.mouseData = MAKELONG(0, HIWORD(Msg.wParam));
622 break;
623 default:
624 MouseHookData.mouseData = 0;
625 break;
626 }
627 MouseHookData.flags = 0;
628 MouseHookData.time = Msg.time;
629 MouseHookData.dwExtraInfo = 0;
630 ProcessMessage = (0 == co_HOOK_CallHooks(WH_MOUSE_LL, HC_ACTION,
631 Msg.message, (LPARAM) &MouseHookData));
632 }
633 else
634 {
635 ProcessMessage = TRUE;
636 }
637 if (ProcessMessage)
638 {
639 UserMsg = ExAllocateFromPagedLookasideList(&MessageLookasideList);
640 /* What to do if out of memory? For now we just panic a bit in debug */
641 ASSERT(UserMsg);
642 UserMsg->FreeLParam = FALSE;
643 UserMsg->Msg = Msg;
644 InsertTailList(&HardwareMessageQueueHead, &UserMsg->ListEntry);
645 }
646 IntLockSystemMessageQueue(OldIrql);
647 }
648 HardwareMessageQueueStamp++;
649 IntUnLockSystemMessageQueue(OldIrql);
650
651 /* Process messages in the queue until we find one to return. */
652 CurrentEntry = HardwareMessageQueueHead.Flink;
653 while (CurrentEntry != &HardwareMessageQueueHead)
654 {
655 PUSER_MESSAGE Current =
656 CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, ListEntry);
657 CurrentEntry = CurrentEntry->Flink;
658 RemoveEntryList(&Current->ListEntry);
659 HardwareMessageQueueStamp++;
660 if (Current->Msg.message >= WM_MOUSEFIRST &&
661 Current->Msg.message <= WM_MOUSELAST)
662 {
663 const ULONG ActiveStamp = HardwareMessageQueueStamp;
664 /* Translate the message. */
665 Accept = co_MsqTranslateMouseMessage(MessageQueue, hWnd, FilterLow, FilterHigh,
666 Current, Remove, &Freed,
667 DesktopWindow, &ScreenPoint, TRUE, NULL);
668 if (Accept)
669 {
670 /* Check for no more messages in the system queue. */
671 IntLockSystemMessageQueue(OldIrql);
672 if (SystemMessageQueueCount == 0 &&
673 IsListEmpty(&HardwareMessageQueueHead))
674 {
675 KeClearEvent(&HardwareMessageEvent);
676 }
677 IntUnLockSystemMessageQueue(OldIrql);
678
679 /*
680 If we aren't removing the message then add it to the private
681 queue.
682 */
683 if (!Remove)
684 {
685 IntLockHardwareMessageQueue(MessageQueue);
686 if(Current->Msg.message == WM_MOUSEMOVE)
687 {
688 if(MessageQueue->MouseMoveMsg)
689 {
690 RemoveEntryList(&MessageQueue->MouseMoveMsg->ListEntry);
691 ExFreePool(MessageQueue->MouseMoveMsg);
692 }
693 MessageQueue->MouseMoveMsg = Current;
694 }
695 InsertTailList(&MessageQueue->HardwareMessagesListHead,
696 &Current->ListEntry);
697 IntUnLockHardwareMessageQueue(MessageQueue);
698 }
699 IntUnLockSystemHardwareMessageQueueLock(FALSE);
700 *Message = Current;
701
702 RETURN(TRUE);
703 }
704 /* If the contents of the queue changed then restart processing. */
705 if (HardwareMessageQueueStamp != ActiveStamp)
706 {
707 CurrentEntry = HardwareMessageQueueHead.Flink;
708 continue;
709 }
710 }
711 }
712
713 /* Check if the system message queue is now empty. */
714 IntLockSystemMessageQueue(OldIrql);
715 if (SystemMessageQueueCount == 0 && IsListEmpty(&HardwareMessageQueueHead))
716 {
717 KeClearEvent(&HardwareMessageEvent);
718 }
719 IntUnLockSystemMessageQueue(OldIrql);
720 IntUnLockSystemHardwareMessageQueueLock(FALSE);
721
722 RETURN(FALSE);
723
724 CLEANUP:
725 if (DesktopWindow) UserDerefObjectCo(DesktopWindow);
726
727 END_CLEANUP;
728 }
729
730 //
731 // Note: Only called from input.c.
732 //
733 VOID FASTCALL
734 co_MsqPostKeyboardMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
735 {
736 PUSER_MESSAGE_QUEUE FocusMessageQueue;
737 MSG Msg;
738 LARGE_INTEGER LargeTickCount;
739 KBDLLHOOKSTRUCT KbdHookData;
740 EVENTMSG Event;
741
742 DPRINT("MsqPostKeyboardMessage(uMsg 0x%x, wParam 0x%x, lParam 0x%x)\n",
743 uMsg, wParam, lParam);
744
745 // Condition may arise when calling MsqPostMessage and waiting for an event.
746 if (!UserIsEntered()) UserEnterExclusive(); // Fixme: Not sure ATM if this thread is locked.
747
748 FocusMessageQueue = IntGetFocusMessageQueue();
749
750 Msg.hwnd = 0;
751
752 if (FocusMessageQueue && (FocusMessageQueue->FocusWindow != (HWND)0))
753 Msg.hwnd = FocusMessageQueue->FocusWindow;
754
755 Msg.message = uMsg;
756 Msg.wParam = wParam;
757 Msg.lParam = lParam;
758
759 KeQueryTickCount(&LargeTickCount);
760 Msg.time = MsqCalculateMessageTime(&LargeTickCount);
761
762 Event.message = Msg.message;
763 Event.hwnd = Msg.hwnd;
764 Event.time = Msg.time;
765 Event.paramL = (Msg.wParam & 0xFF) | (HIWORD(Msg.lParam) << 8);
766 Event.paramH = Msg.lParam & 0x7FFF;
767 if (HIWORD(Msg.lParam) & 0x0100) Event.paramH |= 0x8000;
768 co_HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&Event);
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 DPRINT("Kbd msg %d wParam %d lParam 0x%08x dropped by WH_KEYBOARD_LL hook\n",
783 Msg.message, Msg.wParam, Msg.lParam);
784 return;
785 }
786
787 if (FocusMessageQueue == NULL)
788 {
789 DPRINT("No focus message queue\n");
790 return;
791 }
792
793 if (FocusMessageQueue->FocusWindow != (HWND)0)
794 {
795 Msg.hwnd = FocusMessageQueue->FocusWindow;
796 DPRINT("Msg.hwnd = %x\n", Msg.hwnd);
797
798 FocusMessageQueue->Desktop->DesktopInfo->LastInputWasKbd = TRUE;
799
800 IntGetCursorLocation(FocusMessageQueue->Desktop->WindowStation,
801 &Msg.pt);
802 MsqPostMessage(FocusMessageQueue, &Msg, FALSE, QS_KEY);
803 }
804 else
805 {
806 DPRINT("Invalid focus window handle\n");
807 }
808 if (UserIsEntered()) UserLeave();
809 }
810
811 VOID FASTCALL
812 MsqPostHotKeyMessage(PVOID Thread, HWND hWnd, WPARAM wParam, LPARAM lParam)
813 {
814 PWINDOW_OBJECT Window;
815 PTHREADINFO Win32Thread;
816 PWINSTATION_OBJECT WinSta;
817 MSG Mesg;
818 LARGE_INTEGER LargeTickCount;
819 NTSTATUS Status;
820
821 Status = ObReferenceObjectByPointer (Thread,
822 THREAD_ALL_ACCESS,
823 PsThreadType,
824 KernelMode);
825 if (!NT_SUCCESS(Status))
826 return;
827
828 Win32Thread = ((PETHREAD)Thread)->Tcb.Win32Thread;
829 if (Win32Thread == NULL || Win32Thread->MessageQueue == NULL)
830 {
831 ObDereferenceObject ((PETHREAD)Thread);
832 return;
833 }
834
835 WinSta = Win32Thread->Desktop->WindowStation;
836 Window = IntGetWindowObject(hWnd);
837 if (!Window)
838 {
839 ObDereferenceObject ((PETHREAD)Thread);
840 return;
841 }
842
843 Mesg.hwnd = hWnd;
844 Mesg.message = WM_HOTKEY;
845 Mesg.wParam = wParam;
846 Mesg.lParam = lParam;
847 KeQueryTickCount(&LargeTickCount);
848 Mesg.time = MsqCalculateMessageTime(&LargeTickCount);
849 IntGetCursorLocation(WinSta, &Mesg.pt);
850 MsqPostMessage(Window->MessageQueue, &Mesg, FALSE, QS_HOTKEY);
851 UserDereferenceObject(Window);
852 ObDereferenceObject (Thread);
853
854 // InsertHeadList(&pThread->MessageQueue->PostedMessagesListHead,
855 // &Message->ListEntry);
856 // KeSetEvent(pThread->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
857 }
858
859 PUSER_MESSAGE FASTCALL
860 MsqCreateMessage(LPMSG Msg, BOOLEAN FreeLParam)
861 {
862 PUSER_MESSAGE Message;
863
864 Message = ExAllocateFromPagedLookasideList(&MessageLookasideList);
865 if (!Message)
866 {
867 return NULL;
868 }
869
870 Message->FreeLParam = FreeLParam;
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 BOOL SenderReturned;
917 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage;
918
919 if (IsListEmpty(&MessageQueue->SentMessagesListHead))
920 {
921 return(FALSE);
922 }
923
924 /* remove it from the list of pending messages */
925 Entry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
926 Message = CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry);
927
928 /* insert it to the list of messages that are currently dispatched by this
929 message queue */
930 InsertTailList(&MessageQueue->LocalDispatchingMessagesHead,
931 &Message->ListEntry);
932
933 if (Message->HookMessage == MSQ_ISHOOK)
934 {
935 Result = co_HOOK_CallHooks(Message->Msg.message,
936 (INT)(INT_PTR)Message->Msg.hwnd,
937 Message->Msg.wParam,
938 Message->Msg.lParam);
939 }
940 else if (Message->HookMessage == MSQ_ISEVENT)
941 {
942 Result = co_EVENT_CallEvents( Message->Msg.message,
943 Message->Msg.hwnd,
944 Message->Msg.wParam,
945 Message->Msg.lParam);
946 }
947 else
948 {
949 /* Call the window procedure. */
950 Result = co_IntSendMessage(Message->Msg.hwnd,
951 Message->Msg.message,
952 Message->Msg.wParam,
953 Message->Msg.lParam);
954 }
955
956 /* remove the message from the local dispatching list, because it doesn't need
957 to be cleaned up on thread termination anymore */
958 RemoveEntryList(&Message->ListEntry);
959
960 /* remove the message from the dispatching list, so lock the sender's message queue */
961 SenderReturned = (Message->DispatchingListEntry.Flink == NULL);
962 if(!SenderReturned)
963 {
964 /* only remove it from the dispatching list if not already removed by a timeout */
965 RemoveEntryList(&Message->DispatchingListEntry);
966 }
967 /* still keep the sender's message queue locked, so the sender can't exit the
968 MsqSendMessage() function (if timed out) */
969
970 /* Let the sender know the result. */
971 if (Message->Result != NULL)
972 {
973 *Message->Result = Result;
974 }
975
976 /* Notify the sender. */
977 if (Message->CompletionEvent != NULL)
978 {
979 KeSetEvent(Message->CompletionEvent, IO_NO_INCREMENT, FALSE);
980 }
981
982 /* Notify the sender if they specified a callback. */
983 if (!SenderReturned && Message->CompletionCallback != NULL)
984 {
985 if(!(NotifyMessage = ExAllocatePoolWithTag(NonPagedPool,
986 sizeof(USER_SENT_MESSAGE_NOTIFY), TAG_USRMSG)))
987 {
988 DPRINT1("MsqDispatchOneSentMessage(): Not enough memory to create a callback notify message\n");
989 goto Notified;
990 }
991 NotifyMessage->CompletionCallback =
992 Message->CompletionCallback;
993 NotifyMessage->CompletionCallbackContext =
994 Message->CompletionCallbackContext;
995 NotifyMessage->Result = Result;
996 NotifyMessage->hWnd = Message->Msg.hwnd;
997 NotifyMessage->Msg = Message->Msg.message;
998 MsqSendNotifyMessage(Message->SenderQueue, NotifyMessage);
999 }
1000
1001 Notified:
1002
1003 /* dereference both sender and our queue */
1004 IntDereferenceMessageQueue(MessageQueue);
1005 IntDereferenceMessageQueue(Message->SenderQueue);
1006
1007 /* free the message */
1008 ExFreePool(Message);
1009 return(TRUE);
1010 }
1011
1012 VOID STDCALL
1013 MsqRemoveWindowMessagesFromQueue(PVOID pWindow)
1014 {
1015 PUSER_SENT_MESSAGE SentMessage;
1016 PUSER_MESSAGE PostedMessage;
1017 PUSER_MESSAGE_QUEUE MessageQueue;
1018 PLIST_ENTRY CurrentEntry, ListHead;
1019 PWINDOW_OBJECT Window = pWindow;
1020
1021 ASSERT(Window);
1022
1023 MessageQueue = Window->MessageQueue;
1024 ASSERT(MessageQueue);
1025
1026 /* remove the posted messages for this window */
1027 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
1028 ListHead = &MessageQueue->PostedMessagesListHead;
1029 while (CurrentEntry != ListHead)
1030 {
1031 PostedMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1032 ListEntry);
1033 if (PostedMessage->Msg.hwnd == Window->hSelf)
1034 {
1035 RemoveEntryList(&PostedMessage->ListEntry);
1036 MsqDestroyMessage(PostedMessage);
1037 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
1038 }
1039 else
1040 {
1041 CurrentEntry = CurrentEntry->Flink;
1042 }
1043 }
1044
1045 /* remove the sent messages for this window */
1046 CurrentEntry = MessageQueue->SentMessagesListHead.Flink;
1047 ListHead = &MessageQueue->SentMessagesListHead;
1048 while (CurrentEntry != ListHead)
1049 {
1050 SentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1051 ListEntry);
1052 if(SentMessage->Msg.hwnd == Window->hSelf)
1053 {
1054 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1055
1056 RemoveEntryList(&SentMessage->ListEntry);
1057
1058 /* remove the message from the dispatching list */
1059 if(SentMessage->DispatchingListEntry.Flink != NULL)
1060 {
1061 RemoveEntryList(&SentMessage->DispatchingListEntry);
1062 }
1063
1064 /* wake the sender's thread */
1065 if (SentMessage->CompletionEvent != NULL)
1066 {
1067 KeSetEvent(SentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1068 }
1069
1070 /* dereference our and the sender's message queue */
1071 IntDereferenceMessageQueue(MessageQueue);
1072 IntDereferenceMessageQueue(SentMessage->SenderQueue);
1073
1074 /* free the message */
1075 ExFreePool(SentMessage);
1076
1077 CurrentEntry = MessageQueue->SentMessagesListHead.Flink;
1078 }
1079 else
1080 {
1081 CurrentEntry = CurrentEntry->Flink;
1082 }
1083 }
1084 }
1085
1086 VOID FASTCALL
1087 MsqSendNotifyMessage(PUSER_MESSAGE_QUEUE MessageQueue,
1088 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage)
1089 {
1090 InsertTailList(&MessageQueue->NotifyMessagesListHead,
1091 &NotifyMessage->ListEntry);
1092 MessageQueue->QueueBits |= QS_SENDMESSAGE;
1093 MessageQueue->ChangedBits |= QS_SENDMESSAGE;
1094 if (MessageQueue->WakeMask & QS_SENDMESSAGE)
1095 KeSetEvent(MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
1096 }
1097
1098 NTSTATUS FASTCALL
1099 co_MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue,
1100 HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam,
1101 UINT uTimeout, BOOL Block, INT HookMessage,
1102 ULONG_PTR *uResult)
1103 {
1104 PTHREADINFO pti;
1105 PUSER_SENT_MESSAGE Message;
1106 KEVENT CompletionEvent;
1107 NTSTATUS WaitStatus;
1108 LRESULT Result;
1109 PUSER_MESSAGE_QUEUE ThreadQueue;
1110 LARGE_INTEGER Timeout;
1111 PLIST_ENTRY Entry;
1112
1113 if(!(Message = ExAllocatePoolWithTag(PagedPool, sizeof(USER_SENT_MESSAGE), TAG_USRMSG)))
1114 {
1115 DPRINT1("MsqSendMessage(): Not enough memory to allocate a message");
1116 return STATUS_INSUFFICIENT_RESOURCES;
1117 }
1118
1119 KeInitializeEvent(&CompletionEvent, NotificationEvent, FALSE);
1120
1121 pti = PsGetCurrentThreadWin32Thread();
1122 ThreadQueue = pti->MessageQueue;
1123 ASSERT(ThreadQueue != MessageQueue);
1124
1125 Timeout.QuadPart = (LONGLONG) uTimeout * (LONGLONG) -10000;
1126
1127 /* FIXME - increase reference counter of sender's message queue here */
1128
1129 Result = 0;
1130 Message->Msg.hwnd = Wnd;
1131 Message->Msg.message = Msg;
1132 Message->Msg.wParam = wParam;
1133 Message->Msg.lParam = lParam;
1134 Message->CompletionEvent = &CompletionEvent;
1135 Message->Result = &Result;
1136 Message->SenderQueue = ThreadQueue;
1137 IntReferenceMessageQueue(ThreadQueue);
1138 Message->CompletionCallback = NULL;
1139 Message->HookMessage = HookMessage;
1140
1141 IntReferenceMessageQueue(MessageQueue);
1142
1143 /* add it to the list of pending messages */
1144 InsertTailList(&ThreadQueue->DispatchingMessagesHead, &Message->DispatchingListEntry);
1145
1146 /* queue it in the destination's message queue */
1147 InsertTailList(&MessageQueue->SentMessagesListHead, &Message->ListEntry);
1148
1149 MessageQueue->QueueBits |= QS_SENDMESSAGE;
1150 MessageQueue->ChangedBits |= QS_SENDMESSAGE;
1151 if (MessageQueue->WakeMask & QS_SENDMESSAGE)
1152 KeSetEvent(MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
1153
1154 /* we can't access the Message anymore since it could have already been deleted! */
1155
1156 if(Block)
1157 {
1158 IdlePing();
1159
1160 UserLeaveCo();
1161
1162 /* don't process messages sent to the thread */
1163 WaitStatus = KeWaitForSingleObject(&CompletionEvent, UserRequest, UserMode,
1164 FALSE, (uTimeout ? &Timeout : NULL));
1165
1166 UserEnterCo();
1167
1168 if(WaitStatus == STATUS_TIMEOUT)
1169 {
1170 /* look up if the message has not yet dispatched, if so
1171 make sure it can't pass a result and it must not set the completion event anymore */
1172 Entry = MessageQueue->SentMessagesListHead.Flink;
1173 while (Entry != &MessageQueue->SentMessagesListHead)
1174 {
1175 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry)
1176 == Message)
1177 {
1178 /* we can access Message here, it's secure because the message queue is locked
1179 and the message is still hasn't been dispatched */
1180 Message->CompletionEvent = NULL;
1181 Message->Result = NULL;
1182 break;
1183 }
1184 Entry = Entry->Flink;
1185 }
1186
1187 /* remove from the local dispatching list so the other thread knows,
1188 it can't pass a result and it must not set the completion event anymore */
1189 Entry = ThreadQueue->DispatchingMessagesHead.Flink;
1190 while (Entry != &ThreadQueue->DispatchingMessagesHead)
1191 {
1192 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, DispatchingListEntry)
1193 == Message)
1194 {
1195 /* we can access Message here, it's secure because the sender's message is locked
1196 and the message has definitely not yet been destroyed, otherwise it would
1197 have been removed from this list by the dispatching routine right after
1198 dispatching the message */
1199 Message->CompletionEvent = NULL;
1200 Message->Result = NULL;
1201 RemoveEntryList(&Message->DispatchingListEntry);
1202 Message->DispatchingListEntry.Flink = NULL;
1203 break;
1204 }
1205 Entry = Entry->Flink;
1206 }
1207
1208 DPRINT("MsqSendMessage (blocked) timed out\n");
1209 }
1210 while (co_MsqDispatchOneSentMessage(ThreadQueue))
1211 ;
1212 }
1213 else
1214 {
1215 PVOID WaitObjects[2];
1216
1217 WaitObjects[0] = &CompletionEvent;
1218 WaitObjects[1] = ThreadQueue->NewMessages;
1219 do
1220 {
1221 IdlePing();
1222
1223 UserLeaveCo();
1224
1225 WaitStatus = KeWaitForMultipleObjects(2, WaitObjects, WaitAny, UserRequest,
1226 UserMode, FALSE, (uTimeout ? &Timeout : NULL), NULL);
1227
1228 UserEnterCo();
1229
1230 if(WaitStatus == STATUS_TIMEOUT)
1231 {
1232 /* look up if the message has not yet been dispatched, if so
1233 make sure it can't pass a result and it must not set the completion event anymore */
1234 Entry = MessageQueue->SentMessagesListHead.Flink;
1235 while (Entry != &MessageQueue->SentMessagesListHead)
1236 {
1237 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry)
1238 == Message)
1239 {
1240 /* we can access Message here, it's secure because the message queue is locked
1241 and the message is still hasn't been dispatched */
1242 Message->CompletionEvent = NULL;
1243 Message->Result = NULL;
1244 break;
1245 }
1246 Entry = Entry->Flink;
1247 }
1248
1249 /* remove from the local dispatching list so the other thread knows,
1250 it can't pass a result and it must not set the completion event anymore */
1251 Entry = ThreadQueue->DispatchingMessagesHead.Flink;
1252 while (Entry != &ThreadQueue->DispatchingMessagesHead)
1253 {
1254 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, DispatchingListEntry)
1255 == Message)
1256 {
1257 /* we can access Message here, it's secure because the sender's message is locked
1258 and the message has definitely not yet been destroyed, otherwise it would
1259 have been removed from this list by the dispatching routine right after
1260 dispatching the message */
1261 Message->CompletionEvent = NULL;
1262 Message->Result = NULL;
1263 RemoveEntryList(&Message->DispatchingListEntry);
1264 Message->DispatchingListEntry.Flink = NULL;
1265 break;
1266 }
1267 Entry = Entry->Flink;
1268 }
1269
1270 DPRINT("MsqSendMessage timed out\n");
1271 break;
1272 }
1273 while (co_MsqDispatchOneSentMessage(ThreadQueue))
1274 ;
1275 }
1276 while (NT_SUCCESS(WaitStatus) && STATUS_WAIT_0 != WaitStatus);
1277 }
1278
1279 if(WaitStatus != STATUS_TIMEOUT)
1280 *uResult = (STATUS_WAIT_0 == WaitStatus ? Result : -1);
1281
1282 return WaitStatus;
1283 }
1284
1285 VOID FASTCALL
1286 MsqPostMessage(PUSER_MESSAGE_QUEUE MessageQueue, MSG* Msg, BOOLEAN FreeLParam,
1287 DWORD MessageBits)
1288 {
1289 PUSER_MESSAGE Message;
1290
1291 if(!(Message = MsqCreateMessage(Msg, FreeLParam)))
1292 {
1293 return;
1294 }
1295 InsertTailList(&MessageQueue->PostedMessagesListHead,
1296 &Message->ListEntry);
1297 MessageQueue->QueueBits |= MessageBits;
1298 MessageQueue->ChangedBits |= MessageBits;
1299 if (MessageQueue->WakeMask & MessageBits)
1300 KeSetEvent(MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
1301 }
1302
1303 VOID FASTCALL
1304 MsqPostQuitMessage(PUSER_MESSAGE_QUEUE MessageQueue, ULONG ExitCode)
1305 {
1306 MessageQueue->QuitPosted = TRUE;
1307 MessageQueue->QuitExitCode = ExitCode;
1308 MessageQueue->QueueBits |= QS_POSTMESSAGE;
1309 MessageQueue->ChangedBits |= QS_POSTMESSAGE;
1310 if (MessageQueue->WakeMask & QS_POSTMESSAGE)
1311 KeSetEvent(MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
1312 }
1313
1314 BOOLEAN STDCALL
1315 co_MsqFindMessage(IN PUSER_MESSAGE_QUEUE MessageQueue,
1316 IN BOOLEAN Hardware,
1317 IN BOOLEAN Remove,
1318 IN HWND Wnd,
1319 IN UINT MsgFilterLow,
1320 IN UINT MsgFilterHigh,
1321 OUT PUSER_MESSAGE* Message)
1322 {
1323 PLIST_ENTRY CurrentEntry;
1324 PUSER_MESSAGE CurrentMessage;
1325 PLIST_ENTRY ListHead;
1326
1327 if (Hardware)
1328 {
1329 return(co_MsqPeekHardwareMessage(MessageQueue, Wnd,
1330 MsgFilterLow, MsgFilterHigh,
1331 Remove, Message));
1332 }
1333
1334 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
1335 ListHead = &MessageQueue->PostedMessagesListHead;
1336 while (CurrentEntry != ListHead)
1337 {
1338 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1339 ListEntry);
1340 if ((Wnd == 0 || Wnd == CurrentMessage->Msg.hwnd) &&
1341 ((MsgFilterLow == 0 && MsgFilterHigh == 0) ||
1342 (MsgFilterLow <= CurrentMessage->Msg.message &&
1343 MsgFilterHigh >= CurrentMessage->Msg.message)))
1344 {
1345 if (Remove)
1346 {
1347 RemoveEntryList(&CurrentMessage->ListEntry);
1348 }
1349
1350 *Message = CurrentMessage;
1351 return(TRUE);
1352 }
1353 CurrentEntry = CurrentEntry->Flink;
1354 }
1355
1356 return(FALSE);
1357 }
1358
1359 NTSTATUS FASTCALL
1360 co_MsqWaitForNewMessages(PUSER_MESSAGE_QUEUE MessageQueue, HWND WndFilter,
1361 UINT MsgFilterMin, UINT MsgFilterMax)
1362 {
1363 PVOID WaitObjects[2] = {MessageQueue->NewMessages, &HardwareMessageEvent};
1364 LARGE_INTEGER TimerExpiry;
1365 PLARGE_INTEGER Timeout;
1366 NTSTATUS ret;
1367
1368 if (MsqGetFirstTimerExpiry(MessageQueue, WndFilter, MsgFilterMin, MsgFilterMax, &TimerExpiry))
1369 {
1370 Timeout = &TimerExpiry;
1371 }
1372 else
1373 {
1374 Timeout = NULL;
1375 }
1376
1377 IdlePing(); // Going to wait so send Idle ping.
1378
1379 UserLeaveCo();
1380
1381 ret = KeWaitForMultipleObjects(2,
1382 WaitObjects,
1383 WaitAny,
1384 Executive,
1385 UserMode,
1386 FALSE,
1387 Timeout,
1388 NULL);
1389
1390 UserEnterCo();
1391
1392 return ret;
1393 }
1394
1395 BOOL FASTCALL
1396 MsqIsHung(PUSER_MESSAGE_QUEUE MessageQueue)
1397 {
1398 LARGE_INTEGER LargeTickCount;
1399
1400 KeQueryTickCount(&LargeTickCount);
1401 return ((LargeTickCount.u.LowPart - MessageQueue->LastMsgRead) > MSQ_HUNG);
1402 }
1403
1404 BOOLEAN FASTCALL
1405 MsqInitializeMessageQueue(struct _ETHREAD *Thread, PUSER_MESSAGE_QUEUE MessageQueue)
1406 {
1407 LARGE_INTEGER LargeTickCount;
1408 NTSTATUS Status;
1409
1410 MessageQueue->Thread = Thread;
1411 MessageQueue->CaretInfo = (PTHRDCARETINFO)(MessageQueue + 1);
1412 InitializeListHead(&MessageQueue->PostedMessagesListHead);
1413 InitializeListHead(&MessageQueue->SentMessagesListHead);
1414 InitializeListHead(&MessageQueue->HardwareMessagesListHead);
1415 InitializeListHead(&MessageQueue->TimerListHead);
1416 InitializeListHead(&MessageQueue->DispatchingMessagesHead);
1417 InitializeListHead(&MessageQueue->LocalDispatchingMessagesHead);
1418 KeInitializeMutex(&MessageQueue->HardwareLock, 0);
1419 MessageQueue->QuitPosted = FALSE;
1420 MessageQueue->QuitExitCode = 0;
1421 KeQueryTickCount(&LargeTickCount);
1422 MessageQueue->LastMsgRead = LargeTickCount.u.LowPart;
1423 MessageQueue->FocusWindow = NULL;
1424 MessageQueue->PaintCount = 0;
1425 MessageQueue->WakeMask = ~0;
1426 MessageQueue->NewMessagesHandle = NULL;
1427
1428 Status = ZwCreateEvent(&MessageQueue->NewMessagesHandle, EVENT_ALL_ACCESS,
1429 NULL, SynchronizationEvent, FALSE);
1430 if (!NT_SUCCESS(Status))
1431 {
1432 return FALSE;
1433 }
1434
1435 Status = ObReferenceObjectByHandle(MessageQueue->NewMessagesHandle, 0,
1436 ExEventObjectType, KernelMode,
1437 (PVOID*)&MessageQueue->NewMessages, NULL);
1438 if (!NT_SUCCESS(Status))
1439 {
1440 ZwClose(MessageQueue->NewMessagesHandle);
1441 MessageQueue->NewMessagesHandle = NULL;
1442 return FALSE;
1443 }
1444
1445 return TRUE;
1446 }
1447
1448 VOID FASTCALL
1449 MsqCleanupMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
1450 {
1451 PLIST_ENTRY CurrentEntry;
1452 PUSER_MESSAGE CurrentMessage;
1453 PTIMER_ENTRY CurrentTimer;
1454 PUSER_SENT_MESSAGE CurrentSentMessage;
1455
1456 /* cleanup posted messages */
1457 while (!IsListEmpty(&MessageQueue->PostedMessagesListHead))
1458 {
1459 CurrentEntry = RemoveHeadList(&MessageQueue->PostedMessagesListHead);
1460 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1461 ListEntry);
1462 MsqDestroyMessage(CurrentMessage);
1463 }
1464
1465 /* remove the messages that have not yet been dispatched */
1466 while (!IsListEmpty(&MessageQueue->SentMessagesListHead))
1467 {
1468 CurrentEntry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
1469 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1470 ListEntry);
1471
1472 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1473
1474 /* remove the message from the dispatching list */
1475 if(CurrentSentMessage->DispatchingListEntry.Flink != NULL)
1476 {
1477 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
1478 }
1479
1480 /* wake the sender's thread */
1481 if (CurrentSentMessage->CompletionEvent != NULL)
1482 {
1483 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1484 }
1485
1486 /* dereference our and the sender's message queue */
1487 IntDereferenceMessageQueue(MessageQueue);
1488 IntDereferenceMessageQueue(CurrentSentMessage->SenderQueue);
1489
1490 /* free the message */
1491 ExFreePool(CurrentSentMessage);
1492 }
1493
1494 /* cleanup timers */
1495 while (! IsListEmpty(&MessageQueue->TimerListHead))
1496 {
1497 CurrentEntry = RemoveHeadList(&MessageQueue->TimerListHead);
1498 CurrentTimer = CONTAINING_RECORD(CurrentEntry, TIMER_ENTRY, ListEntry);
1499 ExFreeToPagedLookasideList(&TimerLookasideList, CurrentTimer);
1500 }
1501
1502 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
1503 ExitThread() was called in a SendMessage() umode callback */
1504 while (!IsListEmpty(&MessageQueue->LocalDispatchingMessagesHead))
1505 {
1506 CurrentEntry = RemoveHeadList(&MessageQueue->LocalDispatchingMessagesHead);
1507 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1508 ListEntry);
1509
1510 /* remove the message from the dispatching list */
1511 if(CurrentSentMessage->DispatchingListEntry.Flink != NULL)
1512 {
1513 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
1514 }
1515
1516 DPRINT("Notify the sender, the thread has been terminated while dispatching a message!\n");
1517
1518 /* wake the sender's thread */
1519 if (CurrentSentMessage->CompletionEvent != NULL)
1520 {
1521 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1522 }
1523
1524 /* dereference our and the sender's message queue */
1525 IntDereferenceMessageQueue(MessageQueue);
1526 IntDereferenceMessageQueue(CurrentSentMessage->SenderQueue);
1527
1528 /* free the message */
1529 ExFreePool(CurrentSentMessage);
1530 }
1531
1532 /* tell other threads not to bother returning any info to us */
1533 while (! IsListEmpty(&MessageQueue->DispatchingMessagesHead))
1534 {
1535 CurrentEntry = RemoveHeadList(&MessageQueue->DispatchingMessagesHead);
1536 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1537 DispatchingListEntry);
1538 CurrentSentMessage->CompletionEvent = NULL;
1539 CurrentSentMessage->Result = NULL;
1540
1541 /* do NOT dereference our message queue as it might get attempted to be
1542 locked later */
1543 }
1544
1545 }
1546
1547 PUSER_MESSAGE_QUEUE FASTCALL
1548 MsqCreateMessageQueue(struct _ETHREAD *Thread)
1549 {
1550 PUSER_MESSAGE_QUEUE MessageQueue;
1551
1552 MessageQueue = (PUSER_MESSAGE_QUEUE)ExAllocatePoolWithTag(PagedPool,
1553 sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO),
1554 TAG_MSGQ);
1555
1556 if (!MessageQueue)
1557 {
1558 return NULL;
1559 }
1560
1561 RtlZeroMemory(MessageQueue, sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO));
1562 /* hold at least one reference until it'll be destroyed */
1563 IntReferenceMessageQueue(MessageQueue);
1564 /* initialize the queue */
1565 if (!MsqInitializeMessageQueue(Thread, MessageQueue))
1566 {
1567 IntDereferenceMessageQueue(MessageQueue);
1568 return NULL;
1569 }
1570
1571 return MessageQueue;
1572 }
1573
1574 VOID FASTCALL
1575 MsqDestroyMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
1576 {
1577 PDESKTOP desk;
1578
1579 /* remove the message queue from any desktops */
1580 if ((desk = InterlockedExchangePointer((PVOID*)&MessageQueue->Desktop, 0)))
1581 {
1582 (void)InterlockedExchangePointer((PVOID*)&desk->ActiveMessageQueue, 0);
1583 IntDereferenceMessageQueue(MessageQueue);
1584 }
1585
1586 /* clean it up */
1587 MsqCleanupMessageQueue(MessageQueue);
1588
1589 /* decrease the reference counter, if it hits zero, the queue will be freed */
1590 IntDereferenceMessageQueue(MessageQueue);
1591 }
1592
1593 PHOOKTABLE FASTCALL
1594 MsqGetHooks(PUSER_MESSAGE_QUEUE Queue)
1595 {
1596 return Queue->Hooks;
1597 }
1598
1599 VOID FASTCALL
1600 MsqSetHooks(PUSER_MESSAGE_QUEUE Queue, PHOOKTABLE Hooks)
1601 {
1602 Queue->Hooks = Hooks;
1603 }
1604
1605 LPARAM FASTCALL
1606 MsqSetMessageExtraInfo(LPARAM lParam)
1607 {
1608 LPARAM Ret;
1609 PTHREADINFO pti;
1610 PUSER_MESSAGE_QUEUE MessageQueue;
1611
1612 pti = PsGetCurrentThreadWin32Thread();
1613 MessageQueue = pti->MessageQueue;
1614 if(!MessageQueue)
1615 {
1616 return 0;
1617 }
1618
1619 Ret = MessageQueue->ExtraInfo;
1620 MessageQueue->ExtraInfo = lParam;
1621
1622 return Ret;
1623 }
1624
1625 LPARAM FASTCALL
1626 MsqGetMessageExtraInfo(VOID)
1627 {
1628 PTHREADINFO pti;
1629 PUSER_MESSAGE_QUEUE MessageQueue;
1630
1631 pti = PsGetCurrentThreadWin32Thread();
1632 MessageQueue = pti->MessageQueue;
1633 if(!MessageQueue)
1634 {
1635 return 0;
1636 }
1637
1638 return MessageQueue->ExtraInfo;
1639 }
1640
1641 HWND FASTCALL
1642 MsqSetStateWindow(PUSER_MESSAGE_QUEUE MessageQueue, ULONG Type, HWND hWnd)
1643 {
1644 HWND Prev;
1645
1646 switch(Type)
1647 {
1648 case MSQ_STATE_CAPTURE:
1649 Prev = MessageQueue->CaptureWindow;
1650 MessageQueue->CaptureWindow = hWnd;
1651 return Prev;
1652 case MSQ_STATE_ACTIVE:
1653 Prev = MessageQueue->ActiveWindow;
1654 MessageQueue->ActiveWindow = hWnd;
1655 return Prev;
1656 case MSQ_STATE_FOCUS:
1657 Prev = MessageQueue->FocusWindow;
1658 MessageQueue->FocusWindow = hWnd;
1659 return Prev;
1660 case MSQ_STATE_MENUOWNER:
1661 Prev = MessageQueue->MenuOwner;
1662 MessageQueue->MenuOwner = hWnd;
1663 return Prev;
1664 case MSQ_STATE_MOVESIZE:
1665 Prev = MessageQueue->MoveSize;
1666 MessageQueue->MoveSize = hWnd;
1667 return Prev;
1668 case MSQ_STATE_CARET:
1669 ASSERT(MessageQueue->CaretInfo);
1670 Prev = MessageQueue->CaretInfo->hWnd;
1671 MessageQueue->CaretInfo->hWnd = hWnd;
1672 return Prev;
1673 }
1674
1675 return NULL;
1676 }
1677
1678 #ifndef NDEBUG
1679 static VOID FASTCALL
1680 DumpTimerList(PUSER_MESSAGE_QUEUE MessageQueue)
1681 {
1682 PLIST_ENTRY Current;
1683 PTIMER_ENTRY Timer;
1684
1685 Current = MessageQueue->TimerListHead.Flink;
1686 if (Current == &MessageQueue->TimerListHead)
1687 {
1688 DPRINT("timer list is empty for queue %p\n", MessageQueue);
1689 }
1690 while (Current != &MessageQueue->TimerListHead)
1691 {
1692 Timer = CONTAINING_RECORD(Current, TIMER_ENTRY, ListEntry);
1693 DPRINT("queue %p timer %p expiry %I64d wnd %x id %p period %u timerproc %p msg %u\n",
1694 MessageQueue, Timer, Timer->ExpiryTime.QuadPart, Timer->Wnd, Timer->IDEvent,
1695 Timer->Period, Timer->TimerFunc, Timer->Msg);
1696 Current = Current->Flink;
1697 }
1698 }
1699 #endif /* ! defined(NDEBUG) */
1700
1701 /* Must have the message queue locked while calling this */
1702 static VOID FASTCALL
1703 InsertTimer(PUSER_MESSAGE_QUEUE MessageQueue, PTIMER_ENTRY NewTimer)
1704 {
1705 PLIST_ENTRY Current;
1706
1707 Current = MessageQueue->TimerListHead.Flink;
1708 while (Current != &MessageQueue->TimerListHead)
1709 {
1710 if (NewTimer->ExpiryTime.QuadPart <
1711 CONTAINING_RECORD(Current, TIMER_ENTRY, ListEntry)->ExpiryTime.QuadPart)
1712 {
1713 break;
1714 }
1715 Current = Current->Flink;
1716 }
1717
1718 InsertTailList(Current, &NewTimer->ListEntry);
1719 }
1720
1721 /* Must have the message queue locked while calling this */
1722 static PTIMER_ENTRY FASTCALL
1723 RemoveTimer(PUSER_MESSAGE_QUEUE MessageQueue, HWND Wnd, UINT_PTR IDEvent, UINT Msg)
1724 {
1725 PTIMER_ENTRY Timer;
1726 PLIST_ENTRY EnumEntry;
1727
1728 /* Remove timer if already in the queue */
1729 EnumEntry = MessageQueue->TimerListHead.Flink;
1730 while (EnumEntry != &MessageQueue->TimerListHead)
1731 {
1732 Timer = CONTAINING_RECORD(EnumEntry, TIMER_ENTRY, ListEntry);
1733 EnumEntry = EnumEntry->Flink;
1734
1735 if (Timer->Wnd == Wnd &&
1736 Timer->IDEvent == IDEvent &&
1737 Timer->Msg == Msg)
1738 {
1739 RemoveEntryList(&Timer->ListEntry);
1740 return Timer;
1741 }
1742 }
1743
1744 return NULL;
1745 }
1746
1747 BOOLEAN FASTCALL
1748 MsqSetTimer(PUSER_MESSAGE_QUEUE MessageQueue, HWND Wnd,
1749 UINT_PTR IDEvent, UINT Period, TIMERPROC TimerFunc,
1750 UINT Msg)
1751 {
1752 PTIMER_ENTRY Timer;
1753 LARGE_INTEGER CurrentTime;
1754
1755 DPRINT("MsqSetTimer queue %p wnd %x id %p period %u timerproc %p msg %d\n",
1756 MessageQueue, Wnd, IDEvent, Period, TimerFunc, Msg);
1757
1758 Timer = RemoveTimer(MessageQueue, Wnd, IDEvent, Msg);
1759 if (NULL == Timer)
1760 {
1761 Timer = ExAllocateFromPagedLookasideList(&TimerLookasideList);
1762 if (NULL == Timer)
1763 {
1764 DPRINT1("Failed to allocate timer entry\n");
1765 return FALSE;
1766 }
1767 DPRINT("Allocated new timer entry %p\n", Timer);
1768 Timer->Wnd = Wnd;
1769 Timer->IDEvent = IDEvent;
1770 Timer->Msg = Msg;
1771 }
1772 else
1773 {
1774 DPRINT("Updating existing timer entry %p\n", Timer);
1775 }
1776
1777 KeQuerySystemTime(&CurrentTime);
1778 Timer->ExpiryTime.QuadPart = CurrentTime.QuadPart +
1779 (ULONGLONG) Period * (ULONGLONG) 10000;
1780 Timer->Period = Period;
1781 Timer->TimerFunc = TimerFunc;
1782 DPRINT("Insert timer now %I64d expiry %I64d\n", CurrentTime.QuadPart,
1783 Timer->ExpiryTime.QuadPart);
1784
1785 InsertTimer(MessageQueue, Timer);
1786
1787 #ifndef NDEBUG
1788
1789 DumpTimerList(MessageQueue);
1790 #endif /* ! defined(NDEBUG) */
1791
1792 return TRUE;
1793 }
1794
1795 BOOLEAN FASTCALL
1796 MsqKillTimer(PUSER_MESSAGE_QUEUE MessageQueue, HWND Wnd,
1797 UINT_PTR IDEvent, UINT Msg)
1798 {
1799 PTIMER_ENTRY Timer;
1800
1801 DPRINT("MsqKillTimer queue %p wnd %x id %p msg %d\n",
1802 MessageQueue, Wnd, IDEvent, Msg);
1803
1804 Timer = RemoveTimer(MessageQueue, Wnd, IDEvent, Msg);
1805
1806 if (NULL == Timer)
1807 {
1808 DPRINT("Failed to remove timer from list, not found\n");
1809 }
1810 else
1811 {
1812 ExFreeToPagedLookasideList(&TimerLookasideList, Timer);
1813 }
1814
1815 #ifndef NDEBUG
1816 DumpTimerList(MessageQueue);
1817 #endif /* ! defined(NDEBUG) */
1818
1819 return NULL != Timer;
1820 }
1821
1822 BOOLEAN FASTCALL
1823 MsqGetTimerMessage(PUSER_MESSAGE_QUEUE MessageQueue,
1824 HWND WndFilter, UINT MsgFilterMin, UINT MsgFilterMax,
1825 MSG *Msg, BOOLEAN Restart)
1826 {
1827 PTIMER_ENTRY Timer;
1828 LARGE_INTEGER CurrentTime;
1829 LARGE_INTEGER LargeTickCount;
1830 PLIST_ENTRY EnumEntry;
1831 BOOLEAN GotMessage;
1832 PTHREADINFO pti;
1833
1834 DPRINT("MsqGetTimerMessage queue %p msg %p restart %s\n",
1835 MessageQueue, Msg, Restart ? "TRUE" : "FALSE");
1836
1837 KeQuerySystemTime(&CurrentTime);
1838 DPRINT("Current time %I64d\n", CurrentTime.QuadPart);
1839 EnumEntry = MessageQueue->TimerListHead.Flink;
1840 GotMessage = FALSE;
1841 while (EnumEntry != &MessageQueue->TimerListHead)
1842 {
1843 Timer = CONTAINING_RECORD(MessageQueue->TimerListHead.Flink,
1844 TIMER_ENTRY, ListEntry);
1845 DPRINT("Checking timer %p wnd %x expiry %I64d\n", Timer, Timer->Wnd,
1846 Timer->ExpiryTime.QuadPart);
1847 EnumEntry = EnumEntry->Flink;
1848 if ((NULL == WndFilter || Timer->Wnd == WndFilter) &&
1849 ((MsgFilterMin == 0 && MsgFilterMax == 0) ||
1850 (MsgFilterMin <= Timer->Msg &&
1851 Timer->Msg <= MsgFilterMax)))
1852 {
1853 if (Timer->ExpiryTime.QuadPart <= CurrentTime.QuadPart)
1854 {
1855 DPRINT("Timer is expired\n");
1856 GotMessage = TRUE;
1857 break;
1858 }
1859 else
1860 {
1861 DPRINT("No need to check later timers\n");
1862 break;
1863 }
1864 }
1865 else
1866 {
1867 DPRINT("timer %p (wnd %x msg %d) failed filter wnd %x msgmin %d msgmax %d\n",
1868 Timer, Timer->Wnd, Timer->Msg, WndFilter, MsgFilterMin, MsgFilterMax);
1869 }
1870 }
1871
1872 if (! GotMessage)
1873 {
1874 DPRINT("No timer pending\n");
1875 return FALSE;
1876 }
1877
1878 Msg->hwnd = Timer->Wnd;
1879 Msg->message = Timer->Msg;
1880 Msg->wParam = (WPARAM) Timer->IDEvent;
1881 Msg->lParam = (LPARAM) Timer->TimerFunc;
1882 KeQueryTickCount(&LargeTickCount);
1883 Msg->time = MsqCalculateMessageTime(&LargeTickCount);
1884 pti = PsGetCurrentThreadWin32Thread();
1885 IntGetCursorLocation(pti->Desktop->WindowStation,
1886 &Msg->pt);
1887
1888 if (Restart)
1889 {
1890 RemoveEntryList(&Timer->ListEntry);
1891 Timer->ExpiryTime.QuadPart = CurrentTime.QuadPart +
1892 (ULONGLONG) Timer->Period * (ULONGLONG) 10000;
1893 DPRINT("Restarting timer %p expires %I64d\n", Timer, Timer->ExpiryTime.QuadPart);
1894 InsertTimer(MessageQueue, Timer);
1895
1896 #ifndef NDEBUG
1897
1898 DumpTimerList(MessageQueue);
1899 #endif /* ! defined(NDEBUG) */
1900
1901 }
1902
1903 DPRINT("Created message wnd %x msg %d wParam %u lParam %u\n", Msg->hwnd, Msg->message,
1904 Msg->wParam, Msg->lParam);
1905
1906 return TRUE;
1907 }
1908
1909 VOID FASTCALL
1910 MsqRemoveTimersWindow(PUSER_MESSAGE_QUEUE MessageQueue, HWND Wnd)
1911 {
1912 PTIMER_ENTRY Timer;
1913 PLIST_ENTRY EnumEntry;
1914
1915 DPRINT("MsqRemoveTimersWindow queue %p wnd %x\n", MessageQueue, Wnd);
1916
1917 EnumEntry = MessageQueue->TimerListHead.Flink;
1918 while (EnumEntry != &MessageQueue->TimerListHead)
1919 {
1920 Timer = CONTAINING_RECORD(EnumEntry, TIMER_ENTRY, ListEntry);
1921 EnumEntry = EnumEntry->Flink;
1922 if (Timer->Wnd == Wnd)
1923 {
1924 DPRINT("Removing timer %p because its window is going away\n", Timer);
1925 RemoveEntryList(&Timer->ListEntry);
1926 ExFreeToPagedLookasideList(&TimerLookasideList, Timer);
1927 }
1928 }
1929
1930 #ifndef NDEBUG
1931 DumpTimerList(MessageQueue);
1932 #endif /* ! defined(NDEBUG) */
1933
1934 }
1935
1936 BOOLEAN FASTCALL
1937 MsqGetFirstTimerExpiry(PUSER_MESSAGE_QUEUE MessageQueue,
1938 HWND WndFilter, UINT MsgFilterMin, UINT MsgFilterMax,
1939 PLARGE_INTEGER FirstTimerExpiry)
1940 {
1941 PTIMER_ENTRY Timer;
1942 PLIST_ENTRY EnumEntry;
1943
1944 DPRINT("MsqGetFirstTimerExpiry queue %p wndfilter %x msgfiltermin %d msgfiltermax %d expiry %p\n",
1945 MessageQueue, WndFilter, MsgFilterMin, MsgFilterMax, FirstTimerExpiry);
1946
1947 EnumEntry = MessageQueue->TimerListHead.Flink;
1948 while (EnumEntry != &MessageQueue->TimerListHead)
1949 {
1950 Timer = CONTAINING_RECORD(MessageQueue->TimerListHead.Flink,
1951 TIMER_ENTRY, ListEntry);
1952 EnumEntry = EnumEntry->Flink;
1953 if ((NULL == WndFilter || Timer->Wnd == WndFilter) &&
1954 ((MsgFilterMin == 0 && MsgFilterMax == 0) ||
1955 (MsgFilterMin <= Timer->Msg &&
1956 Timer->Msg <= MsgFilterMax)))
1957 {
1958 *FirstTimerExpiry = Timer->ExpiryTime;
1959 DPRINT("First timer expires %I64d\n", Timer->ExpiryTime);
1960 return TRUE;
1961 }
1962 }
1963
1964 return FALSE;
1965 }
1966
1967 /* EOF */