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