[WIN32K]
[reactos.git] / reactos / win32ss / user / ntuser / msgqueue.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Win32k subsystem
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 */
10
11 #include <win32k.h>
12 DBG_DEFAULT_CHANNEL(UserMsgQ);
13
14 /* GLOBALS *******************************************************************/
15
16 static PPAGED_LOOKASIDE_LIST pgMessageLookasideList;
17 PUSER_MESSAGE_QUEUE gpqCursor;
18
19 /* FUNCTIONS *****************************************************************/
20
21 INIT_FUNCTION
22 NTSTATUS
23 NTAPI
24 MsqInitializeImpl(VOID)
25 {
26 pgMessageLookasideList = ExAllocatePoolWithTag(NonPagedPool, sizeof(PAGED_LOOKASIDE_LIST), TAG_USRMSG);
27 if(!pgMessageLookasideList)
28 return STATUS_NO_MEMORY;
29 ExInitializePagedLookasideList(pgMessageLookasideList,
30 NULL,
31 NULL,
32 0,
33 sizeof(USER_MESSAGE),
34 TAG_USRMSG,
35 256);
36
37 return(STATUS_SUCCESS);
38 }
39
40 PWND FASTCALL
41 IntChildrenWindowFromPoint(PWND pWndTop, INT x, INT y)
42 {
43 PWND pWnd, pWndChild;
44
45 if (!(pWndTop->style & WS_VISIBLE)) return NULL;
46 if ((pWndTop->style & WS_DISABLED)) return NULL;
47 if (!IntPtInWindow(pWndTop, x, y)) return NULL;
48
49 if (x - pWndTop->rcClient.left < pWndTop->rcClient.right &&
50 y - pWndTop->rcClient.top < pWndTop->rcClient.bottom )
51 {
52 for (pWnd = pWndTop->spwndChild;
53 pWnd != NULL;
54 pWnd = pWnd->spwndNext)
55 {
56 if (pWnd->state2 & WNDS2_INDESTROY || pWnd->state & WNDS_DESTROYED )
57 {
58 TRACE("The Window is in DESTROY!\n");
59 continue;
60 }
61
62 pWndChild = IntChildrenWindowFromPoint(pWnd, x, y);
63
64 if (pWndChild)
65 {
66 return pWndChild;
67 }
68 }
69 }
70 return pWndTop;
71 }
72
73 PWND FASTCALL
74 IntTopLevelWindowFromPoint(INT x, INT y)
75 {
76 PWND pWnd, pwndDesktop;
77
78 /* Get the desktop window */
79 pwndDesktop = UserGetDesktopWindow();
80 if (!pwndDesktop)
81 return NULL;
82
83 /* Loop all top level windows */
84 for (pWnd = pwndDesktop->spwndChild;
85 pWnd != NULL;
86 pWnd = pWnd->spwndNext)
87 {
88 if (pWnd->state2 & WNDS2_INDESTROY || pWnd->state & WNDS_DESTROYED)
89 {
90 TRACE("The Window is in DESTROY!\n");
91 continue;
92 }
93
94 if ((pWnd->style & WS_VISIBLE) && IntPtInWindow(pWnd, x, y))
95 return pWnd;
96 }
97
98 /* Window has not been found */
99 return NULL;
100 }
101
102 PCURICON_OBJECT
103 FASTCALL
104 UserSetCursor(
105 PCURICON_OBJECT NewCursor,
106 BOOL ForceChange)
107 {
108 PCURICON_OBJECT OldCursor;
109 HDC hdcScreen;
110 PTHREADINFO pti;
111 PUSER_MESSAGE_QUEUE MessageQueue;
112 PWND pWnd;
113
114 pti = PsGetCurrentThreadWin32Thread();
115 MessageQueue = pti->MessageQueue;
116
117 /* Get the screen DC */
118 if(!(hdcScreen = IntGetScreenDC()))
119 {
120 return NULL;
121 }
122
123 OldCursor = MessageQueue->CursorObject;
124
125 /* Check if cursors are different */
126 if (OldCursor == NewCursor)
127 return OldCursor;
128
129 /* Update cursor for this message queue */
130 MessageQueue->CursorObject = NewCursor;
131
132 /* If cursor is not visible we have nothing to do */
133 if (MessageQueue->ShowingCursor < 0)
134 return OldCursor;
135
136 /* Update cursor if this message queue controls it */
137 pWnd = IntTopLevelWindowFromPoint(gpsi->ptCursor.x, gpsi->ptCursor.y);
138 if (pWnd && pWnd->head.pti->MessageQueue == MessageQueue)
139 {
140 if (NewCursor)
141 {
142 /* Call GDI to set the new screen cursor */
143 GreSetPointerShape(hdcScreen,
144 NewCursor->IconInfo.hbmMask,
145 NewCursor->IconInfo.hbmColor,
146 NewCursor->IconInfo.xHotspot,
147 NewCursor->IconInfo.yHotspot,
148 gpsi->ptCursor.x,
149 gpsi->ptCursor.y);
150 }
151 else /* Note: OldCursor != NewCursor so we have to hide cursor */
152 {
153 /* Remove the cursor */
154 GreMovePointer(hdcScreen, -1, -1);
155 TRACE("Removing pointer!\n");
156 }
157 IntGetSysCursorInfo()->CurrentCursorObject = NewCursor;
158 }
159
160 /* Return the old cursor */
161 return OldCursor;
162 }
163
164 /* Called from NtUserCallOneParam with Routine ONEPARAM_ROUTINE_SHOWCURSOR
165 * User32 macro NtUserShowCursor */
166 int UserShowCursor(BOOL bShow)
167 {
168 HDC hdcScreen;
169 PTHREADINFO pti;
170 PUSER_MESSAGE_QUEUE MessageQueue;
171 PWND pWnd;
172
173 if (!(hdcScreen = IntGetScreenDC()))
174 {
175 return -1; /* No mouse */
176 }
177
178 pti = PsGetCurrentThreadWin32Thread();
179 MessageQueue = pti->MessageQueue;
180
181 /* Update counter */
182 MessageQueue->ShowingCursor += bShow ? 1 : -1;
183
184 /* Check for trivial cases */
185 if ((bShow && MessageQueue->ShowingCursor != 0) ||
186 (!bShow && MessageQueue->ShowingCursor != -1))
187 {
188 /* Note: w don't update global info here because it is used only
189 internally to check if cursor is visible */
190 return MessageQueue->ShowingCursor;
191 }
192
193 /* Check if cursor is above window owned by this MessageQueue */
194 pWnd = IntTopLevelWindowFromPoint(gpsi->ptCursor.x, gpsi->ptCursor.y);
195 if (pWnd && pWnd->head.pti->MessageQueue == MessageQueue)
196 {
197 if (bShow)
198 {
199 /* Show the pointer */
200 GreMovePointer(hdcScreen, gpsi->ptCursor.x, gpsi->ptCursor.y);
201 TRACE("Showing pointer!\n");
202 }
203 else
204 {
205 /* Remove the pointer */
206 GreMovePointer(hdcScreen, -1, -1);
207 TRACE("Removing pointer!\n");
208 }
209
210 /* Update global info */
211 IntGetSysCursorInfo()->ShowingCursor = MessageQueue->ShowingCursor;
212 }
213
214 return MessageQueue->ShowingCursor;
215 }
216
217 DWORD FASTCALL
218 UserGetKeyState(DWORD dwKey)
219 {
220 DWORD dwRet = 0;
221 PTHREADINFO pti;
222 PUSER_MESSAGE_QUEUE MessageQueue;
223
224 pti = PsGetCurrentThreadWin32Thread();
225 MessageQueue = pti->MessageQueue;
226
227 if (dwKey < 0x100)
228 {
229 if (IS_KEY_DOWN(MessageQueue->afKeyState, dwKey))
230 dwRet |= 0xFF80; // If down, windows returns 0xFF80.
231 if (IS_KEY_LOCKED(MessageQueue->afKeyState, dwKey))
232 dwRet |= 0x1;
233 }
234 else
235 {
236 EngSetLastError(ERROR_INVALID_PARAMETER);
237 }
238 return dwRet;
239 }
240
241 /* change the input key state for a given key */
242 static VOID
243 UpdateKeyState(PUSER_MESSAGE_QUEUE MessageQueue, WORD wVk, BOOL bIsDown)
244 {
245 TRACE("UpdateKeyState wVk: %d, bIsDown: %d\n", wVk, bIsDown);
246
247 if (bIsDown)
248 {
249 /* If it's first key down event, xor lock bit */
250 if (!IS_KEY_DOWN(MessageQueue->afKeyState, wVk))
251 SET_KEY_LOCKED(MessageQueue->afKeyState, wVk, !IS_KEY_LOCKED(MessageQueue->afKeyState, wVk));
252
253 SET_KEY_DOWN(MessageQueue->afKeyState, wVk, TRUE);
254 MessageQueue->afKeyRecentDown[wVk / 8] |= (1 << (wVk % 8));
255 }
256 else
257 SET_KEY_DOWN(MessageQueue->afKeyState, wVk, FALSE);
258 }
259
260 /* update the input key state for a keyboard message */
261 static VOID
262 UpdateKeyStateFromMsg(PUSER_MESSAGE_QUEUE MessageQueue, MSG* msg)
263 {
264 UCHAR key;
265 BOOL down = FALSE;
266
267 TRACE("UpdateKeyStateFromMsg message:%d\n", msg->message);
268
269 switch (msg->message)
270 {
271 case WM_LBUTTONDOWN:
272 down = TRUE;
273 /* fall through */
274 case WM_LBUTTONUP:
275 UpdateKeyState(MessageQueue, VK_LBUTTON, down);
276 break;
277 case WM_MBUTTONDOWN:
278 down = TRUE;
279 /* fall through */
280 case WM_MBUTTONUP:
281 UpdateKeyState(MessageQueue, VK_MBUTTON, down);
282 break;
283 case WM_RBUTTONDOWN:
284 down = TRUE;
285 /* fall through */
286 case WM_RBUTTONUP:
287 UpdateKeyState(MessageQueue, VK_RBUTTON, down);
288 break;
289 case WM_XBUTTONDOWN:
290 down = TRUE;
291 /* fall through */
292 case WM_XBUTTONUP:
293 if (msg->wParam == XBUTTON1)
294 UpdateKeyState(MessageQueue, VK_XBUTTON1, down);
295 else if (msg->wParam == XBUTTON2)
296 UpdateKeyState(MessageQueue, VK_XBUTTON2, down);
297 break;
298 case WM_KEYDOWN:
299 case WM_SYSKEYDOWN:
300 down = TRUE;
301 /* fall through */
302 case WM_KEYUP:
303 case WM_SYSKEYUP:
304 key = (UCHAR)msg->wParam;
305 UpdateKeyState(MessageQueue, key, down);
306 switch(key)
307 {
308 case VK_LCONTROL:
309 case VK_RCONTROL:
310 down = IS_KEY_DOWN(MessageQueue->afKeyState, VK_LCONTROL) || IS_KEY_DOWN(MessageQueue->afKeyState, VK_RCONTROL);
311 UpdateKeyState(MessageQueue, VK_CONTROL, down);
312 break;
313 case VK_LMENU:
314 case VK_RMENU:
315 down = IS_KEY_DOWN(MessageQueue->afKeyState, VK_LMENU) || IS_KEY_DOWN(MessageQueue->afKeyState, VK_RMENU);
316 UpdateKeyState(MessageQueue, VK_MENU, down);
317 break;
318 case VK_LSHIFT:
319 case VK_RSHIFT:
320 down = IS_KEY_DOWN(MessageQueue->afKeyState, VK_LSHIFT) || IS_KEY_DOWN(MessageQueue->afKeyState, VK_RSHIFT);
321 UpdateKeyState(MessageQueue, VK_SHIFT, down);
322 break;
323 }
324 break;
325 }
326 }
327
328 HANDLE FASTCALL
329 IntMsqSetWakeMask(DWORD WakeMask)
330 {
331 PTHREADINFO Win32Thread;
332 PUSER_MESSAGE_QUEUE MessageQueue;
333 HANDLE MessageEventHandle;
334 DWORD dwFlags = HIWORD(WakeMask);
335
336 Win32Thread = PsGetCurrentThreadWin32Thread();
337 if (Win32Thread == NULL || Win32Thread->MessageQueue == NULL)
338 return 0;
339
340 MessageQueue = Win32Thread->MessageQueue;
341 // Win32Thread->pEventQueueServer; IntMsqSetWakeMask returns Win32Thread->hEventQueueClient
342 MessageEventHandle = MessageQueue->NewMessagesHandle;
343
344 if (Win32Thread->pcti)
345 {
346 if ( (Win32Thread->pcti->fsChangeBits & LOWORD(WakeMask)) ||
347 ( (dwFlags & MWMO_INPUTAVAILABLE) && (Win32Thread->pcti->fsWakeBits & LOWORD(WakeMask)) ) )
348 {
349 ERR("Chg 0x%x Wake 0x%x Mask 0x%x\n",Win32Thread->pcti->fsChangeBits, Win32Thread->pcti->fsWakeBits, WakeMask);
350 KeSetEvent(MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE); // Wake it up!
351 return MessageEventHandle;
352 }
353 }
354
355 IdlePing();
356
357 return MessageEventHandle;
358 }
359
360 BOOL FASTCALL
361 IntMsqClearWakeMask(VOID)
362 {
363 PTHREADINFO Win32Thread;
364
365 Win32Thread = PsGetCurrentThreadWin32Thread();
366 if (Win32Thread == NULL || Win32Thread->MessageQueue == NULL)
367 return FALSE;
368 // Very hacky, but that is what they do.
369 Win32Thread->pcti->fsWakeBits = 0;
370
371 IdlePong();
372
373 return TRUE;
374 }
375
376 /*
377 Due to the uncertainty of knowing what was set in our multilevel message queue,
378 and even if the bits are all cleared. The same as cTimers/cPaintsReady.
379 I think this is the best solution... (jt) */
380 VOID FASTCALL
381 MsqWakeQueue(PUSER_MESSAGE_QUEUE Queue, DWORD MessageBits, BOOL KeyEvent)
382 {
383 PTHREADINFO pti;
384
385 pti = Queue->Thread->Tcb.Win32Thread;
386 pti->pcti->fsWakeBits |= MessageBits;
387 pti->pcti->fsChangeBits |= MessageBits;
388
389 // Start bit accounting to help clear the main set of bits.
390 if (MessageBits & QS_KEY) Queue->nCntsQBits[QSRosKey]++;
391 if (MessageBits & QS_MOUSEMOVE) Queue->nCntsQBits[QSRosMouseMove]++;
392 if (MessageBits & QS_MOUSEBUTTON) Queue->nCntsQBits[QSRosMouseButton]++;
393 if (MessageBits & QS_POSTMESSAGE) Queue->nCntsQBits[QSRosPostMessage]++;
394 if (MessageBits & QS_SENDMESSAGE) Queue->nCntsQBits[QSRosSendMessage]++;
395 if (MessageBits & QS_HOTKEY) Queue->nCntsQBits[QSRosHotKey]++;
396
397 if (KeyEvent)
398 KeSetEvent(Queue->NewMessages, IO_NO_INCREMENT, FALSE);
399 }
400
401 VOID FASTCALL
402 ClearMsgBitsMask(PUSER_MESSAGE_QUEUE Queue, UINT MessageBits)
403 {
404 PTHREADINFO pti;
405 UINT ClrMask = 0;
406
407 pti = Queue->Thread->Tcb.Win32Thread;
408
409 if (MessageBits & QS_KEY)
410 {
411 if (--Queue->nCntsQBits[QSRosKey] == 0) ClrMask |= QS_KEY;
412 }
413 if (MessageBits & QS_MOUSEMOVE) // ReactOS hard coded.
414 { // Account for tracking mouse moves..
415 if (--Queue->nCntsQBits[QSRosMouseMove] == 0) ClrMask |= QS_MOUSEMOVE;
416 // Handle mouse move bits here.
417 if (Queue->MouseMoved) ClrMask |= QS_MOUSEMOVE;
418 }
419 if (MessageBits & QS_MOUSEBUTTON)
420 {
421 if (--Queue->nCntsQBits[QSRosMouseButton] == 0) ClrMask |= QS_MOUSEBUTTON;
422 }
423 if (MessageBits & QS_POSTMESSAGE)
424 {
425 if (--Queue->nCntsQBits[QSRosPostMessage] == 0) ClrMask |= QS_POSTMESSAGE;
426 }
427 if (MessageBits & QS_TIMER) // ReactOS hard coded.
428 { // Handle timer bits here.
429 if ( pti->cTimersReady )
430 {
431 if (--pti->cTimersReady == 0) ClrMask |= QS_TIMER;
432 }
433 }
434 if (MessageBits & QS_PAINT) // ReactOS hard coded.
435 { // Handle paint bits here.
436 if ( pti->cPaintsReady )
437 {
438 if (--pti->cPaintsReady == 0) ClrMask |= QS_PAINT;
439 }
440 }
441 if (MessageBits & QS_SENDMESSAGE)
442 {
443 if (--Queue->nCntsQBits[QSRosSendMessage] == 0) ClrMask |= QS_SENDMESSAGE;
444 }
445 if (MessageBits & QS_HOTKEY)
446 {
447 if (--Queue->nCntsQBits[QSRosHotKey] == 0) ClrMask |= QS_HOTKEY;
448 }
449
450 pti->pcti->fsWakeBits &= ~ClrMask;
451 pti->pcti->fsChangeBits &= ~ClrMask;
452 }
453
454 VOID FASTCALL
455 MsqIncPaintCountQueue(PUSER_MESSAGE_QUEUE Queue)
456 {
457 PTHREADINFO pti;
458 pti = Queue->Thread->Tcb.Win32Thread;
459 pti->cPaintsReady++;
460 MsqWakeQueue(Queue, QS_PAINT, TRUE);
461 }
462
463 VOID FASTCALL
464 MsqDecPaintCountQueue(PUSER_MESSAGE_QUEUE Queue)
465 {
466 ClearMsgBitsMask(Queue, QS_PAINT);
467 }
468
469 VOID FASTCALL
470 MsqPostMouseMove(PUSER_MESSAGE_QUEUE MessageQueue, MSG* Msg)
471 {
472 MessageQueue->MouseMoveMsg = *Msg;
473 MessageQueue->MouseMoved = TRUE;
474 MsqWakeQueue(MessageQueue, QS_MOUSEMOVE, TRUE);
475 }
476
477 VOID FASTCALL
478 co_MsqInsertMouseMessage(MSG* Msg, DWORD flags, ULONG_PTR dwExtraInfo, BOOL Hook)
479 {
480 LARGE_INTEGER LargeTickCount;
481 MSLLHOOKSTRUCT MouseHookData;
482 PDESKTOP pDesk;
483 PWND pwnd, pwndDesktop;
484 HDC hdcScreen;
485 PSYSTEM_CURSORINFO CurInfo;
486
487 KeQueryTickCount(&LargeTickCount);
488 Msg->time = MsqCalculateMessageTime(&LargeTickCount);
489
490 MouseHookData.pt.x = LOWORD(Msg->lParam);
491 MouseHookData.pt.y = HIWORD(Msg->lParam);
492 switch (Msg->message)
493 {
494 case WM_MOUSEWHEEL:
495 MouseHookData.mouseData = MAKELONG(0, GET_WHEEL_DELTA_WPARAM(Msg->wParam));
496 break;
497 case WM_XBUTTONDOWN:
498 case WM_XBUTTONUP:
499 case WM_XBUTTONDBLCLK:
500 case WM_NCXBUTTONDOWN:
501 case WM_NCXBUTTONUP:
502 case WM_NCXBUTTONDBLCLK:
503 MouseHookData.mouseData = MAKELONG(0, HIWORD(Msg->wParam));
504 break;
505 default:
506 MouseHookData.mouseData = 0;
507 break;
508 }
509
510 MouseHookData.flags = flags; // LLMHF_INJECTED
511 MouseHookData.time = Msg->time;
512 MouseHookData.dwExtraInfo = dwExtraInfo;
513
514 /* If the hook procedure returned non zero, dont send the message */
515 if (Hook)
516 {
517 if (co_HOOK_CallHooks(WH_MOUSE_LL, HC_ACTION, Msg->message, (LPARAM) &MouseHookData))
518 return;
519 }
520
521 /* Get the desktop window */
522 pwndDesktop = UserGetDesktopWindow();
523 if (!pwndDesktop) return;
524 pDesk = pwndDesktop->head.rpdesk;
525
526 /* Check if the mouse is captured */
527 Msg->hwnd = IntGetCaptureWindow();
528 if (Msg->hwnd != NULL)
529 {
530 pwnd = UserGetWindowObject(Msg->hwnd);
531 }
532 else
533 {
534 pwnd = IntTopLevelWindowFromPoint(Msg->pt.x, Msg->pt.y);
535 if (pwnd) Msg->hwnd = pwnd->head.h;
536 }
537
538 hdcScreen = IntGetScreenDC();
539 CurInfo = IntGetSysCursorInfo();
540
541 /* Check if we found a window */
542 if (Msg->hwnd != NULL && pwnd != NULL)
543 {
544 if (Msg->message == WM_MOUSEMOVE)
545 {
546 PUSER_MESSAGE_QUEUE MessageQueue = pwnd->head.pti->MessageQueue;
547
548 /* Check if cursor should be visible */
549 if(hdcScreen &&
550 MessageQueue->CursorObject &&
551 MessageQueue->ShowingCursor >= 0)
552 {
553 /* Check if shape has changed */
554 if(CurInfo->CurrentCursorObject != MessageQueue->CursorObject)
555 {
556 /* Call GDI to set the new screen cursor */
557 GreSetPointerShape(hdcScreen,
558 MessageQueue->CursorObject->IconInfo.hbmMask,
559 MessageQueue->CursorObject->IconInfo.hbmColor,
560 MessageQueue->CursorObject->IconInfo.xHotspot,
561 MessageQueue->CursorObject->IconInfo.yHotspot,
562 gpsi->ptCursor.x,
563 gpsi->ptCursor.y);
564 } else
565 GreMovePointer(hdcScreen, Msg->pt.x, Msg->pt.y);
566 }
567 /* Check if w have to hide cursor */
568 else if (CurInfo->ShowingCursor >= 0)
569 GreMovePointer(hdcScreen, -1, -1);
570
571 /* Update global cursor info */
572 CurInfo->ShowingCursor = MessageQueue->ShowingCursor;
573 CurInfo->CurrentCursorObject = MessageQueue->CursorObject;
574 gpqCursor = MessageQueue;
575
576 /* Mouse move is a special case */
577 MsqPostMouseMove(MessageQueue, Msg);
578 }
579 else
580 {
581 TRACE("Posting mouse message to hwnd=0x%x!\n", UserHMGetHandle(pwnd));
582 MsqPostMessage(pwnd->head.pti->MessageQueue, Msg, TRUE, QS_MOUSEBUTTON);
583 }
584 }
585 else if (hdcScreen)
586 {
587 /* always show cursor on background; FIXME: set default pointer */
588 GreMovePointer(hdcScreen, Msg->pt.x, Msg->pt.y);
589 CurInfo->ShowingCursor = 0;
590 }
591 }
592
593 VOID FASTCALL
594 MsqPostHotKeyMessage(PVOID Thread, HWND hWnd, WPARAM wParam, LPARAM lParam)
595 {
596 PWND Window;
597 PTHREADINFO Win32Thread;
598 MSG Mesg;
599 LARGE_INTEGER LargeTickCount;
600 NTSTATUS Status;
601 INT id;
602 DWORD Type;
603
604 Status = ObReferenceObjectByPointer (Thread,
605 THREAD_ALL_ACCESS,
606 PsThreadType,
607 KernelMode);
608 if (!NT_SUCCESS(Status))
609 return;
610
611 Win32Thread = ((PETHREAD)Thread)->Tcb.Win32Thread;
612 if (Win32Thread == NULL || Win32Thread->MessageQueue == NULL)
613 {
614 ObDereferenceObject ((PETHREAD)Thread);
615 return;
616 }
617
618 Window = IntGetWindowObject(hWnd);
619 if (!Window)
620 {
621 ObDereferenceObject ((PETHREAD)Thread);
622 return;
623 }
624
625 id = wParam; // Check for hot keys unrelated to the hot keys set by RegisterHotKey.
626
627 Mesg.hwnd = hWnd;
628 Mesg.message = id != IDHK_REACTOS ? WM_HOTKEY : WM_SYSCOMMAND;
629 Mesg.wParam = id != IDHK_REACTOS ? wParam : SC_HOTKEY;
630 Mesg.lParam = id != IDHK_REACTOS ? lParam : (LPARAM)hWnd;
631 Type = id != IDHK_REACTOS ? QS_HOTKEY : QS_POSTMESSAGE;
632 KeQueryTickCount(&LargeTickCount);
633 Mesg.time = MsqCalculateMessageTime(&LargeTickCount);
634 Mesg.pt = gpsi->ptCursor;
635 MsqPostMessage(Window->head.pti->MessageQueue, &Mesg, FALSE, Type);
636 UserDereferenceObject(Window);
637 ObDereferenceObject (Thread);
638
639 }
640
641 PUSER_MESSAGE FASTCALL
642 MsqCreateMessage(LPMSG Msg)
643 {
644 PUSER_MESSAGE Message;
645
646 Message = ExAllocateFromPagedLookasideList(pgMessageLookasideList);
647 if (!Message)
648 {
649 return NULL;
650 }
651
652 RtlMoveMemory(&Message->Msg, Msg, sizeof(MSG));
653
654 return Message;
655 }
656
657 VOID FASTCALL
658 MsqDestroyMessage(PUSER_MESSAGE Message)
659 {
660 ExFreeToPagedLookasideList(pgMessageLookasideList, Message);
661 }
662
663 BOOLEAN FASTCALL
664 co_MsqDispatchOneSentMessage(PUSER_MESSAGE_QUEUE MessageQueue)
665 {
666 PUSER_SENT_MESSAGE SaveMsg, Message;
667 PLIST_ENTRY Entry;
668 PTHREADINFO pti;
669 BOOL Ret;
670 LRESULT Result = 0;
671
672 if (IsListEmpty(&MessageQueue->SentMessagesListHead))
673 {
674 return(FALSE);
675 }
676
677 /* remove it from the list of pending messages */
678 Entry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
679 Message = CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry);
680
681 pti = MessageQueue->Thread->Tcb.Win32Thread;
682
683 SaveMsg = pti->pusmCurrent;
684 pti->pusmCurrent = Message;
685
686 // Processing a message sent to it from another thread.
687 if ( ( Message->SenderQueue && MessageQueue != Message->SenderQueue) ||
688 ( Message->CallBackSenderQueue && MessageQueue != Message->CallBackSenderQueue ))
689 { // most likely, but, to be sure.
690 pti->pcti->CTI_flags |= CTI_INSENDMESSAGE; // Let the user know...
691 }
692
693 /* insert it to the list of messages that are currently dispatched by this
694 message queue */
695 InsertTailList(&MessageQueue->LocalDispatchingMessagesHead,
696 &Message->ListEntry);
697
698 ClearMsgBitsMask(MessageQueue, Message->QS_Flags);
699
700 if (Message->HookMessage == MSQ_ISHOOK)
701 { // Direct Hook Call processor
702 Result = co_CallHook( Message->Msg.message, // HookId
703 (INT)(INT_PTR)Message->Msg.hwnd, // Code
704 Message->Msg.wParam,
705 Message->Msg.lParam);
706 }
707 else if (Message->HookMessage == MSQ_ISEVENT)
708 { // Direct Event Call processor
709 Result = co_EVENT_CallEvents( Message->Msg.message,
710 Message->Msg.hwnd,
711 Message->Msg.wParam,
712 Message->Msg.lParam);
713 }
714 else if(Message->HookMessage == MSQ_INJECTMODULE)
715 {
716 Result = IntLoadHookModule(Message->Msg.message,
717 (HHOOK)Message->Msg.lParam,
718 Message->Msg.wParam);
719 }
720 else if ((Message->CompletionCallback) &&
721 (Message->CallBackSenderQueue == MessageQueue))
722 { /* Call the callback routine */
723 if (Message->QS_Flags & QS_SMRESULT)
724 {
725 co_IntCallSentMessageCallback(Message->CompletionCallback,
726 Message->Msg.hwnd,
727 Message->Msg.message,
728 Message->CompletionCallbackContext,
729 Message->lResult);
730 /* Set callback to NULL to prevent reentry */
731 Message->CompletionCallback = NULL;
732 }
733 else
734 {
735 /* The message has not been processed yet, reinsert it. */
736 RemoveEntryList(&Message->ListEntry);
737 InsertTailList(&Message->CallBackSenderQueue->SentMessagesListHead, &Message->ListEntry);
738 TRACE("Callback Message not processed yet. Requeuing the message\n");
739 Ret = FALSE;
740 goto Exit;
741 }
742 }
743 else
744 { /* Call the window procedure. */
745 Result = co_IntSendMessage( Message->Msg.hwnd,
746 Message->Msg.message,
747 Message->Msg.wParam,
748 Message->Msg.lParam);
749 }
750
751 /* remove the message from the local dispatching list, because it doesn't need
752 to be cleaned up on thread termination anymore */
753 RemoveEntryList(&Message->ListEntry);
754
755 /* If the message is a callback, insert it in the callback senders MessageQueue */
756 if (Message->CompletionCallback)
757 {
758 if (Message->CallBackSenderQueue)
759 {
760 Message->lResult = Result;
761 Message->QS_Flags |= QS_SMRESULT;
762
763 /* insert it in the callers message queue */
764 InsertTailList(&Message->CallBackSenderQueue->SentMessagesListHead, &Message->ListEntry);
765 MsqWakeQueue(Message->CallBackSenderQueue, QS_SENDMESSAGE, TRUE);
766 IntDereferenceMessageQueue(Message->CallBackSenderQueue);
767 }
768 Ret = TRUE;
769 goto Exit;
770 }
771
772 /* remove the message from the dispatching list if needed, so lock the sender's message queue */
773 if (Message->SenderQueue)
774 {
775 if (Message->DispatchingListEntry.Flink != NULL)
776 {
777 /* only remove it from the dispatching list if not already removed by a timeout */
778 RemoveEntryList(&Message->DispatchingListEntry);
779 }
780 }
781 /* still keep the sender's message queue locked, so the sender can't exit the
782 MsqSendMessage() function (if timed out) */
783
784 if (Message->QS_Flags & QS_SMRESULT)
785 {
786 Result = Message->lResult;
787 }
788
789 /* Let the sender know the result. */
790 if (Message->Result != NULL)
791 {
792 *Message->Result = Result;
793 }
794
795 if (Message->HasPackedLParam == TRUE)
796 {
797 if (Message->Msg.lParam)
798 ExFreePool((PVOID)Message->Msg.lParam);
799 }
800
801 /* Notify the sender. */
802 if (Message->CompletionEvent != NULL)
803 {
804 KeSetEvent(Message->CompletionEvent, IO_NO_INCREMENT, FALSE);
805 }
806
807 /* if the message has a sender */
808 if (Message->SenderQueue)
809 {
810 /* dereference our and the sender's message queue */
811 IntDereferenceMessageQueue(Message->SenderQueue);
812 IntDereferenceMessageQueue(MessageQueue);
813 }
814
815 /* free the message */
816 ExFreePoolWithTag(Message, TAG_USRMSG);
817 Ret = TRUE;
818 Exit:
819 /* do not hangup on the user if this is reentering */
820 if (!SaveMsg) pti->pcti->CTI_flags &= ~CTI_INSENDMESSAGE;
821 pti->pusmCurrent = SaveMsg;
822
823 return Ret;
824 }
825
826 VOID APIENTRY
827 MsqRemoveWindowMessagesFromQueue(PVOID pWindow)
828 {
829 PUSER_SENT_MESSAGE SentMessage;
830 PUSER_MESSAGE PostedMessage;
831 PUSER_MESSAGE_QUEUE MessageQueue;
832 PLIST_ENTRY CurrentEntry, ListHead;
833 PWND Window = pWindow;
834
835 ASSERT(Window);
836
837 MessageQueue = Window->head.pti->MessageQueue;
838 ASSERT(MessageQueue);
839
840 /* remove the posted messages for this window */
841 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
842 ListHead = &MessageQueue->PostedMessagesListHead;
843 while (CurrentEntry != ListHead)
844 {
845 PostedMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
846 ListEntry);
847 if (PostedMessage->Msg.hwnd == Window->head.h)
848 {
849 RemoveEntryList(&PostedMessage->ListEntry);
850 ClearMsgBitsMask(MessageQueue, PostedMessage->QS_Flags);
851 MsqDestroyMessage(PostedMessage);
852 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
853 }
854 else
855 {
856 CurrentEntry = CurrentEntry->Flink;
857 }
858 }
859
860 /* remove the sent messages for this window */
861 CurrentEntry = MessageQueue->SentMessagesListHead.Flink;
862 ListHead = &MessageQueue->SentMessagesListHead;
863 while (CurrentEntry != ListHead)
864 {
865 SentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
866 ListEntry);
867 if(SentMessage->Msg.hwnd == Window->head.h)
868 {
869 TRACE("Notify the sender and remove a message from the queue that had not been dispatched\n");
870
871 RemoveEntryList(&SentMessage->ListEntry);
872 ClearMsgBitsMask(MessageQueue, SentMessage->QS_Flags);
873
874 /* if it is a callback and this queue is not the sender queue, dereference queue */
875 if ((SentMessage->CompletionCallback) && (SentMessage->CallBackSenderQueue != MessageQueue))
876 {
877 IntDereferenceMessageQueue(SentMessage->CallBackSenderQueue);
878 }
879 /* Only if the message has a sender was the queue referenced */
880 if ((SentMessage->SenderQueue)
881 && (SentMessage->DispatchingListEntry.Flink != NULL))
882 {
883 RemoveEntryList(&SentMessage->DispatchingListEntry);
884 }
885
886 /* wake the sender's thread */
887 if (SentMessage->CompletionEvent != NULL)
888 {
889 KeSetEvent(SentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
890 }
891
892 if (SentMessage->HasPackedLParam == TRUE)
893 {
894 if (SentMessage->Msg.lParam)
895 ExFreePool((PVOID)SentMessage->Msg.lParam);
896 }
897
898 /* if the message has a sender */
899 if (SentMessage->SenderQueue)
900 {
901 /* dereference our and the sender's message queue */
902 IntDereferenceMessageQueue(MessageQueue);
903 IntDereferenceMessageQueue(SentMessage->SenderQueue);
904 }
905
906 /* free the message */
907 ExFreePoolWithTag(SentMessage, TAG_USRMSG);
908
909 CurrentEntry = MessageQueue->SentMessagesListHead.Flink;
910 }
911 else
912 {
913 CurrentEntry = CurrentEntry->Flink;
914 }
915 }
916 }
917
918 BOOL FASTCALL
919 co_MsqSendMessageAsync(PTHREADINFO ptiReceiver,
920 HWND hwnd,
921 UINT Msg,
922 WPARAM wParam,
923 LPARAM lParam,
924 SENDASYNCPROC CompletionCallback,
925 ULONG_PTR CompletionCallbackContext,
926 BOOL HasPackedLParam,
927 INT HookMessage)
928 {
929
930 PTHREADINFO ptiSender;
931 PUSER_SENT_MESSAGE Message;
932
933 if(!(Message = ExAllocatePoolWithTag(NonPagedPool, sizeof(USER_SENT_MESSAGE), TAG_USRMSG)))
934 {
935 ERR("MsqSendMessage(): Not enough memory to allocate a message");
936 return FALSE;
937 }
938
939 ptiSender = PsGetCurrentThreadWin32Thread();
940
941 IntReferenceMessageQueue(ptiReceiver->MessageQueue);
942 /* Take reference on this MessageQueue if its a callback. It will be released
943 when message is processed or removed from target hwnd MessageQueue */
944 if (CompletionCallback)
945 IntReferenceMessageQueue(ptiSender->MessageQueue);
946
947 Message->Msg.hwnd = hwnd;
948 Message->Msg.message = Msg;
949 Message->Msg.wParam = wParam;
950 Message->Msg.lParam = lParam;
951 Message->CompletionEvent = NULL;
952 Message->Result = 0;
953 Message->lResult = 0;
954 Message->SenderQueue = NULL;
955 Message->CallBackSenderQueue = ptiSender->MessageQueue;
956 Message->DispatchingListEntry.Flink = NULL;
957 Message->CompletionCallback = CompletionCallback;
958 Message->CompletionCallbackContext = CompletionCallbackContext;
959 Message->HookMessage = HookMessage;
960 Message->HasPackedLParam = HasPackedLParam;
961 Message->QS_Flags = QS_SENDMESSAGE;
962
963 InsertTailList(&ptiReceiver->MessageQueue->SentMessagesListHead, &Message->ListEntry);
964 MsqWakeQueue(ptiReceiver->MessageQueue, QS_SENDMESSAGE, TRUE);
965 IntDereferenceMessageQueue(ptiReceiver->MessageQueue);
966
967 return TRUE;
968 }
969
970 NTSTATUS FASTCALL
971 co_MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue,
972 HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam,
973 UINT uTimeout, BOOL Block, INT HookMessage,
974 ULONG_PTR *uResult)
975 {
976 PTHREADINFO pti, ptirec;
977 PUSER_SENT_MESSAGE Message;
978 KEVENT CompletionEvent;
979 NTSTATUS WaitStatus;
980 PUSER_MESSAGE_QUEUE ThreadQueue;
981 LARGE_INTEGER Timeout;
982 PLIST_ENTRY Entry;
983 LRESULT Result = 0; //// Result could be trashed. ////
984
985 pti = PsGetCurrentThreadWin32Thread();
986 ThreadQueue = pti->MessageQueue;
987 ptirec = MessageQueue->Thread->Tcb.Win32Thread;
988 ASSERT(ThreadQueue != MessageQueue);
989 ASSERT(ptirec->pcti); // Send must have a client side to receive it!!!!
990
991 /* Don't send from or to a dying thread */
992 if (pti->TIF_flags & TIF_INCLEANUP || ptirec->TIF_flags & TIF_INCLEANUP)
993 {
994 if (uResult) *uResult = -1;
995 ERR("MsqSM: Current pti %d or Rec pti %d\n",pti->TIF_flags & TIF_INCLEANUP, ptirec->TIF_flags & TIF_INCLEANUP);
996 return STATUS_UNSUCCESSFUL;
997 }
998
999 if ( HookMessage == MSQ_NORMAL )
1000 {
1001 // These can not cross International Border lines!
1002 if ( pti->ppi != ptirec->ppi )
1003 {
1004 switch(Msg)
1005 {
1006 case EM_GETLINE:
1007 case EM_SETPASSWORDCHAR:
1008 case WM_GETTEXT:
1009 case WM_NOTIFY:
1010 if (uResult) *uResult = -1;
1011 ERR("Running across the border without a passport!\n");
1012 return STATUS_UNSUCCESSFUL;
1013 }
1014 }
1015
1016 // These can not cross State lines!
1017 if ( Msg == WM_CREATE || Msg == WM_NCCREATE )
1018 {
1019 if (uResult) *uResult = -1;
1020 ERR("Can not tell the other State we have Create!\n");
1021 return STATUS_UNSUCCESSFUL;
1022 }
1023 }
1024
1025 if(!(Message = ExAllocatePoolWithTag(PagedPool, sizeof(USER_SENT_MESSAGE), TAG_USRMSG)))
1026 {
1027 ERR("MsqSendMessage(): Not enough memory to allocate a message");
1028 return STATUS_INSUFFICIENT_RESOURCES;
1029 }
1030
1031 KeInitializeEvent(&CompletionEvent, NotificationEvent, FALSE);
1032
1033 Timeout.QuadPart = (LONGLONG) uTimeout * (LONGLONG) -10000;
1034
1035 /* FIXME: Increase reference counter of sender's message queue here */
1036
1037 Message->Msg.hwnd = Wnd;
1038 Message->Msg.message = Msg;
1039 Message->Msg.wParam = wParam;
1040 Message->Msg.lParam = lParam;
1041 Message->CompletionEvent = &CompletionEvent;
1042 Message->Result = &Result;
1043 Message->lResult = 0;
1044 Message->QS_Flags = 0;
1045 Message->SenderQueue = ThreadQueue;
1046 Message->CallBackSenderQueue = NULL;
1047 IntReferenceMessageQueue(ThreadQueue);
1048 Message->CompletionCallback = NULL;
1049 Message->CompletionCallbackContext = 0;
1050 Message->HookMessage = HookMessage;
1051 Message->HasPackedLParam = FALSE;
1052
1053 IntReferenceMessageQueue(MessageQueue);
1054
1055 /* Add it to the list of pending messages */
1056 InsertTailList(&ThreadQueue->DispatchingMessagesHead, &Message->DispatchingListEntry);
1057
1058 /* Queue it in the destination's message queue */
1059 InsertTailList(&MessageQueue->SentMessagesListHead, &Message->ListEntry);
1060
1061 Message->QS_Flags = QS_SENDMESSAGE;
1062 MsqWakeQueue(MessageQueue, QS_SENDMESSAGE, TRUE);
1063
1064 /* We can't access the Message anymore since it could have already been deleted! */
1065
1066 if(Block)
1067 {
1068 UserLeaveCo();
1069
1070 /* Don't process messages sent to the thread */
1071 WaitStatus = KeWaitForSingleObject(&CompletionEvent, UserRequest, UserMode,
1072 FALSE, (uTimeout ? &Timeout : NULL));
1073
1074 UserEnterCo();
1075
1076 if(WaitStatus == STATUS_TIMEOUT)
1077 {
1078 /* Look up if the message has not yet dispatched, if so
1079 make sure it can't pass a result and it must not set the completion event anymore */
1080 Entry = MessageQueue->SentMessagesListHead.Flink;
1081 while (Entry != &MessageQueue->SentMessagesListHead)
1082 {
1083 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry)
1084 == Message)
1085 {
1086 /* We can access Message here, it's secure because the message queue is locked
1087 and the message is still hasn't been dispatched */
1088 Message->CompletionEvent = NULL;
1089 Message->Result = NULL;
1090 break;
1091 }
1092 Entry = Entry->Flink;
1093 }
1094
1095 /* Remove from the local dispatching list so the other thread knows,
1096 it can't pass a result and it must not set the completion event anymore */
1097 Entry = ThreadQueue->DispatchingMessagesHead.Flink;
1098 while (Entry != &ThreadQueue->DispatchingMessagesHead)
1099 {
1100 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, DispatchingListEntry)
1101 == Message)
1102 {
1103 /* We can access Message here, it's secure because the sender's message is locked
1104 and the message has definitely not yet been destroyed, otherwise it would
1105 have been removed from this list by the dispatching routine right after
1106 dispatching the message */
1107 Message->CompletionEvent = NULL;
1108 Message->Result = NULL;
1109 RemoveEntryList(&Message->DispatchingListEntry);
1110 Message->DispatchingListEntry.Flink = NULL;
1111 break;
1112 }
1113 Entry = Entry->Flink;
1114 }
1115
1116 TRACE("MsqSendMessage (blocked) timed out 1\n");
1117 }
1118 while (co_MsqDispatchOneSentMessage(ThreadQueue))
1119 ;
1120 }
1121 else
1122 {
1123 PVOID WaitObjects[2];
1124
1125 WaitObjects[0] = &CompletionEvent;
1126 WaitObjects[1] = ThreadQueue->NewMessages;
1127 do
1128 {
1129 UserLeaveCo();
1130
1131 WaitStatus = KeWaitForMultipleObjects(2, WaitObjects, WaitAny, UserRequest,
1132 UserMode, FALSE, (uTimeout ? &Timeout : NULL), NULL);
1133
1134 UserEnterCo();
1135
1136 if(WaitStatus == STATUS_TIMEOUT)
1137 {
1138 /* Look up if the message has not yet been dispatched, if so
1139 make sure it can't pass a result and it must not set the completion event anymore */
1140 Entry = MessageQueue->SentMessagesListHead.Flink;
1141 while (Entry != &MessageQueue->SentMessagesListHead)
1142 {
1143 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry)
1144 == Message)
1145 {
1146 /* We can access Message here, it's secure because the message queue is locked
1147 and the message is still hasn't been dispatched */
1148 Message->CompletionEvent = NULL;
1149 Message->Result = NULL;
1150 break;
1151 }
1152 Entry = Entry->Flink;
1153 }
1154
1155 /* Remove from the local dispatching list so the other thread knows,
1156 it can't pass a result and it must not set the completion event anymore */
1157 Entry = ThreadQueue->DispatchingMessagesHead.Flink;
1158 while (Entry != &ThreadQueue->DispatchingMessagesHead)
1159 {
1160 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, DispatchingListEntry)
1161 == Message)
1162 {
1163 /* We can access Message here, it's secure because the sender's message is locked
1164 and the message has definitely not yet been destroyed, otherwise it would
1165 have been removed from this list by the dispatching routine right after
1166 dispatching the message */
1167 Message->CompletionEvent = NULL;
1168 Message->Result = NULL;
1169 RemoveEntryList(&Message->DispatchingListEntry);
1170 Message->DispatchingListEntry.Flink = NULL;
1171 break;
1172 }
1173 Entry = Entry->Flink;
1174 }
1175
1176 TRACE("MsqSendMessage timed out 2\n");
1177 break;
1178 }
1179 while (co_MsqDispatchOneSentMessage(ThreadQueue))
1180 ;
1181 }
1182 while (NT_SUCCESS(WaitStatus) && STATUS_WAIT_0 != WaitStatus);
1183 }
1184
1185 if(WaitStatus != STATUS_TIMEOUT)
1186 *uResult = (STATUS_WAIT_0 == WaitStatus ? Result : -1);
1187
1188 return WaitStatus;
1189 }
1190
1191 VOID FASTCALL
1192 MsqPostMessage(PUSER_MESSAGE_QUEUE MessageQueue, MSG* Msg, BOOLEAN HardwareMessage,
1193 DWORD MessageBits)
1194 {
1195 PUSER_MESSAGE Message;
1196
1197 if(!(Message = MsqCreateMessage(Msg)))
1198 {
1199 return;
1200 }
1201
1202 if(!HardwareMessage)
1203 {
1204 InsertTailList(&MessageQueue->PostedMessagesListHead,
1205 &Message->ListEntry);
1206 }
1207 else
1208 {
1209 InsertTailList(&MessageQueue->HardwareMessagesListHead,
1210 &Message->ListEntry);
1211 }
1212
1213 Message->QS_Flags = MessageBits;
1214 MsqWakeQueue(MessageQueue, MessageBits, (MessageBits & QS_TIMER ? FALSE : TRUE));
1215 }
1216
1217 VOID FASTCALL
1218 MsqPostQuitMessage(PUSER_MESSAGE_QUEUE MessageQueue, ULONG ExitCode)
1219 {
1220 MessageQueue->QuitPosted = TRUE;
1221 MessageQueue->QuitExitCode = ExitCode;
1222 MsqWakeQueue(MessageQueue, QS_POSTMESSAGE|QS_ALLPOSTMESSAGE, TRUE);
1223 }
1224
1225 /***********************************************************************
1226 * MsqSendParentNotify
1227 *
1228 * Send a WM_PARENTNOTIFY to all ancestors of the given window, unless
1229 * the window has the WS_EX_NOPARENTNOTIFY style.
1230 */
1231 static void MsqSendParentNotify( PWND pwnd, WORD event, WORD idChild, POINT pt )
1232 {
1233 PWND pwndDesktop = UserGetDesktopWindow();
1234
1235 /* pt has to be in the client coordinates of the parent window */
1236 pt.x += pwndDesktop->rcClient.left - pwnd->rcClient.left;
1237 pt.y += pwndDesktop->rcClient.top - pwnd->rcClient.top;
1238
1239 for (;;)
1240 {
1241 PWND pwndParent;
1242
1243 if (!(pwnd->style & WS_CHILD)) break;
1244 if (pwnd->ExStyle & WS_EX_NOPARENTNOTIFY) break;
1245 if (!(pwndParent = IntGetParent(pwnd))) break;
1246 if (pwndParent == pwndDesktop) break;
1247 pt.x += pwnd->rcClient.left - pwndParent->rcClient.left;
1248 pt.y += pwnd->rcClient.top - pwndParent->rcClient.top;
1249
1250 pwnd = pwndParent;
1251 co_IntSendMessage( UserHMGetHandle(pwnd), WM_PARENTNOTIFY,
1252 MAKEWPARAM( event, idChild ), MAKELPARAM( pt.x, pt.y ) );
1253 }
1254 }
1255
1256 VOID
1257 FASTCALL
1258 IntTrackMouseMove(PWND pwndTrack, PDESKTOP pDesk, PMSG msg, USHORT hittest)
1259 {
1260 // PWND pwndTrack = IntChildrenWindowFromPoint(pwndMsg, msg->pt.x, msg->pt.y);
1261 hittest = GetNCHitEx(pwndTrack, msg->pt);
1262
1263 if ( pDesk->spwndTrack != pwndTrack || // Change with tracking window or
1264 msg->message != WM_MOUSEMOVE || // Mouse click changes or
1265 pDesk->htEx != hittest) // Change in current hit test states.
1266 {
1267 TRACE("ITMM: Track Mouse Move!\n");
1268
1269 /* Handle only the changing window track and mouse move across a border. */
1270 if ( pDesk->spwndTrack != pwndTrack ||
1271 (pDesk->htEx == HTCLIENT) ^ (hittest == HTCLIENT) )
1272 {
1273 TRACE("ITMM: Another Wnd %d or Across Border %d\n",
1274 pDesk->spwndTrack != pwndTrack,(pDesk->htEx == HTCLIENT) ^ (hittest == HTCLIENT));
1275
1276 if ( pDesk->dwDTFlags & DF_TME_LEAVE )
1277 UserPostMessage( UserHMGetHandle(pDesk->spwndTrack),
1278 (pDesk->htEx != HTCLIENT) ? WM_NCMOUSELEAVE : WM_MOUSELEAVE,
1279 0, 0);
1280
1281 if ( pDesk->dwDTFlags & DF_TME_HOVER )
1282 IntKillTimer(UserHMGetHandle(pDesk->spwndTrack), ID_EVENT_SYSTIMER_MOUSEHOVER, TRUE);
1283
1284 /* Clear the flags to sign a change. */
1285 pDesk->dwDTFlags &= ~(DF_TME_LEAVE|DF_TME_HOVER);
1286 }
1287 /* Set the Track window and hit test. */
1288 pDesk->spwndTrack = pwndTrack;
1289 pDesk->htEx = hittest;
1290 }
1291
1292 /* Reset, Same Track window, Hover set and Mouse Clicks or Clobbered Hover box. */
1293 if ( pDesk->spwndTrack == pwndTrack &&
1294 ( msg->message != WM_MOUSEMOVE || !RECTL_bPointInRect(&pDesk->rcMouseHover, msg->pt.x, msg->pt.y)) &&
1295 pDesk->dwDTFlags & DF_TME_HOVER )
1296 {
1297 TRACE("ITMM: Reset Hover points!\n");
1298 // Restart timer for the hover period.
1299 IntSetTimer(pDesk->spwndTrack, ID_EVENT_SYSTIMER_MOUSEHOVER, pDesk->dwMouseHoverTime, SystemTimerProc, TMRF_SYSTEM);
1300 // Reset desktop mouse hover from the system default hover rectangle.
1301 RECTL_vSetRect(&pDesk->rcMouseHover,
1302 msg->pt.x - gspv.iMouseHoverWidth / 2,
1303 msg->pt.y - gspv.iMouseHoverHeight / 2,
1304 msg->pt.x + gspv.iMouseHoverWidth / 2,
1305 msg->pt.y + gspv.iMouseHoverHeight / 2);
1306 }
1307 }
1308
1309 BOOL co_IntProcessMouseMessage(MSG* msg, BOOL* RemoveMessages, UINT first, UINT last)
1310 {
1311 MSG clk_msg;
1312 POINT pt;
1313 UINT message;
1314 USHORT hittest;
1315 EVENTMSG event;
1316 MOUSEHOOKSTRUCT hook;
1317 BOOL eatMsg;
1318
1319 PWND pwndMsg, pwndDesktop;
1320 PUSER_MESSAGE_QUEUE MessageQueue;
1321 PTHREADINFO pti;
1322 PSYSTEM_CURSORINFO CurInfo;
1323 PDESKTOP pDesk;
1324 DECLARE_RETURN(BOOL);
1325
1326 pti = PsGetCurrentThreadWin32Thread();
1327 pwndDesktop = UserGetDesktopWindow();
1328 MessageQueue = pti->MessageQueue;
1329 CurInfo = IntGetSysCursorInfo();
1330 pwndMsg = UserGetWindowObject(msg->hwnd);
1331 clk_msg = MessageQueue->msgDblClk;
1332 pDesk = pwndDesktop->head.rpdesk;
1333
1334 /* find the window to dispatch this mouse message to */
1335 if (MessageQueue->CaptureWindow)
1336 {
1337 hittest = HTCLIENT;
1338 pwndMsg = IntGetWindowObject(MessageQueue->CaptureWindow);
1339 }
1340 else
1341 {
1342 pwndMsg = co_WinPosWindowFromPoint(pwndMsg, &msg->pt, &hittest);
1343 }
1344
1345 TRACE("Got mouse message for 0x%x, hittest: 0x%x\n", msg->hwnd, hittest );
1346
1347 if (pwndMsg == NULL || pwndMsg->head.pti != pti)
1348 {
1349 /* Remove and ignore the message */
1350 *RemoveMessages = TRUE;
1351 RETURN(FALSE);
1352 }
1353
1354 if ( MessageQueue == gpqCursor ) // Cursor must use the same Queue!
1355 {
1356 IntTrackMouseMove(pwndMsg, pDesk, msg, hittest);
1357 }
1358 else
1359 {
1360 ERR("Not the same cursor!\n");
1361 }
1362
1363 msg->hwnd = UserHMGetHandle(pwndMsg);
1364
1365 #if 0
1366 if (!check_hwnd_filter( msg, hwnd_filter )) RETURN(FALSE);
1367 #endif
1368
1369 pt = msg->pt;
1370 message = msg->message;
1371 /* Note: windows has no concept of a non-client wheel message */
1372 if (message != WM_MOUSEWHEEL)
1373 {
1374 if (hittest != HTCLIENT)
1375 {
1376 message += WM_NCMOUSEMOVE - WM_MOUSEMOVE;
1377 msg->wParam = hittest;
1378 }
1379 else
1380 {
1381 /* coordinates don't get translated while tracking a menu */
1382 /* FIXME: should differentiate popups and top-level menus */
1383 if (!(MessageQueue->MenuOwner))
1384 {
1385 pt.x += pwndDesktop->rcClient.left - pwndMsg->rcClient.left;
1386 pt.y += pwndDesktop->rcClient.top - pwndMsg->rcClient.top;
1387 }
1388 }
1389 }
1390 msg->lParam = MAKELONG( pt.x, pt.y );
1391
1392 /* translate double clicks */
1393
1394 if ((msg->message == WM_LBUTTONDOWN) ||
1395 (msg->message == WM_RBUTTONDOWN) ||
1396 (msg->message == WM_MBUTTONDOWN) ||
1397 (msg->message == WM_XBUTTONDOWN))
1398 {
1399 BOOL update = *RemoveMessages;
1400
1401 /* translate double clicks -
1402 * note that ...MOUSEMOVEs can slip in between
1403 * ...BUTTONDOWN and ...BUTTONDBLCLK messages */
1404
1405 if ((MessageQueue->MenuOwner || MessageQueue->MoveSize) ||
1406 hittest != HTCLIENT ||
1407 (pwndMsg->pcls->style & CS_DBLCLKS))
1408 {
1409 if ((msg->message == clk_msg.message) &&
1410 (msg->hwnd == clk_msg.hwnd) &&
1411 (msg->wParam == clk_msg.wParam) &&
1412 ((msg->time - clk_msg.time) < (ULONG)gspv.iDblClickTime) &&
1413 (abs(msg->pt.x - clk_msg.pt.x) < UserGetSystemMetrics(SM_CXDOUBLECLK)/2) &&
1414 (abs(msg->pt.y - clk_msg.pt.y) < UserGetSystemMetrics(SM_CYDOUBLECLK)/2))
1415 {
1416 message += (WM_LBUTTONDBLCLK - WM_LBUTTONDOWN);
1417 if (update)
1418 {
1419 MessageQueue->msgDblClk.message = 0; /* clear the double click conditions */
1420 update = FALSE;
1421 }
1422 }
1423 }
1424
1425 if (!((first == 0 && last == 0) || (message >= first || message <= last)))
1426 {
1427 TRACE("Message out of range!!!\n");
1428 RETURN(FALSE);
1429 }
1430
1431 /* update static double click conditions */
1432 if (update) MessageQueue->msgDblClk = *msg;
1433 }
1434 else
1435 {
1436 if (!((first == 0 && last == 0) || (message >= first || message <= last)))
1437 {
1438 TRACE("Message out of range!!!\n");
1439 RETURN(FALSE);
1440 }
1441 }
1442
1443 if(gspv.bMouseClickLock)
1444 {
1445 BOOL IsClkLck = FALSE;
1446
1447 if(msg->message == WM_LBUTTONUP)
1448 {
1449 IsClkLck = ((msg->time - CurInfo->ClickLockTime) >= gspv.dwMouseClickLockTime);
1450 if (IsClkLck && (!CurInfo->ClickLockActive))
1451 {
1452 CurInfo->ClickLockActive = TRUE;
1453 }
1454 }
1455 else if (msg->message == WM_LBUTTONDOWN)
1456 {
1457 if (CurInfo->ClickLockActive)
1458 {
1459 IsClkLck = TRUE;
1460 CurInfo->ClickLockActive = FALSE;
1461 }
1462
1463 CurInfo->ClickLockTime = msg->time;
1464 }
1465
1466 if(IsClkLck)
1467 {
1468 /* Remove and ignore the message */
1469 *RemoveMessages = TRUE;
1470 RETURN(FALSE);
1471 }
1472 }
1473
1474 /* message is accepted now (but may still get dropped) */
1475
1476 event.message = msg->message;
1477 event.time = msg->time;
1478 event.hwnd = msg->hwnd;
1479 event.paramL = msg->pt.x;
1480 event.paramH = msg->pt.y;
1481 co_HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&event );
1482
1483 hook.pt = msg->pt;
1484 hook.hwnd = msg->hwnd;
1485 hook.wHitTestCode = hittest;
1486 hook.dwExtraInfo = 0 /* extra_info */ ;
1487 if (co_HOOK_CallHooks( WH_MOUSE, *RemoveMessages ? HC_ACTION : HC_NOREMOVE,
1488 message, (LPARAM)&hook ))
1489 {
1490 hook.pt = msg->pt;
1491 hook.hwnd = msg->hwnd;
1492 hook.wHitTestCode = hittest;
1493 hook.dwExtraInfo = 0 /* extra_info */ ;
1494 co_HOOK_CallHooks( WH_CBT, HCBT_CLICKSKIPPED, message, (LPARAM)&hook );
1495
1496 ERR("WH_MOUSE dorpped mouse message!\n");
1497
1498 /* Remove and skip message */
1499 *RemoveMessages = TRUE;
1500 RETURN(FALSE);
1501 }
1502
1503 if ((hittest == HTERROR) || (hittest == HTNOWHERE))
1504 {
1505 co_IntSendMessage( msg->hwnd, WM_SETCURSOR, (WPARAM)msg->hwnd,
1506 MAKELONG( hittest, msg->message ));
1507
1508 /* Remove and skip message */
1509 *RemoveMessages = TRUE;
1510 RETURN(FALSE);
1511 }
1512
1513 if ((*RemoveMessages == FALSE) || MessageQueue->CaptureWindow)
1514 {
1515 /* Accept the message */
1516 msg->message = message;
1517 RETURN(TRUE);
1518 }
1519
1520 eatMsg = FALSE;
1521
1522 if ((msg->message == WM_LBUTTONDOWN) ||
1523 (msg->message == WM_RBUTTONDOWN) ||
1524 (msg->message == WM_MBUTTONDOWN) ||
1525 (msg->message == WM_XBUTTONDOWN))
1526 {
1527 /* Send the WM_PARENTNOTIFY,
1528 * note that even for double/nonclient clicks
1529 * notification message is still WM_L/M/RBUTTONDOWN.
1530 */
1531 MsqSendParentNotify(pwndMsg, msg->message, 0, msg->pt );
1532
1533 /* Activate the window if needed */
1534
1535 if (msg->hwnd != UserGetForegroundWindow())
1536 {
1537 PWND pwndTop = pwndMsg;
1538 while (pwndTop)
1539 {
1540 if ((pwndTop->style & (WS_POPUP|WS_CHILD)) != WS_CHILD) break;
1541 pwndTop = IntGetParent( pwndTop );
1542 }
1543
1544 if (pwndTop && pwndTop != pwndDesktop)
1545 {
1546 LONG ret = co_IntSendMessage( msg->hwnd,
1547 WM_MOUSEACTIVATE,
1548 (WPARAM)UserHMGetHandle(pwndTop),
1549 MAKELONG( hittest, msg->message));
1550 switch(ret)
1551 {
1552 case MA_NOACTIVATEANDEAT:
1553 eatMsg = TRUE;
1554 /* fall through */
1555 case MA_NOACTIVATE:
1556 break;
1557 case MA_ACTIVATEANDEAT:
1558 eatMsg = TRUE;
1559 /* fall through */
1560 case MA_ACTIVATE:
1561 case 0:
1562 if(!co_IntMouseActivateWindow(pwndMsg)) eatMsg = TRUE;
1563 break;
1564 default:
1565 ERR( "unknown WM_MOUSEACTIVATE code %d\n", ret );
1566 break;
1567 }
1568 }
1569 }
1570 }
1571
1572 /* send the WM_SETCURSOR message */
1573
1574 /* Windows sends the normal mouse message as the message parameter
1575 in the WM_SETCURSOR message even if it's non-client mouse message */
1576 co_IntSendMessage( msg->hwnd, WM_SETCURSOR, (WPARAM)msg->hwnd, MAKELONG( hittest, msg->message ));
1577
1578 msg->message = message;
1579 RETURN(!eatMsg);
1580
1581 CLEANUP:
1582 if(pwndMsg)
1583 UserDereferenceObject(pwndMsg);
1584
1585 END_CLEANUP;
1586 }
1587
1588 BOOL co_IntProcessKeyboardMessage(MSG* Msg, BOOL* RemoveMessages)
1589 {
1590 EVENTMSG Event;
1591
1592 if (Msg->message == WM_KEYDOWN || Msg->message == WM_SYSKEYDOWN ||
1593 Msg->message == WM_KEYUP || Msg->message == WM_SYSKEYUP)
1594 {
1595 switch (Msg->wParam)
1596 {
1597 case VK_LSHIFT: case VK_RSHIFT:
1598 Msg->wParam = VK_SHIFT;
1599 break;
1600 case VK_LCONTROL: case VK_RCONTROL:
1601 Msg->wParam = VK_CONTROL;
1602 break;
1603 case VK_LMENU: case VK_RMENU:
1604 Msg->wParam = VK_MENU;
1605 break;
1606 }
1607 }
1608
1609 Event.message = Msg->message;
1610 Event.hwnd = Msg->hwnd;
1611 Event.time = Msg->time;
1612 Event.paramL = (Msg->wParam & 0xFF) | (HIWORD(Msg->lParam) << 8);
1613 Event.paramH = Msg->lParam & 0x7FFF;
1614 if (HIWORD(Msg->lParam) & 0x0100) Event.paramH |= 0x8000;
1615 co_HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&Event);
1616
1617 if (co_HOOK_CallHooks( WH_KEYBOARD,
1618 *RemoveMessages ? HC_ACTION : HC_NOREMOVE,
1619 LOWORD(Msg->wParam),
1620 Msg->lParam))
1621 {
1622 /* skip this message */
1623 co_HOOK_CallHooks( WH_CBT,
1624 HCBT_KEYSKIPPED,
1625 LOWORD(Msg->wParam),
1626 Msg->lParam );
1627 ERR("KeyboardMessage WH_CBT Call Hook return!\n");
1628 return FALSE;
1629 }
1630 return TRUE;
1631 }
1632
1633 BOOL co_IntProcessHardwareMessage(MSG* Msg, BOOL* RemoveMessages, UINT first, UINT last)
1634 {
1635 if ( IS_MOUSE_MESSAGE(Msg->message))
1636 {
1637 return co_IntProcessMouseMessage(Msg, RemoveMessages, first, last);
1638 }
1639 else if ( IS_KBD_MESSAGE(Msg->message))
1640 {
1641 return co_IntProcessKeyboardMessage(Msg, RemoveMessages);
1642 }
1643
1644 return TRUE;
1645 }
1646
1647 BOOL APIENTRY
1648 co_MsqPeekMouseMove(IN PUSER_MESSAGE_QUEUE MessageQueue,
1649 IN BOOL Remove,
1650 IN PWND Window,
1651 IN UINT MsgFilterLow,
1652 IN UINT MsgFilterHigh,
1653 OUT MSG* pMsg)
1654 {
1655 BOOL AcceptMessage;
1656 MSG msg;
1657
1658 if(!(MessageQueue->MouseMoved))
1659 return FALSE;
1660
1661 msg = MessageQueue->MouseMoveMsg;
1662
1663 AcceptMessage = co_IntProcessMouseMessage(&msg, &Remove, MsgFilterLow, MsgFilterHigh);
1664
1665 if(AcceptMessage)
1666 *pMsg = msg;
1667
1668 if(Remove)
1669 {
1670 ClearMsgBitsMask(MessageQueue, QS_MOUSEMOVE);
1671 MessageQueue->MouseMoved = FALSE;
1672 }
1673
1674 return AcceptMessage;
1675 }
1676
1677 /* check whether a message filter contains at least one potential hardware message */
1678 static INT FASTCALL
1679 filter_contains_hw_range( UINT first, UINT last )
1680 {
1681 /* hardware message ranges are (in numerical order):
1682 * WM_NCMOUSEFIRST .. WM_NCMOUSELAST
1683 * WM_KEYFIRST .. WM_KEYLAST
1684 * WM_MOUSEFIRST .. WM_MOUSELAST
1685 */
1686 if (!last) --last;
1687 if (last < WM_NCMOUSEFIRST) return 0;
1688 if (first > WM_NCMOUSELAST && last < WM_KEYFIRST) return 0;
1689 if (first > WM_KEYLAST && last < WM_MOUSEFIRST) return 0;
1690 if (first > WM_MOUSELAST) return 0;
1691 return 1;
1692 }
1693
1694 BOOL APIENTRY
1695 co_MsqPeekHardwareMessage(IN PUSER_MESSAGE_QUEUE MessageQueue,
1696 IN BOOL Remove,
1697 IN PWND Window,
1698 IN UINT MsgFilterLow,
1699 IN UINT MsgFilterHigh,
1700 IN UINT QSflags,
1701 OUT MSG* pMsg)
1702 {
1703
1704 BOOL AcceptMessage;
1705 PUSER_MESSAGE CurrentMessage;
1706 PLIST_ENTRY ListHead, CurrentEntry = NULL;
1707 MSG msg;
1708
1709 if (!filter_contains_hw_range( MsgFilterLow, MsgFilterHigh )) return FALSE;
1710
1711 ListHead = &MessageQueue->HardwareMessagesListHead;
1712 CurrentEntry = ListHead->Flink;
1713
1714 if (IsListEmpty(CurrentEntry)) return FALSE;
1715
1716 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1717 ListEntry);
1718 do
1719 {
1720 if (IsListEmpty(CurrentEntry)) break;
1721 if (!CurrentMessage) break;
1722 CurrentEntry = CurrentMessage->ListEntry.Flink;
1723 /*
1724 MSDN:
1725 1: any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL.
1726 2: retrieves only messages on the current thread's message queue whose hwnd value is NULL.
1727 3: handle to the window whose messages are to be retrieved.
1728 */
1729 if ( ( !Window || // 1
1730 ( Window == (PWND)HWND_BOTTOM && CurrentMessage->Msg.hwnd == NULL ) || // 2
1731 ( Window != (PWND)HWND_BOTTOM && Window->head.h == CurrentMessage->Msg.hwnd ) ) && // 3
1732 ( ( ( MsgFilterLow == 0 && MsgFilterHigh == 0 ) && CurrentMessage->QS_Flags & QSflags ) ||
1733 ( MsgFilterLow <= CurrentMessage->Msg.message && MsgFilterHigh >= CurrentMessage->Msg.message ) ) )
1734 {
1735 msg = CurrentMessage->Msg;
1736
1737 UpdateKeyStateFromMsg(MessageQueue, &msg);
1738 AcceptMessage = co_IntProcessHardwareMessage(&msg, &Remove, MsgFilterLow, MsgFilterHigh);
1739
1740 if (Remove)
1741 {
1742 RemoveEntryList(&CurrentMessage->ListEntry);
1743 ClearMsgBitsMask(MessageQueue, CurrentMessage->QS_Flags);
1744 MsqDestroyMessage(CurrentMessage);
1745 }
1746
1747 if (AcceptMessage)
1748 {
1749 *pMsg = msg;
1750 return TRUE;
1751 }
1752 }
1753 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1754 ListEntry);
1755 }
1756 while(CurrentEntry != ListHead);
1757
1758 return FALSE;
1759 }
1760
1761 BOOLEAN APIENTRY
1762 MsqPeekMessage(IN PUSER_MESSAGE_QUEUE MessageQueue,
1763 IN BOOLEAN Remove,
1764 IN PWND Window,
1765 IN UINT MsgFilterLow,
1766 IN UINT MsgFilterHigh,
1767 IN UINT QSflags,
1768 OUT PMSG Message)
1769 {
1770 PLIST_ENTRY CurrentEntry;
1771 PUSER_MESSAGE CurrentMessage;
1772 PLIST_ENTRY ListHead;
1773
1774 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
1775 ListHead = &MessageQueue->PostedMessagesListHead;
1776
1777 if (IsListEmpty(CurrentEntry)) return FALSE;
1778
1779 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1780 ListEntry);
1781 do
1782 {
1783 if (IsListEmpty(CurrentEntry)) break;
1784 if (!CurrentMessage) break;
1785 CurrentEntry = CurrentEntry->Flink;
1786 /*
1787 MSDN:
1788 1: any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL.
1789 2: retrieves only messages on the current thread's message queue whose hwnd value is NULL.
1790 3: handle to the window whose messages are to be retrieved.
1791 */
1792 if ( ( !Window || // 1
1793 ( Window == (PWND)HWND_BOTTOM && CurrentMessage->Msg.hwnd == NULL ) || // 2
1794 ( Window != (PWND)HWND_BOTTOM && Window->head.h == CurrentMessage->Msg.hwnd ) ) && // 3
1795 ( ( ( MsgFilterLow == 0 && MsgFilterHigh == 0 ) && CurrentMessage->QS_Flags & QSflags ) ||
1796 ( MsgFilterLow <= CurrentMessage->Msg.message && MsgFilterHigh >= CurrentMessage->Msg.message ) ) )
1797 {
1798 *Message = CurrentMessage->Msg;
1799
1800 if (Remove)
1801 {
1802 RemoveEntryList(&CurrentMessage->ListEntry);
1803 ClearMsgBitsMask(MessageQueue, CurrentMessage->QS_Flags);
1804 MsqDestroyMessage(CurrentMessage);
1805 }
1806 return(TRUE);
1807 }
1808 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1809 ListEntry);
1810 }
1811 while (CurrentEntry != ListHead);
1812
1813 return(FALSE);
1814 }
1815
1816 NTSTATUS FASTCALL
1817 co_MsqWaitForNewMessages(PUSER_MESSAGE_QUEUE MessageQueue, PWND WndFilter,
1818 UINT MsgFilterMin, UINT MsgFilterMax)
1819 {
1820 NTSTATUS ret;
1821 UserLeaveCo();
1822 ret = KeWaitForSingleObject( MessageQueue->NewMessages,
1823 UserRequest,
1824 UserMode,
1825 FALSE,
1826 NULL );
1827 UserEnterCo();
1828 return ret;
1829 }
1830
1831 BOOL FASTCALL
1832 MsqIsHung(PUSER_MESSAGE_QUEUE MessageQueue)
1833 {
1834 LARGE_INTEGER LargeTickCount;
1835
1836 KeQueryTickCount(&LargeTickCount);
1837 return ((LargeTickCount.u.LowPart - MessageQueue->LastMsgRead) > MSQ_HUNG);
1838 }
1839
1840 VOID
1841 CALLBACK
1842 HungAppSysTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
1843 {
1844 DoTheScreenSaver();
1845 TRACE("HungAppSysTimerProc\n");
1846 // Process list of windows that are hung and waiting.
1847 }
1848
1849 BOOLEAN FASTCALL
1850 MsqInitializeMessageQueue(struct _ETHREAD *Thread, PUSER_MESSAGE_QUEUE MessageQueue)
1851 {
1852 LARGE_INTEGER LargeTickCount;
1853 NTSTATUS Status;
1854
1855 MessageQueue->Thread = Thread;
1856 MessageQueue->CaretInfo = (PTHRDCARETINFO)(MessageQueue + 1);
1857 InitializeListHead(&MessageQueue->PostedMessagesListHead);
1858 InitializeListHead(&MessageQueue->SentMessagesListHead);
1859 InitializeListHead(&MessageQueue->HardwareMessagesListHead);
1860 InitializeListHead(&MessageQueue->DispatchingMessagesHead);
1861 InitializeListHead(&MessageQueue->LocalDispatchingMessagesHead);
1862 MessageQueue->QuitPosted = FALSE;
1863 MessageQueue->QuitExitCode = 0;
1864 KeQueryTickCount(&LargeTickCount);
1865 MessageQueue->LastMsgRead = LargeTickCount.u.LowPart;
1866 MessageQueue->spwndFocus = NULL;
1867 MessageQueue->NewMessagesHandle = NULL;
1868 MessageQueue->ShowingCursor = 0;
1869 MessageQueue->CursorObject = NULL;
1870 RtlCopyMemory(MessageQueue->afKeyState, gafAsyncKeyState, sizeof(gafAsyncKeyState));
1871
1872 Status = ZwCreateEvent(&MessageQueue->NewMessagesHandle, EVENT_ALL_ACCESS,
1873 NULL, SynchronizationEvent, FALSE);
1874 if (!NT_SUCCESS(Status))
1875 {
1876 return FALSE;
1877 }
1878
1879 Status = ObReferenceObjectByHandle(MessageQueue->NewMessagesHandle, 0,
1880 ExEventObjectType, KernelMode,
1881 (PVOID*)&MessageQueue->NewMessages, NULL);
1882 if (!NT_SUCCESS(Status))
1883 {
1884 ZwClose(MessageQueue->NewMessagesHandle);
1885 MessageQueue->NewMessagesHandle = NULL;
1886 return FALSE;
1887 }
1888
1889 return TRUE;
1890 }
1891
1892 VOID FASTCALL
1893 MsqCleanupMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
1894 {
1895 PLIST_ENTRY CurrentEntry;
1896 PUSER_MESSAGE CurrentMessage;
1897 PUSER_SENT_MESSAGE CurrentSentMessage;
1898 PTHREADINFO pti;
1899
1900 pti = MessageQueue->Thread->Tcb.Win32Thread;
1901
1902
1903 /* cleanup posted messages */
1904 while (!IsListEmpty(&MessageQueue->PostedMessagesListHead))
1905 {
1906 CurrentEntry = RemoveHeadList(&MessageQueue->PostedMessagesListHead);
1907 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1908 ListEntry);
1909 MsqDestroyMessage(CurrentMessage);
1910 }
1911
1912 /* remove the messages that have not yet been dispatched */
1913 while (!IsListEmpty(&MessageQueue->SentMessagesListHead))
1914 {
1915 CurrentEntry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
1916 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1917 ListEntry);
1918
1919 /* if it is a callback and this queue is not the sender queue, dereference queue */
1920 if ((CurrentSentMessage->CompletionCallback) && (CurrentSentMessage->CallBackSenderQueue != MessageQueue))
1921 {
1922 IntDereferenceMessageQueue(CurrentSentMessage->CallBackSenderQueue);
1923 }
1924
1925 TRACE("Notify the sender and remove a message from the queue that had not been dispatched\n");
1926 /* Only if the message has a sender was the message in the DispatchingList */
1927 if ((CurrentSentMessage->SenderQueue)
1928 && (CurrentSentMessage->DispatchingListEntry.Flink != NULL))
1929 {
1930 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
1931 }
1932
1933 /* wake the sender's thread */
1934 if (CurrentSentMessage->CompletionEvent != NULL)
1935 {
1936 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1937 }
1938
1939 if (CurrentSentMessage->HasPackedLParam == TRUE)
1940 {
1941 if (CurrentSentMessage->Msg.lParam)
1942 ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
1943 }
1944
1945 /* if the message has a sender */
1946 if (CurrentSentMessage->SenderQueue)
1947 {
1948 /* dereference our and the sender's message queue */
1949 IntDereferenceMessageQueue(MessageQueue);
1950 IntDereferenceMessageQueue(CurrentSentMessage->SenderQueue);
1951 }
1952
1953 /* free the message */
1954 ExFreePool(CurrentSentMessage);
1955 }
1956
1957 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
1958 ExitThread() was called in a SendMessage() umode callback */
1959 while (!IsListEmpty(&MessageQueue->LocalDispatchingMessagesHead))
1960 {
1961 CurrentEntry = RemoveHeadList(&MessageQueue->LocalDispatchingMessagesHead);
1962 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1963 ListEntry);
1964
1965 /* if it is a callback and this queue is not the sender queue, dereference queue */
1966 if ((CurrentSentMessage->CompletionCallback) && (CurrentSentMessage->CallBackSenderQueue != MessageQueue))
1967 {
1968 IntDereferenceMessageQueue(CurrentSentMessage->CallBackSenderQueue);
1969 }
1970
1971 /* remove the message from the dispatching list */
1972 if(CurrentSentMessage->DispatchingListEntry.Flink != NULL)
1973 {
1974 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
1975 }
1976
1977 TRACE("Notify the sender, the thread has been terminated while dispatching a message!\n");
1978
1979 /* wake the sender's thread */
1980 if (CurrentSentMessage->CompletionEvent != NULL)
1981 {
1982 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1983 }
1984
1985 if (CurrentSentMessage->HasPackedLParam == TRUE)
1986 {
1987 if (CurrentSentMessage->Msg.lParam)
1988 ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
1989 }
1990
1991 /* if the message has a sender */
1992 if (CurrentSentMessage->SenderQueue)
1993 {
1994 /* dereference our and the sender's message queue */
1995 IntDereferenceMessageQueue(MessageQueue);
1996 IntDereferenceMessageQueue(CurrentSentMessage->SenderQueue);
1997 }
1998
1999 /* free the message */
2000 ExFreePool(CurrentSentMessage);
2001 }
2002
2003 /* tell other threads not to bother returning any info to us */
2004 while (! IsListEmpty(&MessageQueue->DispatchingMessagesHead))
2005 {
2006 CurrentEntry = RemoveHeadList(&MessageQueue->DispatchingMessagesHead);
2007 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
2008 DispatchingListEntry);
2009 CurrentSentMessage->CompletionEvent = NULL;
2010 CurrentSentMessage->Result = NULL;
2011
2012 /* do NOT dereference our message queue as it might get attempted to be
2013 locked later */
2014 }
2015
2016 // Clear it all out.
2017 if(pti->pcti)
2018 {
2019 pti->pcti->fsWakeBits = 0;
2020 pti->pcti->fsChangeBits = 0;
2021 }
2022
2023 MessageQueue->nCntsQBits[QSRosKey] = 0;
2024 MessageQueue->nCntsQBits[QSRosMouseMove] = 0;
2025 MessageQueue->nCntsQBits[QSRosMouseButton] = 0;
2026 MessageQueue->nCntsQBits[QSRosPostMessage] = 0;
2027 MessageQueue->nCntsQBits[QSRosSendMessage] = 0;
2028 MessageQueue->nCntsQBits[QSRosHotKey] = 0;
2029
2030 if (MessageQueue->CursorObject)
2031 {
2032 PCURICON_OBJECT pCursor = MessageQueue->CursorObject;
2033
2034 /* Change to another cursor if we going to dereference current one
2035 Note: we can't use UserSetCursor because it uses current thread
2036 message queue instead of queue given for cleanup */
2037 if (IntGetSysCursorInfo()->CurrentCursorObject == pCursor)
2038 {
2039 HDC hdcScreen;
2040
2041 /* Get the screen DC */
2042 hdcScreen = IntGetScreenDC();
2043 if (hdcScreen)
2044 GreMovePointer(hdcScreen, -1, -1);
2045 IntGetSysCursorInfo()->CurrentCursorObject = NULL;
2046 }
2047
2048 UserDereferenceObject(pCursor);
2049 }
2050
2051 }
2052
2053 PUSER_MESSAGE_QUEUE FASTCALL
2054 MsqCreateMessageQueue(struct _ETHREAD *Thread)
2055 {
2056 PUSER_MESSAGE_QUEUE MessageQueue;
2057
2058 MessageQueue = (PUSER_MESSAGE_QUEUE)ExAllocatePoolWithTag(NonPagedPool,
2059 sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO),
2060 USERTAG_Q);
2061
2062 if (!MessageQueue)
2063 {
2064 return NULL;
2065 }
2066
2067 RtlZeroMemory(MessageQueue, sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO));
2068 /* hold at least one reference until it'll be destroyed */
2069 IntReferenceMessageQueue(MessageQueue);
2070 /* initialize the queue */
2071 if (!MsqInitializeMessageQueue(Thread, MessageQueue))
2072 {
2073 IntDereferenceMessageQueue(MessageQueue);
2074 return NULL;
2075 }
2076
2077 return MessageQueue;
2078 }
2079
2080 VOID FASTCALL
2081 MsqDestroyMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
2082 {
2083 PDESKTOP desk;
2084
2085 MessageQueue->QF_flags |= QF_INDESTROY;
2086
2087 /* remove the message queue from any desktops */
2088 if ((desk = InterlockedExchangePointer((PVOID*)&MessageQueue->Desktop, 0)))
2089 {
2090 (void)InterlockedExchangePointer((PVOID*)&desk->ActiveMessageQueue, 0);
2091 IntDereferenceMessageQueue(MessageQueue);
2092 }
2093
2094 /* clean it up */
2095 MsqCleanupMessageQueue(MessageQueue);
2096
2097 if (MessageQueue->NewMessagesHandle != NULL)
2098 ZwClose(MessageQueue->NewMessagesHandle);
2099 MessageQueue->NewMessagesHandle = NULL;
2100 /* decrease the reference counter, if it hits zero, the queue will be freed */
2101 IntDereferenceMessageQueue(MessageQueue);
2102 }
2103
2104 LPARAM FASTCALL
2105 MsqSetMessageExtraInfo(LPARAM lParam)
2106 {
2107 LPARAM Ret;
2108 PTHREADINFO pti;
2109 PUSER_MESSAGE_QUEUE MessageQueue;
2110
2111 pti = PsGetCurrentThreadWin32Thread();
2112 MessageQueue = pti->MessageQueue;
2113 if(!MessageQueue)
2114 {
2115 return 0;
2116 }
2117
2118 Ret = MessageQueue->ExtraInfo;
2119 MessageQueue->ExtraInfo = lParam;
2120
2121 return Ret;
2122 }
2123
2124 LPARAM FASTCALL
2125 MsqGetMessageExtraInfo(VOID)
2126 {
2127 PTHREADINFO pti;
2128 PUSER_MESSAGE_QUEUE MessageQueue;
2129
2130 pti = PsGetCurrentThreadWin32Thread();
2131 MessageQueue = pti->MessageQueue;
2132 if(!MessageQueue)
2133 {
2134 return 0;
2135 }
2136
2137 return MessageQueue->ExtraInfo;
2138 }
2139
2140 // ReplyMessage is called by the thread receiving the window message.
2141 BOOL FASTCALL
2142 co_MsqReplyMessage( LRESULT lResult )
2143 {
2144 PUSER_SENT_MESSAGE Message;
2145 PTHREADINFO pti;
2146
2147 pti = PsGetCurrentThreadWin32Thread();
2148 Message = pti->pusmCurrent;
2149
2150 if (!Message) return FALSE;
2151
2152 if (Message->QS_Flags & QS_SMRESULT) return FALSE;
2153
2154 // SendMessageXxx || Callback msg and not a notify msg
2155 if (Message->SenderQueue || Message->CompletionCallback)
2156 {
2157 Message->lResult = lResult;
2158 Message->QS_Flags |= QS_SMRESULT;
2159 // See co_MsqDispatchOneSentMessage, change bits already accounted for and cleared and this msg is going away..
2160 }
2161 return TRUE;
2162 }
2163
2164 HWND FASTCALL
2165 MsqSetStateWindow(PUSER_MESSAGE_QUEUE MessageQueue, ULONG Type, HWND hWnd)
2166 {
2167 HWND Prev;
2168
2169 switch(Type)
2170 {
2171 case MSQ_STATE_CAPTURE:
2172 Prev = MessageQueue->CaptureWindow;
2173 MessageQueue->CaptureWindow = hWnd;
2174 return Prev;
2175 case MSQ_STATE_ACTIVE:
2176 Prev = MessageQueue->spwndActive ? UserHMGetHandle(MessageQueue->spwndActive) : 0;
2177 MessageQueue->spwndActive = UserGetWindowObject(hWnd);
2178 return Prev;
2179 case MSQ_STATE_FOCUS:
2180 Prev = MessageQueue->spwndFocus ? UserHMGetHandle(MessageQueue->spwndFocus) : 0;
2181 MessageQueue->spwndFocus = UserGetWindowObject(hWnd);
2182 return Prev;
2183 case MSQ_STATE_MENUOWNER:
2184 Prev = MessageQueue->MenuOwner;
2185 MessageQueue->MenuOwner = hWnd;
2186 return Prev;
2187 case MSQ_STATE_MOVESIZE:
2188 Prev = MessageQueue->MoveSize;
2189 MessageQueue->MoveSize = hWnd;
2190 return Prev;
2191 case MSQ_STATE_CARET:
2192 ASSERT(MessageQueue->CaretInfo);
2193 Prev = MessageQueue->CaretInfo->hWnd;
2194 MessageQueue->CaretInfo->hWnd = hWnd;
2195 return Prev;
2196 }
2197
2198 return NULL;
2199 }
2200
2201 SHORT
2202 APIENTRY
2203 NtUserGetKeyState(INT key)
2204 {
2205 DWORD Ret;
2206
2207 UserEnterShared();
2208
2209 Ret = UserGetKeyState(key);
2210
2211 UserLeave();
2212
2213 return (SHORT)Ret;
2214 }
2215
2216
2217 DWORD
2218 APIENTRY
2219 NtUserGetKeyboardState(LPBYTE lpKeyState)
2220 {
2221 DWORD i, ret = TRUE;
2222 PTHREADINFO pti;
2223 PUSER_MESSAGE_QUEUE MessageQueue;
2224
2225 UserEnterShared();
2226
2227 pti = PsGetCurrentThreadWin32Thread();
2228 MessageQueue = pti->MessageQueue;
2229
2230 _SEH2_TRY
2231 {
2232 /* Probe and copy key state to an array */
2233 ProbeForWrite(lpKeyState, 256 * sizeof(BYTE), 1);
2234 for (i = 0; i < 256; ++i)
2235 {
2236 lpKeyState[i] = 0;
2237 if (IS_KEY_DOWN(MessageQueue->afKeyState, i))
2238 lpKeyState[i] |= KS_DOWN_BIT;
2239 if (IS_KEY_LOCKED(MessageQueue->afKeyState, i))
2240 lpKeyState[i] |= KS_LOCK_BIT;
2241 }
2242 }
2243 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2244 {
2245 SetLastNtError(_SEH2_GetExceptionCode());
2246 ret = FALSE;
2247 }
2248 _SEH2_END;
2249
2250 UserLeave();
2251
2252 return ret;
2253 }
2254
2255 BOOL
2256 APIENTRY
2257 NtUserSetKeyboardState(LPBYTE pKeyState)
2258 {
2259 UINT i;
2260 BOOL bRet = TRUE;
2261 PTHREADINFO pti;
2262 PUSER_MESSAGE_QUEUE MessageQueue;
2263
2264 UserEnterExclusive();
2265
2266 pti = PsGetCurrentThreadWin32Thread();
2267 MessageQueue = pti->MessageQueue;
2268
2269 _SEH2_TRY
2270 {
2271 ProbeForRead(pKeyState, 256 * sizeof(BYTE), 1);
2272 for (i = 0; i < 256; ++i)
2273 {
2274 SET_KEY_DOWN(MessageQueue->afKeyState, i, pKeyState[i] & KS_DOWN_BIT);
2275 SET_KEY_LOCKED(MessageQueue->afKeyState, i, pKeyState[i] & KS_LOCK_BIT);
2276 }
2277 }
2278 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2279 {
2280 SetLastNtError(_SEH2_GetExceptionCode());
2281 bRet = FALSE;
2282 }
2283 _SEH2_END;
2284
2285 UserLeave();
2286
2287 return bRet;
2288 }
2289
2290 /* EOF */