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