[win32k]
[reactos.git] / reactos / subsystems / win32 / win32k / ntuser / msgqueue.c
1 /*
2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 /*
20 * COPYRIGHT: See COPYING in the top level directory
21 * PROJECT: ReactOS kernel
22 * PURPOSE: Message queues
23 * FILE: subsystems/win32/win32k/ntuser/msgqueue.c
24 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
25 * REVISION HISTORY:
26 * 06-06-2001 CSH Created
27 */
28
29 /* INCLUDES ******************************************************************/
30
31 #include <win32k.h>
32
33 #define NDEBUG
34 #include <debug.h>
35
36 /* GLOBALS *******************************************************************/
37
38 #define SYSTEM_MESSAGE_QUEUE_SIZE (256)
39
40 static MSG SystemMessageQueue[SYSTEM_MESSAGE_QUEUE_SIZE];
41 static ULONG SystemMessageQueueHead = 0;
42 static ULONG SystemMessageQueueTail = 0;
43 static ULONG SystemMessageQueueCount = 0;
44 static KSPIN_LOCK SystemMessageQueueLock;
45
46 static ULONG volatile HardwareMessageQueueStamp = 0;
47 static LIST_ENTRY HardwareMessageQueueHead;
48 static KMUTANT HardwareMessageQueueLock;
49
50 static KEVENT HardwareMessageEvent;
51
52 static PAGED_LOOKASIDE_LIST MessageLookasideList;
53 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 if needed, so lock the sender's message queue */
940 if (!(Message->HookMessage & MSQ_SENTNOWAIT))
941 {
942 SenderReturned = (Message->DispatchingListEntry.Flink == NULL);
943 if (!SenderReturned)
944 {
945 /* only remove it from the dispatching list if not already removed by a timeout */
946 RemoveEntryList(&Message->DispatchingListEntry);
947 }
948 }
949 /* still keep the sender's message queue locked, so the sender can't exit the
950 MsqSendMessage() function (if timed out) */
951
952 /* Let the sender know the result. */
953 if (Message->Result != NULL)
954 {
955 *Message->Result = Result;
956 }
957
958 if (Message->HasPackedLParam == TRUE)
959 {
960 if (Message->Msg.lParam)
961 ExFreePool((PVOID)Message->Msg.lParam);
962 }
963
964 /* Notify the sender. */
965 if (Message->CompletionEvent != NULL)
966 {
967 KeSetEvent(Message->CompletionEvent, IO_NO_INCREMENT, FALSE);
968 }
969
970 /* Call the callback if the message was sent with SendMessageCallback */
971 if (!SenderReturned && Message->CompletionCallback != NULL)
972 {
973 co_IntCallSentMessageCallback(Message->CompletionCallback,
974 Message->Msg.hwnd,
975 Message->Msg.message,
976 Message->CompletionCallbackContext,
977 Result);
978 }
979
980 /* Only if it is not a no wait message */
981 if (!(Message->HookMessage & MSQ_SENTNOWAIT))
982 {
983 IntDereferenceMessageQueue(Message->SenderQueue);
984 IntDereferenceMessageQueue(MessageQueue);
985 }
986
987 /* free the message */
988 ExFreePoolWithTag(Message, TAG_USRMSG);
989 return(TRUE);
990 }
991
992 VOID APIENTRY
993 MsqRemoveWindowMessagesFromQueue(PVOID pWindow)
994 {
995 PUSER_SENT_MESSAGE SentMessage;
996 PUSER_MESSAGE PostedMessage;
997 PUSER_MESSAGE_QUEUE MessageQueue;
998 PLIST_ENTRY CurrentEntry, ListHead;
999 PWINDOW_OBJECT Window = pWindow;
1000
1001 ASSERT(Window);
1002
1003 MessageQueue = Window->pti->MessageQueue;
1004 ASSERT(MessageQueue);
1005
1006 /* remove the posted messages for this window */
1007 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
1008 ListHead = &MessageQueue->PostedMessagesListHead;
1009 while (CurrentEntry != ListHead)
1010 {
1011 PostedMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1012 ListEntry);
1013 if (PostedMessage->Msg.hwnd == Window->hSelf)
1014 {
1015 RemoveEntryList(&PostedMessage->ListEntry);
1016 MsqDestroyMessage(PostedMessage);
1017 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
1018 }
1019 else
1020 {
1021 CurrentEntry = CurrentEntry->Flink;
1022 }
1023 }
1024
1025 /* remove the sent messages for this window */
1026 CurrentEntry = MessageQueue->SentMessagesListHead.Flink;
1027 ListHead = &MessageQueue->SentMessagesListHead;
1028 while (CurrentEntry != ListHead)
1029 {
1030 SentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1031 ListEntry);
1032 if(SentMessage->Msg.hwnd == Window->hSelf)
1033 {
1034 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1035
1036 RemoveEntryList(&SentMessage->ListEntry);
1037
1038 /* remove the message from the dispatching list if neede */
1039 if ((!(SentMessage->HookMessage & MSQ_SENTNOWAIT))
1040 && (SentMessage->DispatchingListEntry.Flink != NULL))
1041 {
1042 RemoveEntryList(&SentMessage->DispatchingListEntry);
1043 }
1044
1045 /* wake the sender's thread */
1046 if (SentMessage->CompletionEvent != NULL)
1047 {
1048 KeSetEvent(SentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1049 }
1050
1051 if (SentMessage->HasPackedLParam == TRUE)
1052 {
1053 if (SentMessage->Msg.lParam)
1054 ExFreePool((PVOID)SentMessage->Msg.lParam);
1055 }
1056
1057 /* Only if it is not a no wait message */
1058 if (!(SentMessage->HookMessage & MSQ_SENTNOWAIT))
1059 {
1060 /* dereference our and the sender's message queue */
1061 IntDereferenceMessageQueue(MessageQueue);
1062 IntDereferenceMessageQueue(SentMessage->SenderQueue);
1063 }
1064
1065 /* free the message */
1066 ExFreePoolWithTag(SentMessage, TAG_USRMSG);
1067
1068 CurrentEntry = MessageQueue->SentMessagesListHead.Flink;
1069 }
1070 else
1071 {
1072 CurrentEntry = CurrentEntry->Flink;
1073 }
1074 }
1075 }
1076
1077 VOID FASTCALL
1078 MsqSendNotifyMessage(PUSER_MESSAGE_QUEUE MessageQueue,
1079 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage)
1080 {
1081 InsertTailList(&MessageQueue->NotifyMessagesListHead,
1082 &NotifyMessage->ListEntry);
1083 MessageQueue->QueueBits |= QS_SENDMESSAGE;
1084 MessageQueue->ChangedBits |= QS_SENDMESSAGE;
1085 if (MessageQueue->WakeMask & QS_SENDMESSAGE)
1086 KeSetEvent(MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
1087 }
1088
1089 NTSTATUS FASTCALL
1090 co_MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue,
1091 HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam,
1092 UINT uTimeout, BOOL Block, INT HookMessage,
1093 ULONG_PTR *uResult)
1094 {
1095 PTHREADINFO pti;
1096 PUSER_SENT_MESSAGE Message;
1097 KEVENT CompletionEvent;
1098 NTSTATUS WaitStatus;
1099 LRESULT Result;
1100 PUSER_MESSAGE_QUEUE ThreadQueue;
1101 LARGE_INTEGER Timeout;
1102 PLIST_ENTRY Entry;
1103
1104 if(!(Message = ExAllocatePoolWithTag(PagedPool, sizeof(USER_SENT_MESSAGE), TAG_USRMSG)))
1105 {
1106 DPRINT1("MsqSendMessage(): Not enough memory to allocate a message");
1107 return STATUS_INSUFFICIENT_RESOURCES;
1108 }
1109
1110 KeInitializeEvent(&CompletionEvent, NotificationEvent, FALSE);
1111
1112 pti = PsGetCurrentThreadWin32Thread();
1113 ThreadQueue = pti->MessageQueue;
1114 ASSERT(ThreadQueue != MessageQueue);
1115
1116 Timeout.QuadPart = (LONGLONG) uTimeout * (LONGLONG) -10000;
1117
1118 /* FIXME - increase reference counter of sender's message queue here */
1119
1120 Result = 0;
1121 Message->Msg.hwnd = Wnd;
1122 Message->Msg.message = Msg;
1123 Message->Msg.wParam = wParam;
1124 Message->Msg.lParam = lParam;
1125 Message->CompletionEvent = &CompletionEvent;
1126 Message->Result = &Result;
1127 Message->SenderQueue = ThreadQueue;
1128 IntReferenceMessageQueue(ThreadQueue);
1129 Message->CompletionCallback = NULL;
1130 Message->HookMessage = HookMessage;
1131 Message->HasPackedLParam = FALSE;
1132
1133 IntReferenceMessageQueue(MessageQueue);
1134
1135 /* add it to the list of pending messages */
1136 InsertTailList(&ThreadQueue->DispatchingMessagesHead, &Message->DispatchingListEntry);
1137
1138 /* queue it in the destination's message queue */
1139 InsertTailList(&MessageQueue->SentMessagesListHead, &Message->ListEntry);
1140
1141 MessageQueue->QueueBits |= QS_SENDMESSAGE;
1142 MessageQueue->ChangedBits |= QS_SENDMESSAGE;
1143 if (MessageQueue->WakeMask & QS_SENDMESSAGE)
1144 KeSetEvent(MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
1145
1146 /* we can't access the Message anymore since it could have already been deleted! */
1147
1148 if(Block)
1149 {
1150 IdlePing();
1151
1152 UserLeaveCo();
1153
1154 /* don't process messages sent to the thread */
1155 WaitStatus = KeWaitForSingleObject(&CompletionEvent, UserRequest, UserMode,
1156 FALSE, (uTimeout ? &Timeout : NULL));
1157
1158 UserEnterCo();
1159
1160 if(WaitStatus == STATUS_TIMEOUT)
1161 {
1162 /* look up if the message has not yet dispatched, if so
1163 make sure it can't pass a result and it must not set the completion event anymore */
1164 Entry = MessageQueue->SentMessagesListHead.Flink;
1165 while (Entry != &MessageQueue->SentMessagesListHead)
1166 {
1167 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry)
1168 == Message)
1169 {
1170 /* we can access Message here, it's secure because the message queue is locked
1171 and the message is still hasn't been dispatched */
1172 Message->CompletionEvent = NULL;
1173 Message->Result = NULL;
1174 break;
1175 }
1176 Entry = Entry->Flink;
1177 }
1178
1179 /* remove from the local dispatching list so the other thread knows,
1180 it can't pass a result and it must not set the completion event anymore */
1181 Entry = ThreadQueue->DispatchingMessagesHead.Flink;
1182 while (Entry != &ThreadQueue->DispatchingMessagesHead)
1183 {
1184 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, DispatchingListEntry)
1185 == Message)
1186 {
1187 /* we can access Message here, it's secure because the sender's message is locked
1188 and the message has definitely not yet been destroyed, otherwise it would
1189 have been removed from this list by the dispatching routine right after
1190 dispatching the message */
1191 Message->CompletionEvent = NULL;
1192 Message->Result = NULL;
1193 RemoveEntryList(&Message->DispatchingListEntry);
1194 Message->DispatchingListEntry.Flink = NULL;
1195 break;
1196 }
1197 Entry = Entry->Flink;
1198 }
1199
1200 DPRINT("MsqSendMessage (blocked) timed out\n");
1201 }
1202 while (co_MsqDispatchOneSentMessage(ThreadQueue))
1203 ;
1204 }
1205 else
1206 {
1207 PVOID WaitObjects[2];
1208
1209 WaitObjects[0] = &CompletionEvent;
1210 WaitObjects[1] = ThreadQueue->NewMessages;
1211 do
1212 {
1213 IdlePing();
1214
1215 UserLeaveCo();
1216
1217 WaitStatus = KeWaitForMultipleObjects(2, WaitObjects, WaitAny, UserRequest,
1218 UserMode, FALSE, (uTimeout ? &Timeout : NULL), NULL);
1219
1220 UserEnterCo();
1221
1222 if(WaitStatus == STATUS_TIMEOUT)
1223 {
1224 /* look up if the message has not yet been dispatched, if so
1225 make sure it can't pass a result and it must not set the completion event anymore */
1226 Entry = MessageQueue->SentMessagesListHead.Flink;
1227 while (Entry != &MessageQueue->SentMessagesListHead)
1228 {
1229 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry)
1230 == Message)
1231 {
1232 /* we can access Message here, it's secure because the message queue is locked
1233 and the message is still hasn't been dispatched */
1234 Message->CompletionEvent = NULL;
1235 Message->Result = NULL;
1236 break;
1237 }
1238 Entry = Entry->Flink;
1239 }
1240
1241 /* remove from the local dispatching list so the other thread knows,
1242 it can't pass a result and it must not set the completion event anymore */
1243 Entry = ThreadQueue->DispatchingMessagesHead.Flink;
1244 while (Entry != &ThreadQueue->DispatchingMessagesHead)
1245 {
1246 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, DispatchingListEntry)
1247 == Message)
1248 {
1249 /* we can access Message here, it's secure because the sender's message is locked
1250 and the message has definitely not yet been destroyed, otherwise it would
1251 have been removed from this list by the dispatching routine right after
1252 dispatching the message */
1253 Message->CompletionEvent = NULL;
1254 Message->Result = NULL;
1255 RemoveEntryList(&Message->DispatchingListEntry);
1256 Message->DispatchingListEntry.Flink = NULL;
1257 break;
1258 }
1259 Entry = Entry->Flink;
1260 }
1261
1262 DPRINT("MsqSendMessage timed out\n");
1263 break;
1264 }
1265 while (co_MsqDispatchOneSentMessage(ThreadQueue))
1266 ;
1267 }
1268 while (NT_SUCCESS(WaitStatus) && STATUS_WAIT_0 != WaitStatus);
1269 }
1270
1271 if(WaitStatus != STATUS_TIMEOUT)
1272 *uResult = (STATUS_WAIT_0 == WaitStatus ? Result : -1);
1273
1274 return WaitStatus;
1275 }
1276
1277 VOID FASTCALL
1278 MsqPostMessage(PUSER_MESSAGE_QUEUE MessageQueue, MSG* Msg, BOOLEAN FreeLParam,
1279 DWORD MessageBits)
1280 {
1281 PUSER_MESSAGE Message;
1282
1283 if(!(Message = MsqCreateMessage(Msg, FreeLParam)))
1284 {
1285 return;
1286 }
1287 InsertTailList(&MessageQueue->PostedMessagesListHead,
1288 &Message->ListEntry);
1289 MessageQueue->QueueBits |= MessageBits;
1290 MessageQueue->ChangedBits |= MessageBits;
1291 if (MessageQueue->WakeMask & MessageBits)
1292 KeSetEvent(MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
1293 }
1294
1295 VOID FASTCALL
1296 MsqPostQuitMessage(PUSER_MESSAGE_QUEUE MessageQueue, ULONG ExitCode)
1297 {
1298 MessageQueue->QuitPosted = TRUE;
1299 MessageQueue->QuitExitCode = ExitCode;
1300 MessageQueue->QueueBits |= QS_POSTMESSAGE;
1301 MessageQueue->ChangedBits |= QS_POSTMESSAGE;
1302 if (MessageQueue->WakeMask & QS_POSTMESSAGE)
1303 KeSetEvent(MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
1304 }
1305
1306 BOOLEAN APIENTRY
1307 co_MsqFindMessage(IN PUSER_MESSAGE_QUEUE MessageQueue,
1308 IN BOOLEAN Hardware,
1309 IN BOOLEAN Remove,
1310 IN PWINDOW_OBJECT Window,
1311 IN UINT MsgFilterLow,
1312 IN UINT MsgFilterHigh,
1313 OUT PUSER_MESSAGE* Message)
1314 {
1315 PLIST_ENTRY CurrentEntry;
1316 PUSER_MESSAGE CurrentMessage;
1317 PLIST_ENTRY ListHead;
1318
1319 if (Hardware)
1320 {
1321 return(co_MsqPeekHardwareMessage( MessageQueue,
1322 Window,
1323 MsgFilterLow,
1324 MsgFilterHigh,
1325 Remove,
1326 Message));
1327 }
1328
1329 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
1330 ListHead = &MessageQueue->PostedMessagesListHead;
1331 while (CurrentEntry != ListHead)
1332 {
1333 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1334 ListEntry);
1335 if ( ( !Window ||
1336 PtrToInt(Window) == 1 ||
1337 Window->hSelf == CurrentMessage->Msg.hwnd ) &&
1338 ( (MsgFilterLow == 0 && MsgFilterHigh == 0) ||
1339 ( MsgFilterLow <= CurrentMessage->Msg.message &&
1340 MsgFilterHigh >= CurrentMessage->Msg.message ) ) )
1341 {
1342 if (Remove)
1343 {
1344 RemoveEntryList(&CurrentMessage->ListEntry);
1345 }
1346
1347 *Message = CurrentMessage;
1348 return(TRUE);
1349 }
1350 CurrentEntry = CurrentEntry->Flink;
1351 }
1352
1353 return(FALSE);
1354 }
1355
1356 NTSTATUS FASTCALL
1357 co_MsqWaitForNewMessages(PUSER_MESSAGE_QUEUE MessageQueue, PWINDOW_OBJECT WndFilter,
1358 UINT MsgFilterMin, UINT MsgFilterMax)
1359 {
1360 PVOID WaitObjects[2] = {MessageQueue->NewMessages, &HardwareMessageEvent};
1361 NTSTATUS ret;
1362
1363 IdlePing(); // Going to wait so send Idle ping.
1364
1365 UserLeaveCo();
1366
1367 ret = KeWaitForMultipleObjects(2,
1368 WaitObjects,
1369 WaitAny,
1370 Executive,
1371 UserMode,
1372 FALSE,
1373 NULL,
1374 NULL);
1375 UserEnterCo();
1376 return ret;
1377 }
1378
1379 BOOL FASTCALL
1380 MsqIsHung(PUSER_MESSAGE_QUEUE MessageQueue)
1381 {
1382 LARGE_INTEGER LargeTickCount;
1383
1384 KeQueryTickCount(&LargeTickCount);
1385 return ((LargeTickCount.u.LowPart - MessageQueue->LastMsgRead) > MSQ_HUNG);
1386 }
1387
1388 BOOLEAN FASTCALL
1389 MsqInitializeMessageQueue(struct _ETHREAD *Thread, PUSER_MESSAGE_QUEUE MessageQueue)
1390 {
1391 LARGE_INTEGER LargeTickCount;
1392 NTSTATUS Status;
1393
1394 MessageQueue->Thread = Thread;
1395 MessageQueue->CaretInfo = (PTHRDCARETINFO)(MessageQueue + 1);
1396 InitializeListHead(&MessageQueue->PostedMessagesListHead);
1397 InitializeListHead(&MessageQueue->SentMessagesListHead);
1398 InitializeListHead(&MessageQueue->HardwareMessagesListHead);
1399 InitializeListHead(&MessageQueue->TimerListHead);
1400 InitializeListHead(&MessageQueue->DispatchingMessagesHead);
1401 InitializeListHead(&MessageQueue->LocalDispatchingMessagesHead);
1402 KeInitializeMutex(&MessageQueue->HardwareLock, 0);
1403 MessageQueue->QuitPosted = FALSE;
1404 MessageQueue->QuitExitCode = 0;
1405 KeQueryTickCount(&LargeTickCount);
1406 MessageQueue->LastMsgRead = LargeTickCount.u.LowPart;
1407 MessageQueue->FocusWindow = NULL;
1408 MessageQueue->PaintCount = 0;
1409 // HACK!!!!!!! Newbies that wrote this should hold your head down in shame! (jt)
1410 MessageQueue->WakeMask = ~0;
1411 MessageQueue->NewMessagesHandle = NULL;
1412
1413 Status = ZwCreateEvent(&MessageQueue->NewMessagesHandle, EVENT_ALL_ACCESS,
1414 NULL, SynchronizationEvent, FALSE);
1415 if (!NT_SUCCESS(Status))
1416 {
1417 return FALSE;
1418 }
1419
1420 Status = ObReferenceObjectByHandle(MessageQueue->NewMessagesHandle, 0,
1421 ExEventObjectType, KernelMode,
1422 (PVOID*)&MessageQueue->NewMessages, NULL);
1423 if (!NT_SUCCESS(Status))
1424 {
1425 ZwClose(MessageQueue->NewMessagesHandle);
1426 MessageQueue->NewMessagesHandle = NULL;
1427 return FALSE;
1428 }
1429
1430 return TRUE;
1431 }
1432
1433 VOID FASTCALL
1434 MsqCleanupMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
1435 {
1436 PLIST_ENTRY CurrentEntry;
1437 PUSER_MESSAGE CurrentMessage;
1438 PTIMER_ENTRY CurrentTimer;
1439 PUSER_SENT_MESSAGE CurrentSentMessage;
1440
1441 /* cleanup posted messages */
1442 while (!IsListEmpty(&MessageQueue->PostedMessagesListHead))
1443 {
1444 CurrentEntry = RemoveHeadList(&MessageQueue->PostedMessagesListHead);
1445 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1446 ListEntry);
1447 MsqDestroyMessage(CurrentMessage);
1448 }
1449
1450 /* remove the messages that have not yet been dispatched */
1451 while (!IsListEmpty(&MessageQueue->SentMessagesListHead))
1452 {
1453 CurrentEntry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
1454 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1455 ListEntry);
1456
1457 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1458
1459 /* remove the message from the dispatching list if needed */
1460 if ((!(CurrentSentMessage->HookMessage & MSQ_SENTNOWAIT))
1461 && (CurrentSentMessage->DispatchingListEntry.Flink != NULL))
1462 {
1463 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
1464 }
1465
1466 /* wake the sender's thread */
1467 if (CurrentSentMessage->CompletionEvent != NULL)
1468 {
1469 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1470 }
1471
1472 if (CurrentSentMessage->HasPackedLParam == TRUE)
1473 {
1474 if (CurrentSentMessage->Msg.lParam)
1475 ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
1476 }
1477
1478 /* Only if it is not a no wait message */
1479 if (!(CurrentSentMessage->HookMessage & MSQ_SENTNOWAIT))
1480 {
1481 /* dereference our and the sender's message queue */
1482 IntDereferenceMessageQueue(MessageQueue);
1483 IntDereferenceMessageQueue(CurrentSentMessage->SenderQueue);
1484 }
1485
1486 /* free the message */
1487 ExFreePool(CurrentSentMessage);
1488 }
1489
1490 /* cleanup timers */
1491 while (! IsListEmpty(&MessageQueue->TimerListHead))
1492 {
1493 CurrentEntry = RemoveHeadList(&MessageQueue->TimerListHead);
1494 CurrentTimer = CONTAINING_RECORD(CurrentEntry, TIMER_ENTRY, ListEntry);
1495 ExFreeToPagedLookasideList(&TimerLookasideList, CurrentTimer);
1496 }
1497
1498 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
1499 ExitThread() was called in a SendMessage() umode callback */
1500 while (!IsListEmpty(&MessageQueue->LocalDispatchingMessagesHead))
1501 {
1502 CurrentEntry = RemoveHeadList(&MessageQueue->LocalDispatchingMessagesHead);
1503 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1504 ListEntry);
1505
1506 /* remove the message from the dispatching list */
1507 if(CurrentSentMessage->DispatchingListEntry.Flink != NULL)
1508 {
1509 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
1510 }
1511
1512 DPRINT("Notify the sender, the thread has been terminated while dispatching a message!\n");
1513
1514 /* wake the sender's thread */
1515 if (CurrentSentMessage->CompletionEvent != NULL)
1516 {
1517 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1518 }
1519
1520 if (CurrentSentMessage->HasPackedLParam == TRUE)
1521 {
1522 if (CurrentSentMessage->Msg.lParam)
1523 ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
1524 }
1525
1526 /* Only if it is not a no wait message */
1527 if (!(CurrentSentMessage->HookMessage & MSQ_SENTNOWAIT))
1528 {
1529 /* dereference our and the sender's message queue */
1530 IntDereferenceMessageQueue(MessageQueue);
1531 IntDereferenceMessageQueue(CurrentSentMessage->SenderQueue);
1532 }
1533
1534 /* free the message */
1535 ExFreePool(CurrentSentMessage);
1536 }
1537
1538 /* tell other threads not to bother returning any info to us */
1539 while (! IsListEmpty(&MessageQueue->DispatchingMessagesHead))
1540 {
1541 CurrentEntry = RemoveHeadList(&MessageQueue->DispatchingMessagesHead);
1542 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1543 DispatchingListEntry);
1544 CurrentSentMessage->CompletionEvent = NULL;
1545 CurrentSentMessage->Result = NULL;
1546
1547 /* do NOT dereference our message queue as it might get attempted to be
1548 locked later */
1549 }
1550
1551 }
1552
1553 PUSER_MESSAGE_QUEUE FASTCALL
1554 MsqCreateMessageQueue(struct _ETHREAD *Thread)
1555 {
1556 PUSER_MESSAGE_QUEUE MessageQueue;
1557
1558 MessageQueue = (PUSER_MESSAGE_QUEUE)ExAllocatePoolWithTag(NonPagedPool,
1559 sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO),
1560 TAG_MSGQ);
1561
1562 if (!MessageQueue)
1563 {
1564 return NULL;
1565 }
1566
1567 RtlZeroMemory(MessageQueue, sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO));
1568 /* hold at least one reference until it'll be destroyed */
1569 IntReferenceMessageQueue(MessageQueue);
1570 /* initialize the queue */
1571 if (!MsqInitializeMessageQueue(Thread, MessageQueue))
1572 {
1573 IntDereferenceMessageQueue(MessageQueue);
1574 return NULL;
1575 }
1576
1577 return MessageQueue;
1578 }
1579
1580 VOID FASTCALL
1581 MsqDestroyMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
1582 {
1583 PDESKTOP desk;
1584
1585 /* remove the message queue from any desktops */
1586 if ((desk = InterlockedExchangePointer((PVOID*)&MessageQueue->Desktop, 0)))
1587 {
1588 (void)InterlockedExchangePointer((PVOID*)&desk->ActiveMessageQueue, 0);
1589 IntDereferenceMessageQueue(MessageQueue);
1590 }
1591
1592 /* clean it up */
1593 MsqCleanupMessageQueue(MessageQueue);
1594
1595 /* decrease the reference counter, if it hits zero, the queue will be freed */
1596 IntDereferenceMessageQueue(MessageQueue);
1597 }
1598
1599 PHOOKTABLE FASTCALL
1600 MsqGetHooks(PUSER_MESSAGE_QUEUE Queue)
1601 {
1602 return Queue->Hooks;
1603 }
1604
1605 VOID FASTCALL
1606 MsqSetHooks(PUSER_MESSAGE_QUEUE Queue, PHOOKTABLE Hooks)
1607 {
1608 Queue->Hooks = Hooks;
1609 }
1610
1611 LPARAM FASTCALL
1612 MsqSetMessageExtraInfo(LPARAM lParam)
1613 {
1614 LPARAM Ret;
1615 PTHREADINFO pti;
1616 PUSER_MESSAGE_QUEUE MessageQueue;
1617
1618 pti = PsGetCurrentThreadWin32Thread();
1619 MessageQueue = pti->MessageQueue;
1620 if(!MessageQueue)
1621 {
1622 return 0;
1623 }
1624
1625 Ret = MessageQueue->ExtraInfo;
1626 MessageQueue->ExtraInfo = lParam;
1627
1628 return Ret;
1629 }
1630
1631 LPARAM FASTCALL
1632 MsqGetMessageExtraInfo(VOID)
1633 {
1634 PTHREADINFO pti;
1635 PUSER_MESSAGE_QUEUE MessageQueue;
1636
1637 pti = PsGetCurrentThreadWin32Thread();
1638 MessageQueue = pti->MessageQueue;
1639 if(!MessageQueue)
1640 {
1641 return 0;
1642 }
1643
1644 return MessageQueue->ExtraInfo;
1645 }
1646
1647 HWND FASTCALL
1648 MsqSetStateWindow(PUSER_MESSAGE_QUEUE MessageQueue, ULONG Type, HWND hWnd)
1649 {
1650 HWND Prev;
1651
1652 switch(Type)
1653 {
1654 case MSQ_STATE_CAPTURE:
1655 Prev = MessageQueue->CaptureWindow;
1656 MessageQueue->CaptureWindow = hWnd;
1657 return Prev;
1658 case MSQ_STATE_ACTIVE:
1659 Prev = MessageQueue->ActiveWindow;
1660 MessageQueue->ActiveWindow = hWnd;
1661 return Prev;
1662 case MSQ_STATE_FOCUS:
1663 Prev = MessageQueue->FocusWindow;
1664 MessageQueue->FocusWindow = hWnd;
1665 return Prev;
1666 case MSQ_STATE_MENUOWNER:
1667 Prev = MessageQueue->MenuOwner;
1668 MessageQueue->MenuOwner = hWnd;
1669 return Prev;
1670 case MSQ_STATE_MOVESIZE:
1671 Prev = MessageQueue->MoveSize;
1672 MessageQueue->MoveSize = hWnd;
1673 return Prev;
1674 case MSQ_STATE_CARET:
1675 ASSERT(MessageQueue->CaretInfo);
1676 Prev = MessageQueue->CaretInfo->hWnd;
1677 MessageQueue->CaretInfo->hWnd = hWnd;
1678 return Prev;
1679 }
1680
1681 return NULL;
1682 }
1683
1684 #ifndef NDEBUG
1685 static VOID FASTCALL
1686 DumpTimerList(PUSER_MESSAGE_QUEUE MessageQueue)
1687 {
1688 PLIST_ENTRY Current;
1689 PTIMER_ENTRY Timer;
1690
1691 Current = MessageQueue->TimerListHead.Flink;
1692 if (Current == &MessageQueue->TimerListHead)
1693 {
1694 DPRINT("timer list is empty for queue %p\n", MessageQueue);
1695 }
1696 while (Current != &MessageQueue->TimerListHead)
1697 {
1698 Timer = CONTAINING_RECORD(Current, TIMER_ENTRY, ListEntry);
1699 DPRINT("queue %p timer %p expiry %I64d wnd %x id %p period %u timerproc %p msg %u\n",
1700 MessageQueue, Timer, Timer->ExpiryTime.QuadPart, Timer->Wnd, Timer->IDEvent,
1701 Timer->Period, Timer->TimerFunc, Timer->Msg);
1702 Current = Current->Flink;
1703 }
1704 }
1705 #endif /* ! defined(NDEBUG) */
1706
1707 /* Must have the message queue locked while calling this */
1708 static VOID FASTCALL
1709 InsertTimer(PUSER_MESSAGE_QUEUE MessageQueue, PTIMER_ENTRY NewTimer)
1710 {
1711 PLIST_ENTRY Current;
1712
1713 Current = MessageQueue->TimerListHead.Flink;
1714 while (Current != &MessageQueue->TimerListHead)
1715 {
1716 if (NewTimer->ExpiryTime.QuadPart <
1717 CONTAINING_RECORD(Current, TIMER_ENTRY, ListEntry)->ExpiryTime.QuadPart)
1718 {
1719 break;
1720 }
1721 Current = Current->Flink;
1722 }
1723
1724 InsertTailList(Current, &NewTimer->ListEntry);
1725 }
1726
1727 /* Must have the message queue locked while calling this */
1728 static PTIMER_ENTRY FASTCALL
1729 RemoveTimer(PUSER_MESSAGE_QUEUE MessageQueue, HWND Wnd, UINT_PTR IDEvent, UINT Msg)
1730 {
1731 PTIMER_ENTRY Timer;
1732 PLIST_ENTRY EnumEntry;
1733
1734 /* Remove timer if already in the queue */
1735 EnumEntry = MessageQueue->TimerListHead.Flink;
1736 while (EnumEntry != &MessageQueue->TimerListHead)
1737 {
1738 Timer = CONTAINING_RECORD(EnumEntry, TIMER_ENTRY, ListEntry);
1739 EnumEntry = EnumEntry->Flink;
1740
1741 if (Timer->Wnd == Wnd &&
1742 Timer->IDEvent == IDEvent &&
1743 Timer->Msg == Msg)
1744 {
1745 RemoveEntryList(&Timer->ListEntry);
1746 return Timer;
1747 }
1748 }
1749
1750 return NULL;
1751 }
1752
1753 BOOLEAN FASTCALL
1754 MsqSetTimer(PUSER_MESSAGE_QUEUE MessageQueue, HWND Wnd,
1755 UINT_PTR IDEvent, UINT Period, TIMERPROC TimerFunc,
1756 UINT Msg)
1757 {
1758 PTIMER_ENTRY Timer;
1759 LARGE_INTEGER CurrentTime;
1760
1761 DPRINT("MsqSetTimer queue %p wnd %x id %p period %u timerproc %p msg %d\n",
1762 MessageQueue, Wnd, IDEvent, Period, TimerFunc, Msg);
1763
1764 Timer = RemoveTimer(MessageQueue, Wnd, IDEvent, Msg);
1765 if (NULL == Timer)
1766 {
1767 Timer = ExAllocateFromPagedLookasideList(&TimerLookasideList);
1768 if (NULL == Timer)
1769 {
1770 DPRINT1("Failed to allocate timer entry\n");
1771 return FALSE;
1772 }
1773 DPRINT("Allocated new timer entry %p\n", Timer);
1774 Timer->Wnd = Wnd;
1775 Timer->IDEvent = IDEvent;
1776 Timer->Msg = Msg;
1777 }
1778 else
1779 {
1780 DPRINT("Updating existing timer entry %p\n", Timer);
1781 }
1782
1783 KeQuerySystemTime(&CurrentTime);
1784 Timer->ExpiryTime.QuadPart = CurrentTime.QuadPart +
1785 (ULONGLONG) Period * (ULONGLONG) 10000;
1786 Timer->Period = Period;
1787 Timer->TimerFunc = TimerFunc;
1788 DPRINT("Insert timer now %I64d expiry %I64d\n", CurrentTime.QuadPart,
1789 Timer->ExpiryTime.QuadPart);
1790
1791 InsertTimer(MessageQueue, Timer);
1792
1793 #ifndef NDEBUG
1794
1795 DumpTimerList(MessageQueue);
1796 #endif /* ! defined(NDEBUG) */
1797
1798 return TRUE;
1799 }
1800
1801 BOOLEAN FASTCALL
1802 MsqKillTimer(PUSER_MESSAGE_QUEUE MessageQueue, HWND Wnd,
1803 UINT_PTR IDEvent, UINT Msg)
1804 {
1805 PTIMER_ENTRY Timer;
1806
1807 DPRINT("MsqKillTimer queue %p wnd %x id %p msg %d\n",
1808 MessageQueue, Wnd, IDEvent, Msg);
1809
1810 Timer = RemoveTimer(MessageQueue, Wnd, IDEvent, Msg);
1811
1812 if (NULL == Timer)
1813 {
1814 DPRINT("Failed to remove timer from list, not found\n");
1815 }
1816 else
1817 {
1818 ExFreeToPagedLookasideList(&TimerLookasideList, Timer);
1819 }
1820
1821 #ifndef NDEBUG
1822 DumpTimerList(MessageQueue);
1823 #endif /* ! defined(NDEBUG) */
1824
1825 return NULL != Timer;
1826 }
1827
1828 BOOLEAN FASTCALL
1829 MsqGetTimerMessage(PUSER_MESSAGE_QUEUE MessageQueue,
1830 PWINDOW_OBJECT WindowFilter, UINT MsgFilterMin, UINT MsgFilterMax,
1831 MSG *Msg, BOOLEAN Restart)
1832 {
1833 PTIMER_ENTRY Timer;
1834 LARGE_INTEGER CurrentTime;
1835 LARGE_INTEGER LargeTickCount;
1836 PLIST_ENTRY EnumEntry;
1837 BOOLEAN GotMessage;
1838
1839 DPRINT("MsqGetTimerMessage queue %p msg %p restart %s\n",
1840 MessageQueue, Msg, Restart ? "TRUE" : "FALSE");
1841
1842 KeQuerySystemTime(&CurrentTime);
1843 DPRINT("Current time %I64d\n", CurrentTime.QuadPart);
1844 EnumEntry = MessageQueue->TimerListHead.Flink;
1845 GotMessage = FALSE;
1846 while (EnumEntry != &MessageQueue->TimerListHead)
1847 {
1848 Timer = CONTAINING_RECORD(MessageQueue->TimerListHead.Flink,
1849 TIMER_ENTRY, ListEntry);
1850 DPRINT("Checking timer %p wnd %x expiry %I64d\n", Timer, Timer->Wnd,
1851 Timer->ExpiryTime.QuadPart);
1852 EnumEntry = EnumEntry->Flink;
1853 if ((NULL == WindowFilter || Timer->Wnd == WindowFilter->hSelf) &&
1854 ((MsgFilterMin == 0 && MsgFilterMax == 0) ||
1855 (MsgFilterMin <= Timer->Msg &&
1856 Timer->Msg <= MsgFilterMax)))
1857 {
1858 if (Timer->ExpiryTime.QuadPart <= CurrentTime.QuadPart)
1859 {
1860 DPRINT("Timer is expired\n");
1861 GotMessage = TRUE;
1862 break;
1863 }
1864 else
1865 {
1866 DPRINT("No need to check later timers\n");
1867 break;
1868 }
1869 }
1870 else
1871 {
1872 DPRINT("timer %p (wnd %x msg %d) failed filter wnd %x msgmin %d msgmax %d\n",
1873 Timer, Timer->Wnd, Timer->Msg, WindowFilter->hSelf, MsgFilterMin, MsgFilterMax);
1874 }
1875 }
1876
1877 if (! GotMessage)
1878 {
1879 DPRINT("No timer pending\n");
1880 return FALSE;
1881 }
1882
1883 Msg->hwnd = Timer->Wnd;
1884 Msg->message = Timer->Msg;
1885 Msg->wParam = (WPARAM) Timer->IDEvent;
1886 Msg->lParam = (LPARAM) Timer->TimerFunc;
1887 KeQueryTickCount(&LargeTickCount);
1888 Msg->time = MsqCalculateMessageTime(&LargeTickCount);
1889 Msg->pt = gpsi->ptCursor;
1890
1891 if (Restart)
1892 {
1893 RemoveEntryList(&Timer->ListEntry);
1894 Timer->ExpiryTime.QuadPart = CurrentTime.QuadPart +
1895 (ULONGLONG) Timer->Period * (ULONGLONG) 10000;
1896 DPRINT("Restarting timer %p expires %I64d\n", Timer, Timer->ExpiryTime.QuadPart);
1897 InsertTimer(MessageQueue, Timer);
1898
1899 #ifndef NDEBUG
1900
1901 DumpTimerList(MessageQueue);
1902 #endif /* ! defined(NDEBUG) */
1903
1904 }
1905
1906 DPRINT("Created message wnd %x msg %d wParam %u lParam %u\n", Msg->hwnd, Msg->message,
1907 Msg->wParam, Msg->lParam);
1908
1909 return TRUE;
1910 }
1911
1912 VOID FASTCALL
1913 MsqRemoveTimersWindow(PUSER_MESSAGE_QUEUE MessageQueue, HWND Wnd)
1914 {
1915 PTIMER_ENTRY Timer;
1916 PLIST_ENTRY EnumEntry;
1917
1918 DPRINT("MsqRemoveTimersWindow queue %p wnd %x\n", MessageQueue, Wnd);
1919
1920 EnumEntry = MessageQueue->TimerListHead.Flink;
1921 while (EnumEntry != &MessageQueue->TimerListHead)
1922 {
1923 Timer = CONTAINING_RECORD(EnumEntry, TIMER_ENTRY, ListEntry);
1924 EnumEntry = EnumEntry->Flink;
1925 if (Timer->Wnd == Wnd)
1926 {
1927 DPRINT("Removing timer %p because its window is going away\n", Timer);
1928 RemoveEntryList(&Timer->ListEntry);
1929 ExFreeToPagedLookasideList(&TimerLookasideList, Timer);
1930 }
1931 }
1932
1933 #ifndef NDEBUG
1934 DumpTimerList(MessageQueue);
1935 #endif /* ! defined(NDEBUG) */
1936
1937 }
1938
1939 BOOLEAN FASTCALL
1940 MsqGetFirstTimerExpiry(PUSER_MESSAGE_QUEUE MessageQueue,
1941 PWINDOW_OBJECT WndFilter, UINT MsgFilterMin, UINT MsgFilterMax,
1942 PLARGE_INTEGER FirstTimerExpiry)
1943 {
1944 PTIMER_ENTRY Timer;
1945 PLIST_ENTRY EnumEntry;
1946
1947 DPRINT("MsqGetFirstTimerExpiry queue %p wndfilter %x msgfiltermin %d msgfiltermax %d expiry %p\n",
1948 MessageQueue, WndFilter, MsgFilterMin, MsgFilterMax, FirstTimerExpiry);
1949
1950 EnumEntry = MessageQueue->TimerListHead.Flink;
1951 while (EnumEntry != &MessageQueue->TimerListHead)
1952 {
1953 Timer = CONTAINING_RECORD(MessageQueue->TimerListHead.Flink,
1954 TIMER_ENTRY, ListEntry);
1955 EnumEntry = EnumEntry->Flink;
1956 if ((NULL == WndFilter || PtrToInt(WndFilter) == 1 || Timer->Wnd == WndFilter->hSelf) &&
1957 ((MsgFilterMin == 0 && MsgFilterMax == 0) ||
1958 (MsgFilterMin <= Timer->Msg &&
1959 Timer->Msg <= MsgFilterMax)))
1960 {
1961 *FirstTimerExpiry = Timer->ExpiryTime;
1962 DPRINT("First timer expires %I64d\n", Timer->ExpiryTime);
1963 return TRUE;
1964 }
1965 }
1966
1967 return FALSE;
1968 }
1969
1970 /* EOF */