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