dc315b5ccaf33a9646598c23ef79e4b111fe4846
[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 PWINDOW_OBJECT Window;
73 PPROCESSINFO W32d = PsGetCurrentProcessWin32Process();
74
75 hWnd = UserGetForegroundWindow();
76
77 Window = UserGetWindowObject(hWnd);
78
79 if (Window && Window->pti)
80 {
81 if (Window->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, PWINDOW_OBJECT Window, UINT FilterLow, UINT FilterHigh,
346 PUSER_MESSAGE Message, BOOL Remove, PBOOL Freed,
347 PWINDOW_OBJECT ScopeWin, PPOINT ScreenPoint, BOOL FromGlobalQueue, PLIST_ENTRY *Next)
348 {
349 USHORT Msg = Message->Msg.message;
350 PWINDOW_OBJECT CaptureWindow = NULL;
351 HWND hCaptureWin;
352
353 ASSERT_REFS_CO(ScopeWin);
354
355 /*
356 co_WinPosWindowFromPoint can return a Window, and in that case
357 that window has a ref that we need to deref. Thats why we add "dummy"
358 refs in all other cases.
359 */
360
361 hCaptureWin = IntGetCaptureWindow();
362 if (hCaptureWin == NULL)
363 {
364 if (Msg == WM_MOUSEWHEEL)
365 {
366 CaptureWindow = UserGetWindowObject(IntGetFocusWindow());
367 if (CaptureWindow) UserReferenceObject(CaptureWindow);
368 }
369 else
370 {
371 co_WinPosWindowFromPoint(ScopeWin, NULL, &Message->Msg.pt, &CaptureWindow);
372 if(CaptureWindow == NULL)
373 {
374 CaptureWindow = ScopeWin;
375 if (CaptureWindow) UserReferenceObject(CaptureWindow);
376 }
377 else
378 {
379 /* this is the one case where we dont add a ref, since the returned
380 window is already referenced */
381 }
382 }
383 }
384 else
385 {
386 /* FIXME - window messages should go to the right window if no buttons are
387 pressed */
388 CaptureWindow = UserGetWindowObject(hCaptureWin);
389 if (CaptureWindow) UserReferenceObject(CaptureWindow);
390 }
391
392
393
394 if (CaptureWindow == NULL)
395 {
396 if(!FromGlobalQueue)
397 {
398 RemoveEntryList(&Message->ListEntry);
399 if(MessageQueue->MouseMoveMsg == Message)
400 {
401 MessageQueue->MouseMoveMsg = NULL;
402 }
403 }
404 // when FromGlobalQueue is true, the caller has already removed the Message
405 ExFreePool(Message);
406 *Freed = TRUE;
407 return(FALSE);
408 }
409
410 if (CaptureWindow->pti->MessageQueue != MessageQueue)
411 {
412 if (! FromGlobalQueue)
413 {
414 DPRINT("Moving msg between private queues\n");
415 /* This message is already queued in a private queue, but we need
416 * to move it to a different queue, perhaps because a new window
417 * was created which now covers the screen area previously taken
418 * by another window. To move it, we need to take it out of the
419 * old queue. Note that we're already holding the lock mutexes of the
420 * old queue */
421 RemoveEntryList(&Message->ListEntry);
422
423 /* remove the pointer for the current WM_MOUSEMOVE message in case we
424 just removed it */
425 if(MessageQueue->MouseMoveMsg == Message)
426 {
427 MessageQueue->MouseMoveMsg = NULL;
428 }
429 }
430
431 /* lock the destination message queue, so we don't get in trouble with other
432 threads, messing with it at the same time */
433 IntLockHardwareMessageQueue(CaptureWindow->pti->MessageQueue);
434 InsertTailList(&CaptureWindow->pti->MessageQueue->HardwareMessagesListHead,
435 &Message->ListEntry);
436 if(Message->Msg.message == WM_MOUSEMOVE)
437 {
438 if(CaptureWindow->pti->MessageQueue->MouseMoveMsg)
439 {
440 /* remove the old WM_MOUSEMOVE message, we're processing a more recent
441 one */
442 RemoveEntryList(&CaptureWindow->pti->MessageQueue->MouseMoveMsg->ListEntry);
443 ExFreePool(CaptureWindow->pti->MessageQueue->MouseMoveMsg);
444 }
445 /* save the pointer to the WM_MOUSEMOVE message in the new queue */
446 CaptureWindow->pti->MessageQueue->MouseMoveMsg = Message;
447
448 CaptureWindow->pti->MessageQueue->QueueBits |= QS_MOUSEMOVE;
449 CaptureWindow->pti->MessageQueue->ChangedBits |= QS_MOUSEMOVE;
450 if (CaptureWindow->pti->MessageQueue->WakeMask & QS_MOUSEMOVE)
451 KeSetEvent(CaptureWindow->pti->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
452 }
453 else
454 {
455 CaptureWindow->pti->MessageQueue->QueueBits |= QS_MOUSEBUTTON;
456 CaptureWindow->pti->MessageQueue->ChangedBits |= QS_MOUSEBUTTON;
457 if (CaptureWindow->pti->MessageQueue->WakeMask & QS_MOUSEBUTTON)
458 KeSetEvent(CaptureWindow->pti->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
459 }
460 IntUnLockHardwareMessageQueue(CaptureWindow->pti->MessageQueue);
461
462 *Freed = FALSE;
463 UserDereferenceObject(CaptureWindow);
464 return(FALSE);
465 }
466
467 /* From here on, we're in the same message queue as the caller! */
468
469 *ScreenPoint = Message->Msg.pt;
470
471 if((Window != NULL && PtrToInt(Window) != 1 && CaptureWindow->hSelf != Window->hSelf) ||
472 ((FilterLow != 0 || FilterHigh != 0) && (Msg < FilterLow || Msg > FilterHigh)))
473 {
474 /* Reject the message because it doesn't match the filter */
475
476 if(FromGlobalQueue)
477 {
478 /* Lock the message queue so no other thread can mess with it.
479 Our own message queue is not locked while fetching from the global
480 queue, so we have to make sure nothing interferes! */
481 IntLockHardwareMessageQueue(CaptureWindow->pti->MessageQueue);
482 /* if we're from the global queue, we need to add our message to our
483 private queue so we don't loose it! */
484 InsertTailList(&CaptureWindow->pti->MessageQueue->HardwareMessagesListHead,
485 &Message->ListEntry);
486 }
487
488 if (Message->Msg.message == WM_MOUSEMOVE)
489 {
490 if(CaptureWindow->pti->MessageQueue->MouseMoveMsg &&
491 (CaptureWindow->pti->MessageQueue->MouseMoveMsg != Message))
492 {
493 /* delete the old message */
494 RemoveEntryList(&CaptureWindow->pti->MessageQueue->MouseMoveMsg->ListEntry);
495 ExFreePool(CaptureWindow->pti->MessageQueue->MouseMoveMsg);
496 if (!FromGlobalQueue)
497 {
498 // We might have deleted the next one in our queue, so fix next
499 *Next = Message->ListEntry.Flink;
500 }
501 }
502 /* always save a pointer to this WM_MOUSEMOVE message here because we're
503 sure that the message is in the private queue */
504 CaptureWindow->pti->MessageQueue->MouseMoveMsg = Message;
505 }
506 if(FromGlobalQueue)
507 {
508 IntUnLockHardwareMessageQueue(CaptureWindow->pti->MessageQueue);
509 }
510
511 UserDereferenceObject(CaptureWindow);
512 *Freed = FALSE;
513 return(FALSE);
514 }
515
516 /* FIXME - only assign if removing? */
517 Message->Msg.hwnd = CaptureWindow->hSelf;
518 Message->Msg.message = Msg;
519 Message->Msg.lParam = MAKELONG(Message->Msg.pt.x, Message->Msg.pt.y);
520
521 /* remove the reference to the current WM_(NC)MOUSEMOVE message, if this message
522 is it */
523 if (Message->Msg.message == WM_MOUSEMOVE ||
524 Message->Msg.message == WM_NCMOUSEMOVE)
525 {
526 if(FromGlobalQueue)
527 {
528 /* Lock the message queue so no other thread can mess with it.
529 Our own message queue is not locked while fetching from the global
530 queue, so we have to make sure nothing interferes! */
531 IntLockHardwareMessageQueue(CaptureWindow->pti->MessageQueue);
532 if(CaptureWindow->pti->MessageQueue->MouseMoveMsg)
533 {
534 /* delete the WM_(NC)MOUSEMOVE message in the private queue, we're dealing
535 with one that's been sent later */
536 RemoveEntryList(&CaptureWindow->pti->MessageQueue->MouseMoveMsg->ListEntry);
537 ExFreePool(CaptureWindow->pti->MessageQueue->MouseMoveMsg);
538 /* our message is not in the private queue so we can remove the pointer
539 instead of setting it to the current message we're processing */
540 CaptureWindow->pti->MessageQueue->MouseMoveMsg = NULL;
541 }
542 IntUnLockHardwareMessageQueue(CaptureWindow->pti->MessageQueue);
543 }
544 else if (CaptureWindow->pti->MessageQueue->MouseMoveMsg == Message)
545 {
546 CaptureWindow->pti->MessageQueue->MouseMoveMsg = NULL;
547 }
548 }
549
550 UserDereferenceObject(CaptureWindow);
551 *Freed = FALSE;
552 return(TRUE);
553 }
554
555 BOOL APIENTRY
556 co_MsqPeekHardwareMessage(PUSER_MESSAGE_QUEUE MessageQueue, PWINDOW_OBJECT Window,
557 UINT FilterLow, UINT FilterHigh, BOOL Remove,
558 PUSER_MESSAGE* Message)
559 {
560 KIRQL OldIrql;
561 POINT ScreenPoint;
562 BOOL Accept, Freed;
563 PLIST_ENTRY CurrentEntry;
564 PWINDOW_OBJECT DesktopWindow = NULL;
565 PVOID WaitObjects[2];
566 NTSTATUS WaitStatus;
567 DECLARE_RETURN(BOOL);
568 USER_REFERENCE_ENTRY Ref;
569 PDESKTOPINFO Desk = NULL;
570
571 WaitObjects[1] = MessageQueue->NewMessages;
572 WaitObjects[0] = &HardwareMessageQueueLock;
573 do
574 {
575 IdlePing();
576
577 UserLeaveCo();
578
579 WaitStatus = KeWaitForMultipleObjects(2, WaitObjects, WaitAny, UserRequest,
580 UserMode, FALSE, NULL, NULL);
581
582 UserEnterCo();
583 }
584 while (NT_SUCCESS(WaitStatus) && STATUS_WAIT_0 != WaitStatus);
585
586 DesktopWindow = UserGetWindowObject(IntGetDesktopWindow());
587
588 if (DesktopWindow)
589 {
590 UserRefObjectCo(DesktopWindow, &Ref);//can DesktopWindow be NULL?
591 Desk = DesktopWindow->pti->pDeskInfo;
592 }
593
594 /* Process messages in the message queue itself. */
595 IntLockHardwareMessageQueue(MessageQueue);
596 CurrentEntry = MessageQueue->HardwareMessagesListHead.Flink;
597 while (CurrentEntry != &MessageQueue->HardwareMessagesListHead)
598 {
599 PUSER_MESSAGE Current =
600 CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, ListEntry);
601 CurrentEntry = CurrentEntry->Flink;
602 if (Current->Msg.message >= WM_MOUSEFIRST &&
603 Current->Msg.message <= WM_MOUSELAST)
604 {
605
606
607 Accept = co_MsqTranslateMouseMessage(MessageQueue, Window, FilterLow, FilterHigh,
608 Current, Remove, &Freed,
609 DesktopWindow, &ScreenPoint, FALSE, &CurrentEntry);
610 if (Accept)
611 {
612 if (Remove)
613 {
614 RemoveEntryList(&Current->ListEntry);
615 }
616 IntUnLockHardwareMessageQueue(MessageQueue);
617 IntUnLockSystemHardwareMessageQueueLock(FALSE);
618 *Message = Current;
619
620 if (Desk)
621 Desk->LastInputWasKbd = FALSE;
622
623 RETURN(TRUE);
624 }
625
626 }
627 }
628 IntUnLockHardwareMessageQueue(MessageQueue);
629
630 /* Now try the global queue. */
631
632 /* Transfer all messages from the DPC accessible queue to the main queue. */
633 IntLockSystemMessageQueue(OldIrql);
634 while (SystemMessageQueueCount > 0)
635 {
636 PUSER_MESSAGE UserMsg;
637 MSG Msg;
638
639 ASSERT(SystemMessageQueueHead < SYSTEM_MESSAGE_QUEUE_SIZE);
640 Msg = SystemMessageQueue[SystemMessageQueueHead];
641 SystemMessageQueueHead =
642 (SystemMessageQueueHead + 1) % SYSTEM_MESSAGE_QUEUE_SIZE;
643 SystemMessageQueueCount--;
644 IntUnLockSystemMessageQueue(OldIrql);
645
646 UserMsg = ExAllocateFromPagedLookasideList(&MessageLookasideList);
647 /* What to do if out of memory? For now we just panic a bit in debug */
648 ASSERT(UserMsg);
649 UserMsg->FreeLParam = FALSE;
650 UserMsg->Msg = Msg;
651 InsertTailList(&HardwareMessageQueueHead, &UserMsg->ListEntry);
652
653 IntLockSystemMessageQueue(OldIrql);
654 }
655 HardwareMessageQueueStamp++;
656 IntUnLockSystemMessageQueue(OldIrql);
657
658 /* Process messages in the queue until we find one to return. */
659 CurrentEntry = HardwareMessageQueueHead.Flink;
660 while (CurrentEntry != &HardwareMessageQueueHead)
661 {
662 PUSER_MESSAGE Current =
663 CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, ListEntry);
664 CurrentEntry = CurrentEntry->Flink;
665 RemoveEntryList(&Current->ListEntry);
666 HardwareMessageQueueStamp++;
667 if (Current->Msg.message >= WM_MOUSEFIRST &&
668 Current->Msg.message <= WM_MOUSELAST)
669 {
670 const ULONG ActiveStamp = HardwareMessageQueueStamp;
671 /* Translate the message. */
672 Accept = co_MsqTranslateMouseMessage(MessageQueue, Window, FilterLow, FilterHigh,
673 Current, Remove, &Freed,
674 DesktopWindow, &ScreenPoint, TRUE, NULL);
675 if (Accept)
676 {
677 /* Check for no more messages in the system queue. */
678 IntLockSystemMessageQueue(OldIrql);
679 if (SystemMessageQueueCount == 0 &&
680 IsListEmpty(&HardwareMessageQueueHead))
681 {
682 KeClearEvent(&HardwareMessageEvent);
683 }
684 IntUnLockSystemMessageQueue(OldIrql);
685
686 /*
687 If we aren't removing the message then add it to the private
688 queue.
689 */
690 if (!Remove)
691 {
692 IntLockHardwareMessageQueue(MessageQueue);
693 if(Current->Msg.message == WM_MOUSEMOVE)
694 {
695 if(MessageQueue->MouseMoveMsg)
696 {
697 RemoveEntryList(&MessageQueue->MouseMoveMsg->ListEntry);
698 ExFreePool(MessageQueue->MouseMoveMsg);
699 }
700 MessageQueue->MouseMoveMsg = Current;
701 }
702 InsertTailList(&MessageQueue->HardwareMessagesListHead,
703 &Current->ListEntry);
704 IntUnLockHardwareMessageQueue(MessageQueue);
705 }
706 IntUnLockSystemHardwareMessageQueueLock(FALSE);
707 *Message = Current;
708
709 RETURN(TRUE);
710 }
711 /* If the contents of the queue changed then restart processing. */
712 if (HardwareMessageQueueStamp != ActiveStamp)
713 {
714 CurrentEntry = HardwareMessageQueueHead.Flink;
715 continue;
716 }
717 }
718 }
719
720 /* Check if the system message queue is now empty. */
721 IntLockSystemMessageQueue(OldIrql);
722 if (SystemMessageQueueCount == 0 && IsListEmpty(&HardwareMessageQueueHead))
723 {
724 KeClearEvent(&HardwareMessageEvent);
725 }
726 IntUnLockSystemMessageQueue(OldIrql);
727 IntUnLockSystemHardwareMessageQueueLock(FALSE);
728
729 RETURN(FALSE);
730
731 CLEANUP:
732 if (DesktopWindow) UserDerefObjectCo(DesktopWindow);
733
734 END_CLEANUP;
735 }
736
737 //
738 // Note: Only called from input.c.
739 //
740 VOID FASTCALL
741 co_MsqPostKeyboardMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
742 {
743 PUSER_MESSAGE_QUEUE FocusMessageQueue;
744 MSG Msg;
745 LARGE_INTEGER LargeTickCount;
746 KBDLLHOOKSTRUCT KbdHookData;
747 BOOLEAN Entered = FALSE;
748
749 DPRINT("MsqPostKeyboardMessage(uMsg 0x%x, wParam 0x%x, lParam 0x%x)\n",
750 uMsg, wParam, lParam);
751
752 // Condition may arise when calling MsqPostMessage and waiting for an event.
753 if (!UserIsEntered())
754 {
755 // Fixme: Not sure ATM if this thread is locked.
756 UserEnterExclusive();
757 Entered = TRUE;
758 }
759
760 FocusMessageQueue = IntGetFocusMessageQueue();
761
762 Msg.hwnd = 0;
763
764 if (FocusMessageQueue && (FocusMessageQueue->FocusWindow != (HWND)0))
765 Msg.hwnd = FocusMessageQueue->FocusWindow;
766
767 Msg.message = uMsg;
768 Msg.wParam = wParam;
769 Msg.lParam = lParam;
770
771 KeQueryTickCount(&LargeTickCount);
772 Msg.time = MsqCalculateMessageTime(&LargeTickCount);
773
774 /* We can't get the Msg.pt point here since we don't know thread
775 (and thus the window station) the message will end up in yet. */
776
777 KbdHookData.vkCode = Msg.wParam;
778 KbdHookData.scanCode = (Msg.lParam >> 16) & 0xff;
779 KbdHookData.flags = (0 == (Msg.lParam & 0x01000000) ? 0 : LLKHF_EXTENDED) |
780 (0 == (Msg.lParam & 0x20000000) ? 0 : LLKHF_ALTDOWN) |
781 (0 == (Msg.lParam & 0x80000000) ? 0 : LLKHF_UP);
782 KbdHookData.time = Msg.time;
783 KbdHookData.dwExtraInfo = 0;
784 if (co_HOOK_CallHooks(WH_KEYBOARD_LL, HC_ACTION, Msg.message, (LPARAM) &KbdHookData))
785 {
786 DPRINT("Kbd msg %d wParam %d lParam 0x%08x dropped by WH_KEYBOARD_LL hook\n",
787 Msg.message, Msg.wParam, Msg.lParam);
788 if (Entered) UserLeave();
789 return;
790 }
791
792 if (FocusMessageQueue == NULL)
793 {
794 DPRINT("No focus message queue\n");
795 if (Entered) UserLeave();
796 return;
797 }
798
799 if (FocusMessageQueue->FocusWindow != (HWND)0)
800 {
801 Msg.hwnd = FocusMessageQueue->FocusWindow;
802 DPRINT("Msg.hwnd = %x\n", Msg.hwnd);
803
804 FocusMessageQueue->Desktop->pDeskInfo->LastInputWasKbd = TRUE;
805
806 Msg.pt = gpsi->ptCursor;
807 MsqPostMessage(FocusMessageQueue, &Msg, FALSE, QS_KEY);
808 }
809 else
810 {
811 DPRINT("Invalid focus window handle\n");
812 }
813
814 if (Entered) UserLeave();
815 return;
816 }
817
818 VOID FASTCALL
819 MsqPostHotKeyMessage(PVOID Thread, HWND hWnd, WPARAM wParam, LPARAM lParam)
820 {
821 PWINDOW_OBJECT Window;
822 PTHREADINFO Win32Thread;
823 MSG Mesg;
824 LARGE_INTEGER LargeTickCount;
825 NTSTATUS Status;
826
827 Status = ObReferenceObjectByPointer (Thread,
828 THREAD_ALL_ACCESS,
829 PsThreadType,
830 KernelMode);
831 if (!NT_SUCCESS(Status))
832 return;
833
834 Win32Thread = ((PETHREAD)Thread)->Tcb.Win32Thread;
835 if (Win32Thread == NULL || Win32Thread->MessageQueue == NULL)
836 {
837 ObDereferenceObject ((PETHREAD)Thread);
838 return;
839 }
840
841 Window = IntGetWindowObject(hWnd);
842 if (!Window)
843 {
844 ObDereferenceObject ((PETHREAD)Thread);
845 return;
846 }
847
848 Mesg.hwnd = hWnd;
849 Mesg.message = WM_HOTKEY;
850 Mesg.wParam = wParam;
851 Mesg.lParam = lParam;
852 KeQueryTickCount(&LargeTickCount);
853 Mesg.time = MsqCalculateMessageTime(&LargeTickCount);
854 Mesg.pt = gpsi->ptCursor;
855 MsqPostMessage(Window->pti->MessageQueue, &Mesg, FALSE, QS_HOTKEY);
856 UserDereferenceObject(Window);
857 ObDereferenceObject (Thread);
858
859 // InsertHeadList(&pThread->MessageQueue->PostedMessagesListHead,
860 // &Message->ListEntry);
861 // KeSetEvent(pThread->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
862 }
863
864 PUSER_MESSAGE FASTCALL
865 MsqCreateMessage(LPMSG Msg, BOOLEAN FreeLParam)
866 {
867 PUSER_MESSAGE Message;
868
869 Message = ExAllocateFromPagedLookasideList(&MessageLookasideList);
870 if (!Message)
871 {
872 return NULL;
873 }
874
875 Message->FreeLParam = FreeLParam;
876 RtlMoveMemory(&Message->Msg, Msg, sizeof(MSG));
877
878 return Message;
879 }
880
881 VOID FASTCALL
882 MsqDestroyMessage(PUSER_MESSAGE Message)
883 {
884 ExFreeToPagedLookasideList(&MessageLookasideList, Message);
885 }
886
887 VOID FASTCALL
888 co_MsqDispatchSentNotifyMessages(PUSER_MESSAGE_QUEUE MessageQueue)
889 {
890 PLIST_ENTRY ListEntry;
891 PUSER_SENT_MESSAGE_NOTIFY Message;
892
893 while (!IsListEmpty(&MessageQueue->SentMessagesListHead))
894 {
895 ListEntry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
896 Message = CONTAINING_RECORD(ListEntry, USER_SENT_MESSAGE_NOTIFY,
897 ListEntry);
898
899 co_IntCallSentMessageCallback(Message->CompletionCallback,
900 Message->hWnd,
901 Message->Msg,
902 Message->CompletionCallbackContext,
903 Message->Result);
904
905 }
906
907 }
908
909 BOOLEAN FASTCALL
910 MsqPeekSentMessages(PUSER_MESSAGE_QUEUE MessageQueue)
911 {
912 return(!IsListEmpty(&MessageQueue->SentMessagesListHead));
913 }
914
915 BOOLEAN FASTCALL
916 co_MsqDispatchOneSentMessage(PUSER_MESSAGE_QUEUE MessageQueue)
917 {
918 PUSER_SENT_MESSAGE Message;
919 PLIST_ENTRY Entry;
920 LRESULT Result;
921
922 if (IsListEmpty(&MessageQueue->SentMessagesListHead))
923 {
924 return(FALSE);
925 }
926
927 /* remove it from the list of pending messages */
928 Entry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
929 Message = CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry);
930
931 /* insert it to the list of messages that are currently dispatched by this
932 message queue */
933 InsertTailList(&MessageQueue->LocalDispatchingMessagesHead,
934 &Message->ListEntry);
935
936 if (Message->HookMessage == MSQ_ISHOOK)
937 {
938 Result = co_HOOK_CallHooks(Message->Msg.message,
939 (INT)(INT_PTR)Message->Msg.hwnd,
940 Message->Msg.wParam,
941 Message->Msg.lParam);
942 }
943 else if (Message->HookMessage == MSQ_ISEVENT)
944 {
945 Result = co_EVENT_CallEvents( Message->Msg.message,
946 Message->Msg.hwnd,
947 Message->Msg.wParam,
948 Message->Msg.lParam);
949 }
950 else
951 {
952 /* Call the window procedure. */
953 Result = co_IntSendMessage(Message->Msg.hwnd,
954 Message->Msg.message,
955 Message->Msg.wParam,
956 Message->Msg.lParam);
957 }
958
959 /* remove the message from the local dispatching list, because it doesn't need
960 to be cleaned up on thread termination anymore */
961 RemoveEntryList(&Message->ListEntry);
962
963 /* remove the message from the dispatching list if needed, so lock the sender's message queue */
964 if (!(Message->HookMessage & MSQ_SENTNOWAIT))
965 {
966 if (Message->DispatchingListEntry.Flink != NULL)
967 {
968 /* only remove it from the dispatching list if not already removed by a timeout */
969 RemoveEntryList(&Message->DispatchingListEntry);
970 }
971 }
972 /* still keep the sender's message queue locked, so the sender can't exit the
973 MsqSendMessage() function (if timed out) */
974
975 /* Let the sender know the result. */
976 if (Message->Result != NULL)
977 {
978 *Message->Result = Result;
979 }
980
981 if (Message->HasPackedLParam == TRUE)
982 {
983 if (Message->Msg.lParam)
984 ExFreePool((PVOID)Message->Msg.lParam);
985 }
986
987 /* Notify the sender. */
988 if (Message->CompletionEvent != NULL)
989 {
990 KeSetEvent(Message->CompletionEvent, IO_NO_INCREMENT, FALSE);
991 }
992
993 /* Call the callback if the message was sent with SendMessageCallback */
994 if (Message->CompletionCallback != NULL)
995 {
996 co_IntCallSentMessageCallback(Message->CompletionCallback,
997 Message->Msg.hwnd,
998 Message->Msg.message,
999 Message->CompletionCallbackContext,
1000 Result);
1001 }
1002
1003 /* Only if it is not a no wait message */
1004 if (!(Message->HookMessage & MSQ_SENTNOWAIT))
1005 {
1006 IntDereferenceMessageQueue(Message->SenderQueue);
1007 IntDereferenceMessageQueue(MessageQueue);
1008 }
1009
1010 /* free the message */
1011 ExFreePoolWithTag(Message, TAG_USRMSG);
1012 return(TRUE);
1013 }
1014
1015 VOID APIENTRY
1016 MsqRemoveWindowMessagesFromQueue(PVOID pWindow)
1017 {
1018 PUSER_SENT_MESSAGE SentMessage;
1019 PUSER_MESSAGE PostedMessage;
1020 PUSER_MESSAGE_QUEUE MessageQueue;
1021 PLIST_ENTRY CurrentEntry, ListHead;
1022 PWINDOW_OBJECT Window = pWindow;
1023
1024 ASSERT(Window);
1025
1026 MessageQueue = Window->pti->MessageQueue;
1027 ASSERT(MessageQueue);
1028
1029 /* remove the posted messages for this window */
1030 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
1031 ListHead = &MessageQueue->PostedMessagesListHead;
1032 while (CurrentEntry != ListHead)
1033 {
1034 PostedMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1035 ListEntry);
1036 if (PostedMessage->Msg.hwnd == Window->hSelf)
1037 {
1038 RemoveEntryList(&PostedMessage->ListEntry);
1039 MsqDestroyMessage(PostedMessage);
1040 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
1041 }
1042 else
1043 {
1044 CurrentEntry = CurrentEntry->Flink;
1045 }
1046 }
1047
1048 /* remove the sent messages for this window */
1049 CurrentEntry = MessageQueue->SentMessagesListHead.Flink;
1050 ListHead = &MessageQueue->SentMessagesListHead;
1051 while (CurrentEntry != ListHead)
1052 {
1053 SentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1054 ListEntry);
1055 if(SentMessage->Msg.hwnd == Window->hSelf)
1056 {
1057 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1058
1059 RemoveEntryList(&SentMessage->ListEntry);
1060
1061 /* remove the message from the dispatching list if neede */
1062 if ((!(SentMessage->HookMessage & MSQ_SENTNOWAIT))
1063 && (SentMessage->DispatchingListEntry.Flink != NULL))
1064 {
1065 RemoveEntryList(&SentMessage->DispatchingListEntry);
1066 }
1067
1068 /* wake the sender's thread */
1069 if (SentMessage->CompletionEvent != NULL)
1070 {
1071 KeSetEvent(SentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1072 }
1073
1074 if (SentMessage->HasPackedLParam == TRUE)
1075 {
1076 if (SentMessage->Msg.lParam)
1077 ExFreePool((PVOID)SentMessage->Msg.lParam);
1078 }
1079
1080 /* Only if it is not a no wait message */
1081 if (!(SentMessage->HookMessage & MSQ_SENTNOWAIT))
1082 {
1083 /* dereference our and the sender's message queue */
1084 IntDereferenceMessageQueue(MessageQueue);
1085 IntDereferenceMessageQueue(SentMessage->SenderQueue);
1086 }
1087
1088 /* free the message */
1089 ExFreePoolWithTag(SentMessage, TAG_USRMSG);
1090
1091 CurrentEntry = MessageQueue->SentMessagesListHead.Flink;
1092 }
1093 else
1094 {
1095 CurrentEntry = CurrentEntry->Flink;
1096 }
1097 }
1098 }
1099
1100 VOID FASTCALL
1101 MsqSendNotifyMessage(PUSER_MESSAGE_QUEUE MessageQueue,
1102 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage)
1103 {
1104 InsertTailList(&MessageQueue->NotifyMessagesListHead,
1105 &NotifyMessage->ListEntry);
1106 MessageQueue->QueueBits |= QS_SENDMESSAGE;
1107 MessageQueue->ChangedBits |= QS_SENDMESSAGE;
1108 if (MessageQueue->WakeMask & QS_SENDMESSAGE)
1109 KeSetEvent(MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
1110 }
1111
1112 NTSTATUS FASTCALL
1113 co_MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue,
1114 HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam,
1115 UINT uTimeout, BOOL Block, INT HookMessage,
1116 ULONG_PTR *uResult)
1117 {
1118 PTHREADINFO pti;
1119 PUSER_SENT_MESSAGE Message;
1120 KEVENT CompletionEvent;
1121 NTSTATUS WaitStatus;
1122 LRESULT Result;
1123 PUSER_MESSAGE_QUEUE ThreadQueue;
1124 LARGE_INTEGER Timeout;
1125 PLIST_ENTRY Entry;
1126
1127 if(!(Message = ExAllocatePoolWithTag(PagedPool, sizeof(USER_SENT_MESSAGE), TAG_USRMSG)))
1128 {
1129 DPRINT1("MsqSendMessage(): Not enough memory to allocate a message");
1130 return STATUS_INSUFFICIENT_RESOURCES;
1131 }
1132
1133 KeInitializeEvent(&CompletionEvent, NotificationEvent, FALSE);
1134
1135 pti = PsGetCurrentThreadWin32Thread();
1136 ThreadQueue = pti->MessageQueue;
1137 ASSERT(ThreadQueue != MessageQueue);
1138
1139 Timeout.QuadPart = (LONGLONG) uTimeout * (LONGLONG) -10000;
1140
1141 /* FIXME - increase reference counter of sender's message queue here */
1142
1143 Result = 0;
1144 Message->Msg.hwnd = Wnd;
1145 Message->Msg.message = Msg;
1146 Message->Msg.wParam = wParam;
1147 Message->Msg.lParam = lParam;
1148 Message->CompletionEvent = &CompletionEvent;
1149 Message->Result = &Result;
1150 Message->SenderQueue = ThreadQueue;
1151 IntReferenceMessageQueue(ThreadQueue);
1152 Message->CompletionCallback = NULL;
1153 Message->HookMessage = HookMessage;
1154 Message->HasPackedLParam = FALSE;
1155
1156 IntReferenceMessageQueue(MessageQueue);
1157
1158 /* add it to the list of pending messages */
1159 InsertTailList(&ThreadQueue->DispatchingMessagesHead, &Message->DispatchingListEntry);
1160
1161 /* queue it in the destination's message queue */
1162 InsertTailList(&MessageQueue->SentMessagesListHead, &Message->ListEntry);
1163
1164 MessageQueue->QueueBits |= QS_SENDMESSAGE;
1165 MessageQueue->ChangedBits |= QS_SENDMESSAGE;
1166 if (MessageQueue->WakeMask & QS_SENDMESSAGE)
1167 KeSetEvent(MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
1168
1169 /* we can't access the Message anymore since it could have already been deleted! */
1170
1171 if(Block)
1172 {
1173 IdlePing();
1174
1175 UserLeaveCo();
1176
1177 /* don't process messages sent to the thread */
1178 WaitStatus = KeWaitForSingleObject(&CompletionEvent, UserRequest, UserMode,
1179 FALSE, (uTimeout ? &Timeout : NULL));
1180
1181 UserEnterCo();
1182
1183 if(WaitStatus == STATUS_TIMEOUT)
1184 {
1185 /* look up if the message has not yet dispatched, if so
1186 make sure it can't pass a result and it must not set the completion event anymore */
1187 Entry = MessageQueue->SentMessagesListHead.Flink;
1188 while (Entry != &MessageQueue->SentMessagesListHead)
1189 {
1190 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry)
1191 == Message)
1192 {
1193 /* we can access Message here, it's secure because the message queue is locked
1194 and the message is still hasn't been dispatched */
1195 Message->CompletionEvent = NULL;
1196 Message->Result = NULL;
1197 break;
1198 }
1199 Entry = Entry->Flink;
1200 }
1201
1202 /* remove from the local dispatching list so the other thread knows,
1203 it can't pass a result and it must not set the completion event anymore */
1204 Entry = ThreadQueue->DispatchingMessagesHead.Flink;
1205 while (Entry != &ThreadQueue->DispatchingMessagesHead)
1206 {
1207 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, DispatchingListEntry)
1208 == Message)
1209 {
1210 /* we can access Message here, it's secure because the sender's message is locked
1211 and the message has definitely not yet been destroyed, otherwise it would
1212 have been removed from this list by the dispatching routine right after
1213 dispatching the message */
1214 Message->CompletionEvent = NULL;
1215 Message->Result = NULL;
1216 RemoveEntryList(&Message->DispatchingListEntry);
1217 Message->DispatchingListEntry.Flink = NULL;
1218 break;
1219 }
1220 Entry = Entry->Flink;
1221 }
1222
1223 DPRINT("MsqSendMessage (blocked) timed out\n");
1224 }
1225 while (co_MsqDispatchOneSentMessage(ThreadQueue))
1226 ;
1227 }
1228 else
1229 {
1230 PVOID WaitObjects[2];
1231
1232 WaitObjects[0] = &CompletionEvent;
1233 WaitObjects[1] = ThreadQueue->NewMessages;
1234 do
1235 {
1236 IdlePing();
1237
1238 UserLeaveCo();
1239
1240 WaitStatus = KeWaitForMultipleObjects(2, WaitObjects, WaitAny, UserRequest,
1241 UserMode, FALSE, (uTimeout ? &Timeout : NULL), NULL);
1242
1243 UserEnterCo();
1244
1245 if(WaitStatus == STATUS_TIMEOUT)
1246 {
1247 /* look up if the message has not yet been dispatched, if so
1248 make sure it can't pass a result and it must not set the completion event anymore */
1249 Entry = MessageQueue->SentMessagesListHead.Flink;
1250 while (Entry != &MessageQueue->SentMessagesListHead)
1251 {
1252 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry)
1253 == Message)
1254 {
1255 /* we can access Message here, it's secure because the message queue is locked
1256 and the message is still hasn't been dispatched */
1257 Message->CompletionEvent = NULL;
1258 Message->Result = NULL;
1259 break;
1260 }
1261 Entry = Entry->Flink;
1262 }
1263
1264 /* remove from the local dispatching list so the other thread knows,
1265 it can't pass a result and it must not set the completion event anymore */
1266 Entry = ThreadQueue->DispatchingMessagesHead.Flink;
1267 while (Entry != &ThreadQueue->DispatchingMessagesHead)
1268 {
1269 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, DispatchingListEntry)
1270 == Message)
1271 {
1272 /* we can access Message here, it's secure because the sender's message is locked
1273 and the message has definitely not yet been destroyed, otherwise it would
1274 have been removed from this list by the dispatching routine right after
1275 dispatching the message */
1276 Message->CompletionEvent = NULL;
1277 Message->Result = NULL;
1278 RemoveEntryList(&Message->DispatchingListEntry);
1279 Message->DispatchingListEntry.Flink = NULL;
1280 break;
1281 }
1282 Entry = Entry->Flink;
1283 }
1284
1285 DPRINT("MsqSendMessage timed out\n");
1286 break;
1287 }
1288 while (co_MsqDispatchOneSentMessage(ThreadQueue))
1289 ;
1290 }
1291 while (NT_SUCCESS(WaitStatus) && STATUS_WAIT_0 != WaitStatus);
1292 }
1293
1294 if(WaitStatus != STATUS_TIMEOUT)
1295 *uResult = (STATUS_WAIT_0 == WaitStatus ? Result : -1);
1296
1297 return WaitStatus;
1298 }
1299
1300 VOID FASTCALL
1301 MsqPostMessage(PUSER_MESSAGE_QUEUE MessageQueue, MSG* Msg, BOOLEAN FreeLParam,
1302 DWORD MessageBits)
1303 {
1304 PUSER_MESSAGE Message;
1305
1306 if(!(Message = MsqCreateMessage(Msg, FreeLParam)))
1307 {
1308 return;
1309 }
1310 InsertTailList(&MessageQueue->PostedMessagesListHead,
1311 &Message->ListEntry);
1312 MessageQueue->QueueBits |= MessageBits;
1313 MessageQueue->ChangedBits |= MessageBits;
1314 if (MessageQueue->WakeMask & MessageBits)
1315 KeSetEvent(MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
1316 }
1317
1318 VOID FASTCALL
1319 MsqPostQuitMessage(PUSER_MESSAGE_QUEUE MessageQueue, ULONG ExitCode)
1320 {
1321 MessageQueue->QuitPosted = TRUE;
1322 MessageQueue->QuitExitCode = ExitCode;
1323 MessageQueue->QueueBits |= QS_POSTMESSAGE;
1324 MessageQueue->ChangedBits |= QS_POSTMESSAGE;
1325 if (MessageQueue->WakeMask & QS_POSTMESSAGE)
1326 KeSetEvent(MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
1327 }
1328
1329 BOOLEAN APIENTRY
1330 co_MsqFindMessage(IN PUSER_MESSAGE_QUEUE MessageQueue,
1331 IN BOOLEAN Hardware,
1332 IN BOOLEAN Remove,
1333 IN PWINDOW_OBJECT Window,
1334 IN UINT MsgFilterLow,
1335 IN UINT MsgFilterHigh,
1336 OUT PUSER_MESSAGE* Message)
1337 {
1338 PLIST_ENTRY CurrentEntry;
1339 PUSER_MESSAGE CurrentMessage;
1340 PLIST_ENTRY ListHead;
1341
1342 if (Hardware)
1343 {
1344 return(co_MsqPeekHardwareMessage( MessageQueue,
1345 Window,
1346 MsgFilterLow,
1347 MsgFilterHigh,
1348 Remove,
1349 Message));
1350 }
1351
1352 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
1353 ListHead = &MessageQueue->PostedMessagesListHead;
1354 while (CurrentEntry != ListHead)
1355 {
1356 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1357 ListEntry);
1358 if ( ( !Window ||
1359 PtrToInt(Window) == 1 ||
1360 Window->hSelf == CurrentMessage->Msg.hwnd ) &&
1361 ( (MsgFilterLow == 0 && MsgFilterHigh == 0) ||
1362 ( MsgFilterLow <= CurrentMessage->Msg.message &&
1363 MsgFilterHigh >= CurrentMessage->Msg.message ) ) )
1364 {
1365 if (Remove)
1366 {
1367 RemoveEntryList(&CurrentMessage->ListEntry);
1368 }
1369
1370 *Message = CurrentMessage;
1371 return(TRUE);
1372 }
1373 CurrentEntry = CurrentEntry->Flink;
1374 }
1375
1376 return(FALSE);
1377 }
1378
1379 NTSTATUS FASTCALL
1380 co_MsqWaitForNewMessages(PUSER_MESSAGE_QUEUE MessageQueue, PWINDOW_OBJECT WndFilter,
1381 UINT MsgFilterMin, UINT MsgFilterMax)
1382 {
1383 PVOID WaitObjects[2] = {MessageQueue->NewMessages, &HardwareMessageEvent};
1384 NTSTATUS ret;
1385
1386 IdlePing(); // Going to wait so send Idle ping.
1387
1388 UserLeaveCo();
1389
1390 ret = KeWaitForMultipleObjects(2,
1391 WaitObjects,
1392 WaitAny,
1393 Executive,
1394 UserMode,
1395 FALSE,
1396 NULL,
1397 NULL);
1398 UserEnterCo();
1399 return ret;
1400 }
1401
1402 BOOL FASTCALL
1403 MsqIsHung(PUSER_MESSAGE_QUEUE MessageQueue)
1404 {
1405 LARGE_INTEGER LargeTickCount;
1406
1407 KeQueryTickCount(&LargeTickCount);
1408 return ((LargeTickCount.u.LowPart - MessageQueue->LastMsgRead) > MSQ_HUNG);
1409 }
1410
1411 BOOLEAN FASTCALL
1412 MsqInitializeMessageQueue(struct _ETHREAD *Thread, PUSER_MESSAGE_QUEUE MessageQueue)
1413 {
1414 LARGE_INTEGER LargeTickCount;
1415 NTSTATUS Status;
1416
1417 MessageQueue->Thread = Thread;
1418 MessageQueue->CaretInfo = (PTHRDCARETINFO)(MessageQueue + 1);
1419 InitializeListHead(&MessageQueue->PostedMessagesListHead);
1420 InitializeListHead(&MessageQueue->SentMessagesListHead);
1421 InitializeListHead(&MessageQueue->HardwareMessagesListHead);
1422 InitializeListHead(&MessageQueue->DispatchingMessagesHead);
1423 InitializeListHead(&MessageQueue->LocalDispatchingMessagesHead);
1424 KeInitializeMutex(&MessageQueue->HardwareLock, 0);
1425 MessageQueue->QuitPosted = FALSE;
1426 MessageQueue->QuitExitCode = 0;
1427 KeQueryTickCount(&LargeTickCount);
1428 MessageQueue->LastMsgRead = LargeTickCount.u.LowPart;
1429 MessageQueue->FocusWindow = NULL;
1430 MessageQueue->PaintCount = 0;
1431 // HACK!!!!!!! Newbies that wrote this should hold your head down in shame! (jt)
1432 MessageQueue->WakeMask = ~0;
1433 MessageQueue->NewMessagesHandle = NULL;
1434
1435 Status = ZwCreateEvent(&MessageQueue->NewMessagesHandle, EVENT_ALL_ACCESS,
1436 NULL, SynchronizationEvent, FALSE);
1437 if (!NT_SUCCESS(Status))
1438 {
1439 return FALSE;
1440 }
1441
1442 Status = ObReferenceObjectByHandle(MessageQueue->NewMessagesHandle, 0,
1443 ExEventObjectType, KernelMode,
1444 (PVOID*)&MessageQueue->NewMessages, NULL);
1445 if (!NT_SUCCESS(Status))
1446 {
1447 ZwClose(MessageQueue->NewMessagesHandle);
1448 MessageQueue->NewMessagesHandle = NULL;
1449 return FALSE;
1450 }
1451
1452 return TRUE;
1453 }
1454
1455 VOID FASTCALL
1456 MsqCleanupMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
1457 {
1458 PLIST_ENTRY CurrentEntry;
1459 PUSER_MESSAGE CurrentMessage;
1460 PUSER_SENT_MESSAGE CurrentSentMessage;
1461
1462 /* cleanup posted messages */
1463 while (!IsListEmpty(&MessageQueue->PostedMessagesListHead))
1464 {
1465 CurrentEntry = RemoveHeadList(&MessageQueue->PostedMessagesListHead);
1466 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1467 ListEntry);
1468 MsqDestroyMessage(CurrentMessage);
1469 }
1470
1471 /* remove the messages that have not yet been dispatched */
1472 while (!IsListEmpty(&MessageQueue->SentMessagesListHead))
1473 {
1474 CurrentEntry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
1475 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1476 ListEntry);
1477
1478 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1479
1480 /* remove the message from the dispatching list if needed */
1481 if ((!(CurrentSentMessage->HookMessage & MSQ_SENTNOWAIT))
1482 && (CurrentSentMessage->DispatchingListEntry.Flink != NULL))
1483 {
1484 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
1485 }
1486
1487 /* wake the sender's thread */
1488 if (CurrentSentMessage->CompletionEvent != NULL)
1489 {
1490 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1491 }
1492
1493 if (CurrentSentMessage->HasPackedLParam == TRUE)
1494 {
1495 if (CurrentSentMessage->Msg.lParam)
1496 ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
1497 }
1498
1499 /* Only if it is not a no wait message */
1500 if (!(CurrentSentMessage->HookMessage & MSQ_SENTNOWAIT))
1501 {
1502 /* dereference our and the sender's message queue */
1503 IntDereferenceMessageQueue(MessageQueue);
1504 IntDereferenceMessageQueue(CurrentSentMessage->SenderQueue);
1505 }
1506
1507 /* free the message */
1508 ExFreePool(CurrentSentMessage);
1509 }
1510
1511 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
1512 ExitThread() was called in a SendMessage() umode callback */
1513 while (!IsListEmpty(&MessageQueue->LocalDispatchingMessagesHead))
1514 {
1515 CurrentEntry = RemoveHeadList(&MessageQueue->LocalDispatchingMessagesHead);
1516 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1517 ListEntry);
1518
1519 /* remove the message from the dispatching list */
1520 if(CurrentSentMessage->DispatchingListEntry.Flink != NULL)
1521 {
1522 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
1523 }
1524
1525 DPRINT("Notify the sender, the thread has been terminated while dispatching a message!\n");
1526
1527 /* wake the sender's thread */
1528 if (CurrentSentMessage->CompletionEvent != NULL)
1529 {
1530 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1531 }
1532
1533 if (CurrentSentMessage->HasPackedLParam == TRUE)
1534 {
1535 if (CurrentSentMessage->Msg.lParam)
1536 ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
1537 }
1538
1539 /* Only if it is not a no wait message */
1540 if (!(CurrentSentMessage->HookMessage & MSQ_SENTNOWAIT))
1541 {
1542 /* dereference our and the sender's message queue */
1543 IntDereferenceMessageQueue(MessageQueue);
1544 IntDereferenceMessageQueue(CurrentSentMessage->SenderQueue);
1545 }
1546
1547 /* free the message */
1548 ExFreePool(CurrentSentMessage);
1549 }
1550
1551 /* tell other threads not to bother returning any info to us */
1552 while (! IsListEmpty(&MessageQueue->DispatchingMessagesHead))
1553 {
1554 CurrentEntry = RemoveHeadList(&MessageQueue->DispatchingMessagesHead);
1555 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1556 DispatchingListEntry);
1557 CurrentSentMessage->CompletionEvent = NULL;
1558 CurrentSentMessage->Result = NULL;
1559
1560 /* do NOT dereference our message queue as it might get attempted to be
1561 locked later */
1562 }
1563
1564 }
1565
1566 PUSER_MESSAGE_QUEUE FASTCALL
1567 MsqCreateMessageQueue(struct _ETHREAD *Thread)
1568 {
1569 PUSER_MESSAGE_QUEUE MessageQueue;
1570
1571 MessageQueue = (PUSER_MESSAGE_QUEUE)ExAllocatePoolWithTag(NonPagedPool,
1572 sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO),
1573 TAG_MSGQ);
1574
1575 if (!MessageQueue)
1576 {
1577 return NULL;
1578 }
1579
1580 RtlZeroMemory(MessageQueue, sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO));
1581 /* hold at least one reference until it'll be destroyed */
1582 IntReferenceMessageQueue(MessageQueue);
1583 /* initialize the queue */
1584 if (!MsqInitializeMessageQueue(Thread, MessageQueue))
1585 {
1586 IntDereferenceMessageQueue(MessageQueue);
1587 return NULL;
1588 }
1589
1590 return MessageQueue;
1591 }
1592
1593 VOID FASTCALL
1594 MsqDestroyMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
1595 {
1596 PDESKTOP desk;
1597
1598 /* remove the message queue from any desktops */
1599 if ((desk = InterlockedExchangePointer((PVOID*)&MessageQueue->Desktop, 0)))
1600 {
1601 (void)InterlockedExchangePointer((PVOID*)&desk->ActiveMessageQueue, 0);
1602 IntDereferenceMessageQueue(MessageQueue);
1603 }
1604
1605 /* clean it up */
1606 MsqCleanupMessageQueue(MessageQueue);
1607
1608 /* decrease the reference counter, if it hits zero, the queue will be freed */
1609 IntDereferenceMessageQueue(MessageQueue);
1610 }
1611
1612 PHOOKTABLE FASTCALL
1613 MsqGetHooks(PUSER_MESSAGE_QUEUE Queue)
1614 {
1615 return Queue->Hooks;
1616 }
1617
1618 VOID FASTCALL
1619 MsqSetHooks(PUSER_MESSAGE_QUEUE Queue, PHOOKTABLE Hooks)
1620 {
1621 Queue->Hooks = Hooks;
1622 }
1623
1624 LPARAM FASTCALL
1625 MsqSetMessageExtraInfo(LPARAM lParam)
1626 {
1627 LPARAM Ret;
1628 PTHREADINFO pti;
1629 PUSER_MESSAGE_QUEUE MessageQueue;
1630
1631 pti = PsGetCurrentThreadWin32Thread();
1632 MessageQueue = pti->MessageQueue;
1633 if(!MessageQueue)
1634 {
1635 return 0;
1636 }
1637
1638 Ret = MessageQueue->ExtraInfo;
1639 MessageQueue->ExtraInfo = lParam;
1640
1641 return Ret;
1642 }
1643
1644 LPARAM FASTCALL
1645 MsqGetMessageExtraInfo(VOID)
1646 {
1647 PTHREADINFO pti;
1648 PUSER_MESSAGE_QUEUE MessageQueue;
1649
1650 pti = PsGetCurrentThreadWin32Thread();
1651 MessageQueue = pti->MessageQueue;
1652 if(!MessageQueue)
1653 {
1654 return 0;
1655 }
1656
1657 return MessageQueue->ExtraInfo;
1658 }
1659
1660 HWND FASTCALL
1661 MsqSetStateWindow(PUSER_MESSAGE_QUEUE MessageQueue, ULONG Type, HWND hWnd)
1662 {
1663 HWND Prev;
1664
1665 switch(Type)
1666 {
1667 case MSQ_STATE_CAPTURE:
1668 Prev = MessageQueue->CaptureWindow;
1669 MessageQueue->CaptureWindow = hWnd;
1670 return Prev;
1671 case MSQ_STATE_ACTIVE:
1672 Prev = MessageQueue->ActiveWindow;
1673 MessageQueue->ActiveWindow = hWnd;
1674 return Prev;
1675 case MSQ_STATE_FOCUS:
1676 Prev = MessageQueue->FocusWindow;
1677 MessageQueue->FocusWindow = hWnd;
1678 return Prev;
1679 case MSQ_STATE_MENUOWNER:
1680 Prev = MessageQueue->MenuOwner;
1681 MessageQueue->MenuOwner = hWnd;
1682 return Prev;
1683 case MSQ_STATE_MOVESIZE:
1684 Prev = MessageQueue->MoveSize;
1685 MessageQueue->MoveSize = hWnd;
1686 return Prev;
1687 case MSQ_STATE_CARET:
1688 ASSERT(MessageQueue->CaretInfo);
1689 Prev = MessageQueue->CaretInfo->hWnd;
1690 MessageQueue->CaretInfo->hWnd = hWnd;
1691 return Prev;
1692 }
1693
1694 return NULL;
1695 }
1696
1697 /* EOF */