[win32k]
[reactos.git] / reactos / subsystems / win32 / win32k / ntuser / msgqueue.c
1 /*
2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 /*
20 * COPYRIGHT: See COPYING in the top level directory
21 * PROJECT: ReactOS kernel
22 * PURPOSE: Message queues
23 * FILE: subsystems/win32/win32k/ntuser/msgqueue.c
24 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
25 * REVISION HISTORY:
26 * 06-06-2001 CSH Created
27 */
28
29 /* INCLUDES ******************************************************************/
30
31 #include <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->Desktop == 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->Desktop == 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->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->MessageQueue);
422 InsertTailList(&CaptureWindow->MessageQueue->HardwareMessagesListHead,
423 &Message->ListEntry);
424 if(Message->Msg.message == WM_MOUSEMOVE)
425 {
426 if(CaptureWindow->MessageQueue->MouseMoveMsg)
427 {
428 /* remove the old WM_MOUSEMOVE message, we're processing a more recent
429 one */
430 RemoveEntryList(&CaptureWindow->MessageQueue->MouseMoveMsg->ListEntry);
431 ExFreePool(CaptureWindow->MessageQueue->MouseMoveMsg);
432 }
433 /* save the pointer to the WM_MOUSEMOVE message in the new queue */
434 CaptureWindow->MessageQueue->MouseMoveMsg = Message;
435
436 CaptureWindow->MessageQueue->QueueBits |= QS_MOUSEMOVE;
437 CaptureWindow->MessageQueue->ChangedBits |= QS_MOUSEMOVE;
438 if (CaptureWindow->MessageQueue->WakeMask & QS_MOUSEMOVE)
439 KeSetEvent(CaptureWindow->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
440 }
441 else
442 {
443 CaptureWindow->MessageQueue->QueueBits |= QS_MOUSEBUTTON;
444 CaptureWindow->MessageQueue->ChangedBits |= QS_MOUSEBUTTON;
445 if (CaptureWindow->MessageQueue->WakeMask & QS_MOUSEBUTTON)
446 KeSetEvent(CaptureWindow->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
447 }
448 IntUnLockHardwareMessageQueue(CaptureWindow->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 && (int)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->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->MessageQueue->HardwareMessagesListHead,
473 &Message->ListEntry);
474 }
475
476 if (Message->Msg.message == WM_MOUSEMOVE)
477 {
478 if(CaptureWindow->MessageQueue->MouseMoveMsg &&
479 (CaptureWindow->MessageQueue->MouseMoveMsg != Message))
480 {
481 /* delete the old message */
482 RemoveEntryList(&CaptureWindow->MessageQueue->MouseMoveMsg->ListEntry);
483 ExFreePool(CaptureWindow->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->MessageQueue->MouseMoveMsg = Message;
493 }
494 if(FromGlobalQueue)
495 {
496 IntUnLockHardwareMessageQueue(CaptureWindow->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->MessageQueue);
520 if(CaptureWindow->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->MessageQueue->MouseMoveMsg->ListEntry);
525 ExFreePool(CaptureWindow->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->MessageQueue->MouseMoveMsg = NULL;
529 }
530 IntUnLockHardwareMessageQueue(CaptureWindow->MessageQueue);
531 }
532 else if (CaptureWindow->MessageQueue->MouseMoveMsg == Message)
533 {
534 CaptureWindow->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->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->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, Window,
1367 MsgFilterLow, MsgFilterHigh,
1368 Remove, Message));
1369 }
1370
1371 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
1372 ListHead = &MessageQueue->PostedMessagesListHead;
1373 while (CurrentEntry != ListHead)
1374 {
1375 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1376 ListEntry);
1377 if ((!Window || (int)Window == 1 || Window->hSelf == CurrentMessage->Msg.hwnd) &&
1378 ((MsgFilterLow == 0 && MsgFilterHigh == 0) ||
1379 (MsgFilterLow <= CurrentMessage->Msg.message &&
1380 MsgFilterHigh >= CurrentMessage->Msg.message)))
1381 {
1382 if (Remove)
1383 {
1384 RemoveEntryList(&CurrentMessage->ListEntry);
1385 }
1386
1387 *Message = CurrentMessage;
1388 return(TRUE);
1389 }
1390 CurrentEntry = CurrentEntry->Flink;
1391 }
1392
1393 return(FALSE);
1394 }
1395
1396 NTSTATUS FASTCALL
1397 co_MsqWaitForNewMessages(PUSER_MESSAGE_QUEUE MessageQueue, PWINDOW_OBJECT WndFilter,
1398 UINT MsgFilterMin, UINT MsgFilterMax)
1399 {
1400 PVOID WaitObjects[2] = {MessageQueue->NewMessages, &HardwareMessageEvent};
1401 LARGE_INTEGER TimerExpiry;
1402 PLARGE_INTEGER Timeout;
1403 NTSTATUS ret;
1404
1405 if (MsqGetFirstTimerExpiry(MessageQueue, WndFilter, MsgFilterMin, MsgFilterMax, &TimerExpiry))
1406 {
1407 Timeout = &TimerExpiry;
1408 }
1409 else
1410 {
1411 Timeout = NULL;
1412 }
1413
1414 IdlePing(); // Going to wait so send Idle ping.
1415
1416 UserLeaveCo();
1417
1418 ret = KeWaitForMultipleObjects(2,
1419 WaitObjects,
1420 WaitAny,
1421 Executive,
1422 UserMode,
1423 FALSE,
1424 Timeout,
1425 NULL);
1426
1427 UserEnterCo();
1428
1429 return ret;
1430 }
1431
1432 BOOL FASTCALL
1433 MsqIsHung(PUSER_MESSAGE_QUEUE MessageQueue)
1434 {
1435 LARGE_INTEGER LargeTickCount;
1436
1437 KeQueryTickCount(&LargeTickCount);
1438 return ((LargeTickCount.u.LowPart - MessageQueue->LastMsgRead) > MSQ_HUNG);
1439 }
1440
1441 BOOLEAN FASTCALL
1442 MsqInitializeMessageQueue(struct _ETHREAD *Thread, PUSER_MESSAGE_QUEUE MessageQueue)
1443 {
1444 LARGE_INTEGER LargeTickCount;
1445 NTSTATUS Status;
1446
1447 MessageQueue->Thread = Thread;
1448 MessageQueue->CaretInfo = (PTHRDCARETINFO)(MessageQueue + 1);
1449 InitializeListHead(&MessageQueue->PostedMessagesListHead);
1450 InitializeListHead(&MessageQueue->SentMessagesListHead);
1451 InitializeListHead(&MessageQueue->HardwareMessagesListHead);
1452 InitializeListHead(&MessageQueue->TimerListHead);
1453 InitializeListHead(&MessageQueue->DispatchingMessagesHead);
1454 InitializeListHead(&MessageQueue->LocalDispatchingMessagesHead);
1455 KeInitializeMutex(&MessageQueue->HardwareLock, 0);
1456 MessageQueue->QuitPosted = FALSE;
1457 MessageQueue->QuitExitCode = 0;
1458 KeQueryTickCount(&LargeTickCount);
1459 MessageQueue->LastMsgRead = LargeTickCount.u.LowPart;
1460 MessageQueue->FocusWindow = NULL;
1461 MessageQueue->PaintCount = 0;
1462 // HACK!!!!!!! Newbies that wrote this should hold your head down in shame! (jt)
1463 MessageQueue->WakeMask = ~0;
1464 MessageQueue->NewMessagesHandle = NULL;
1465
1466 Status = ZwCreateEvent(&MessageQueue->NewMessagesHandle, EVENT_ALL_ACCESS,
1467 NULL, SynchronizationEvent, FALSE);
1468 if (!NT_SUCCESS(Status))
1469 {
1470 return FALSE;
1471 }
1472
1473 Status = ObReferenceObjectByHandle(MessageQueue->NewMessagesHandle, 0,
1474 ExEventObjectType, KernelMode,
1475 (PVOID*)&MessageQueue->NewMessages, NULL);
1476 if (!NT_SUCCESS(Status))
1477 {
1478 ZwClose(MessageQueue->NewMessagesHandle);
1479 MessageQueue->NewMessagesHandle = NULL;
1480 return FALSE;
1481 }
1482
1483 return TRUE;
1484 }
1485
1486 VOID FASTCALL
1487 MsqCleanupMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
1488 {
1489 PLIST_ENTRY CurrentEntry;
1490 PUSER_MESSAGE CurrentMessage;
1491 PTIMER_ENTRY CurrentTimer;
1492 PUSER_SENT_MESSAGE CurrentSentMessage;
1493
1494 /* cleanup posted messages */
1495 while (!IsListEmpty(&MessageQueue->PostedMessagesListHead))
1496 {
1497 CurrentEntry = RemoveHeadList(&MessageQueue->PostedMessagesListHead);
1498 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1499 ListEntry);
1500 MsqDestroyMessage(CurrentMessage);
1501 }
1502
1503 /* remove the messages that have not yet been dispatched */
1504 while (!IsListEmpty(&MessageQueue->SentMessagesListHead))
1505 {
1506 CurrentEntry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
1507 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1508 ListEntry);
1509
1510 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1511
1512 /* remove the message from the dispatching list */
1513 if(CurrentSentMessage->DispatchingListEntry.Flink != NULL)
1514 {
1515 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
1516 }
1517
1518 /* wake the sender's thread */
1519 if (CurrentSentMessage->CompletionEvent != NULL)
1520 {
1521 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1522 }
1523
1524 /* dereference our and the sender's message queue */
1525 IntDereferenceMessageQueue(MessageQueue);
1526 IntDereferenceMessageQueue(CurrentSentMessage->SenderQueue);
1527
1528 /* free the message */
1529 ExFreePool(CurrentSentMessage);
1530 }
1531
1532 /* cleanup timers */
1533 while (! IsListEmpty(&MessageQueue->TimerListHead))
1534 {
1535 CurrentEntry = RemoveHeadList(&MessageQueue->TimerListHead);
1536 CurrentTimer = CONTAINING_RECORD(CurrentEntry, TIMER_ENTRY, ListEntry);
1537 ExFreeToPagedLookasideList(&TimerLookasideList, CurrentTimer);
1538 }
1539
1540 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
1541 ExitThread() was called in a SendMessage() umode callback */
1542 while (!IsListEmpty(&MessageQueue->LocalDispatchingMessagesHead))
1543 {
1544 CurrentEntry = RemoveHeadList(&MessageQueue->LocalDispatchingMessagesHead);
1545 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1546 ListEntry);
1547
1548 /* remove the message from the dispatching list */
1549 if(CurrentSentMessage->DispatchingListEntry.Flink != NULL)
1550 {
1551 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
1552 }
1553
1554 DPRINT("Notify the sender, the thread has been terminated while dispatching a message!\n");
1555
1556 /* wake the sender's thread */
1557 if (CurrentSentMessage->CompletionEvent != NULL)
1558 {
1559 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1560 }
1561
1562 /* dereference our and the sender's message queue */
1563 IntDereferenceMessageQueue(MessageQueue);
1564 IntDereferenceMessageQueue(CurrentSentMessage->SenderQueue);
1565
1566 /* free the message */
1567 ExFreePool(CurrentSentMessage);
1568 }
1569
1570 /* tell other threads not to bother returning any info to us */
1571 while (! IsListEmpty(&MessageQueue->DispatchingMessagesHead))
1572 {
1573 CurrentEntry = RemoveHeadList(&MessageQueue->DispatchingMessagesHead);
1574 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1575 DispatchingListEntry);
1576 CurrentSentMessage->CompletionEvent = NULL;
1577 CurrentSentMessage->Result = NULL;
1578
1579 /* do NOT dereference our message queue as it might get attempted to be
1580 locked later */
1581 }
1582
1583 }
1584
1585 PUSER_MESSAGE_QUEUE FASTCALL
1586 MsqCreateMessageQueue(struct _ETHREAD *Thread)
1587 {
1588 PUSER_MESSAGE_QUEUE MessageQueue;
1589
1590 MessageQueue = (PUSER_MESSAGE_QUEUE)ExAllocatePoolWithTag(NonPagedPool,
1591 sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO),
1592 TAG_MSGQ);
1593
1594 if (!MessageQueue)
1595 {
1596 return NULL;
1597 }
1598
1599 RtlZeroMemory(MessageQueue, sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO));
1600 /* hold at least one reference until it'll be destroyed */
1601 IntReferenceMessageQueue(MessageQueue);
1602 /* initialize the queue */
1603 if (!MsqInitializeMessageQueue(Thread, MessageQueue))
1604 {
1605 IntDereferenceMessageQueue(MessageQueue);
1606 return NULL;
1607 }
1608
1609 return MessageQueue;
1610 }
1611
1612 VOID FASTCALL
1613 MsqDestroyMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
1614 {
1615 PDESKTOP desk;
1616
1617 /* remove the message queue from any desktops */
1618 if ((desk = InterlockedExchangePointer((PVOID*)&MessageQueue->Desktop, 0)))
1619 {
1620 (void)InterlockedExchangePointer((PVOID*)&desk->ActiveMessageQueue, 0);
1621 IntDereferenceMessageQueue(MessageQueue);
1622 }
1623
1624 /* clean it up */
1625 MsqCleanupMessageQueue(MessageQueue);
1626
1627 /* decrease the reference counter, if it hits zero, the queue will be freed */
1628 IntDereferenceMessageQueue(MessageQueue);
1629 }
1630
1631 PHOOKTABLE FASTCALL
1632 MsqGetHooks(PUSER_MESSAGE_QUEUE Queue)
1633 {
1634 return Queue->Hooks;
1635 }
1636
1637 VOID FASTCALL
1638 MsqSetHooks(PUSER_MESSAGE_QUEUE Queue, PHOOKTABLE Hooks)
1639 {
1640 Queue->Hooks = Hooks;
1641 }
1642
1643 LPARAM FASTCALL
1644 MsqSetMessageExtraInfo(LPARAM lParam)
1645 {
1646 LPARAM Ret;
1647 PTHREADINFO pti;
1648 PUSER_MESSAGE_QUEUE MessageQueue;
1649
1650 pti = PsGetCurrentThreadWin32Thread();
1651 MessageQueue = pti->MessageQueue;
1652 if(!MessageQueue)
1653 {
1654 return 0;
1655 }
1656
1657 Ret = MessageQueue->ExtraInfo;
1658 MessageQueue->ExtraInfo = lParam;
1659
1660 return Ret;
1661 }
1662
1663 LPARAM FASTCALL
1664 MsqGetMessageExtraInfo(VOID)
1665 {
1666 PTHREADINFO pti;
1667 PUSER_MESSAGE_QUEUE MessageQueue;
1668
1669 pti = PsGetCurrentThreadWin32Thread();
1670 MessageQueue = pti->MessageQueue;
1671 if(!MessageQueue)
1672 {
1673 return 0;
1674 }
1675
1676 return MessageQueue->ExtraInfo;
1677 }
1678
1679 HWND FASTCALL
1680 MsqSetStateWindow(PUSER_MESSAGE_QUEUE MessageQueue, ULONG Type, HWND hWnd)
1681 {
1682 HWND Prev;
1683
1684 switch(Type)
1685 {
1686 case MSQ_STATE_CAPTURE:
1687 Prev = MessageQueue->CaptureWindow;
1688 MessageQueue->CaptureWindow = hWnd;
1689 return Prev;
1690 case MSQ_STATE_ACTIVE:
1691 Prev = MessageQueue->ActiveWindow;
1692 MessageQueue->ActiveWindow = hWnd;
1693 return Prev;
1694 case MSQ_STATE_FOCUS:
1695 Prev = MessageQueue->FocusWindow;
1696 MessageQueue->FocusWindow = hWnd;
1697 return Prev;
1698 case MSQ_STATE_MENUOWNER:
1699 Prev = MessageQueue->MenuOwner;
1700 MessageQueue->MenuOwner = hWnd;
1701 return Prev;
1702 case MSQ_STATE_MOVESIZE:
1703 Prev = MessageQueue->MoveSize;
1704 MessageQueue->MoveSize = hWnd;
1705 return Prev;
1706 case MSQ_STATE_CARET:
1707 ASSERT(MessageQueue->CaretInfo);
1708 Prev = MessageQueue->CaretInfo->hWnd;
1709 MessageQueue->CaretInfo->hWnd = hWnd;
1710 return Prev;
1711 }
1712
1713 return NULL;
1714 }
1715
1716 #ifndef NDEBUG
1717 static VOID FASTCALL
1718 DumpTimerList(PUSER_MESSAGE_QUEUE MessageQueue)
1719 {
1720 PLIST_ENTRY Current;
1721 PTIMER_ENTRY Timer;
1722
1723 Current = MessageQueue->TimerListHead.Flink;
1724 if (Current == &MessageQueue->TimerListHead)
1725 {
1726 DPRINT("timer list is empty for queue %p\n", MessageQueue);
1727 }
1728 while (Current != &MessageQueue->TimerListHead)
1729 {
1730 Timer = CONTAINING_RECORD(Current, TIMER_ENTRY, ListEntry);
1731 DPRINT("queue %p timer %p expiry %I64d wnd %x id %p period %u timerproc %p msg %u\n",
1732 MessageQueue, Timer, Timer->ExpiryTime.QuadPart, Timer->Wnd, Timer->IDEvent,
1733 Timer->Period, Timer->TimerFunc, Timer->Msg);
1734 Current = Current->Flink;
1735 }
1736 }
1737 #endif /* ! defined(NDEBUG) */
1738
1739 /* Must have the message queue locked while calling this */
1740 static VOID FASTCALL
1741 InsertTimer(PUSER_MESSAGE_QUEUE MessageQueue, PTIMER_ENTRY NewTimer)
1742 {
1743 PLIST_ENTRY Current;
1744
1745 Current = MessageQueue->TimerListHead.Flink;
1746 while (Current != &MessageQueue->TimerListHead)
1747 {
1748 if (NewTimer->ExpiryTime.QuadPart <
1749 CONTAINING_RECORD(Current, TIMER_ENTRY, ListEntry)->ExpiryTime.QuadPart)
1750 {
1751 break;
1752 }
1753 Current = Current->Flink;
1754 }
1755
1756 InsertTailList(Current, &NewTimer->ListEntry);
1757 }
1758
1759 /* Must have the message queue locked while calling this */
1760 static PTIMER_ENTRY FASTCALL
1761 RemoveTimer(PUSER_MESSAGE_QUEUE MessageQueue, HWND Wnd, UINT_PTR IDEvent, UINT Msg)
1762 {
1763 PTIMER_ENTRY Timer;
1764 PLIST_ENTRY EnumEntry;
1765
1766 /* Remove timer if already in the queue */
1767 EnumEntry = MessageQueue->TimerListHead.Flink;
1768 while (EnumEntry != &MessageQueue->TimerListHead)
1769 {
1770 Timer = CONTAINING_RECORD(EnumEntry, TIMER_ENTRY, ListEntry);
1771 EnumEntry = EnumEntry->Flink;
1772
1773 if (Timer->Wnd == Wnd &&
1774 Timer->IDEvent == IDEvent &&
1775 Timer->Msg == Msg)
1776 {
1777 RemoveEntryList(&Timer->ListEntry);
1778 return Timer;
1779 }
1780 }
1781
1782 return NULL;
1783 }
1784
1785 BOOLEAN FASTCALL
1786 MsqSetTimer(PUSER_MESSAGE_QUEUE MessageQueue, HWND Wnd,
1787 UINT_PTR IDEvent, UINT Period, TIMERPROC TimerFunc,
1788 UINT Msg)
1789 {
1790 PTIMER_ENTRY Timer;
1791 LARGE_INTEGER CurrentTime;
1792
1793 DPRINT("MsqSetTimer queue %p wnd %x id %p period %u timerproc %p msg %d\n",
1794 MessageQueue, Wnd, IDEvent, Period, TimerFunc, Msg);
1795
1796 Timer = RemoveTimer(MessageQueue, Wnd, IDEvent, Msg);
1797 if (NULL == Timer)
1798 {
1799 Timer = ExAllocateFromPagedLookasideList(&TimerLookasideList);
1800 if (NULL == Timer)
1801 {
1802 DPRINT1("Failed to allocate timer entry\n");
1803 return FALSE;
1804 }
1805 DPRINT("Allocated new timer entry %p\n", Timer);
1806 Timer->Wnd = Wnd;
1807 Timer->IDEvent = IDEvent;
1808 Timer->Msg = Msg;
1809 }
1810 else
1811 {
1812 DPRINT("Updating existing timer entry %p\n", Timer);
1813 }
1814
1815 KeQuerySystemTime(&CurrentTime);
1816 Timer->ExpiryTime.QuadPart = CurrentTime.QuadPart +
1817 (ULONGLONG) Period * (ULONGLONG) 10000;
1818 Timer->Period = Period;
1819 Timer->TimerFunc = TimerFunc;
1820 DPRINT("Insert timer now %I64d expiry %I64d\n", CurrentTime.QuadPart,
1821 Timer->ExpiryTime.QuadPart);
1822
1823 InsertTimer(MessageQueue, Timer);
1824
1825 #ifndef NDEBUG
1826
1827 DumpTimerList(MessageQueue);
1828 #endif /* ! defined(NDEBUG) */
1829
1830 return TRUE;
1831 }
1832
1833 BOOLEAN FASTCALL
1834 MsqKillTimer(PUSER_MESSAGE_QUEUE MessageQueue, HWND Wnd,
1835 UINT_PTR IDEvent, UINT Msg)
1836 {
1837 PTIMER_ENTRY Timer;
1838
1839 DPRINT("MsqKillTimer queue %p wnd %x id %p msg %d\n",
1840 MessageQueue, Wnd, IDEvent, Msg);
1841
1842 Timer = RemoveTimer(MessageQueue, Wnd, IDEvent, Msg);
1843
1844 if (NULL == Timer)
1845 {
1846 DPRINT("Failed to remove timer from list, not found\n");
1847 }
1848 else
1849 {
1850 ExFreeToPagedLookasideList(&TimerLookasideList, Timer);
1851 }
1852
1853 #ifndef NDEBUG
1854 DumpTimerList(MessageQueue);
1855 #endif /* ! defined(NDEBUG) */
1856
1857 return NULL != Timer;
1858 }
1859
1860 BOOLEAN FASTCALL
1861 MsqGetTimerMessage(PUSER_MESSAGE_QUEUE MessageQueue,
1862 PWINDOW_OBJECT WindowFilter, UINT MsgFilterMin, UINT MsgFilterMax,
1863 MSG *Msg, BOOLEAN Restart)
1864 {
1865 PTIMER_ENTRY Timer;
1866 LARGE_INTEGER CurrentTime;
1867 LARGE_INTEGER LargeTickCount;
1868 PLIST_ENTRY EnumEntry;
1869 BOOLEAN GotMessage;
1870
1871 DPRINT("MsqGetTimerMessage queue %p msg %p restart %s\n",
1872 MessageQueue, Msg, Restart ? "TRUE" : "FALSE");
1873
1874 KeQuerySystemTime(&CurrentTime);
1875 DPRINT("Current time %I64d\n", CurrentTime.QuadPart);
1876 EnumEntry = MessageQueue->TimerListHead.Flink;
1877 GotMessage = FALSE;
1878 while (EnumEntry != &MessageQueue->TimerListHead)
1879 {
1880 Timer = CONTAINING_RECORD(MessageQueue->TimerListHead.Flink,
1881 TIMER_ENTRY, ListEntry);
1882 DPRINT("Checking timer %p wnd %x expiry %I64d\n", Timer, Timer->Wnd,
1883 Timer->ExpiryTime.QuadPart);
1884 EnumEntry = EnumEntry->Flink;
1885 if ((NULL == WindowFilter || Timer->Wnd == WindowFilter->hSelf) &&
1886 ((MsgFilterMin == 0 && MsgFilterMax == 0) ||
1887 (MsgFilterMin <= Timer->Msg &&
1888 Timer->Msg <= MsgFilterMax)))
1889 {
1890 if (Timer->ExpiryTime.QuadPart <= CurrentTime.QuadPart)
1891 {
1892 DPRINT("Timer is expired\n");
1893 GotMessage = TRUE;
1894 break;
1895 }
1896 else
1897 {
1898 DPRINT("No need to check later timers\n");
1899 break;
1900 }
1901 }
1902 else
1903 {
1904 DPRINT("timer %p (wnd %x msg %d) failed filter wnd %x msgmin %d msgmax %d\n",
1905 Timer, Timer->Wnd, Timer->Msg, WindowFilter->hSelf, MsgFilterMin, MsgFilterMax);
1906 }
1907 }
1908
1909 if (! GotMessage)
1910 {
1911 DPRINT("No timer pending\n");
1912 return FALSE;
1913 }
1914
1915 Msg->hwnd = Timer->Wnd;
1916 Msg->message = Timer->Msg;
1917 Msg->wParam = (WPARAM) Timer->IDEvent;
1918 Msg->lParam = (LPARAM) Timer->TimerFunc;
1919 KeQueryTickCount(&LargeTickCount);
1920 Msg->time = MsqCalculateMessageTime(&LargeTickCount);
1921 Msg->pt = gpsi->ptCursor;
1922
1923 if (Restart)
1924 {
1925 RemoveEntryList(&Timer->ListEntry);
1926 Timer->ExpiryTime.QuadPart = CurrentTime.QuadPart +
1927 (ULONGLONG) Timer->Period * (ULONGLONG) 10000;
1928 DPRINT("Restarting timer %p expires %I64d\n", Timer, Timer->ExpiryTime.QuadPart);
1929 InsertTimer(MessageQueue, Timer);
1930
1931 #ifndef NDEBUG
1932
1933 DumpTimerList(MessageQueue);
1934 #endif /* ! defined(NDEBUG) */
1935
1936 }
1937
1938 DPRINT("Created message wnd %x msg %d wParam %u lParam %u\n", Msg->hwnd, Msg->message,
1939 Msg->wParam, Msg->lParam);
1940
1941 return TRUE;
1942 }
1943
1944 VOID FASTCALL
1945 MsqRemoveTimersWindow(PUSER_MESSAGE_QUEUE MessageQueue, HWND Wnd)
1946 {
1947 PTIMER_ENTRY Timer;
1948 PLIST_ENTRY EnumEntry;
1949
1950 DPRINT("MsqRemoveTimersWindow queue %p wnd %x\n", MessageQueue, Wnd);
1951
1952 EnumEntry = MessageQueue->TimerListHead.Flink;
1953 while (EnumEntry != &MessageQueue->TimerListHead)
1954 {
1955 Timer = CONTAINING_RECORD(EnumEntry, TIMER_ENTRY, ListEntry);
1956 EnumEntry = EnumEntry->Flink;
1957 if (Timer->Wnd == Wnd)
1958 {
1959 DPRINT("Removing timer %p because its window is going away\n", Timer);
1960 RemoveEntryList(&Timer->ListEntry);
1961 ExFreeToPagedLookasideList(&TimerLookasideList, Timer);
1962 }
1963 }
1964
1965 #ifndef NDEBUG
1966 DumpTimerList(MessageQueue);
1967 #endif /* ! defined(NDEBUG) */
1968
1969 }
1970
1971 BOOLEAN FASTCALL
1972 MsqGetFirstTimerExpiry(PUSER_MESSAGE_QUEUE MessageQueue,
1973 PWINDOW_OBJECT WndFilter, UINT MsgFilterMin, UINT MsgFilterMax,
1974 PLARGE_INTEGER FirstTimerExpiry)
1975 {
1976 PTIMER_ENTRY Timer;
1977 PLIST_ENTRY EnumEntry;
1978
1979 DPRINT("MsqGetFirstTimerExpiry queue %p wndfilter %x msgfiltermin %d msgfiltermax %d expiry %p\n",
1980 MessageQueue, WndFilter, MsgFilterMin, MsgFilterMax, FirstTimerExpiry);
1981
1982 EnumEntry = MessageQueue->TimerListHead.Flink;
1983 while (EnumEntry != &MessageQueue->TimerListHead)
1984 {
1985 Timer = CONTAINING_RECORD(MessageQueue->TimerListHead.Flink,
1986 TIMER_ENTRY, ListEntry);
1987 EnumEntry = EnumEntry->Flink;
1988 if ((NULL == WndFilter || (int)WndFilter == 1 || Timer->Wnd == WndFilter->hSelf) &&
1989 ((MsgFilterMin == 0 && MsgFilterMax == 0) ||
1990 (MsgFilterMin <= Timer->Msg &&
1991 Timer->Msg <= MsgFilterMax)))
1992 {
1993 *FirstTimerExpiry = Timer->ExpiryTime;
1994 DPRINT("First timer expires %I64d\n", Timer->ExpiryTime);
1995 return TRUE;
1996 }
1997 }
1998
1999 return FALSE;
2000 }
2001
2002 /* EOF */