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