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