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