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