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