4ab1ca4d5cf53334ab7f0681c610e6e2ea6930ec
[reactos.git] / reactos / subsystems / win32 / win32k / ntuser / msgqueue.c
1 /*
2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /*
20 * COPYRIGHT: See COPYING in the top level directory
21 * PROJECT: ReactOS kernel
22 * PURPOSE: Message queues
23 * FILE: subsystems/win32/win32k/ntuser/msgqueue.c
24 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
25 * REVISION HISTORY:
26 * 06-06-2001 CSH Created
27 */
28
29 /* INCLUDES ******************************************************************/
30
31 #include <w32k.h>
32
33 #define NDEBUG
34 #include <debug.h>
35
36 /* GLOBALS *******************************************************************/
37
38 #define SYSTEM_MESSAGE_QUEUE_SIZE (256)
39
40 static MSG SystemMessageQueue[SYSTEM_MESSAGE_QUEUE_SIZE];
41 static ULONG SystemMessageQueueHead = 0;
42 static ULONG SystemMessageQueueTail = 0;
43 static ULONG SystemMessageQueueCount = 0;
44 static KSPIN_LOCK SystemMessageQueueLock;
45
46 static ULONG volatile HardwareMessageQueueStamp = 0;
47 static LIST_ENTRY HardwareMessageQueueHead;
48 static KMUTANT HardwareMessageQueueLock;
49
50 static KEVENT HardwareMessageEvent;
51
52 static PAGED_LOOKASIDE_LIST MessageLookasideList;
53 static PAGED_LOOKASIDE_LIST TimerLookasideList;
54
55 #define IntLockSystemMessageQueue(OldIrql) \
56 KeAcquireSpinLock(&SystemMessageQueueLock, &OldIrql)
57
58 #define IntUnLockSystemMessageQueue(OldIrql) \
59 KeReleaseSpinLock(&SystemMessageQueueLock, OldIrql)
60
61 #define IntUnLockSystemHardwareMessageQueueLock(Wait) \
62 KeReleaseMutant(&HardwareMessageQueueLock, IO_NO_INCREMENT, FALSE, Wait)
63
64 /* FUNCTIONS *****************************************************************/
65
66 //
67 // Wakeup any thread/process waiting on idle input.
68 //
69 static VOID FASTCALL
70 IdlePing(VOID)
71 {
72 HWND hWnd;
73 PWINDOW_OBJECT Window;
74 PPROCESSINFO W32d = PsGetCurrentProcessWin32Process();
75
76 hWnd = UserGetForegroundWindow();
77
78 Window = UserGetWindowObject(hWnd);
79
80 if (Window && Window->ti)
81 {
82 if (Window->ti->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 EVENTMSG Event;
178
179 IntLockSystemMessageQueue(OldIrql);
180
181 /*
182 * Bail out if the queue is full. FIXME: We should handle this case
183 * more gracefully.
184 */
185
186 if (SystemMessageQueueCount == SYSTEM_MESSAGE_QUEUE_SIZE)
187 {
188 IntUnLockSystemMessageQueue(OldIrql);
189 return;
190 }
191
192 KeQueryTickCount(&LargeTickCount);
193 Msg->time = MsqCalculateMessageTime(&LargeTickCount);
194
195 Event.message = Msg->message;
196 Event.time = Msg->time;
197 Event.hwnd = Msg->hwnd;
198 Event.paramL = Msg->pt.x;
199 Event.paramH = Msg->pt.y;
200 co_HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&Event);
201
202 /*
203 * If we got WM_MOUSEMOVE and there are already messages in the
204 * system message queue, check if the last message is mouse move
205 * and if it is then just overwrite it.
206 */
207
208 if (Msg->message == WM_MOUSEMOVE && SystemMessageQueueCount)
209 {
210 if (SystemMessageQueueTail == 0)
211 Prev = SYSTEM_MESSAGE_QUEUE_SIZE - 1;
212 else
213 Prev = SystemMessageQueueTail - 1;
214 if (SystemMessageQueue[Prev].message == WM_MOUSEMOVE)
215 {
216 SystemMessageQueueTail = Prev;
217 SystemMessageQueueCount--;
218 }
219 }
220
221 /*
222 * Actually insert the message into the system message queue.
223 */
224
225 SystemMessageQueue[SystemMessageQueueTail] = *Msg;
226 SystemMessageQueueTail =
227 (SystemMessageQueueTail + 1) % SYSTEM_MESSAGE_QUEUE_SIZE;
228 SystemMessageQueueCount++;
229
230 IntUnLockSystemMessageQueue(OldIrql);
231
232 KeSetEvent(&HardwareMessageEvent, IO_NO_INCREMENT, FALSE);
233 }
234
235 BOOL FASTCALL
236 MsqIsDblClk(LPMSG Msg, BOOL Remove)
237 {
238 PTHREADINFO pti;
239 PWINSTATION_OBJECT WinStaObject;
240 PSYSTEM_CURSORINFO CurInfo;
241 LONG dX, dY;
242 BOOL Res;
243
244 pti = PsGetCurrentThreadWin32Thread();
245 if (pti->Desktop == NULL)
246 {
247 return FALSE;
248 }
249
250 WinStaObject = pti->Desktop->WindowStation;
251
252 CurInfo = IntGetSysCursorInfo(WinStaObject);
253 Res = (Msg->hwnd == (HWND)CurInfo->LastClkWnd) &&
254 ((Msg->time - CurInfo->LastBtnDown) < CurInfo->DblClickSpeed);
255 if(Res)
256 {
257
258 dX = CurInfo->LastBtnDownX - Msg->pt.x;
259 dY = CurInfo->LastBtnDownY - Msg->pt.y;
260 if(dX < 0)
261 dX = -dX;
262 if(dY < 0)
263 dY = -dY;
264
265 Res = (dX <= CurInfo->DblClickWidth) &&
266 (dY <= CurInfo->DblClickHeight);
267
268 if(Res)
269 {
270 if(CurInfo->ButtonsDown)
271 Res = (CurInfo->ButtonsDown == Msg->message);
272 }
273 }
274
275 if(Remove)
276 {
277 if (Res)
278 {
279 CurInfo->LastBtnDown = 0;
280 CurInfo->LastBtnDownX = Msg->pt.x;
281 CurInfo->LastBtnDownY = Msg->pt.y;
282 CurInfo->LastClkWnd = NULL;
283 CurInfo->ButtonsDown = Msg->message;
284 }
285 else
286 {
287 CurInfo->LastBtnDownX = Msg->pt.x;
288 CurInfo->LastBtnDownY = Msg->pt.y;
289 CurInfo->LastClkWnd = (HANDLE)Msg->hwnd;
290 CurInfo->LastBtnDown = Msg->time;
291 CurInfo->ButtonsDown = Msg->message;
292 }
293 }
294
295 return Res;
296 }
297
298 static BOOL APIENTRY
299 co_MsqTranslateMouseMessage(PUSER_MESSAGE_QUEUE MessageQueue, PWINDOW_OBJECT Window, UINT FilterLow, UINT FilterHigh,
300 PUSER_MESSAGE Message, BOOL Remove, PBOOL Freed,
301 PWINDOW_OBJECT ScopeWin, PPOINT ScreenPoint, BOOL FromGlobalQueue, PLIST_ENTRY *Next)
302 {
303 USHORT Msg = Message->Msg.message;
304 PWINDOW_OBJECT CaptureWindow = NULL;
305 HWND hCaptureWin;
306
307 ASSERT_REFS_CO(ScopeWin);
308
309 /*
310 co_WinPosWindowFromPoint can return a Window, and in that case
311 that window has a ref that we need to deref. Thats why we add "dummy"
312 refs in all other cases.
313 */
314
315 hCaptureWin = IntGetCaptureWindow();
316 if (hCaptureWin == NULL)
317 {
318 if (Msg == WM_MOUSEWHEEL)
319 {
320 CaptureWindow = UserGetWindowObject(IntGetFocusWindow());
321 if (CaptureWindow) UserReferenceObject(CaptureWindow);
322 }
323 else
324 {
325 co_WinPosWindowFromPoint(ScopeWin, NULL, &Message->Msg.pt, &CaptureWindow);
326 if(CaptureWindow == NULL)
327 {
328 CaptureWindow = ScopeWin;
329 if (CaptureWindow) UserReferenceObject(CaptureWindow);
330 }
331 else
332 {
333 /* this is the one case where we dont add a ref, since the returned
334 window is already referenced */
335 }
336 }
337 }
338 else
339 {
340 /* FIXME - window messages should go to the right window if no buttons are
341 pressed */
342 CaptureWindow = UserGetWindowObject(hCaptureWin);
343 if (CaptureWindow) UserReferenceObject(CaptureWindow);
344 }
345
346
347
348 if (CaptureWindow == NULL)
349 {
350 if(!FromGlobalQueue)
351 {
352 RemoveEntryList(&Message->ListEntry);
353 if(MessageQueue->MouseMoveMsg == Message)
354 {
355 MessageQueue->MouseMoveMsg = NULL;
356 }
357 }
358 // when FromGlobalQueue is true, the caller has already removed the Message
359 ExFreePool(Message);
360 *Freed = TRUE;
361 return(FALSE);
362 }
363
364 if (CaptureWindow->MessageQueue != MessageQueue)
365 {
366 if (! FromGlobalQueue)
367 {
368 DPRINT("Moving msg between private queues\n");
369 /* This message is already queued in a private queue, but we need
370 * to move it to a different queue, perhaps because a new window
371 * was created which now covers the screen area previously taken
372 * by another window. To move it, we need to take it out of the
373 * old queue. Note that we're already holding the lock mutexes of the
374 * old queue */
375 RemoveEntryList(&Message->ListEntry);
376
377 /* remove the pointer for the current WM_MOUSEMOVE message in case we
378 just removed it */
379 if(MessageQueue->MouseMoveMsg == Message)
380 {
381 MessageQueue->MouseMoveMsg = NULL;
382 }
383 }
384
385 /* lock the destination message queue, so we don't get in trouble with other
386 threads, messing with it at the same time */
387 IntLockHardwareMessageQueue(CaptureWindow->MessageQueue);
388 InsertTailList(&CaptureWindow->MessageQueue->HardwareMessagesListHead,
389 &Message->ListEntry);
390 if(Message->Msg.message == WM_MOUSEMOVE)
391 {
392 if(CaptureWindow->MessageQueue->MouseMoveMsg)
393 {
394 /* remove the old WM_MOUSEMOVE message, we're processing a more recent
395 one */
396 RemoveEntryList(&CaptureWindow->MessageQueue->MouseMoveMsg->ListEntry);
397 ExFreePool(CaptureWindow->MessageQueue->MouseMoveMsg);
398 }
399 /* save the pointer to the WM_MOUSEMOVE message in the new queue */
400 CaptureWindow->MessageQueue->MouseMoveMsg = Message;
401
402 CaptureWindow->MessageQueue->QueueBits |= QS_MOUSEMOVE;
403 CaptureWindow->MessageQueue->ChangedBits |= QS_MOUSEMOVE;
404 if (CaptureWindow->MessageQueue->WakeMask & QS_MOUSEMOVE)
405 KeSetEvent(CaptureWindow->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
406 }
407 else
408 {
409 CaptureWindow->MessageQueue->QueueBits |= QS_MOUSEBUTTON;
410 CaptureWindow->MessageQueue->ChangedBits |= QS_MOUSEBUTTON;
411 if (CaptureWindow->MessageQueue->WakeMask & QS_MOUSEBUTTON)
412 KeSetEvent(CaptureWindow->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
413 }
414 IntUnLockHardwareMessageQueue(CaptureWindow->MessageQueue);
415
416 *Freed = FALSE;
417 UserDereferenceObject(CaptureWindow);
418 return(FALSE);
419 }
420
421 /* From here on, we're in the same message queue as the caller! */
422
423 *ScreenPoint = Message->Msg.pt;
424
425 if((Window != NULL && (int)Window != 1 && CaptureWindow->hSelf != Window->hSelf) ||
426 ((FilterLow != 0 || FilterHigh != 0) && (Msg < FilterLow || Msg > FilterHigh)))
427 {
428 /* Reject the message because it doesn't match the filter */
429
430 if(FromGlobalQueue)
431 {
432 /* Lock the message queue so no other thread can mess with it.
433 Our own message queue is not locked while fetching from the global
434 queue, so we have to make sure nothing interferes! */
435 IntLockHardwareMessageQueue(CaptureWindow->MessageQueue);
436 /* if we're from the global queue, we need to add our message to our
437 private queue so we don't loose it! */
438 InsertTailList(&CaptureWindow->MessageQueue->HardwareMessagesListHead,
439 &Message->ListEntry);
440 }
441
442 if (Message->Msg.message == WM_MOUSEMOVE)
443 {
444 if(CaptureWindow->MessageQueue->MouseMoveMsg &&
445 (CaptureWindow->MessageQueue->MouseMoveMsg != Message))
446 {
447 /* delete the old message */
448 RemoveEntryList(&CaptureWindow->MessageQueue->MouseMoveMsg->ListEntry);
449 ExFreePool(CaptureWindow->MessageQueue->MouseMoveMsg);
450 if (!FromGlobalQueue)
451 {
452 // We might have deleted the next one in our queue, so fix next
453 *Next = Message->ListEntry.Flink;
454 }
455 }
456 /* always save a pointer to this WM_MOUSEMOVE message here because we're
457 sure that the message is in the private queue */
458 CaptureWindow->MessageQueue->MouseMoveMsg = Message;
459 }
460 if(FromGlobalQueue)
461 {
462 IntUnLockHardwareMessageQueue(CaptureWindow->MessageQueue);
463 }
464
465 UserDereferenceObject(CaptureWindow);
466 *Freed = FALSE;
467 return(FALSE);
468 }
469
470 /* FIXME - only assign if removing? */
471 Message->Msg.hwnd = CaptureWindow->hSelf;
472 Message->Msg.message = Msg;
473 Message->Msg.lParam = MAKELONG(Message->Msg.pt.x, Message->Msg.pt.y);
474
475 /* remove the reference to the current WM_(NC)MOUSEMOVE message, if this message
476 is it */
477 if (Message->Msg.message == WM_MOUSEMOVE ||
478 Message->Msg.message == WM_NCMOUSEMOVE)
479 {
480 if(FromGlobalQueue)
481 {
482 /* Lock the message queue so no other thread can mess with it.
483 Our own message queue is not locked while fetching from the global
484 queue, so we have to make sure nothing interferes! */
485 IntLockHardwareMessageQueue(CaptureWindow->MessageQueue);
486 if(CaptureWindow->MessageQueue->MouseMoveMsg)
487 {
488 /* delete the WM_(NC)MOUSEMOVE message in the private queue, we're dealing
489 with one that's been sent later */
490 RemoveEntryList(&CaptureWindow->MessageQueue->MouseMoveMsg->ListEntry);
491 ExFreePool(CaptureWindow->MessageQueue->MouseMoveMsg);
492 /* our message is not in the private queue so we can remove the pointer
493 instead of setting it to the current message we're processing */
494 CaptureWindow->MessageQueue->MouseMoveMsg = NULL;
495 }
496 IntUnLockHardwareMessageQueue(CaptureWindow->MessageQueue);
497 }
498 else if (CaptureWindow->MessageQueue->MouseMoveMsg == Message)
499 {
500 CaptureWindow->MessageQueue->MouseMoveMsg = NULL;
501 }
502 }
503
504 UserDereferenceObject(CaptureWindow);
505 *Freed = FALSE;
506 return(TRUE);
507 }
508
509 BOOL APIENTRY
510 co_MsqPeekHardwareMessage(PUSER_MESSAGE_QUEUE MessageQueue, PWINDOW_OBJECT Window,
511 UINT FilterLow, UINT FilterHigh, BOOL Remove,
512 PUSER_MESSAGE* Message)
513 {
514 KIRQL OldIrql;
515 POINT ScreenPoint;
516 BOOL Accept, Freed;
517 PLIST_ENTRY CurrentEntry;
518 PWINDOW_OBJECT DesktopWindow = NULL;
519 PVOID WaitObjects[2];
520 NTSTATUS WaitStatus;
521 DECLARE_RETURN(BOOL);
522 USER_REFERENCE_ENTRY Ref;
523 PDESKTOPINFO Desk = NULL;
524
525 WaitObjects[1] = MessageQueue->NewMessages;
526 WaitObjects[0] = &HardwareMessageQueueLock;
527 do
528 {
529 IdlePing();
530
531 UserLeaveCo();
532
533 WaitStatus = KeWaitForMultipleObjects(2, WaitObjects, WaitAny, UserRequest,
534 UserMode, FALSE, NULL, NULL);
535
536 UserEnterCo();
537
538 while (co_MsqDispatchOneSentMessage(MessageQueue))
539 {
540 ;
541 }
542 }
543 while (NT_SUCCESS(WaitStatus) && STATUS_WAIT_0 != WaitStatus);
544
545 DesktopWindow = UserGetWindowObject(IntGetDesktopWindow());
546
547 if (DesktopWindow)
548 {
549 UserRefObjectCo(DesktopWindow, &Ref);//can DesktopWindow be NULL?
550 Desk = DesktopWindow->ti->pDeskInfo;
551 }
552
553 /* Process messages in the message queue itself. */
554 IntLockHardwareMessageQueue(MessageQueue);
555 CurrentEntry = MessageQueue->HardwareMessagesListHead.Flink;
556 while (CurrentEntry != &MessageQueue->HardwareMessagesListHead)
557 {
558 PUSER_MESSAGE Current =
559 CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, ListEntry);
560 CurrentEntry = CurrentEntry->Flink;
561 if (Current->Msg.message >= WM_MOUSEFIRST &&
562 Current->Msg.message <= WM_MOUSELAST)
563 {
564
565
566 Accept = co_MsqTranslateMouseMessage(MessageQueue, Window, FilterLow, FilterHigh,
567 Current, Remove, &Freed,
568 DesktopWindow, &ScreenPoint, FALSE, &CurrentEntry);
569 if (Accept)
570 {
571 if (Remove)
572 {
573 RemoveEntryList(&Current->ListEntry);
574 }
575 IntUnLockHardwareMessageQueue(MessageQueue);
576 IntUnLockSystemHardwareMessageQueueLock(FALSE);
577 *Message = Current;
578
579 if (Desk)
580 Desk->LastInputWasKbd = FALSE;
581
582 RETURN(TRUE);
583 }
584
585 }
586 }
587 IntUnLockHardwareMessageQueue(MessageQueue);
588
589 /* Now try the global queue. */
590
591 /* Transfer all messages from the DPC accessible queue to the main queue. */
592 IntLockSystemMessageQueue(OldIrql);
593 while (SystemMessageQueueCount > 0)
594 {
595 PUSER_MESSAGE UserMsg;
596 MSG Msg;
597 BOOL ProcessMessage;
598
599 ASSERT(SystemMessageQueueHead < SYSTEM_MESSAGE_QUEUE_SIZE);
600 Msg = SystemMessageQueue[SystemMessageQueueHead];
601 SystemMessageQueueHead =
602 (SystemMessageQueueHead + 1) % SYSTEM_MESSAGE_QUEUE_SIZE;
603 SystemMessageQueueCount--;
604 IntUnLockSystemMessageQueue(OldIrql);
605 if (WM_MOUSEFIRST <= Msg.message && Msg.message <= WM_MOUSELAST)
606 {
607 MSLLHOOKSTRUCT MouseHookData;
608
609 MouseHookData.pt.x = LOWORD(Msg.lParam);
610 MouseHookData.pt.y = HIWORD(Msg.lParam);
611 switch(Msg.message)
612 {
613 case WM_MOUSEWHEEL:
614 MouseHookData.mouseData = MAKELONG(0, GET_WHEEL_DELTA_WPARAM(Msg.wParam));
615 break;
616 case WM_XBUTTONDOWN:
617 case WM_XBUTTONUP:
618 case WM_XBUTTONDBLCLK:
619 case WM_NCXBUTTONDOWN:
620 case WM_NCXBUTTONUP:
621 case WM_NCXBUTTONDBLCLK:
622 MouseHookData.mouseData = MAKELONG(0, HIWORD(Msg.wParam));
623 break;
624 default:
625 MouseHookData.mouseData = 0;
626 break;
627 }
628 MouseHookData.flags = 0;
629 MouseHookData.time = Msg.time;
630 MouseHookData.dwExtraInfo = 0;
631 ProcessMessage = (0 == co_HOOK_CallHooks(WH_MOUSE_LL, HC_ACTION,
632 Msg.message, (LPARAM) &MouseHookData));
633 }
634 else
635 {
636 ProcessMessage = TRUE;
637 }
638 if (ProcessMessage)
639 {
640 UserMsg = ExAllocateFromPagedLookasideList(&MessageLookasideList);
641 /* What to do if out of memory? For now we just panic a bit in debug */
642 ASSERT(UserMsg);
643 UserMsg->FreeLParam = FALSE;
644 UserMsg->Msg = Msg;
645 InsertTailList(&HardwareMessageQueueHead, &UserMsg->ListEntry);
646 }
647 IntLockSystemMessageQueue(OldIrql);
648 }
649 HardwareMessageQueueStamp++;
650 IntUnLockSystemMessageQueue(OldIrql);
651
652 /* Process messages in the queue until we find one to return. */
653 CurrentEntry = HardwareMessageQueueHead.Flink;
654 while (CurrentEntry != &HardwareMessageQueueHead)
655 {
656 PUSER_MESSAGE Current =
657 CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, ListEntry);
658 CurrentEntry = CurrentEntry->Flink;
659 RemoveEntryList(&Current->ListEntry);
660 HardwareMessageQueueStamp++;
661 if (Current->Msg.message >= WM_MOUSEFIRST &&
662 Current->Msg.message <= WM_MOUSELAST)
663 {
664 const ULONG ActiveStamp = HardwareMessageQueueStamp;
665 /* Translate the message. */
666 Accept = co_MsqTranslateMouseMessage(MessageQueue, Window, FilterLow, FilterHigh,
667 Current, Remove, &Freed,
668 DesktopWindow, &ScreenPoint, TRUE, NULL);
669 if (Accept)
670 {
671 /* Check for no more messages in the system queue. */
672 IntLockSystemMessageQueue(OldIrql);
673 if (SystemMessageQueueCount == 0 &&
674 IsListEmpty(&HardwareMessageQueueHead))
675 {
676 KeClearEvent(&HardwareMessageEvent);
677 }
678 IntUnLockSystemMessageQueue(OldIrql);
679
680 /*
681 If we aren't removing the message then add it to the private
682 queue.
683 */
684 if (!Remove)
685 {
686 IntLockHardwareMessageQueue(MessageQueue);
687 if(Current->Msg.message == WM_MOUSEMOVE)
688 {
689 if(MessageQueue->MouseMoveMsg)
690 {
691 RemoveEntryList(&MessageQueue->MouseMoveMsg->ListEntry);
692 ExFreePool(MessageQueue->MouseMoveMsg);
693 }
694 MessageQueue->MouseMoveMsg = Current;
695 }
696 InsertTailList(&MessageQueue->HardwareMessagesListHead,
697 &Current->ListEntry);
698 IntUnLockHardwareMessageQueue(MessageQueue);
699 }
700 IntUnLockSystemHardwareMessageQueueLock(FALSE);
701 *Message = Current;
702
703 RETURN(TRUE);
704 }
705 /* If the contents of the queue changed then restart processing. */
706 if (HardwareMessageQueueStamp != ActiveStamp)
707 {
708 CurrentEntry = HardwareMessageQueueHead.Flink;
709 continue;
710 }
711 }
712 }
713
714 /* Check if the system message queue is now empty. */
715 IntLockSystemMessageQueue(OldIrql);
716 if (SystemMessageQueueCount == 0 && IsListEmpty(&HardwareMessageQueueHead))
717 {
718 KeClearEvent(&HardwareMessageEvent);
719 }
720 IntUnLockSystemMessageQueue(OldIrql);
721 IntUnLockSystemHardwareMessageQueueLock(FALSE);
722
723 RETURN(FALSE);
724
725 CLEANUP:
726 if (DesktopWindow) UserDerefObjectCo(DesktopWindow);
727
728 END_CLEANUP;
729 }
730
731 //
732 // Note: Only called from input.c.
733 //
734 VOID FASTCALL
735 co_MsqPostKeyboardMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
736 {
737 PUSER_MESSAGE_QUEUE FocusMessageQueue;
738 MSG Msg;
739 LARGE_INTEGER LargeTickCount;
740 KBDLLHOOKSTRUCT KbdHookData;
741 EVENTMSG Event;
742 BOOLEAN Entered = FALSE;
743
744 DPRINT("MsqPostKeyboardMessage(uMsg 0x%x, wParam 0x%x, lParam 0x%x)\n",
745 uMsg, wParam, lParam);
746
747 // Condition may arise when calling MsqPostMessage and waiting for an event.
748 if (!UserIsEntered())
749 {
750 // Fixme: Not sure ATM if this thread is locked.
751 UserEnterExclusive();
752 Entered = TRUE;
753 }
754
755 FocusMessageQueue = IntGetFocusMessageQueue();
756
757 Msg.hwnd = 0;
758
759 if (FocusMessageQueue && (FocusMessageQueue->FocusWindow != (HWND)0))
760 Msg.hwnd = FocusMessageQueue->FocusWindow;
761
762 Msg.message = uMsg;
763 Msg.wParam = wParam;
764 Msg.lParam = lParam;
765
766 KeQueryTickCount(&LargeTickCount);
767 Msg.time = MsqCalculateMessageTime(&LargeTickCount);
768
769 Event.message = Msg.message;
770 Event.hwnd = Msg.hwnd;
771 Event.time = Msg.time;
772 Event.paramL = (Msg.wParam & 0xFF) | (HIWORD(Msg.lParam) << 8);
773 Event.paramH = Msg.lParam & 0x7FFF;
774 if (HIWORD(Msg.lParam) & 0x0100) Event.paramH |= 0x8000;
775 co_HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&Event);
776
777 /* We can't get the Msg.pt point here since we don't know thread
778 (and thus the window station) the message will end up in yet. */
779
780 KbdHookData.vkCode = Msg.wParam;
781 KbdHookData.scanCode = (Msg.lParam >> 16) & 0xff;
782 KbdHookData.flags = (0 == (Msg.lParam & 0x01000000) ? 0 : LLKHF_EXTENDED) |
783 (0 == (Msg.lParam & 0x20000000) ? 0 : LLKHF_ALTDOWN) |
784 (0 == (Msg.lParam & 0x80000000) ? 0 : LLKHF_UP);
785 KbdHookData.time = Msg.time;
786 KbdHookData.dwExtraInfo = 0;
787 if (co_HOOK_CallHooks(WH_KEYBOARD_LL, HC_ACTION, Msg.message, (LPARAM) &KbdHookData))
788 {
789 DPRINT("Kbd msg %d wParam %d lParam 0x%08x dropped by WH_KEYBOARD_LL hook\n",
790 Msg.message, Msg.wParam, Msg.lParam);
791 if (Entered) UserLeave();
792 return;
793 }
794
795 if (FocusMessageQueue == NULL)
796 {
797 DPRINT("No focus message queue\n");
798 if (Entered) UserLeave();
799 return;
800 }
801
802 if (FocusMessageQueue->FocusWindow != (HWND)0)
803 {
804 Msg.hwnd = FocusMessageQueue->FocusWindow;
805 DPRINT("Msg.hwnd = %x\n", Msg.hwnd);
806
807 FocusMessageQueue->Desktop->DesktopInfo->LastInputWasKbd = TRUE;
808
809 IntGetCursorLocation(FocusMessageQueue->Desktop->WindowStation,
810 &Msg.pt);
811 MsqPostMessage(FocusMessageQueue, &Msg, FALSE, QS_KEY);
812 }
813 else
814 {
815 DPRINT("Invalid focus window handle\n");
816 }
817
818 if (Entered) UserLeave();
819 return;
820 }
821
822 VOID FASTCALL
823 MsqPostHotKeyMessage(PVOID Thread, HWND hWnd, WPARAM wParam, LPARAM lParam)
824 {
825 PWINDOW_OBJECT Window;
826 PTHREADINFO Win32Thread;
827 PWINSTATION_OBJECT WinSta;
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 WinSta = Win32Thread->Desktop->WindowStation;
847 Window = IntGetWindowObject(hWnd);
848 if (!Window)
849 {
850 ObDereferenceObject ((PETHREAD)Thread);
851 return;
852 }
853
854 Mesg.hwnd = hWnd;
855 Mesg.message = WM_HOTKEY;
856 Mesg.wParam = wParam;
857 Mesg.lParam = lParam;
858 KeQueryTickCount(&LargeTickCount);
859 Mesg.time = MsqCalculateMessageTime(&LargeTickCount);
860 IntGetCursorLocation(WinSta, &Mesg.pt);
861 MsqPostMessage(Window->MessageQueue, &Mesg, FALSE, QS_HOTKEY);
862 UserDereferenceObject(Window);
863 ObDereferenceObject (Thread);
864
865 // InsertHeadList(&pThread->MessageQueue->PostedMessagesListHead,
866 // &Message->ListEntry);
867 // KeSetEvent(pThread->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
868 }
869
870 PUSER_MESSAGE FASTCALL
871 MsqCreateMessage(LPMSG Msg, BOOLEAN FreeLParam)
872 {
873 PUSER_MESSAGE Message;
874
875 Message = ExAllocateFromPagedLookasideList(&MessageLookasideList);
876 if (!Message)
877 {
878 return NULL;
879 }
880
881 Message->FreeLParam = FreeLParam;
882 RtlMoveMemory(&Message->Msg, Msg, sizeof(MSG));
883
884 return Message;
885 }
886
887 VOID FASTCALL
888 MsqDestroyMessage(PUSER_MESSAGE Message)
889 {
890 ExFreeToPagedLookasideList(&MessageLookasideList, Message);
891 }
892
893 VOID FASTCALL
894 co_MsqDispatchSentNotifyMessages(PUSER_MESSAGE_QUEUE MessageQueue)
895 {
896 PLIST_ENTRY ListEntry;
897 PUSER_SENT_MESSAGE_NOTIFY Message;
898
899 while (!IsListEmpty(&MessageQueue->SentMessagesListHead))
900 {
901 ListEntry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
902 Message = CONTAINING_RECORD(ListEntry, USER_SENT_MESSAGE_NOTIFY,
903 ListEntry);
904
905 co_IntCallSentMessageCallback(Message->CompletionCallback,
906 Message->hWnd,
907 Message->Msg,
908 Message->CompletionCallbackContext,
909 Message->Result);
910
911 }
912
913 }
914
915 BOOLEAN FASTCALL
916 MsqPeekSentMessages(PUSER_MESSAGE_QUEUE MessageQueue)
917 {
918 return(!IsListEmpty(&MessageQueue->SentMessagesListHead));
919 }
920
921 BOOLEAN FASTCALL
922 co_MsqDispatchOneSentMessage(PUSER_MESSAGE_QUEUE MessageQueue)
923 {
924 PUSER_SENT_MESSAGE Message;
925 PLIST_ENTRY Entry;
926 LRESULT Result;
927 BOOL SenderReturned;
928 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage;
929
930 if (IsListEmpty(&MessageQueue->SentMessagesListHead))
931 {
932 return(FALSE);
933 }
934
935 /* remove it from the list of pending messages */
936 Entry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
937 Message = CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry);
938
939 /* insert it to the list of messages that are currently dispatched by this
940 message queue */
941 InsertTailList(&MessageQueue->LocalDispatchingMessagesHead,
942 &Message->ListEntry);
943
944 if (Message->HookMessage == MSQ_ISHOOK)
945 {
946 Result = co_HOOK_CallHooks(Message->Msg.message,
947 (INT)(INT_PTR)Message->Msg.hwnd,
948 Message->Msg.wParam,
949 Message->Msg.lParam);
950 }
951 else if (Message->HookMessage == MSQ_ISEVENT)
952 {
953 Result = co_EVENT_CallEvents( Message->Msg.message,
954 Message->Msg.hwnd,
955 Message->Msg.wParam,
956 Message->Msg.lParam);
957 }
958 else
959 {
960 /* Call the window procedure. */
961 Result = co_IntSendMessage(Message->Msg.hwnd,
962 Message->Msg.message,
963 Message->Msg.wParam,
964 Message->Msg.lParam);
965 }
966
967 /* remove the message from the local dispatching list, because it doesn't need
968 to be cleaned up on thread termination anymore */
969 RemoveEntryList(&Message->ListEntry);
970
971 /* remove the message from the dispatching list, so lock the sender's message queue */
972 SenderReturned = (Message->DispatchingListEntry.Flink == NULL);
973 if(!SenderReturned)
974 {
975 /* only remove it from the dispatching list if not already removed by a timeout */
976 RemoveEntryList(&Message->DispatchingListEntry);
977 }
978 /* still keep the sender's message queue locked, so the sender can't exit the
979 MsqSendMessage() function (if timed out) */
980
981 /* Let the sender know the result. */
982 if (Message->Result != NULL)
983 {
984 *Message->Result = Result;
985 }
986
987 /* Notify the sender. */
988 if (Message->CompletionEvent != NULL)
989 {
990 KeSetEvent(Message->CompletionEvent, IO_NO_INCREMENT, FALSE);
991 }
992
993 /* Notify the sender if they specified a callback. */
994 if (!SenderReturned && Message->CompletionCallback != NULL)
995 {
996 if(!(NotifyMessage = ExAllocatePoolWithTag(NonPagedPool,
997 sizeof(USER_SENT_MESSAGE_NOTIFY), TAG_USRMSG)))
998 {
999 DPRINT1("MsqDispatchOneSentMessage(): Not enough memory to create a callback notify message\n");
1000 goto Notified;
1001 }
1002 NotifyMessage->CompletionCallback =
1003 Message->CompletionCallback;
1004 NotifyMessage->CompletionCallbackContext =
1005 Message->CompletionCallbackContext;
1006 NotifyMessage->Result = Result;
1007 NotifyMessage->hWnd = Message->Msg.hwnd;
1008 NotifyMessage->Msg = Message->Msg.message;
1009 MsqSendNotifyMessage(Message->SenderQueue, NotifyMessage);
1010 }
1011
1012 Notified:
1013
1014 /* dereference both sender and our queue */
1015 IntDereferenceMessageQueue(MessageQueue);
1016 IntDereferenceMessageQueue(Message->SenderQueue);
1017
1018 /* free the message */
1019 ExFreePool(Message);
1020 return(TRUE);
1021 }
1022
1023 VOID APIENTRY
1024 MsqRemoveWindowMessagesFromQueue(PVOID pWindow)
1025 {
1026 PUSER_SENT_MESSAGE SentMessage;
1027 PUSER_MESSAGE PostedMessage;
1028 PUSER_MESSAGE_QUEUE MessageQueue;
1029 PLIST_ENTRY CurrentEntry, ListHead;
1030 PWINDOW_OBJECT Window = pWindow;
1031
1032 ASSERT(Window);
1033
1034 MessageQueue = Window->MessageQueue;
1035 ASSERT(MessageQueue);
1036
1037 /* remove the posted messages for this window */
1038 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
1039 ListHead = &MessageQueue->PostedMessagesListHead;
1040 while (CurrentEntry != ListHead)
1041 {
1042 PostedMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1043 ListEntry);
1044 if (PostedMessage->Msg.hwnd == Window->hSelf)
1045 {
1046 RemoveEntryList(&PostedMessage->ListEntry);
1047 MsqDestroyMessage(PostedMessage);
1048 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
1049 }
1050 else
1051 {
1052 CurrentEntry = CurrentEntry->Flink;
1053 }
1054 }
1055
1056 /* remove the sent messages for this window */
1057 CurrentEntry = MessageQueue->SentMessagesListHead.Flink;
1058 ListHead = &MessageQueue->SentMessagesListHead;
1059 while (CurrentEntry != ListHead)
1060 {
1061 SentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1062 ListEntry);
1063 if(SentMessage->Msg.hwnd == Window->hSelf)
1064 {
1065 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1066
1067 RemoveEntryList(&SentMessage->ListEntry);
1068
1069 /* remove the message from the dispatching list */
1070 if(SentMessage->DispatchingListEntry.Flink != NULL)
1071 {
1072 RemoveEntryList(&SentMessage->DispatchingListEntry);
1073 }
1074
1075 /* wake the sender's thread */
1076 if (SentMessage->CompletionEvent != NULL)
1077 {
1078 KeSetEvent(SentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1079 }
1080
1081 /* dereference our and the sender's message queue */
1082 IntDereferenceMessageQueue(MessageQueue);
1083 IntDereferenceMessageQueue(SentMessage->SenderQueue);
1084
1085 /* free the message */
1086 ExFreePool(SentMessage);
1087
1088 CurrentEntry = MessageQueue->SentMessagesListHead.Flink;
1089 }
1090 else
1091 {
1092 CurrentEntry = CurrentEntry->Flink;
1093 }
1094 }
1095 }
1096
1097 VOID FASTCALL
1098 MsqSendNotifyMessage(PUSER_MESSAGE_QUEUE MessageQueue,
1099 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage)
1100 {
1101 InsertTailList(&MessageQueue->NotifyMessagesListHead,
1102 &NotifyMessage->ListEntry);
1103 MessageQueue->QueueBits |= QS_SENDMESSAGE;
1104 MessageQueue->ChangedBits |= QS_SENDMESSAGE;
1105 if (MessageQueue->WakeMask & QS_SENDMESSAGE)
1106 KeSetEvent(MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
1107 }
1108
1109 NTSTATUS FASTCALL
1110 co_MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue,
1111 HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam,
1112 UINT uTimeout, BOOL Block, INT HookMessage,
1113 ULONG_PTR *uResult)
1114 {
1115 PTHREADINFO pti;
1116 PUSER_SENT_MESSAGE Message;
1117 KEVENT CompletionEvent;
1118 NTSTATUS WaitStatus;
1119 LRESULT Result;
1120 PUSER_MESSAGE_QUEUE ThreadQueue;
1121 LARGE_INTEGER Timeout;
1122 PLIST_ENTRY Entry;
1123
1124 if(!(Message = ExAllocatePoolWithTag(PagedPool, sizeof(USER_SENT_MESSAGE), TAG_USRMSG)))
1125 {
1126 DPRINT1("MsqSendMessage(): Not enough memory to allocate a message");
1127 return STATUS_INSUFFICIENT_RESOURCES;
1128 }
1129
1130 KeInitializeEvent(&CompletionEvent, NotificationEvent, FALSE);
1131
1132 pti = PsGetCurrentThreadWin32Thread();
1133 ThreadQueue = pti->MessageQueue;
1134 ASSERT(ThreadQueue != MessageQueue);
1135
1136 Timeout.QuadPart = (LONGLONG) uTimeout * (LONGLONG) -10000;
1137
1138 /* FIXME - increase reference counter of sender's message queue here */
1139
1140 Result = 0;
1141 Message->Msg.hwnd = Wnd;
1142 Message->Msg.message = Msg;
1143 Message->Msg.wParam = wParam;
1144 Message->Msg.lParam = lParam;
1145 Message->CompletionEvent = &CompletionEvent;
1146 Message->Result = &Result;
1147 Message->SenderQueue = ThreadQueue;
1148 IntReferenceMessageQueue(ThreadQueue);
1149 Message->CompletionCallback = NULL;
1150 Message->HookMessage = HookMessage;
1151
1152 IntReferenceMessageQueue(MessageQueue);
1153
1154 /* add it to the list of pending messages */
1155 InsertTailList(&ThreadQueue->DispatchingMessagesHead, &Message->DispatchingListEntry);
1156
1157 /* queue it in the destination's message queue */
1158 InsertTailList(&MessageQueue->SentMessagesListHead, &Message->ListEntry);
1159
1160 MessageQueue->QueueBits |= QS_SENDMESSAGE;
1161 MessageQueue->ChangedBits |= QS_SENDMESSAGE;
1162 if (MessageQueue->WakeMask & QS_SENDMESSAGE)
1163 KeSetEvent(MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
1164
1165 /* we can't access the Message anymore since it could have already been deleted! */
1166
1167 if(Block)
1168 {
1169 IdlePing();
1170
1171 UserLeaveCo();
1172
1173 /* don't process messages sent to the thread */
1174 WaitStatus = KeWaitForSingleObject(&CompletionEvent, UserRequest, UserMode,
1175 FALSE, (uTimeout ? &Timeout : NULL));
1176
1177 UserEnterCo();
1178
1179 if(WaitStatus == STATUS_TIMEOUT)
1180 {
1181 /* look up if the message has not yet dispatched, if so
1182 make sure it can't pass a result and it must not set the completion event anymore */
1183 Entry = MessageQueue->SentMessagesListHead.Flink;
1184 while (Entry != &MessageQueue->SentMessagesListHead)
1185 {
1186 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry)
1187 == Message)
1188 {
1189 /* we can access Message here, it's secure because the message queue is locked
1190 and the message is still hasn't been dispatched */
1191 Message->CompletionEvent = NULL;
1192 Message->Result = NULL;
1193 break;
1194 }
1195 Entry = Entry->Flink;
1196 }
1197
1198 /* remove from the local dispatching list so the other thread knows,
1199 it can't pass a result and it must not set the completion event anymore */
1200 Entry = ThreadQueue->DispatchingMessagesHead.Flink;
1201 while (Entry != &ThreadQueue->DispatchingMessagesHead)
1202 {
1203 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, DispatchingListEntry)
1204 == Message)
1205 {
1206 /* we can access Message here, it's secure because the sender's message is locked
1207 and the message has definitely not yet been destroyed, otherwise it would
1208 have been removed from this list by the dispatching routine right after
1209 dispatching the message */
1210 Message->CompletionEvent = NULL;
1211 Message->Result = NULL;
1212 RemoveEntryList(&Message->DispatchingListEntry);
1213 Message->DispatchingListEntry.Flink = NULL;
1214 break;
1215 }
1216 Entry = Entry->Flink;
1217 }
1218
1219 DPRINT("MsqSendMessage (blocked) timed out\n");
1220 }
1221 while (co_MsqDispatchOneSentMessage(ThreadQueue))
1222 ;
1223 }
1224 else
1225 {
1226 PVOID WaitObjects[2];
1227
1228 WaitObjects[0] = &CompletionEvent;
1229 WaitObjects[1] = ThreadQueue->NewMessages;
1230 do
1231 {
1232 IdlePing();
1233
1234 UserLeaveCo();
1235
1236 WaitStatus = KeWaitForMultipleObjects(2, WaitObjects, WaitAny, UserRequest,
1237 UserMode, FALSE, (uTimeout ? &Timeout : NULL), NULL);
1238
1239 UserEnterCo();
1240
1241 if(WaitStatus == STATUS_TIMEOUT)
1242 {
1243 /* look up if the message has not yet been dispatched, if so
1244 make sure it can't pass a result and it must not set the completion event anymore */
1245 Entry = MessageQueue->SentMessagesListHead.Flink;
1246 while (Entry != &MessageQueue->SentMessagesListHead)
1247 {
1248 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry)
1249 == Message)
1250 {
1251 /* we can access Message here, it's secure because the message queue is locked
1252 and the message is still hasn't been dispatched */
1253 Message->CompletionEvent = NULL;
1254 Message->Result = NULL;
1255 break;
1256 }
1257 Entry = Entry->Flink;
1258 }
1259
1260 /* remove from the local dispatching list so the other thread knows,
1261 it can't pass a result and it must not set the completion event anymore */
1262 Entry = ThreadQueue->DispatchingMessagesHead.Flink;
1263 while (Entry != &ThreadQueue->DispatchingMessagesHead)
1264 {
1265 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, DispatchingListEntry)
1266 == Message)
1267 {
1268 /* we can access Message here, it's secure because the sender's message is locked
1269 and the message has definitely not yet been destroyed, otherwise it would
1270 have been removed from this list by the dispatching routine right after
1271 dispatching the message */
1272 Message->CompletionEvent = NULL;
1273 Message->Result = NULL;
1274 RemoveEntryList(&Message->DispatchingListEntry);
1275 Message->DispatchingListEntry.Flink = NULL;
1276 break;
1277 }
1278 Entry = Entry->Flink;
1279 }
1280
1281 DPRINT("MsqSendMessage timed out\n");
1282 break;
1283 }
1284 while (co_MsqDispatchOneSentMessage(ThreadQueue))
1285 ;
1286 }
1287 while (NT_SUCCESS(WaitStatus) && STATUS_WAIT_0 != WaitStatus);
1288 }
1289
1290 if(WaitStatus != STATUS_TIMEOUT)
1291 *uResult = (STATUS_WAIT_0 == WaitStatus ? Result : -1);
1292
1293 return WaitStatus;
1294 }
1295
1296 VOID FASTCALL
1297 MsqPostMessage(PUSER_MESSAGE_QUEUE MessageQueue, MSG* Msg, BOOLEAN FreeLParam,
1298 DWORD MessageBits)
1299 {
1300 PUSER_MESSAGE Message;
1301
1302 if(!(Message = MsqCreateMessage(Msg, FreeLParam)))
1303 {
1304 return;
1305 }
1306 InsertTailList(&MessageQueue->PostedMessagesListHead,
1307 &Message->ListEntry);
1308 MessageQueue->QueueBits |= MessageBits;
1309 MessageQueue->ChangedBits |= MessageBits;
1310 if (MessageQueue->WakeMask & MessageBits)
1311 KeSetEvent(MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
1312 }
1313
1314 VOID FASTCALL
1315 MsqPostQuitMessage(PUSER_MESSAGE_QUEUE MessageQueue, ULONG ExitCode)
1316 {
1317 MessageQueue->QuitPosted = TRUE;
1318 MessageQueue->QuitExitCode = ExitCode;
1319 MessageQueue->QueueBits |= QS_POSTMESSAGE;
1320 MessageQueue->ChangedBits |= QS_POSTMESSAGE;
1321 if (MessageQueue->WakeMask & QS_POSTMESSAGE)
1322 KeSetEvent(MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
1323 }
1324
1325 BOOLEAN APIENTRY
1326 co_MsqFindMessage(IN PUSER_MESSAGE_QUEUE MessageQueue,
1327 IN BOOLEAN Hardware,
1328 IN BOOLEAN Remove,
1329 IN PWINDOW_OBJECT Window,
1330 IN UINT MsgFilterLow,
1331 IN UINT MsgFilterHigh,
1332 OUT PUSER_MESSAGE* Message)
1333 {
1334 PLIST_ENTRY CurrentEntry;
1335 PUSER_MESSAGE CurrentMessage;
1336 PLIST_ENTRY ListHead;
1337
1338 if (Hardware)
1339 {
1340 return(co_MsqPeekHardwareMessage(MessageQueue, Window,
1341 MsgFilterLow, MsgFilterHigh,
1342 Remove, Message));
1343 }
1344
1345 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
1346 ListHead = &MessageQueue->PostedMessagesListHead;
1347 while (CurrentEntry != ListHead)
1348 {
1349 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1350 ListEntry);
1351 if ((!Window || (int)Window == 1 || Window->hSelf == CurrentMessage->Msg.hwnd) &&
1352 ((MsgFilterLow == 0 && MsgFilterHigh == 0) ||
1353 (MsgFilterLow <= CurrentMessage->Msg.message &&
1354 MsgFilterHigh >= CurrentMessage->Msg.message)))
1355 {
1356 if (Remove)
1357 {
1358 RemoveEntryList(&CurrentMessage->ListEntry);
1359 }
1360
1361 *Message = CurrentMessage;
1362 return(TRUE);
1363 }
1364 CurrentEntry = CurrentEntry->Flink;
1365 }
1366
1367 return(FALSE);
1368 }
1369
1370 NTSTATUS FASTCALL
1371 co_MsqWaitForNewMessages(PUSER_MESSAGE_QUEUE MessageQueue, PWINDOW_OBJECT WndFilter,
1372 UINT MsgFilterMin, UINT MsgFilterMax)
1373 {
1374 PVOID WaitObjects[2] = {MessageQueue->NewMessages, &HardwareMessageEvent};
1375 LARGE_INTEGER TimerExpiry;
1376 PLARGE_INTEGER Timeout;
1377 NTSTATUS ret;
1378
1379 if (MsqGetFirstTimerExpiry(MessageQueue, WndFilter, MsgFilterMin, MsgFilterMax, &TimerExpiry))
1380 {
1381 Timeout = &TimerExpiry;
1382 }
1383 else
1384 {
1385 Timeout = NULL;
1386 }
1387
1388 IdlePing(); // Going to wait so send Idle ping.
1389
1390 UserLeaveCo();
1391
1392 ret = KeWaitForMultipleObjects(2,
1393 WaitObjects,
1394 WaitAny,
1395 Executive,
1396 UserMode,
1397 FALSE,
1398 Timeout,
1399 NULL);
1400
1401 UserEnterCo();
1402
1403 return ret;
1404 }
1405
1406 BOOL FASTCALL
1407 MsqIsHung(PUSER_MESSAGE_QUEUE MessageQueue)
1408 {
1409 LARGE_INTEGER LargeTickCount;
1410
1411 KeQueryTickCount(&LargeTickCount);
1412 return ((LargeTickCount.u.LowPart - MessageQueue->LastMsgRead) > MSQ_HUNG);
1413 }
1414
1415 BOOLEAN FASTCALL
1416 MsqInitializeMessageQueue(struct _ETHREAD *Thread, PUSER_MESSAGE_QUEUE MessageQueue)
1417 {
1418 LARGE_INTEGER LargeTickCount;
1419 NTSTATUS Status;
1420
1421 MessageQueue->Thread = Thread;
1422 MessageQueue->CaretInfo = (PTHRDCARETINFO)(MessageQueue + 1);
1423 InitializeListHead(&MessageQueue->PostedMessagesListHead);
1424 InitializeListHead(&MessageQueue->SentMessagesListHead);
1425 InitializeListHead(&MessageQueue->HardwareMessagesListHead);
1426 InitializeListHead(&MessageQueue->TimerListHead);
1427 InitializeListHead(&MessageQueue->DispatchingMessagesHead);
1428 InitializeListHead(&MessageQueue->LocalDispatchingMessagesHead);
1429 KeInitializeMutex(&MessageQueue->HardwareLock, 0);
1430 MessageQueue->QuitPosted = FALSE;
1431 MessageQueue->QuitExitCode = 0;
1432 KeQueryTickCount(&LargeTickCount);
1433 MessageQueue->LastMsgRead = LargeTickCount.u.LowPart;
1434 MessageQueue->FocusWindow = NULL;
1435 MessageQueue->PaintCount = 0;
1436 // HACK!!!!!!! Newbies that wrote this should hold your head down in shame! (jt)
1437 MessageQueue->WakeMask = ~0;
1438 MessageQueue->NewMessagesHandle = NULL;
1439
1440 Status = ZwCreateEvent(&MessageQueue->NewMessagesHandle, EVENT_ALL_ACCESS,
1441 NULL, SynchronizationEvent, FALSE);
1442 if (!NT_SUCCESS(Status))
1443 {
1444 return FALSE;
1445 }
1446
1447 Status = ObReferenceObjectByHandle(MessageQueue->NewMessagesHandle, 0,
1448 ExEventObjectType, KernelMode,
1449 (PVOID*)&MessageQueue->NewMessages, NULL);
1450 if (!NT_SUCCESS(Status))
1451 {
1452 ZwClose(MessageQueue->NewMessagesHandle);
1453 MessageQueue->NewMessagesHandle = NULL;
1454 return FALSE;
1455 }
1456
1457 return TRUE;
1458 }
1459
1460 VOID FASTCALL
1461 MsqCleanupMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
1462 {
1463 PLIST_ENTRY CurrentEntry;
1464 PUSER_MESSAGE CurrentMessage;
1465 PTIMER_ENTRY CurrentTimer;
1466 PUSER_SENT_MESSAGE CurrentSentMessage;
1467
1468 /* cleanup posted messages */
1469 while (!IsListEmpty(&MessageQueue->PostedMessagesListHead))
1470 {
1471 CurrentEntry = RemoveHeadList(&MessageQueue->PostedMessagesListHead);
1472 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1473 ListEntry);
1474 MsqDestroyMessage(CurrentMessage);
1475 }
1476
1477 /* remove the messages that have not yet been dispatched */
1478 while (!IsListEmpty(&MessageQueue->SentMessagesListHead))
1479 {
1480 CurrentEntry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
1481 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1482 ListEntry);
1483
1484 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1485
1486 /* remove the message from the dispatching list */
1487 if(CurrentSentMessage->DispatchingListEntry.Flink != NULL)
1488 {
1489 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
1490 }
1491
1492 /* wake the sender's thread */
1493 if (CurrentSentMessage->CompletionEvent != NULL)
1494 {
1495 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1496 }
1497
1498 /* dereference our and the sender's message queue */
1499 IntDereferenceMessageQueue(MessageQueue);
1500 IntDereferenceMessageQueue(CurrentSentMessage->SenderQueue);
1501
1502 /* free the message */
1503 ExFreePool(CurrentSentMessage);
1504 }
1505
1506 /* cleanup timers */
1507 while (! IsListEmpty(&MessageQueue->TimerListHead))
1508 {
1509 CurrentEntry = RemoveHeadList(&MessageQueue->TimerListHead);
1510 CurrentTimer = CONTAINING_RECORD(CurrentEntry, TIMER_ENTRY, ListEntry);
1511 ExFreeToPagedLookasideList(&TimerLookasideList, CurrentTimer);
1512 }
1513
1514 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
1515 ExitThread() was called in a SendMessage() umode callback */
1516 while (!IsListEmpty(&MessageQueue->LocalDispatchingMessagesHead))
1517 {
1518 CurrentEntry = RemoveHeadList(&MessageQueue->LocalDispatchingMessagesHead);
1519 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1520 ListEntry);
1521
1522 /* remove the message from the dispatching list */
1523 if(CurrentSentMessage->DispatchingListEntry.Flink != NULL)
1524 {
1525 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
1526 }
1527
1528 DPRINT("Notify the sender, the thread has been terminated while dispatching a message!\n");
1529
1530 /* wake the sender's thread */
1531 if (CurrentSentMessage->CompletionEvent != NULL)
1532 {
1533 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1534 }
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 PTHREADINFO pti;
1845
1846 DPRINT("MsqGetTimerMessage queue %p msg %p restart %s\n",
1847 MessageQueue, Msg, Restart ? "TRUE" : "FALSE");
1848
1849 KeQuerySystemTime(&CurrentTime);
1850 DPRINT("Current time %I64d\n", CurrentTime.QuadPart);
1851 EnumEntry = MessageQueue->TimerListHead.Flink;
1852 GotMessage = FALSE;
1853 while (EnumEntry != &MessageQueue->TimerListHead)
1854 {
1855 Timer = CONTAINING_RECORD(MessageQueue->TimerListHead.Flink,
1856 TIMER_ENTRY, ListEntry);
1857 DPRINT("Checking timer %p wnd %x expiry %I64d\n", Timer, Timer->Wnd,
1858 Timer->ExpiryTime.QuadPart);
1859 EnumEntry = EnumEntry->Flink;
1860 if ((NULL == WindowFilter || Timer->Wnd == WindowFilter->hSelf) &&
1861 ((MsgFilterMin == 0 && MsgFilterMax == 0) ||
1862 (MsgFilterMin <= Timer->Msg &&
1863 Timer->Msg <= MsgFilterMax)))
1864 {
1865 if (Timer->ExpiryTime.QuadPart <= CurrentTime.QuadPart)
1866 {
1867 DPRINT("Timer is expired\n");
1868 GotMessage = TRUE;
1869 break;
1870 }
1871 else
1872 {
1873 DPRINT("No need to check later timers\n");
1874 break;
1875 }
1876 }
1877 else
1878 {
1879 DPRINT("timer %p (wnd %x msg %d) failed filter wnd %x msgmin %d msgmax %d\n",
1880 Timer, Timer->Wnd, Timer->Msg, WindowFilter->hSelf, MsgFilterMin, MsgFilterMax);
1881 }
1882 }
1883
1884 if (! GotMessage)
1885 {
1886 DPRINT("No timer pending\n");
1887 return FALSE;
1888 }
1889
1890 Msg->hwnd = Timer->Wnd;
1891 Msg->message = Timer->Msg;
1892 Msg->wParam = (WPARAM) Timer->IDEvent;
1893 Msg->lParam = (LPARAM) Timer->TimerFunc;
1894 KeQueryTickCount(&LargeTickCount);
1895 Msg->time = MsqCalculateMessageTime(&LargeTickCount);
1896 pti = PsGetCurrentThreadWin32Thread();
1897 IntGetCursorLocation(pti->Desktop->WindowStation,
1898 &Msg->pt);
1899
1900 if (Restart)
1901 {
1902 RemoveEntryList(&Timer->ListEntry);
1903 Timer->ExpiryTime.QuadPart = CurrentTime.QuadPart +
1904 (ULONGLONG) Timer->Period * (ULONGLONG) 10000;
1905 DPRINT("Restarting timer %p expires %I64d\n", Timer, Timer->ExpiryTime.QuadPart);
1906 InsertTimer(MessageQueue, Timer);
1907
1908 #ifndef NDEBUG
1909
1910 DumpTimerList(MessageQueue);
1911 #endif /* ! defined(NDEBUG) */
1912
1913 }
1914
1915 DPRINT("Created message wnd %x msg %d wParam %u lParam %u\n", Msg->hwnd, Msg->message,
1916 Msg->wParam, Msg->lParam);
1917
1918 return TRUE;
1919 }
1920
1921 VOID FASTCALL
1922 MsqRemoveTimersWindow(PUSER_MESSAGE_QUEUE MessageQueue, HWND Wnd)
1923 {
1924 PTIMER_ENTRY Timer;
1925 PLIST_ENTRY EnumEntry;
1926
1927 DPRINT("MsqRemoveTimersWindow queue %p wnd %x\n", MessageQueue, Wnd);
1928
1929 EnumEntry = MessageQueue->TimerListHead.Flink;
1930 while (EnumEntry != &MessageQueue->TimerListHead)
1931 {
1932 Timer = CONTAINING_RECORD(EnumEntry, TIMER_ENTRY, ListEntry);
1933 EnumEntry = EnumEntry->Flink;
1934 if (Timer->Wnd == Wnd)
1935 {
1936 DPRINT("Removing timer %p because its window is going away\n", Timer);
1937 RemoveEntryList(&Timer->ListEntry);
1938 ExFreeToPagedLookasideList(&TimerLookasideList, Timer);
1939 }
1940 }
1941
1942 #ifndef NDEBUG
1943 DumpTimerList(MessageQueue);
1944 #endif /* ! defined(NDEBUG) */
1945
1946 }
1947
1948 BOOLEAN FASTCALL
1949 MsqGetFirstTimerExpiry(PUSER_MESSAGE_QUEUE MessageQueue,
1950 PWINDOW_OBJECT WndFilter, UINT MsgFilterMin, UINT MsgFilterMax,
1951 PLARGE_INTEGER FirstTimerExpiry)
1952 {
1953 PTIMER_ENTRY Timer;
1954 PLIST_ENTRY EnumEntry;
1955
1956 DPRINT("MsqGetFirstTimerExpiry queue %p wndfilter %x msgfiltermin %d msgfiltermax %d expiry %p\n",
1957 MessageQueue, WndFilter, MsgFilterMin, MsgFilterMax, FirstTimerExpiry);
1958
1959 EnumEntry = MessageQueue->TimerListHead.Flink;
1960 while (EnumEntry != &MessageQueue->TimerListHead)
1961 {
1962 Timer = CONTAINING_RECORD(MessageQueue->TimerListHead.Flink,
1963 TIMER_ENTRY, ListEntry);
1964 EnumEntry = EnumEntry->Flink;
1965 if ((NULL == WndFilter || (int)WndFilter == 1 || Timer->Wnd == WndFilter->hSelf) &&
1966 ((MsgFilterMin == 0 && MsgFilterMax == 0) ||
1967 (MsgFilterMin <= Timer->Msg &&
1968 Timer->Msg <= MsgFilterMax)))
1969 {
1970 *FirstTimerExpiry = Timer->ExpiryTime;
1971 DPRINT("First timer expires %I64d\n", Timer->ExpiryTime);
1972 return TRUE;
1973 }
1974 }
1975
1976 return FALSE;
1977 }
1978
1979 /* EOF */