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