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