- Revert 51255 that was committed on accident. The patch is not ready yet.
[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 PTHREADINFO pti;
1357 NTSTATUS ret = STATUS_SUCCESS;
1358
1359 pti = MessageQueue->Thread->Tcb.Win32Thread;
1360
1361 while ( co_MsqDispatchOneSentMessage(MessageQueue) );
1362
1363 if (pti->pcti->fsWakeBits & pti->pcti->fsChangeBits )
1364 {
1365 return ret;
1366 }
1367
1368 pti->pClientInfo->cSpins = 0;
1369 IdlePing();
1370 UserLeaveCo();
1371 ret = KeWaitForSingleObject(MessageQueue->NewMessages,
1372 Executive,
1373 UserMode,
1374 FALSE,
1375 NULL);
1376 UserEnterCo();
1377 IdlePong();
1378 return ret;
1379 }
1380
1381 BOOL FASTCALL
1382 MsqIsHung(PUSER_MESSAGE_QUEUE MessageQueue)
1383 {
1384 LARGE_INTEGER LargeTickCount;
1385
1386 KeQueryTickCount(&LargeTickCount);
1387 return ((LargeTickCount.u.LowPart - MessageQueue->LastMsgRead) > MSQ_HUNG);
1388 }
1389
1390 VOID
1391 CALLBACK
1392 HungAppSysTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
1393 {
1394 //DoTheScreenSaver();
1395 DPRINT("HungAppSysTimerProc\n");
1396 // Process list of windows that are hung and waiting.
1397 }
1398
1399 BOOLEAN FASTCALL
1400 MsqInitializeMessageQueue(struct _ETHREAD *Thread, PUSER_MESSAGE_QUEUE MessageQueue)
1401 {
1402 LARGE_INTEGER LargeTickCount;
1403 NTSTATUS Status;
1404
1405 MessageQueue->Thread = Thread;
1406 MessageQueue->CaretInfo = (PTHRDCARETINFO)(MessageQueue + 1);
1407 InitializeListHead(&MessageQueue->PostedMessagesListHead);
1408 InitializeListHead(&MessageQueue->SentMessagesListHead);
1409 InitializeListHead(&MessageQueue->HardwareMessagesListHead);
1410 InitializeListHead(&MessageQueue->DispatchingMessagesHead);
1411 InitializeListHead(&MessageQueue->LocalDispatchingMessagesHead);
1412 KeInitializeMutex(&MessageQueue->HardwareLock, 0);
1413 MessageQueue->QuitPosted = FALSE;
1414 MessageQueue->QuitExitCode = 0;
1415 KeQueryTickCount(&LargeTickCount);
1416 MessageQueue->LastMsgRead = LargeTickCount.u.LowPart;
1417 MessageQueue->FocusWindow = NULL;
1418 MessageQueue->NewMessagesHandle = NULL;
1419
1420 Status = ZwCreateEvent(&MessageQueue->NewMessagesHandle, EVENT_ALL_ACCESS,
1421 NULL, SynchronizationEvent, FALSE);
1422 if (!NT_SUCCESS(Status))
1423 {
1424 return FALSE;
1425 }
1426
1427 Status = ObReferenceObjectByHandle(MessageQueue->NewMessagesHandle, 0,
1428 ExEventObjectType, KernelMode,
1429 (PVOID*)&MessageQueue->NewMessages, NULL);
1430 if (!NT_SUCCESS(Status))
1431 {
1432 ZwClose(MessageQueue->NewMessagesHandle);
1433 MessageQueue->NewMessagesHandle = NULL;
1434 return FALSE;
1435 }
1436
1437 return TRUE;
1438 }
1439
1440 VOID FASTCALL
1441 MsqCleanupMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
1442 {
1443 PLIST_ENTRY CurrentEntry;
1444 PUSER_MESSAGE CurrentMessage;
1445 PUSER_SENT_MESSAGE CurrentSentMessage;
1446 PTHREADINFO pti;
1447
1448 pti = MessageQueue->Thread->Tcb.Win32Thread;
1449
1450
1451 /* cleanup posted messages */
1452 while (!IsListEmpty(&MessageQueue->PostedMessagesListHead))
1453 {
1454 CurrentEntry = RemoveHeadList(&MessageQueue->PostedMessagesListHead);
1455 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1456 ListEntry);
1457 MsqDestroyMessage(CurrentMessage);
1458 }
1459
1460 /* remove the messages that have not yet been dispatched */
1461 while (!IsListEmpty(&MessageQueue->SentMessagesListHead))
1462 {
1463 CurrentEntry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
1464 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1465 ListEntry);
1466
1467 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1468
1469 /* remove the message from the dispatching list if needed */
1470 if ((!(CurrentSentMessage->HookMessage & MSQ_SENTNOWAIT))
1471 && (CurrentSentMessage->DispatchingListEntry.Flink != NULL))
1472 {
1473 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
1474 }
1475
1476 /* wake the sender's thread */
1477 if (CurrentSentMessage->CompletionEvent != NULL)
1478 {
1479 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1480 }
1481
1482 if (CurrentSentMessage->HasPackedLParam == TRUE)
1483 {
1484 if (CurrentSentMessage->Msg.lParam)
1485 ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
1486 }
1487
1488 /* Only if it is not a no wait message */
1489 if (!(CurrentSentMessage->HookMessage & MSQ_SENTNOWAIT))
1490 {
1491 /* dereference our and the sender's message queue */
1492 IntDereferenceMessageQueue(MessageQueue);
1493 IntDereferenceMessageQueue(CurrentSentMessage->SenderQueue);
1494 }
1495
1496 /* free the message */
1497 ExFreePool(CurrentSentMessage);
1498 }
1499
1500 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
1501 ExitThread() was called in a SendMessage() umode callback */
1502 while (!IsListEmpty(&MessageQueue->LocalDispatchingMessagesHead))
1503 {
1504 CurrentEntry = RemoveHeadList(&MessageQueue->LocalDispatchingMessagesHead);
1505 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1506 ListEntry);
1507
1508 /* remove the message from the dispatching list */
1509 if(CurrentSentMessage->DispatchingListEntry.Flink != NULL)
1510 {
1511 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
1512 }
1513
1514 DPRINT("Notify the sender, the thread has been terminated while dispatching a message!\n");
1515
1516 /* wake the sender's thread */
1517 if (CurrentSentMessage->CompletionEvent != NULL)
1518 {
1519 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1520 }
1521
1522 if (CurrentSentMessage->HasPackedLParam == TRUE)
1523 {
1524 if (CurrentSentMessage->Msg.lParam)
1525 ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
1526 }
1527
1528 /* Only if it is not a no wait message */
1529 if (!(CurrentSentMessage->HookMessage & MSQ_SENTNOWAIT))
1530 {
1531 /* dereference our and the sender's message queue */
1532 IntDereferenceMessageQueue(MessageQueue);
1533 IntDereferenceMessageQueue(CurrentSentMessage->SenderQueue);
1534 }
1535
1536 /* free the message */
1537 ExFreePool(CurrentSentMessage);
1538 }
1539
1540 /* tell other threads not to bother returning any info to us */
1541 while (! IsListEmpty(&MessageQueue->DispatchingMessagesHead))
1542 {
1543 CurrentEntry = RemoveHeadList(&MessageQueue->DispatchingMessagesHead);
1544 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1545 DispatchingListEntry);
1546 CurrentSentMessage->CompletionEvent = NULL;
1547 CurrentSentMessage->Result = NULL;
1548
1549 /* do NOT dereference our message queue as it might get attempted to be
1550 locked later */
1551 }
1552
1553 // Clear it all out.
1554 pti->pcti->fsWakeBits = 0;
1555 pti->pcti->fsChangeBits = 0;
1556
1557 MessageQueue->nCntsQBits[QSRosKey] = 0;
1558 MessageQueue->nCntsQBits[QSRosMouseMove] = 0;
1559 MessageQueue->nCntsQBits[QSRosMouseButton] = 0;
1560 MessageQueue->nCntsQBits[QSRosPostMessage] = 0;
1561 MessageQueue->nCntsQBits[QSRosSendMessage] = 0;
1562 MessageQueue->nCntsQBits[QSRosHotKey] = 0;
1563 }
1564
1565 PUSER_MESSAGE_QUEUE FASTCALL
1566 MsqCreateMessageQueue(struct _ETHREAD *Thread)
1567 {
1568 PUSER_MESSAGE_QUEUE MessageQueue;
1569
1570 MessageQueue = (PUSER_MESSAGE_QUEUE)ExAllocatePoolWithTag(NonPagedPool,
1571 sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO),
1572 USERTAG_Q);
1573
1574 if (!MessageQueue)
1575 {
1576 return NULL;
1577 }
1578
1579 RtlZeroMemory(MessageQueue, sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO));
1580 /* hold at least one reference until it'll be destroyed */
1581 IntReferenceMessageQueue(MessageQueue);
1582 /* initialize the queue */
1583 if (!MsqInitializeMessageQueue(Thread, MessageQueue))
1584 {
1585 IntDereferenceMessageQueue(MessageQueue);
1586 return NULL;
1587 }
1588
1589 return MessageQueue;
1590 }
1591
1592 VOID FASTCALL
1593 MsqDestroyMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
1594 {
1595 PDESKTOP desk;
1596
1597 MessageQueue->QF_flags |= QF_INDESTROY;
1598
1599 /* remove the message queue from any desktops */
1600 if ((desk = InterlockedExchangePointer((PVOID*)&MessageQueue->Desktop, 0)))
1601 {
1602 (void)InterlockedExchangePointer((PVOID*)&desk->ActiveMessageQueue, 0);
1603 IntDereferenceMessageQueue(MessageQueue);
1604 }
1605
1606 /* clean it up */
1607 MsqCleanupMessageQueue(MessageQueue);
1608
1609 /* decrease the reference counter, if it hits zero, the queue will be freed */
1610 IntDereferenceMessageQueue(MessageQueue);
1611 }
1612
1613 LPARAM FASTCALL
1614 MsqSetMessageExtraInfo(LPARAM lParam)
1615 {
1616 LPARAM Ret;
1617 PTHREADINFO pti;
1618 PUSER_MESSAGE_QUEUE MessageQueue;
1619
1620 pti = PsGetCurrentThreadWin32Thread();
1621 MessageQueue = pti->MessageQueue;
1622 if(!MessageQueue)
1623 {
1624 return 0;
1625 }
1626
1627 Ret = MessageQueue->ExtraInfo;
1628 MessageQueue->ExtraInfo = lParam;
1629
1630 return Ret;
1631 }
1632
1633 LPARAM FASTCALL
1634 MsqGetMessageExtraInfo(VOID)
1635 {
1636 PTHREADINFO pti;
1637 PUSER_MESSAGE_QUEUE MessageQueue;
1638
1639 pti = PsGetCurrentThreadWin32Thread();
1640 MessageQueue = pti->MessageQueue;
1641 if(!MessageQueue)
1642 {
1643 return 0;
1644 }
1645
1646 return MessageQueue->ExtraInfo;
1647 }
1648
1649 // ReplyMessage is called by the thread receiving the window message.
1650 BOOL FASTCALL
1651 co_MsqReplyMessage( LRESULT lResult )
1652 {
1653 PUSER_SENT_MESSAGE Message;
1654 PTHREADINFO pti;
1655
1656 pti = PsGetCurrentThreadWin32Thread();
1657 Message = pti->pusmCurrent;
1658
1659 if (!Message) return FALSE;
1660
1661 if (Message->QS_Flags & QS_SMRESULT) return FALSE;
1662
1663 // SendMessageXxx || Callback msg and not a notify msg
1664 if (Message->SenderQueue || Message->CompletionCallback)
1665 {
1666 Message->lResult = lResult;
1667 Message->QS_Flags |= QS_SMRESULT;
1668 // See co_MsqDispatchOneSentMessage, change bits already accounted for and cleared and this msg is going away..
1669 }
1670 return TRUE;
1671 }
1672
1673 HWND FASTCALL
1674 MsqSetStateWindow(PUSER_MESSAGE_QUEUE MessageQueue, ULONG Type, HWND hWnd)
1675 {
1676 HWND Prev;
1677
1678 switch(Type)
1679 {
1680 case MSQ_STATE_CAPTURE:
1681 Prev = MessageQueue->CaptureWindow;
1682 MessageQueue->CaptureWindow = hWnd;
1683 return Prev;
1684 case MSQ_STATE_ACTIVE:
1685 Prev = MessageQueue->ActiveWindow;
1686 MessageQueue->ActiveWindow = hWnd;
1687 return Prev;
1688 case MSQ_STATE_FOCUS:
1689 Prev = MessageQueue->FocusWindow;
1690 MessageQueue->FocusWindow = hWnd;
1691 return Prev;
1692 case MSQ_STATE_MENUOWNER:
1693 Prev = MessageQueue->MenuOwner;
1694 MessageQueue->MenuOwner = hWnd;
1695 return Prev;
1696 case MSQ_STATE_MOVESIZE:
1697 Prev = MessageQueue->MoveSize;
1698 MessageQueue->MoveSize = hWnd;
1699 return Prev;
1700 case MSQ_STATE_CARET:
1701 ASSERT(MessageQueue->CaretInfo);
1702 Prev = MessageQueue->CaretInfo->hWnd;
1703 MessageQueue->CaretInfo->hWnd = hWnd;
1704 return Prev;
1705 }
1706
1707 return NULL;
1708 }
1709
1710 /* EOF */