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