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