[CMAKE]
[reactos.git] / 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);
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 else
632 {
633 if (Remove)
634 {
635 RemoveEntryList(&Current->ListEntry);
636 }
637 IntUnLockHardwareMessageQueue(MessageQueue);
638 IntUnLockSystemHardwareMessageQueueLock(FALSE);
639 *Message = Current;
640
641 RETURN(TRUE);
642 }
643 }
644 IntUnLockHardwareMessageQueue(MessageQueue);
645
646 /* Now try the global queue. */
647
648 /* Transfer all messages from the DPC accessible queue to the main queue. */
649 IntLockSystemMessageQueue(OldIrql);
650 while (SystemMessageQueueCount > 0)
651 {
652 PUSER_MESSAGE UserMsg;
653 MSG Msg;
654
655 ASSERT(SystemMessageQueueHead < SYSTEM_MESSAGE_QUEUE_SIZE);
656 Msg = SystemMessageQueue[SystemMessageQueueHead];
657 SystemMessageQueueHead =
658 (SystemMessageQueueHead + 1) % SYSTEM_MESSAGE_QUEUE_SIZE;
659 SystemMessageQueueCount--;
660 IntUnLockSystemMessageQueue(OldIrql);
661
662 UserMsg = ExAllocateFromPagedLookasideList(&MessageLookasideList);
663 /* What to do if out of memory? For now we just panic a bit in debug */
664 ASSERT(UserMsg);
665 UserMsg->Msg = Msg;
666 InsertTailList(&HardwareMessageQueueHead, &UserMsg->ListEntry);
667
668 IntLockSystemMessageQueue(OldIrql);
669 }
670 HardwareMessageQueueStamp++;
671 IntUnLockSystemMessageQueue(OldIrql);
672
673 /* Process messages in the queue until we find one to return. */
674 CurrentEntry = HardwareMessageQueueHead.Flink;
675 while (CurrentEntry != &HardwareMessageQueueHead)
676 {
677 PUSER_MESSAGE Current =
678 CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, ListEntry);
679 CurrentEntry = CurrentEntry->Flink;
680 RemoveEntryList(&Current->ListEntry);
681 HardwareMessageQueueStamp++;
682 if (Current->Msg.message >= WM_MOUSEFIRST &&
683 Current->Msg.message <= WM_MOUSELAST)
684 {
685 const ULONG ActiveStamp = HardwareMessageQueueStamp;
686 /* Translate the message. */
687 Accept = co_MsqTranslateMouseMessage(MessageQueue, Window, FilterLow, FilterHigh,
688 Current, Remove, &Freed,
689 DesktopWindow, &ScreenPoint, TRUE, NULL);
690 if (Accept)
691 {
692 /* Check for no more messages in the system queue. */
693 IntLockSystemMessageQueue(OldIrql);
694 if (SystemMessageQueueCount == 0 &&
695 IsListEmpty(&HardwareMessageQueueHead))
696 {
697 KeClearEvent(&HardwareMessageEvent);
698 }
699 IntUnLockSystemMessageQueue(OldIrql);
700
701 /*
702 If we aren't removing the message then add it to the private
703 queue.
704 */
705 if (!Remove)
706 {
707 IntLockHardwareMessageQueue(MessageQueue);
708 if(Current->Msg.message == WM_MOUSEMOVE)
709 {
710 if(MessageQueue->MouseMoveMsg)
711 {
712 RemoveEntryList(&MessageQueue->MouseMoveMsg->ListEntry);
713 ExFreePool(MessageQueue->MouseMoveMsg);
714 }
715 MessageQueue->MouseMoveMsg = Current;
716 }
717 InsertTailList(&MessageQueue->HardwareMessagesListHead,
718 &Current->ListEntry);
719 IntUnLockHardwareMessageQueue(MessageQueue);
720 }
721 IntUnLockSystemHardwareMessageQueueLock(FALSE);
722 *Message = Current;
723
724 RETURN(TRUE);
725 }
726 /* If the contents of the queue changed then restart processing. */
727 if (HardwareMessageQueueStamp != ActiveStamp)
728 {
729 CurrentEntry = HardwareMessageQueueHead.Flink;
730 continue;
731 }
732 }
733 }
734
735 /* Check if the system message queue is now empty. */
736 IntLockSystemMessageQueue(OldIrql);
737 if (SystemMessageQueueCount == 0 && IsListEmpty(&HardwareMessageQueueHead))
738 {
739 KeClearEvent(&HardwareMessageEvent);
740 }
741 IntUnLockSystemMessageQueue(OldIrql);
742 IntUnLockSystemHardwareMessageQueueLock(FALSE);
743
744 RETURN(FALSE);
745
746 CLEANUP:
747 if (DesktopWindow) UserDerefObjectCo(DesktopWindow);
748
749 END_CLEANUP;
750 }
751
752 //
753 // Note: Only called from input.c.
754 //
755 VOID FASTCALL
756 co_MsqPostKeyboardMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
757 {
758 PUSER_MESSAGE_QUEUE FocusMessageQueue;
759 MSG Msg;
760 LARGE_INTEGER LargeTickCount;
761 KBDLLHOOKSTRUCT KbdHookData;
762 BOOLEAN Entered = FALSE;
763
764 DPRINT("MsqPostKeyboardMessage(uMsg 0x%x, wParam 0x%x, lParam 0x%x)\n",
765 uMsg, wParam, lParam);
766
767 // Condition may arise when calling MsqPostMessage and waiting for an event.
768 if (!UserIsEntered())
769 {
770 // Fixme: Not sure ATM if this thread is locked.
771 UserEnterExclusive();
772 Entered = TRUE;
773 }
774
775 FocusMessageQueue = IntGetFocusMessageQueue();
776
777 Msg.hwnd = 0;
778
779 if (FocusMessageQueue && (FocusMessageQueue->FocusWindow != (HWND)0))
780 Msg.hwnd = FocusMessageQueue->FocusWindow;
781
782 Msg.message = uMsg;
783 Msg.wParam = wParam;
784 Msg.lParam = lParam;
785
786 KeQueryTickCount(&LargeTickCount);
787 Msg.time = MsqCalculateMessageTime(&LargeTickCount);
788
789 /* We can't get the Msg.pt point here since we don't know thread
790 (and thus the window station) the message will end up in yet. */
791
792 KbdHookData.vkCode = Msg.wParam;
793 KbdHookData.scanCode = (Msg.lParam >> 16) & 0xff;
794 KbdHookData.flags = (0 == (Msg.lParam & 0x01000000) ? 0 : LLKHF_EXTENDED) |
795 (0 == (Msg.lParam & 0x20000000) ? 0 : LLKHF_ALTDOWN) |
796 (0 == (Msg.lParam & 0x80000000) ? 0 : LLKHF_UP);
797 KbdHookData.time = Msg.time;
798 KbdHookData.dwExtraInfo = 0;
799 if (co_HOOK_CallHooks(WH_KEYBOARD_LL, HC_ACTION, Msg.message, (LPARAM) &KbdHookData))
800 {
801 DPRINT("Kbd msg %d wParam %d lParam 0x%08x dropped by WH_KEYBOARD_LL hook\n",
802 Msg.message, Msg.wParam, Msg.lParam);
803 if (Entered) UserLeave();
804 return;
805 }
806
807 if (FocusMessageQueue == NULL)
808 {
809 DPRINT("No focus message queue\n");
810 if (Entered) UserLeave();
811 return;
812 }
813
814 if (FocusMessageQueue->FocusWindow != (HWND)0)
815 {
816 Msg.hwnd = FocusMessageQueue->FocusWindow;
817 DPRINT("Msg.hwnd = %x\n", Msg.hwnd);
818
819 FocusMessageQueue->Desktop->pDeskInfo->LastInputWasKbd = TRUE;
820
821 Msg.pt = gpsi->ptCursor;
822 MsqPostMessage(FocusMessageQueue, &Msg, TRUE, QS_KEY);
823 }
824 else
825 {
826 DPRINT("Invalid focus window handle\n");
827 }
828
829 if (Entered) UserLeave();
830 return;
831 }
832
833 VOID FASTCALL
834 MsqPostHotKeyMessage(PVOID Thread, HWND hWnd, WPARAM wParam, LPARAM lParam)
835 {
836 PWND Window;
837 PTHREADINFO Win32Thread;
838 MSG Mesg;
839 LARGE_INTEGER LargeTickCount;
840 NTSTATUS Status;
841
842 Status = ObReferenceObjectByPointer (Thread,
843 THREAD_ALL_ACCESS,
844 PsThreadType,
845 KernelMode);
846 if (!NT_SUCCESS(Status))
847 return;
848
849 Win32Thread = ((PETHREAD)Thread)->Tcb.Win32Thread;
850 if (Win32Thread == NULL || Win32Thread->MessageQueue == NULL)
851 {
852 ObDereferenceObject ((PETHREAD)Thread);
853 return;
854 }
855
856 Window = IntGetWindowObject(hWnd);
857 if (!Window)
858 {
859 ObDereferenceObject ((PETHREAD)Thread);
860 return;
861 }
862
863 Mesg.hwnd = hWnd;
864 Mesg.message = WM_HOTKEY;
865 Mesg.wParam = wParam;
866 Mesg.lParam = lParam;
867 KeQueryTickCount(&LargeTickCount);
868 Mesg.time = MsqCalculateMessageTime(&LargeTickCount);
869 Mesg.pt = gpsi->ptCursor;
870 MsqPostMessage(Window->head.pti->MessageQueue, &Mesg, FALSE, QS_HOTKEY);
871 UserDereferenceObject(Window);
872 ObDereferenceObject (Thread);
873
874 // InsertHeadList(&pThread->MessageQueue->PostedMessagesListHead,
875 // &Message->ListEntry);
876 // KeSetEvent(pThread->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
877 }
878
879 PUSER_MESSAGE FASTCALL
880 MsqCreateMessage(LPMSG Msg)
881 {
882 PUSER_MESSAGE Message;
883
884 Message = ExAllocateFromPagedLookasideList(&MessageLookasideList);
885 if (!Message)
886 {
887 return NULL;
888 }
889
890 RtlMoveMemory(&Message->Msg, Msg, sizeof(MSG));
891
892 return Message;
893 }
894
895 VOID FASTCALL
896 MsqDestroyMessage(PUSER_MESSAGE Message)
897 {
898 ExFreeToPagedLookasideList(&MessageLookasideList, Message);
899 }
900
901 VOID FASTCALL
902 co_MsqDispatchSentNotifyMessages(PUSER_MESSAGE_QUEUE MessageQueue)
903 {
904 PLIST_ENTRY ListEntry;
905 PUSER_SENT_MESSAGE_NOTIFY Message;
906
907 while (!IsListEmpty(&MessageQueue->SentMessagesListHead))
908 {
909 ListEntry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
910 Message = CONTAINING_RECORD(ListEntry, USER_SENT_MESSAGE_NOTIFY,
911 ListEntry);
912
913 co_IntCallSentMessageCallback(Message->CompletionCallback,
914 Message->hWnd,
915 Message->Msg,
916 Message->CompletionCallbackContext,
917 Message->Result);
918
919 }
920
921 }
922
923 BOOLEAN FASTCALL
924 MsqPeekSentMessages(PUSER_MESSAGE_QUEUE MessageQueue)
925 {
926 return(!IsListEmpty(&MessageQueue->SentMessagesListHead));
927 }
928
929 BOOLEAN FASTCALL
930 co_MsqDispatchOneSentMessage(PUSER_MESSAGE_QUEUE MessageQueue)
931 {
932 PUSER_SENT_MESSAGE Message;
933 PLIST_ENTRY Entry;
934 LRESULT Result;
935
936 if (IsListEmpty(&MessageQueue->SentMessagesListHead))
937 {
938 return(FALSE);
939 }
940
941 /* remove it from the list of pending messages */
942 Entry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
943 Message = CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry);
944
945 /* insert it to the list of messages that are currently dispatched by this
946 message queue */
947 InsertTailList(&MessageQueue->LocalDispatchingMessagesHead,
948 &Message->ListEntry);
949
950 if (Message->HookMessage == MSQ_ISHOOK)
951 {
952 Result = co_HOOK_CallHooks(Message->Msg.message,
953 (INT)(INT_PTR)Message->Msg.hwnd,
954 Message->Msg.wParam,
955 Message->Msg.lParam);
956 }
957 else if (Message->HookMessage == MSQ_ISEVENT)
958 {
959 Result = co_EVENT_CallEvents( Message->Msg.message,
960 Message->Msg.hwnd,
961 Message->Msg.wParam,
962 Message->Msg.lParam);
963 }
964 else
965 {
966 /* Call the window procedure. */
967 Result = co_IntSendMessage(Message->Msg.hwnd,
968 Message->Msg.message,
969 Message->Msg.wParam,
970 Message->Msg.lParam);
971 }
972
973 /* remove the message from the local dispatching list, because it doesn't need
974 to be cleaned up on thread termination anymore */
975 RemoveEntryList(&Message->ListEntry);
976
977 /* remove the message from the dispatching list if needed, so lock the sender's message queue */
978 if (!(Message->HookMessage & MSQ_SENTNOWAIT))
979 {
980 if (Message->DispatchingListEntry.Flink != NULL)
981 {
982 /* only remove it from the dispatching list if not already removed by a timeout */
983 RemoveEntryList(&Message->DispatchingListEntry);
984 }
985 }
986 /* still keep the sender's message queue locked, so the sender can't exit the
987 MsqSendMessage() function (if timed out) */
988
989 /* Let the sender know the result. */
990 if (Message->Result != NULL)
991 {
992 *Message->Result = Result;
993 }
994
995 if (Message->HasPackedLParam == TRUE)
996 {
997 if (Message->Msg.lParam)
998 ExFreePool((PVOID)Message->Msg.lParam);
999 }
1000
1001 /* Notify the sender. */
1002 if (Message->CompletionEvent != NULL)
1003 {
1004 KeSetEvent(Message->CompletionEvent, IO_NO_INCREMENT, FALSE);
1005 }
1006
1007 /* Call the callback if the message was sent with SendMessageCallback */
1008 if (Message->CompletionCallback != NULL)
1009 {
1010 co_IntCallSentMessageCallback(Message->CompletionCallback,
1011 Message->Msg.hwnd,
1012 Message->Msg.message,
1013 Message->CompletionCallbackContext,
1014 Result);
1015 }
1016
1017 /* Only if it is not a no wait message */
1018 if (!(Message->HookMessage & MSQ_SENTNOWAIT))
1019 {
1020 IntDereferenceMessageQueue(Message->SenderQueue);
1021 IntDereferenceMessageQueue(MessageQueue);
1022 }
1023
1024 /* free the message */
1025 ExFreePoolWithTag(Message, TAG_USRMSG);
1026 return(TRUE);
1027 }
1028
1029 VOID APIENTRY
1030 MsqRemoveWindowMessagesFromQueue(PVOID pWindow)
1031 {
1032 PUSER_SENT_MESSAGE SentMessage;
1033 PUSER_MESSAGE PostedMessage;
1034 PUSER_MESSAGE_QUEUE MessageQueue;
1035 PLIST_ENTRY CurrentEntry, ListHead;
1036 PWND Window = pWindow;
1037
1038 ASSERT(Window);
1039
1040 MessageQueue = Window->head.pti->MessageQueue;
1041 ASSERT(MessageQueue);
1042
1043 /* remove the posted messages for this window */
1044 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
1045 ListHead = &MessageQueue->PostedMessagesListHead;
1046 while (CurrentEntry != ListHead)
1047 {
1048 PostedMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1049 ListEntry);
1050 if (PostedMessage->Msg.hwnd == Window->head.h)
1051 {
1052 RemoveEntryList(&PostedMessage->ListEntry);
1053 MsqDestroyMessage(PostedMessage);
1054 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
1055 }
1056 else
1057 {
1058 CurrentEntry = CurrentEntry->Flink;
1059 }
1060 }
1061
1062 /* remove the sent messages for this window */
1063 CurrentEntry = MessageQueue->SentMessagesListHead.Flink;
1064 ListHead = &MessageQueue->SentMessagesListHead;
1065 while (CurrentEntry != ListHead)
1066 {
1067 SentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1068 ListEntry);
1069 if(SentMessage->Msg.hwnd == Window->head.h)
1070 {
1071 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1072
1073 RemoveEntryList(&SentMessage->ListEntry);
1074
1075 /* remove the message from the dispatching list if neede */
1076 if ((!(SentMessage->HookMessage & MSQ_SENTNOWAIT))
1077 && (SentMessage->DispatchingListEntry.Flink != NULL))
1078 {
1079 RemoveEntryList(&SentMessage->DispatchingListEntry);
1080 }
1081
1082 /* wake the sender's thread */
1083 if (SentMessage->CompletionEvent != NULL)
1084 {
1085 KeSetEvent(SentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1086 }
1087
1088 if (SentMessage->HasPackedLParam == TRUE)
1089 {
1090 if (SentMessage->Msg.lParam)
1091 ExFreePool((PVOID)SentMessage->Msg.lParam);
1092 }
1093
1094 /* Only if it is not a no wait message */
1095 if (!(SentMessage->HookMessage & MSQ_SENTNOWAIT))
1096 {
1097 /* dereference our and the sender's message queue */
1098 IntDereferenceMessageQueue(MessageQueue);
1099 IntDereferenceMessageQueue(SentMessage->SenderQueue);
1100 }
1101
1102 /* free the message */
1103 ExFreePoolWithTag(SentMessage, TAG_USRMSG);
1104
1105 CurrentEntry = MessageQueue->SentMessagesListHead.Flink;
1106 }
1107 else
1108 {
1109 CurrentEntry = CurrentEntry->Flink;
1110 }
1111 }
1112 }
1113
1114 VOID FASTCALL
1115 MsqSendNotifyMessage(PUSER_MESSAGE_QUEUE MessageQueue,
1116 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage)
1117 {
1118 InsertTailList(&MessageQueue->NotifyMessagesListHead,
1119 &NotifyMessage->ListEntry);
1120 MessageQueue->QueueBits |= QS_SENDMESSAGE;
1121 MessageQueue->ChangedBits |= QS_SENDMESSAGE;
1122 if (MessageQueue->WakeMask & QS_SENDMESSAGE)
1123 KeSetEvent(MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
1124 }
1125
1126 NTSTATUS FASTCALL
1127 co_MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue,
1128 HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam,
1129 UINT uTimeout, BOOL Block, INT HookMessage,
1130 ULONG_PTR *uResult)
1131 {
1132 PTHREADINFO pti;
1133 PUSER_SENT_MESSAGE Message;
1134 KEVENT CompletionEvent;
1135 NTSTATUS WaitStatus;
1136 LRESULT Result;
1137 PUSER_MESSAGE_QUEUE ThreadQueue;
1138 LARGE_INTEGER Timeout;
1139 PLIST_ENTRY Entry;
1140
1141 if(!(Message = ExAllocatePoolWithTag(PagedPool, sizeof(USER_SENT_MESSAGE), TAG_USRMSG)))
1142 {
1143 DPRINT1("MsqSendMessage(): Not enough memory to allocate a message");
1144 return STATUS_INSUFFICIENT_RESOURCES;
1145 }
1146
1147 KeInitializeEvent(&CompletionEvent, NotificationEvent, FALSE);
1148
1149 pti = PsGetCurrentThreadWin32Thread();
1150 ThreadQueue = pti->MessageQueue;
1151 ASSERT(ThreadQueue != MessageQueue);
1152
1153 Timeout.QuadPart = (LONGLONG) uTimeout * (LONGLONG) -10000;
1154
1155 /* FIXME - increase reference counter of sender's message queue here */
1156
1157 Result = 0;
1158 Message->Msg.hwnd = Wnd;
1159 Message->Msg.message = Msg;
1160 Message->Msg.wParam = wParam;
1161 Message->Msg.lParam = lParam;
1162 Message->CompletionEvent = &CompletionEvent;
1163 Message->Result = &Result;
1164 Message->SenderQueue = ThreadQueue;
1165 IntReferenceMessageQueue(ThreadQueue);
1166 Message->CompletionCallback = NULL;
1167 Message->HookMessage = HookMessage;
1168 Message->HasPackedLParam = FALSE;
1169
1170 IntReferenceMessageQueue(MessageQueue);
1171
1172 /* add it to the list of pending messages */
1173 InsertTailList(&ThreadQueue->DispatchingMessagesHead, &Message->DispatchingListEntry);
1174
1175 /* queue it in the destination's message queue */
1176 InsertTailList(&MessageQueue->SentMessagesListHead, &Message->ListEntry);
1177
1178 MessageQueue->QueueBits |= QS_SENDMESSAGE;
1179 MessageQueue->ChangedBits |= QS_SENDMESSAGE;
1180 if (MessageQueue->WakeMask & QS_SENDMESSAGE)
1181 KeSetEvent(MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
1182
1183 /* we can't access the Message anymore since it could have already been deleted! */
1184
1185 if(Block)
1186 {
1187 IdlePing();
1188
1189 UserLeaveCo();
1190
1191 /* don't process messages sent to the thread */
1192 WaitStatus = KeWaitForSingleObject(&CompletionEvent, UserRequest, UserMode,
1193 FALSE, (uTimeout ? &Timeout : NULL));
1194
1195 UserEnterCo();
1196
1197 if(WaitStatus == STATUS_TIMEOUT)
1198 {
1199 /* look up if the message has not yet dispatched, if so
1200 make sure it can't pass a result and it must not set the completion event anymore */
1201 Entry = MessageQueue->SentMessagesListHead.Flink;
1202 while (Entry != &MessageQueue->SentMessagesListHead)
1203 {
1204 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry)
1205 == Message)
1206 {
1207 /* we can access Message here, it's secure because the message queue is locked
1208 and the message is still hasn't been dispatched */
1209 Message->CompletionEvent = NULL;
1210 Message->Result = NULL;
1211 break;
1212 }
1213 Entry = Entry->Flink;
1214 }
1215
1216 /* remove from the local dispatching list so the other thread knows,
1217 it can't pass a result and it must not set the completion event anymore */
1218 Entry = ThreadQueue->DispatchingMessagesHead.Flink;
1219 while (Entry != &ThreadQueue->DispatchingMessagesHead)
1220 {
1221 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, DispatchingListEntry)
1222 == Message)
1223 {
1224 /* we can access Message here, it's secure because the sender's message is locked
1225 and the message has definitely not yet been destroyed, otherwise it would
1226 have been removed from this list by the dispatching routine right after
1227 dispatching the message */
1228 Message->CompletionEvent = NULL;
1229 Message->Result = NULL;
1230 RemoveEntryList(&Message->DispatchingListEntry);
1231 Message->DispatchingListEntry.Flink = NULL;
1232 break;
1233 }
1234 Entry = Entry->Flink;
1235 }
1236
1237 DPRINT("MsqSendMessage (blocked) timed out\n");
1238 }
1239 while (co_MsqDispatchOneSentMessage(ThreadQueue))
1240 ;
1241 }
1242 else
1243 {
1244 PVOID WaitObjects[2];
1245
1246 WaitObjects[0] = &CompletionEvent;
1247 WaitObjects[1] = ThreadQueue->NewMessages;
1248 do
1249 {
1250 IdlePing();
1251
1252 UserLeaveCo();
1253
1254 WaitStatus = KeWaitForMultipleObjects(2, WaitObjects, WaitAny, UserRequest,
1255 UserMode, FALSE, (uTimeout ? &Timeout : NULL), NULL);
1256
1257 UserEnterCo();
1258
1259 if(WaitStatus == STATUS_TIMEOUT)
1260 {
1261 /* look up if the message has not yet been dispatched, if so
1262 make sure it can't pass a result and it must not set the completion event anymore */
1263 Entry = MessageQueue->SentMessagesListHead.Flink;
1264 while (Entry != &MessageQueue->SentMessagesListHead)
1265 {
1266 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry)
1267 == Message)
1268 {
1269 /* we can access Message here, it's secure because the message queue is locked
1270 and the message is still hasn't been dispatched */
1271 Message->CompletionEvent = NULL;
1272 Message->Result = NULL;
1273 break;
1274 }
1275 Entry = Entry->Flink;
1276 }
1277
1278 /* remove from the local dispatching list so the other thread knows,
1279 it can't pass a result and it must not set the completion event anymore */
1280 Entry = ThreadQueue->DispatchingMessagesHead.Flink;
1281 while (Entry != &ThreadQueue->DispatchingMessagesHead)
1282 {
1283 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, DispatchingListEntry)
1284 == Message)
1285 {
1286 /* we can access Message here, it's secure because the sender's message is locked
1287 and the message has definitely not yet been destroyed, otherwise it would
1288 have been removed from this list by the dispatching routine right after
1289 dispatching the message */
1290 Message->CompletionEvent = NULL;
1291 Message->Result = NULL;
1292 RemoveEntryList(&Message->DispatchingListEntry);
1293 Message->DispatchingListEntry.Flink = NULL;
1294 break;
1295 }
1296 Entry = Entry->Flink;
1297 }
1298
1299 DPRINT("MsqSendMessage timed out\n");
1300 break;
1301 }
1302 while (co_MsqDispatchOneSentMessage(ThreadQueue))
1303 ;
1304 }
1305 while (NT_SUCCESS(WaitStatus) && STATUS_WAIT_0 != WaitStatus);
1306 }
1307
1308 if(WaitStatus != STATUS_TIMEOUT)
1309 *uResult = (STATUS_WAIT_0 == WaitStatus ? Result : -1);
1310
1311 return WaitStatus;
1312 }
1313
1314 VOID FASTCALL
1315 MsqPostMessage(PUSER_MESSAGE_QUEUE MessageQueue, MSG* Msg, BOOLEAN HardwareMessage,
1316 DWORD MessageBits)
1317 {
1318 PUSER_MESSAGE Message;
1319
1320 if(!(Message = MsqCreateMessage(Msg)))
1321 {
1322 return;
1323 }
1324
1325 if(!HardwareMessage)
1326 {
1327 InsertTailList(&MessageQueue->PostedMessagesListHead,
1328 &Message->ListEntry);
1329 }
1330 else
1331 {
1332 InsertTailList(&MessageQueue->HardwareMessagesListHead,
1333 &Message->ListEntry);
1334 }
1335 MessageQueue->QueueBits |= MessageBits;
1336 MessageQueue->ChangedBits |= MessageBits;
1337 if (MessageQueue->WakeMask & MessageBits)
1338 KeSetEvent(MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
1339 }
1340
1341 VOID FASTCALL
1342 MsqPostQuitMessage(PUSER_MESSAGE_QUEUE MessageQueue, ULONG ExitCode)
1343 {
1344 MessageQueue->QuitPosted = TRUE;
1345 MessageQueue->QuitExitCode = ExitCode;
1346 MessageQueue->QueueBits |= QS_POSTMESSAGE;
1347 MessageQueue->ChangedBits |= QS_POSTMESSAGE;
1348 if (MessageQueue->WakeMask & QS_POSTMESSAGE)
1349 KeSetEvent(MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
1350 }
1351
1352 BOOLEAN APIENTRY
1353 co_MsqFindMessage(IN PUSER_MESSAGE_QUEUE MessageQueue,
1354 IN BOOLEAN Hardware,
1355 IN BOOLEAN Remove,
1356 IN PWND Window,
1357 IN UINT MsgFilterLow,
1358 IN UINT MsgFilterHigh,
1359 OUT PUSER_MESSAGE* Message)
1360 {
1361 PLIST_ENTRY CurrentEntry;
1362 PUSER_MESSAGE CurrentMessage;
1363 PLIST_ENTRY ListHead;
1364
1365 if (Hardware)
1366 {
1367 return(co_MsqPeekHardwareMessage( MessageQueue,
1368 Window,
1369 MsgFilterLow,
1370 MsgFilterHigh,
1371 Remove,
1372 Message));
1373 }
1374
1375 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
1376 ListHead = &MessageQueue->PostedMessagesListHead;
1377 while (CurrentEntry != ListHead)
1378 {
1379 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1380 ListEntry);
1381 if ( ( !Window ||
1382 PtrToInt(Window) == 1 ||
1383 Window->head.h == CurrentMessage->Msg.hwnd ) &&
1384 ( (MsgFilterLow == 0 && MsgFilterHigh == 0) ||
1385 ( MsgFilterLow <= CurrentMessage->Msg.message &&
1386 MsgFilterHigh >= CurrentMessage->Msg.message ) ) )
1387 {
1388 if (Remove)
1389 {
1390 RemoveEntryList(&CurrentMessage->ListEntry);
1391 }
1392
1393 *Message = CurrentMessage;
1394 return(TRUE);
1395 }
1396 CurrentEntry = CurrentEntry->Flink;
1397 }
1398
1399 return(FALSE);
1400 }
1401
1402 NTSTATUS FASTCALL
1403 co_MsqWaitForNewMessages(PUSER_MESSAGE_QUEUE MessageQueue, PWND WndFilter,
1404 UINT MsgFilterMin, UINT MsgFilterMax)
1405 {
1406 PVOID WaitObjects[2] = {MessageQueue->NewMessages, &HardwareMessageEvent};
1407 NTSTATUS ret;
1408
1409 IdlePing(); // Going to wait so send Idle ping.
1410
1411 UserLeaveCo();
1412
1413 ret = KeWaitForMultipleObjects(2,
1414 WaitObjects,
1415 WaitAny,
1416 Executive,
1417 UserMode,
1418 FALSE,
1419 NULL,
1420 NULL);
1421 UserEnterCo();
1422 return ret;
1423 }
1424
1425 BOOL FASTCALL
1426 MsqIsHung(PUSER_MESSAGE_QUEUE MessageQueue)
1427 {
1428 LARGE_INTEGER LargeTickCount;
1429
1430 KeQueryTickCount(&LargeTickCount);
1431 return ((LargeTickCount.u.LowPart - MessageQueue->LastMsgRead) > MSQ_HUNG);
1432 }
1433
1434 BOOLEAN FASTCALL
1435 MsqInitializeMessageQueue(struct _ETHREAD *Thread, PUSER_MESSAGE_QUEUE MessageQueue)
1436 {
1437 LARGE_INTEGER LargeTickCount;
1438 NTSTATUS Status;
1439
1440 MessageQueue->Thread = Thread;
1441 MessageQueue->CaretInfo = (PTHRDCARETINFO)(MessageQueue + 1);
1442 InitializeListHead(&MessageQueue->PostedMessagesListHead);
1443 InitializeListHead(&MessageQueue->SentMessagesListHead);
1444 InitializeListHead(&MessageQueue->HardwareMessagesListHead);
1445 InitializeListHead(&MessageQueue->DispatchingMessagesHead);
1446 InitializeListHead(&MessageQueue->LocalDispatchingMessagesHead);
1447 KeInitializeMutex(&MessageQueue->HardwareLock, 0);
1448 MessageQueue->QuitPosted = FALSE;
1449 MessageQueue->QuitExitCode = 0;
1450 KeQueryTickCount(&LargeTickCount);
1451 MessageQueue->LastMsgRead = LargeTickCount.u.LowPart;
1452 MessageQueue->FocusWindow = NULL;
1453 MessageQueue->PaintCount = 0;
1454 // HACK!!!!!!! Newbies that wrote this should hold your head down in shame! (jt)
1455 MessageQueue->WakeMask = ~0;
1456 MessageQueue->NewMessagesHandle = NULL;
1457
1458 Status = ZwCreateEvent(&MessageQueue->NewMessagesHandle, EVENT_ALL_ACCESS,
1459 NULL, SynchronizationEvent, FALSE);
1460 if (!NT_SUCCESS(Status))
1461 {
1462 return FALSE;
1463 }
1464
1465 Status = ObReferenceObjectByHandle(MessageQueue->NewMessagesHandle, 0,
1466 ExEventObjectType, KernelMode,
1467 (PVOID*)&MessageQueue->NewMessages, NULL);
1468 if (!NT_SUCCESS(Status))
1469 {
1470 ZwClose(MessageQueue->NewMessagesHandle);
1471 MessageQueue->NewMessagesHandle = NULL;
1472 return FALSE;
1473 }
1474
1475 return TRUE;
1476 }
1477
1478 VOID FASTCALL
1479 MsqCleanupMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
1480 {
1481 PLIST_ENTRY CurrentEntry;
1482 PUSER_MESSAGE CurrentMessage;
1483 PUSER_SENT_MESSAGE CurrentSentMessage;
1484
1485 /* cleanup posted messages */
1486 while (!IsListEmpty(&MessageQueue->PostedMessagesListHead))
1487 {
1488 CurrentEntry = RemoveHeadList(&MessageQueue->PostedMessagesListHead);
1489 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1490 ListEntry);
1491 MsqDestroyMessage(CurrentMessage);
1492 }
1493
1494 /* remove the messages that have not yet been dispatched */
1495 while (!IsListEmpty(&MessageQueue->SentMessagesListHead))
1496 {
1497 CurrentEntry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
1498 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1499 ListEntry);
1500
1501 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1502
1503 /* remove the message from the dispatching list if needed */
1504 if ((!(CurrentSentMessage->HookMessage & MSQ_SENTNOWAIT))
1505 && (CurrentSentMessage->DispatchingListEntry.Flink != NULL))
1506 {
1507 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
1508 }
1509
1510 /* wake the sender's thread */
1511 if (CurrentSentMessage->CompletionEvent != NULL)
1512 {
1513 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1514 }
1515
1516 if (CurrentSentMessage->HasPackedLParam == TRUE)
1517 {
1518 if (CurrentSentMessage->Msg.lParam)
1519 ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
1520 }
1521
1522 /* Only if it is not a no wait message */
1523 if (!(CurrentSentMessage->HookMessage & MSQ_SENTNOWAIT))
1524 {
1525 /* dereference our and the sender's message queue */
1526 IntDereferenceMessageQueue(MessageQueue);
1527 IntDereferenceMessageQueue(CurrentSentMessage->SenderQueue);
1528 }
1529
1530 /* free the message */
1531 ExFreePool(CurrentSentMessage);
1532 }
1533
1534 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
1535 ExitThread() was called in a SendMessage() umode callback */
1536 while (!IsListEmpty(&MessageQueue->LocalDispatchingMessagesHead))
1537 {
1538 CurrentEntry = RemoveHeadList(&MessageQueue->LocalDispatchingMessagesHead);
1539 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1540 ListEntry);
1541
1542 /* remove the message from the dispatching list */
1543 if(CurrentSentMessage->DispatchingListEntry.Flink != NULL)
1544 {
1545 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
1546 }
1547
1548 DPRINT("Notify the sender, the thread has been terminated while dispatching a message!\n");
1549
1550 /* wake the sender's thread */
1551 if (CurrentSentMessage->CompletionEvent != NULL)
1552 {
1553 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1554 }
1555
1556 if (CurrentSentMessage->HasPackedLParam == TRUE)
1557 {
1558 if (CurrentSentMessage->Msg.lParam)
1559 ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
1560 }
1561
1562 /* Only if it is not a no wait message */
1563 if (!(CurrentSentMessage->HookMessage & MSQ_SENTNOWAIT))
1564 {
1565 /* dereference our and the sender's message queue */
1566 IntDereferenceMessageQueue(MessageQueue);
1567 IntDereferenceMessageQueue(CurrentSentMessage->SenderQueue);
1568 }
1569
1570 /* free the message */
1571 ExFreePool(CurrentSentMessage);
1572 }
1573
1574 /* tell other threads not to bother returning any info to us */
1575 while (! IsListEmpty(&MessageQueue->DispatchingMessagesHead))
1576 {
1577 CurrentEntry = RemoveHeadList(&MessageQueue->DispatchingMessagesHead);
1578 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1579 DispatchingListEntry);
1580 CurrentSentMessage->CompletionEvent = NULL;
1581 CurrentSentMessage->Result = NULL;
1582
1583 /* do NOT dereference our message queue as it might get attempted to be
1584 locked later */
1585 }
1586
1587 }
1588
1589 PUSER_MESSAGE_QUEUE FASTCALL
1590 MsqCreateMessageQueue(struct _ETHREAD *Thread)
1591 {
1592 PUSER_MESSAGE_QUEUE MessageQueue;
1593
1594 MessageQueue = (PUSER_MESSAGE_QUEUE)ExAllocatePoolWithTag(NonPagedPool,
1595 sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO),
1596 TAG_MSGQ);
1597
1598 if (!MessageQueue)
1599 {
1600 return NULL;
1601 }
1602
1603 RtlZeroMemory(MessageQueue, sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO));
1604 /* hold at least one reference until it'll be destroyed */
1605 IntReferenceMessageQueue(MessageQueue);
1606 /* initialize the queue */
1607 if (!MsqInitializeMessageQueue(Thread, MessageQueue))
1608 {
1609 IntDereferenceMessageQueue(MessageQueue);
1610 return NULL;
1611 }
1612
1613 return MessageQueue;
1614 }
1615
1616 VOID FASTCALL
1617 MsqDestroyMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
1618 {
1619 PDESKTOP desk;
1620
1621 /* remove the message queue from any desktops */
1622 if ((desk = InterlockedExchangePointer((PVOID*)&MessageQueue->Desktop, 0)))
1623 {
1624 (void)InterlockedExchangePointer((PVOID*)&desk->ActiveMessageQueue, 0);
1625 IntDereferenceMessageQueue(MessageQueue);
1626 }
1627
1628 /* clean it up */
1629 MsqCleanupMessageQueue(MessageQueue);
1630
1631 /* decrease the reference counter, if it hits zero, the queue will be freed */
1632 IntDereferenceMessageQueue(MessageQueue);
1633 }
1634
1635 PHOOKTABLE FASTCALL
1636 MsqGetHooks(PUSER_MESSAGE_QUEUE Queue)
1637 {
1638 return Queue->Hooks;
1639 }
1640
1641 VOID FASTCALL
1642 MsqSetHooks(PUSER_MESSAGE_QUEUE Queue, PHOOKTABLE Hooks)
1643 {
1644 Queue->Hooks = Hooks;
1645 }
1646
1647 LPARAM FASTCALL
1648 MsqSetMessageExtraInfo(LPARAM lParam)
1649 {
1650 LPARAM Ret;
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 Ret = MessageQueue->ExtraInfo;
1662 MessageQueue->ExtraInfo = lParam;
1663
1664 return Ret;
1665 }
1666
1667 LPARAM FASTCALL
1668 MsqGetMessageExtraInfo(VOID)
1669 {
1670 PTHREADINFO pti;
1671 PUSER_MESSAGE_QUEUE MessageQueue;
1672
1673 pti = PsGetCurrentThreadWin32Thread();
1674 MessageQueue = pti->MessageQueue;
1675 if(!MessageQueue)
1676 {
1677 return 0;
1678 }
1679
1680 return MessageQueue->ExtraInfo;
1681 }
1682
1683 HWND FASTCALL
1684 MsqSetStateWindow(PUSER_MESSAGE_QUEUE MessageQueue, ULONG Type, HWND hWnd)
1685 {
1686 HWND Prev;
1687
1688 switch(Type)
1689 {
1690 case MSQ_STATE_CAPTURE:
1691 Prev = MessageQueue->CaptureWindow;
1692 MessageQueue->CaptureWindow = hWnd;
1693 return Prev;
1694 case MSQ_STATE_ACTIVE:
1695 Prev = MessageQueue->ActiveWindow;
1696 MessageQueue->ActiveWindow = hWnd;
1697 return Prev;
1698 case MSQ_STATE_FOCUS:
1699 Prev = MessageQueue->FocusWindow;
1700 MessageQueue->FocusWindow = hWnd;
1701 return Prev;
1702 case MSQ_STATE_MENUOWNER:
1703 Prev = MessageQueue->MenuOwner;
1704 MessageQueue->MenuOwner = hWnd;
1705 return Prev;
1706 case MSQ_STATE_MOVESIZE:
1707 Prev = MessageQueue->MoveSize;
1708 MessageQueue->MoveSize = hWnd;
1709 return Prev;
1710 case MSQ_STATE_CARET:
1711 ASSERT(MessageQueue->CaretInfo);
1712 Prev = MessageQueue->CaretInfo->hWnd;
1713 MessageQueue->CaretInfo->hWnd = hWnd;
1714 return Prev;
1715 }
1716
1717 return NULL;
1718 }
1719
1720 /* EOF */