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