812c8d1ae6240f67c401798f2a6f20d71b351f10
[reactos.git] / 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 // Unless we are dying and need to tell our parents.
977 if (pti->TIF_flags & TIF_INCLEANUP && !(ptirec->TIF_flags & TIF_INCLEANUP))
978 {
979 // Parent notify is the big one. Fire and forget!
980 TRACE("Send message from dying thread %d\n",Msg);
981 co_MsqSendMessageAsync(ptirec, Wnd, Msg, wParam, lParam, NULL, 0, FALSE, HookMessage);
982 }
983 if (uResult) *uResult = -1;
984 TRACE("MsqSM: Msg %d Current pti %lu or Rec pti %lu\n", Msg, pti->TIF_flags & TIF_INCLEANUP, ptirec->TIF_flags & TIF_INCLEANUP);
985 return STATUS_UNSUCCESSFUL;
986 }
987
988 if ( HookMessage == MSQ_NORMAL )
989 {
990 pWnd = ValidateHwndNoErr(Wnd);
991
992 // These can not cross International Border lines!
993 if ( pti->ppi != ptirec->ppi && pWnd )
994 {
995 switch(Msg)
996 {
997 // Handle the special case when working with password transfers across bordering processes.
998 case EM_GETLINE:
999 case EM_SETPASSWORDCHAR:
1000 case WM_GETTEXT:
1001 // Look for edit controls setup for passwords.
1002 if ( gpsi->atomSysClass[ICLS_EDIT] == pWnd->pcls->atomClassName && // Use atomNVClassName.
1003 pWnd->style & ES_PASSWORD )
1004 {
1005 if (uResult) *uResult = -1;
1006 ERR("Running across the border without a passport!\n");
1007 EngSetLastError(ERROR_ACCESS_DENIED);
1008 return STATUS_UNSUCCESSFUL;
1009 }
1010 break;
1011 case WM_NOTIFY:
1012 if (uResult) *uResult = -1;
1013 ERR("Running across the border without a passport!\n");
1014 return STATUS_UNSUCCESSFUL;
1015 }
1016 }
1017
1018 // These can not cross State lines!
1019 if ( Msg == WM_CREATE || Msg == WM_NCCREATE )
1020 {
1021 if (uResult) *uResult = -1;
1022 ERR("Can not tell the other State we have Create!\n");
1023 return STATUS_UNSUCCESSFUL;
1024 }
1025 }
1026
1027 if(!(Message = ExAllocatePoolWithTag(PagedPool, sizeof(USER_SENT_MESSAGE), TAG_USRMSG)))
1028 {
1029 ERR("MsqSendMessage(): Not enough memory to allocate a message");
1030 return STATUS_INSUFFICIENT_RESOURCES;
1031 }
1032
1033 KeInitializeEvent(&CompletionEvent, NotificationEvent, FALSE);
1034
1035 Timeout.QuadPart = (LONGLONG) uTimeout * (LONGLONG) -10000;
1036
1037 /* FIXME: Increase reference counter of sender's message queue here */
1038
1039 Message->Msg.hwnd = Wnd;
1040 Message->Msg.message = Msg;
1041 Message->Msg.wParam = wParam;
1042 Message->Msg.lParam = lParam;
1043 Message->CompletionEvent = &CompletionEvent;
1044 Message->Result = &Result;
1045 Message->lResult = 0;
1046 Message->QS_Flags = 0;
1047 Message->ptiReceiver = ptirec;
1048 Message->ptiSender = pti;
1049 Message->ptiCallBackSender = NULL;
1050 Message->CompletionCallback = NULL;
1051 Message->CompletionCallbackContext = 0;
1052 Message->HookMessage = HookMessage;
1053 Message->HasPackedLParam = FALSE;
1054
1055 /* Add it to the list of pending messages */
1056 InsertTailList(&pti->DispatchingMessagesHead, &Message->DispatchingListEntry);
1057
1058 /* Queue it in the destination's message queue */
1059 InsertTailList(&ptirec->SentMessagesListHead, &Message->ListEntry);
1060
1061 Message->QS_Flags = QS_SENDMESSAGE;
1062 MsqWakeQueue(ptirec, QS_SENDMESSAGE, TRUE);
1063
1064 /* We can't access the Message anymore since it could have already been deleted! */
1065
1066 if(Block)
1067 {
1068 UserLeaveCo();
1069
1070 /* Don't process messages sent to the thread */
1071 WaitStatus = KeWaitForSingleObject(&CompletionEvent, UserRequest, UserMode,
1072 FALSE, (uTimeout ? &Timeout : NULL));
1073
1074 UserEnterCo();
1075
1076 if(WaitStatus == STATUS_TIMEOUT)
1077 {
1078 /* Look up if the message has not yet dispatched, if so
1079 make sure it can't pass a result and it must not set the completion event anymore */
1080 Entry = ptirec->SentMessagesListHead.Flink;
1081 while (Entry != &ptirec->SentMessagesListHead)
1082 {
1083 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry)
1084 == Message)
1085 {
1086 /* We can access Message here, it's secure because the message queue is locked
1087 and the message is still hasn't been dispatched */
1088 Message->CompletionEvent = NULL;
1089 Message->Result = NULL;
1090 break;
1091 }
1092 Entry = Entry->Flink;
1093 }
1094
1095 /* Remove from the local dispatching list so the other thread knows,
1096 it can't pass a result and it must not set the completion event anymore */
1097 Entry = pti->DispatchingMessagesHead.Flink;
1098 while (Entry != &pti->DispatchingMessagesHead)
1099 {
1100 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, DispatchingListEntry)
1101 == Message)
1102 {
1103 /* We can access Message here, it's secure because the sender's message is locked
1104 and the message has definitely not yet been destroyed, otherwise it would
1105 have been removed from this list by the dispatching routine right after
1106 dispatching the message */
1107 Message->CompletionEvent = NULL;
1108 Message->Result = NULL;
1109 RemoveEntryList(&Message->DispatchingListEntry);
1110 Message->DispatchingListEntry.Flink = NULL;
1111 break;
1112 }
1113 Entry = Entry->Flink;
1114 }
1115
1116 TRACE("MsqSendMessage (blocked) timed out 1\n");
1117 }
1118 while (co_MsqDispatchOneSentMessage(ptirec))
1119 ;
1120 }
1121 else
1122 {
1123 PVOID WaitObjects[3];
1124
1125 WaitObjects[0] = &CompletionEvent; // Wait 0
1126 WaitObjects[1] = pti->pEventQueueServer; // Wait 1
1127 WaitObjects[2] = ptirec->pEThread; // Wait 2
1128
1129 do
1130 {
1131 UserLeaveCo();
1132
1133 WaitStatus = KeWaitForMultipleObjects(3, WaitObjects, WaitAny, UserRequest,
1134 UserMode, FALSE, (uTimeout ? &Timeout : NULL), NULL);
1135
1136 UserEnterCo();
1137
1138 if(WaitStatus == STATUS_TIMEOUT)
1139 {
1140 /* Look up if the message has not yet been dispatched, if so
1141 make sure it can't pass a result and it must not set the completion event anymore */
1142 Entry = ptirec->SentMessagesListHead.Flink;
1143 while (Entry != &ptirec->SentMessagesListHead)
1144 {
1145 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry)
1146 == Message)
1147 {
1148 /* We can access Message here, it's secure because the message queue is locked
1149 and the message is still hasn't been dispatched */
1150 Message->CompletionEvent = NULL;
1151 Message->Result = NULL;
1152 break;
1153 }
1154 Entry = Entry->Flink;
1155 }
1156
1157 /* Remove from the local dispatching list so the other thread knows,
1158 it can't pass a result and it must not set the completion event anymore */
1159 Entry = pti->DispatchingMessagesHead.Flink;
1160 while (Entry != &pti->DispatchingMessagesHead)
1161 {
1162 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, DispatchingListEntry)
1163 == Message)
1164 {
1165 /* We can access Message here, it's secure because the sender's message is locked
1166 and the message has definitely not yet been destroyed, otherwise it would
1167 have been removed from this list by the dispatching routine right after
1168 dispatching the message */
1169 Message->CompletionEvent = NULL;
1170 Message->Result = NULL;
1171 RemoveEntryList(&Message->DispatchingListEntry);
1172 Message->DispatchingListEntry.Flink = NULL;
1173 break;
1174 }
1175 Entry = Entry->Flink;
1176 }
1177
1178 TRACE("MsqSendMessage timed out 2\n");
1179 break;
1180 }
1181 // Receiving thread passed on and left us hanging with issues still pending.
1182 if ( WaitStatus == STATUS_WAIT_2 )
1183 {
1184 ERR("Receiving Thread woken up dead!\n");
1185 Entry = pti->DispatchingMessagesHead.Flink;
1186 while (Entry != &pti->DispatchingMessagesHead)
1187 {
1188 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, DispatchingListEntry)
1189 == Message)
1190 {
1191 Message->CompletionEvent = NULL;
1192 Message->Result = NULL;
1193 RemoveEntryList(&Message->DispatchingListEntry);
1194 Message->DispatchingListEntry.Flink = NULL;
1195 break;
1196 }
1197 Entry = Entry->Flink;
1198 }
1199 }
1200 while (co_MsqDispatchOneSentMessage(pti))
1201 ;
1202 }
1203 while (NT_SUCCESS(WaitStatus) && WaitStatus == STATUS_WAIT_1);
1204 }
1205
1206 if(WaitStatus != STATUS_TIMEOUT)
1207 if (uResult) *uResult = (STATUS_WAIT_0 == WaitStatus ? Result : -1);
1208
1209 return WaitStatus;
1210 }
1211
1212 VOID FASTCALL
1213 MsqPostMessage(PTHREADINFO pti,
1214 MSG* Msg,
1215 BOOLEAN HardwareMessage,
1216 DWORD MessageBits,
1217 DWORD dwQEvent)
1218 {
1219 PUSER_MESSAGE Message;
1220 PUSER_MESSAGE_QUEUE MessageQueue;
1221
1222 if ( pti->TIF_flags & TIF_INCLEANUP || pti->MessageQueue->QF_flags & QF_INDESTROY )
1223 {
1224 ERR("Post Msg; Thread or Q is Dead!\n");
1225 return;
1226 }
1227
1228 if(!(Message = MsqCreateMessage(Msg)))
1229 {
1230 return;
1231 }
1232
1233 MessageQueue = pti->MessageQueue;
1234
1235 if (dwQEvent)
1236 {
1237 ERR("Post Msg; System Qeued Event Message!\n");
1238 InsertHeadList(&pti->PostedMessagesListHead,
1239 &Message->ListEntry);
1240 }
1241 else if (!HardwareMessage)
1242 {
1243 InsertTailList(&pti->PostedMessagesListHead,
1244 &Message->ListEntry);
1245 }
1246 else
1247 {
1248 InsertTailList(&MessageQueue->HardwareMessagesListHead,
1249 &Message->ListEntry);
1250 }
1251
1252 if (Msg->message == WM_HOTKEY) MessageBits |= QS_HOTKEY; // Justin Case, just set it.
1253 Message->dwQEvent = dwQEvent;
1254 Message->QS_Flags = MessageBits;
1255 Message->pti = pti;
1256 MsqWakeQueue(pti, MessageBits, TRUE);
1257 }
1258
1259 VOID FASTCALL
1260 MsqPostQuitMessage(PTHREADINFO pti, ULONG ExitCode)
1261 {
1262 pti->QuitPosted = TRUE;
1263 pti->exitCode = ExitCode;
1264 MsqWakeQueue(pti, QS_POSTMESSAGE|QS_ALLPOSTMESSAGE, TRUE);
1265 }
1266
1267 /***********************************************************************
1268 * MsqSendParentNotify
1269 *
1270 * Send a WM_PARENTNOTIFY to all ancestors of the given window, unless
1271 * the window has the WS_EX_NOPARENTNOTIFY style.
1272 */
1273 static void MsqSendParentNotify( PWND pwnd, WORD event, WORD idChild, POINT pt )
1274 {
1275 PWND pwndDesktop = UserGetDesktopWindow();
1276
1277 /* pt has to be in the client coordinates of the parent window */
1278 pt.x += pwndDesktop->rcClient.left - pwnd->rcClient.left;
1279 pt.y += pwndDesktop->rcClient.top - pwnd->rcClient.top;
1280
1281 for (;;)
1282 {
1283 PWND pwndParent;
1284
1285 if (!(pwnd->style & WS_CHILD)) break;
1286 if (pwnd->ExStyle & WS_EX_NOPARENTNOTIFY) break;
1287 if (!(pwndParent = IntGetParent(pwnd))) break;
1288 if (pwndParent == pwndDesktop) break;
1289 pt.x += pwnd->rcClient.left - pwndParent->rcClient.left;
1290 pt.y += pwnd->rcClient.top - pwndParent->rcClient.top;
1291
1292 pwnd = pwndParent;
1293 co_IntSendMessage( UserHMGetHandle(pwnd), WM_PARENTNOTIFY,
1294 MAKEWPARAM( event, idChild ), MAKELPARAM( pt.x, pt.y ) );
1295 }
1296 }
1297
1298 VOID
1299 FASTCALL
1300 IntTrackMouseMove(PWND pwndTrack, PDESKTOP pDesk, PMSG msg, USHORT hittest)
1301 {
1302 // PWND pwndTrack = IntChildrenWindowFromPoint(pwndMsg, msg->pt.x, msg->pt.y);
1303 // hittest = (USHORT)GetNCHitEx(pwndTrack, msg->pt); /// @todo WTF is this???
1304
1305 if ( pDesk->spwndTrack != pwndTrack || // Change with tracking window or
1306 msg->message != WM_MOUSEMOVE || // Mouse click changes or
1307 pDesk->htEx != hittest) // Change in current hit test states.
1308 {
1309 TRACE("ITMM: Track Mouse Move!\n");
1310
1311 /* Handle only the changing window track and mouse move across a border. */
1312 if ( pDesk->spwndTrack != pwndTrack ||
1313 (pDesk->htEx == HTCLIENT) ^ (hittest == HTCLIENT) )
1314 {
1315 TRACE("ITMM: Another Wnd %d or Across Border %d\n",
1316 pDesk->spwndTrack != pwndTrack,(pDesk->htEx == HTCLIENT) ^ (hittest == HTCLIENT));
1317
1318 if ( pDesk->dwDTFlags & DF_TME_LEAVE )
1319 UserPostMessage( UserHMGetHandle(pDesk->spwndTrack),
1320 (pDesk->htEx != HTCLIENT) ? WM_NCMOUSELEAVE : WM_MOUSELEAVE,
1321 0, 0);
1322
1323 if ( pDesk->dwDTFlags & DF_TME_HOVER )
1324 IntKillTimer(pDesk->spwndTrack, ID_EVENT_SYSTIMER_MOUSEHOVER, TRUE);
1325
1326 /* Clear the flags to sign a change. */
1327 pDesk->dwDTFlags &= ~(DF_TME_LEAVE|DF_TME_HOVER);
1328 }
1329 /* Set the Track window and hit test. */
1330 pDesk->spwndTrack = pwndTrack;
1331 pDesk->htEx = hittest;
1332 }
1333
1334 /* Reset, Same Track window, Hover set and Mouse Clicks or Clobbered Hover box. */
1335 if ( pDesk->spwndTrack == pwndTrack &&
1336 ( msg->message != WM_MOUSEMOVE || !RECTL_bPointInRect(&pDesk->rcMouseHover, msg->pt.x, msg->pt.y)) &&
1337 pDesk->dwDTFlags & DF_TME_HOVER )
1338 {
1339 TRACE("ITMM: Reset Hover points!\n");
1340 // Restart timer for the hover period.
1341 IntSetTimer(pDesk->spwndTrack, ID_EVENT_SYSTIMER_MOUSEHOVER, pDesk->dwMouseHoverTime, SystemTimerProc, TMRF_SYSTEM);
1342 // Reset desktop mouse hover from the system default hover rectangle.
1343 RECTL_vSetRect(&pDesk->rcMouseHover,
1344 msg->pt.x - gspv.iMouseHoverWidth / 2,
1345 msg->pt.y - gspv.iMouseHoverHeight / 2,
1346 msg->pt.x + gspv.iMouseHoverWidth / 2,
1347 msg->pt.y + gspv.iMouseHoverHeight / 2);
1348 }
1349 }
1350
1351 BOOL co_IntProcessMouseMessage(MSG* msg, BOOL* RemoveMessages, UINT first, UINT last)
1352 {
1353 MSG clk_msg;
1354 POINT pt;
1355 UINT message;
1356 USHORT hittest;
1357 EVENTMSG event;
1358 MOUSEHOOKSTRUCT hook;
1359 BOOL eatMsg = FALSE;
1360
1361 PWND pwndMsg, pwndDesktop;
1362 PUSER_MESSAGE_QUEUE MessageQueue;
1363 PTHREADINFO pti;
1364 PSYSTEM_CURSORINFO CurInfo;
1365 PDESKTOP pDesk;
1366 DECLARE_RETURN(BOOL);
1367
1368 pti = PsGetCurrentThreadWin32Thread();
1369 pwndDesktop = UserGetDesktopWindow();
1370 MessageQueue = pti->MessageQueue;
1371 CurInfo = IntGetSysCursorInfo();
1372 pwndMsg = ValidateHwndNoErr(msg->hwnd);
1373 clk_msg = MessageQueue->msgDblClk;
1374 pDesk = pwndDesktop->head.rpdesk;
1375
1376 /* find the window to dispatch this mouse message to */
1377 if (MessageQueue->spwndCapture)
1378 {
1379 hittest = HTCLIENT;
1380 pwndMsg = MessageQueue->spwndCapture;
1381 if (pwndMsg) UserReferenceObject(pwndMsg);
1382 }
1383 else
1384 {
1385 pwndMsg = co_WinPosWindowFromPoint(NULL, &msg->pt, &hittest, FALSE);//TRUE);
1386 }
1387
1388 TRACE("Got mouse message for %p, hittest: 0x%x\n", msg->hwnd, hittest);
1389
1390 if (pwndMsg == NULL || pwndMsg->head.pti != pti)
1391 {
1392 /* Remove and ignore the message */
1393 *RemoveMessages = TRUE;
1394 RETURN(FALSE);
1395 }
1396
1397 if ( MessageQueue == gpqCursor ) // Cursor must use the same Queue!
1398 {
1399 IntTrackMouseMove(pwndMsg, pDesk, msg, hittest);
1400 }
1401 else
1402 {
1403 ERR("Not the same cursor!\n");
1404 }
1405
1406 msg->hwnd = UserHMGetHandle(pwndMsg);
1407
1408 pt = msg->pt;
1409 message = msg->message;
1410 /* Note: windows has no concept of a non-client wheel message */
1411 if (message != WM_MOUSEWHEEL)
1412 {
1413 if (hittest != HTCLIENT)
1414 {
1415 message += WM_NCMOUSEMOVE - WM_MOUSEMOVE;
1416 msg->wParam = hittest;
1417 }
1418 else
1419 {
1420 /* coordinates don't get translated while tracking a menu */
1421 /* FIXME: should differentiate popups and top-level menus */
1422 if (!(MessageQueue->MenuOwner))
1423 {
1424 pt.x += pwndDesktop->rcClient.left - pwndMsg->rcClient.left;
1425 pt.y += pwndDesktop->rcClient.top - pwndMsg->rcClient.top;
1426 }
1427 }
1428 }
1429 msg->lParam = MAKELONG( pt.x, pt.y );
1430
1431 /* translate double clicks */
1432
1433 if ((msg->message == WM_LBUTTONDOWN) ||
1434 (msg->message == WM_RBUTTONDOWN) ||
1435 (msg->message == WM_MBUTTONDOWN) ||
1436 (msg->message == WM_XBUTTONDOWN))
1437 {
1438 BOOL update = *RemoveMessages;
1439
1440 /* translate double clicks -
1441 * note that ...MOUSEMOVEs can slip in between
1442 * ...BUTTONDOWN and ...BUTTONDBLCLK messages */
1443
1444 if ((MessageQueue->MenuOwner || MessageQueue->MoveSize) ||
1445 hittest != HTCLIENT ||
1446 (pwndMsg->pcls->style & CS_DBLCLKS))
1447 {
1448 if ((msg->message == clk_msg.message) &&
1449 (msg->hwnd == clk_msg.hwnd) &&
1450 (msg->wParam == clk_msg.wParam) &&
1451 ((msg->time - clk_msg.time) < (ULONG)gspv.iDblClickTime) &&
1452 (abs(msg->pt.x - clk_msg.pt.x) < UserGetSystemMetrics(SM_CXDOUBLECLK)/2) &&
1453 (abs(msg->pt.y - clk_msg.pt.y) < UserGetSystemMetrics(SM_CYDOUBLECLK)/2))
1454 {
1455 message += (WM_LBUTTONDBLCLK - WM_LBUTTONDOWN);
1456 if (update)
1457 {
1458 MessageQueue->msgDblClk.message = 0; /* clear the double click conditions */
1459 update = FALSE;
1460 }
1461 }
1462 }
1463
1464 if (!((first == 0 && last == 0) || (message >= first || message <= last)))
1465 {
1466 TRACE("Message out of range!!!\n");
1467 RETURN(FALSE);
1468 }
1469
1470 /* update static double click conditions */
1471 if (update) MessageQueue->msgDblClk = *msg;
1472 }
1473 else
1474 {
1475 if (!((first == 0 && last == 0) || (message >= first || message <= last)))
1476 {
1477 TRACE("Message out of range!!!\n");
1478 RETURN(FALSE);
1479 }
1480 }
1481
1482 if(gspv.bMouseClickLock)
1483 {
1484 BOOL IsClkLck = FALSE;
1485
1486 if(msg->message == WM_LBUTTONUP)
1487 {
1488 IsClkLck = ((msg->time - CurInfo->ClickLockTime) >= gspv.dwMouseClickLockTime);
1489 if (IsClkLck && (!CurInfo->ClickLockActive))
1490 {
1491 CurInfo->ClickLockActive = TRUE;
1492 }
1493 }
1494 else if (msg->message == WM_LBUTTONDOWN)
1495 {
1496 if (CurInfo->ClickLockActive)
1497 {
1498 IsClkLck = TRUE;
1499 CurInfo->ClickLockActive = FALSE;
1500 }
1501
1502 CurInfo->ClickLockTime = msg->time;
1503 }
1504
1505 if(IsClkLck)
1506 {
1507 /* Remove and ignore the message */
1508 *RemoveMessages = TRUE;
1509 RETURN(FALSE);
1510 }
1511 }
1512
1513 /* message is accepted now (but may still get dropped) */
1514
1515 event.message = msg->message;
1516 event.time = msg->time;
1517 event.hwnd = msg->hwnd;
1518 event.paramL = msg->pt.x;
1519 event.paramH = msg->pt.y;
1520 co_HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&event );
1521
1522 hook.pt = msg->pt;
1523 hook.hwnd = msg->hwnd;
1524 hook.wHitTestCode = hittest;
1525 hook.dwExtraInfo = 0 /* extra_info */ ;
1526 if (co_HOOK_CallHooks( WH_MOUSE, *RemoveMessages ? HC_ACTION : HC_NOREMOVE,
1527 message, (LPARAM)&hook ))
1528 {
1529 hook.pt = msg->pt;
1530 hook.hwnd = msg->hwnd;
1531 hook.wHitTestCode = hittest;
1532 hook.dwExtraInfo = 0 /* extra_info */ ;
1533 co_HOOK_CallHooks( WH_CBT, HCBT_CLICKSKIPPED, message, (LPARAM)&hook );
1534
1535 ERR("WH_MOUSE dropped mouse message!\n");
1536
1537 /* Remove and skip message */
1538 *RemoveMessages = TRUE;
1539 RETURN(FALSE);
1540 }
1541
1542 if ((hittest == HTERROR) || (hittest == HTNOWHERE))
1543 {
1544 co_IntSendMessage( msg->hwnd, WM_SETCURSOR, (WPARAM)msg->hwnd,
1545 MAKELONG( hittest, msg->message ));
1546
1547 /* Remove and skip message */
1548 *RemoveMessages = TRUE;
1549 RETURN(FALSE);
1550 }
1551
1552 if ((*RemoveMessages == FALSE) || MessageQueue->spwndCapture)
1553 {
1554 /* Accept the message */
1555 msg->message = message;
1556 RETURN(TRUE);
1557 }
1558
1559 if ((msg->message == WM_LBUTTONDOWN) ||
1560 (msg->message == WM_RBUTTONDOWN) ||
1561 (msg->message == WM_MBUTTONDOWN) ||
1562 (msg->message == WM_XBUTTONDOWN))
1563 {
1564 /* Send the WM_PARENTNOTIFY,
1565 * note that even for double/nonclient clicks
1566 * notification message is still WM_L/M/RBUTTONDOWN.
1567 */
1568 MsqSendParentNotify(pwndMsg, msg->message, 0, msg->pt );
1569
1570 /* Activate the window if needed */
1571
1572 if (pwndMsg != MessageQueue->spwndActive)
1573 {
1574 PWND pwndTop = pwndMsg;
1575 pwndTop = IntGetNonChildAncestor(pwndTop);
1576
1577 if (pwndTop && pwndTop != pwndDesktop)
1578 {
1579 LONG ret = co_IntSendMessage( msg->hwnd,
1580 WM_MOUSEACTIVATE,
1581 (WPARAM)UserHMGetHandle(pwndTop),
1582 MAKELONG( hittest, msg->message));
1583 switch(ret)
1584 {
1585 case MA_NOACTIVATEANDEAT:
1586 eatMsg = TRUE;
1587 /* fall through */
1588 case MA_NOACTIVATE:
1589 break;
1590 case MA_ACTIVATEANDEAT:
1591 eatMsg = TRUE;
1592 /* fall through */
1593 case MA_ACTIVATE:
1594 case 0:
1595 if (!co_IntMouseActivateWindow( pwndTop )) eatMsg = TRUE;
1596 break;
1597 default:
1598 ERR( "unknown WM_MOUSEACTIVATE code %d\n", ret );
1599 break;
1600 }
1601 }
1602 }
1603 }
1604
1605 /* send the WM_SETCURSOR message */
1606
1607 /* Windows sends the normal mouse message as the message parameter
1608 in the WM_SETCURSOR message even if it's non-client mouse message */
1609 co_IntSendMessage( msg->hwnd, WM_SETCURSOR, (WPARAM)msg->hwnd, MAKELONG( hittest, msg->message ));
1610
1611 msg->message = message;
1612 RETURN(!eatMsg);
1613
1614 CLEANUP:
1615 if(pwndMsg)
1616 UserDereferenceObject(pwndMsg);
1617
1618 END_CLEANUP;
1619 }
1620
1621 BOOL co_IntProcessKeyboardMessage(MSG* Msg, BOOL* RemoveMessages)
1622 {
1623 EVENTMSG Event;
1624
1625 if (Msg->message == WM_KEYDOWN || Msg->message == WM_SYSKEYDOWN ||
1626 Msg->message == WM_KEYUP || Msg->message == WM_SYSKEYUP)
1627 {
1628 switch (Msg->wParam)
1629 {
1630 case VK_LSHIFT: case VK_RSHIFT:
1631 Msg->wParam = VK_SHIFT;
1632 break;
1633 case VK_LCONTROL: case VK_RCONTROL:
1634 Msg->wParam = VK_CONTROL;
1635 break;
1636 case VK_LMENU: case VK_RMENU:
1637 Msg->wParam = VK_MENU;
1638 break;
1639 }
1640 }
1641
1642 Event.message = Msg->message;
1643 Event.hwnd = Msg->hwnd;
1644 Event.time = Msg->time;
1645 Event.paramL = (Msg->wParam & 0xFF) | (HIWORD(Msg->lParam) << 8);
1646 Event.paramH = Msg->lParam & 0x7FFF;
1647 if (HIWORD(Msg->lParam) & 0x0100) Event.paramH |= 0x8000;
1648 co_HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&Event);
1649
1650 if (co_HOOK_CallHooks( WH_KEYBOARD,
1651 *RemoveMessages ? HC_ACTION : HC_NOREMOVE,
1652 LOWORD(Msg->wParam),
1653 Msg->lParam))
1654 {
1655 /* skip this message */
1656 co_HOOK_CallHooks( WH_CBT,
1657 HCBT_KEYSKIPPED,
1658 LOWORD(Msg->wParam),
1659 Msg->lParam );
1660 ERR("KeyboardMessage WH_CBT Call Hook return!\n");
1661 return FALSE;
1662 }
1663 return TRUE;
1664 }
1665
1666 BOOL co_IntProcessHardwareMessage(MSG* Msg, BOOL* RemoveMessages, UINT first, UINT last)
1667 {
1668 if ( IS_MOUSE_MESSAGE(Msg->message))
1669 {
1670 return co_IntProcessMouseMessage(Msg, RemoveMessages, first, last);
1671 }
1672 else if ( IS_KBD_MESSAGE(Msg->message))
1673 {
1674 return co_IntProcessKeyboardMessage(Msg, RemoveMessages);
1675 }
1676
1677 return TRUE;
1678 }
1679
1680 BOOL APIENTRY
1681 co_MsqPeekMouseMove(IN PTHREADINFO pti,
1682 IN BOOL Remove,
1683 IN PWND Window,
1684 IN UINT MsgFilterLow,
1685 IN UINT MsgFilterHigh,
1686 OUT MSG* pMsg)
1687 {
1688 BOOL AcceptMessage;
1689 MSG msg;
1690 PUSER_MESSAGE_QUEUE MessageQueue = pti->MessageQueue;
1691
1692 if(!(MessageQueue->MouseMoved))
1693 return FALSE;
1694
1695 if (!MessageQueue->ptiSysLock)
1696 {
1697 MessageQueue->ptiSysLock = pti;
1698 pti->pcti->CTI_flags |= CTI_THREADSYSLOCK;
1699 }
1700
1701 if (MessageQueue->ptiSysLock != pti)
1702 {
1703 ERR("MsqPeekMouseMove: Thread Q is locked to another pti!\n");
1704 return FALSE;
1705 }
1706
1707 msg = MessageQueue->MouseMoveMsg;
1708
1709 AcceptMessage = co_IntProcessMouseMessage(&msg, &Remove, MsgFilterLow, MsgFilterHigh);
1710
1711 if(AcceptMessage)
1712 *pMsg = msg;
1713
1714 if(Remove)
1715 {
1716 ClearMsgBitsMask(pti, QS_MOUSEMOVE);
1717 MessageQueue->MouseMoved = FALSE;
1718 }
1719
1720 MessageQueue->ptiSysLock = NULL;
1721 pti->pcti->CTI_flags &= ~CTI_THREADSYSLOCK;
1722 return AcceptMessage;
1723 }
1724
1725 /* check whether a message filter contains at least one potential hardware message */
1726 static INT FASTCALL
1727 filter_contains_hw_range( UINT first, UINT last )
1728 {
1729 /* hardware message ranges are (in numerical order):
1730 * WM_NCMOUSEFIRST .. WM_NCMOUSELAST
1731 * WM_KEYFIRST .. WM_KEYLAST
1732 * WM_MOUSEFIRST .. WM_MOUSELAST
1733 */
1734 if (!last) --last;
1735 if (last < WM_NCMOUSEFIRST) return 0;
1736 if (first > WM_NCMOUSELAST && last < WM_KEYFIRST) return 0;
1737 if (first > WM_KEYLAST && last < WM_MOUSEFIRST) return 0;
1738 if (first > WM_MOUSELAST) return 0;
1739 return 1;
1740 }
1741
1742 BOOL APIENTRY
1743 co_MsqPeekHardwareMessage(IN PTHREADINFO pti,
1744 IN BOOL Remove,
1745 IN PWND Window,
1746 IN UINT MsgFilterLow,
1747 IN UINT MsgFilterHigh,
1748 IN UINT QSflags,
1749 OUT MSG* pMsg)
1750 {
1751
1752 BOOL AcceptMessage;
1753 PUSER_MESSAGE CurrentMessage;
1754 PLIST_ENTRY ListHead, CurrentEntry = NULL;
1755 MSG msg;
1756 BOOL Ret = FALSE;
1757 PUSER_MESSAGE_QUEUE MessageQueue = pti->MessageQueue;
1758
1759 if (!filter_contains_hw_range( MsgFilterLow, MsgFilterHigh )) return FALSE;
1760
1761 ListHead = &MessageQueue->HardwareMessagesListHead;
1762 CurrentEntry = ListHead->Flink;
1763
1764 if (IsListEmpty(CurrentEntry)) return FALSE;
1765
1766 if (!MessageQueue->ptiSysLock)
1767 {
1768 MessageQueue->ptiSysLock = pti;
1769 pti->pcti->CTI_flags |= CTI_THREADSYSLOCK;
1770 }
1771
1772 if (MessageQueue->ptiSysLock != pti)
1773 {
1774 ERR("MsqPeekHardwareMessage: Thread Q is locked to another pti!\n");
1775 return FALSE;
1776 }
1777
1778 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1779 ListEntry);
1780 do
1781 {
1782 if (IsListEmpty(CurrentEntry)) break;
1783 if (!CurrentMessage) break;
1784 CurrentEntry = CurrentMessage->ListEntry.Flink;
1785 if (!CurrentEntry) break; //// Fix CORE-6734 reported crash.
1786 /*
1787 MSDN:
1788 1: any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL.
1789 2: retrieves only messages on the current thread's message queue whose hwnd value is NULL.
1790 3: handle to the window whose messages are to be retrieved.
1791 */
1792 if ( ( !Window || // 1
1793 ( Window == PWND_BOTTOM && CurrentMessage->Msg.hwnd == NULL ) || // 2
1794 ( Window != PWND_BOTTOM && Window->head.h == CurrentMessage->Msg.hwnd ) ) && // 3
1795 ( ( ( MsgFilterLow == 0 && MsgFilterHigh == 0 ) && CurrentMessage->QS_Flags & QSflags ) ||
1796 ( MsgFilterLow <= CurrentMessage->Msg.message && MsgFilterHigh >= CurrentMessage->Msg.message ) ) )
1797 {
1798 msg = CurrentMessage->Msg;
1799
1800 UpdateKeyStateFromMsg(MessageQueue, &msg);
1801 AcceptMessage = co_IntProcessHardwareMessage(&msg, &Remove, MsgFilterLow, MsgFilterHigh);
1802
1803 if (Remove)
1804 {
1805 RemoveEntryList(&CurrentMessage->ListEntry);
1806 ClearMsgBitsMask(pti, CurrentMessage->QS_Flags);
1807 MsqDestroyMessage(CurrentMessage);
1808 }
1809
1810 if (AcceptMessage)
1811 {
1812 *pMsg = msg;
1813 Ret = TRUE;
1814 break;
1815 }
1816 }
1817 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, ListEntry);
1818 }
1819 while(CurrentEntry != ListHead);
1820
1821 MessageQueue->ptiSysLock = NULL;
1822 pti->pcti->CTI_flags &= ~CTI_THREADSYSLOCK;
1823 return Ret;
1824 }
1825
1826 BOOLEAN APIENTRY
1827 MsqPeekMessage(IN PTHREADINFO pti,
1828 IN BOOLEAN Remove,
1829 IN PWND Window,
1830 IN UINT MsgFilterLow,
1831 IN UINT MsgFilterHigh,
1832 IN UINT QSflags,
1833 OUT PMSG Message)
1834 {
1835 PLIST_ENTRY CurrentEntry;
1836 PUSER_MESSAGE CurrentMessage;
1837 PLIST_ENTRY ListHead;
1838 BOOL Ret = FALSE;
1839
1840 CurrentEntry = pti->PostedMessagesListHead.Flink;
1841 ListHead = &pti->PostedMessagesListHead;
1842
1843 if (IsListEmpty(CurrentEntry)) return FALSE;
1844
1845 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1846 ListEntry);
1847 do
1848 {
1849 if (IsListEmpty(CurrentEntry)) break;
1850 if (!CurrentMessage) break;
1851 CurrentEntry = CurrentEntry->Flink;
1852 /*
1853 MSDN:
1854 1: any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL.
1855 2: retrieves only messages on the current thread's message queue whose hwnd value is NULL.
1856 3: handle to the window whose messages are to be retrieved.
1857 */
1858 if ( ( !Window || // 1
1859 ( Window == PWND_BOTTOM && CurrentMessage->Msg.hwnd == NULL ) || // 2
1860 ( Window != PWND_BOTTOM && Window->head.h == CurrentMessage->Msg.hwnd ) ) && // 3
1861 ( ( ( MsgFilterLow == 0 && MsgFilterHigh == 0 ) && CurrentMessage->QS_Flags & QSflags ) ||
1862 ( MsgFilterLow <= CurrentMessage->Msg.message && MsgFilterHigh >= CurrentMessage->Msg.message ) ) )
1863 {
1864 *Message = CurrentMessage->Msg;
1865
1866 if (Remove)
1867 {
1868 RemoveEntryList(&CurrentMessage->ListEntry);
1869 ClearMsgBitsMask(pti, CurrentMessage->QS_Flags);
1870 MsqDestroyMessage(CurrentMessage);
1871 }
1872 Ret = TRUE;
1873 break;
1874 }
1875 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1876 ListEntry);
1877 }
1878 while (CurrentEntry != ListHead);
1879
1880 return Ret;
1881 }
1882
1883 NTSTATUS FASTCALL
1884 co_MsqWaitForNewMessages(PTHREADINFO pti, PWND WndFilter,
1885 UINT MsgFilterMin, UINT MsgFilterMax)
1886 {
1887 NTSTATUS ret;
1888 UserLeaveCo();
1889 ret = KeWaitForSingleObject( pti->pEventQueueServer,
1890 UserRequest,
1891 UserMode,
1892 FALSE,
1893 NULL );
1894 UserEnterCo();
1895 return ret;
1896 }
1897
1898 BOOL FASTCALL
1899 MsqIsHung(PTHREADINFO pti)
1900 {
1901 LARGE_INTEGER LargeTickCount;
1902
1903 KeQueryTickCount(&LargeTickCount);
1904 return ((LargeTickCount.u.LowPart - pti->timeLast) > MSQ_HUNG);
1905 }
1906
1907 VOID
1908 CALLBACK
1909 HungAppSysTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
1910 {
1911 DoTheScreenSaver();
1912 TRACE("HungAppSysTimerProc\n");
1913 // Process list of windows that are hung and waiting.
1914 }
1915
1916 BOOLEAN FASTCALL
1917 MsqInitializeMessageQueue(PTHREADINFO pti, PUSER_MESSAGE_QUEUE MessageQueue)
1918 {
1919 MessageQueue->CaretInfo = (PTHRDCARETINFO)(MessageQueue + 1);
1920 InitializeListHead(&MessageQueue->HardwareMessagesListHead); // Keep here!
1921 MessageQueue->spwndFocus = NULL;
1922 MessageQueue->iCursorLevel = 0;
1923 MessageQueue->CursorObject = NULL;
1924 RtlCopyMemory(MessageQueue->afKeyState, gafAsyncKeyState, sizeof(gafAsyncKeyState));
1925 MessageQueue->ptiMouse = pti;
1926 MessageQueue->ptiKeyboard = pti;
1927 MessageQueue->cThreads++;
1928
1929 return TRUE;
1930 }
1931
1932 VOID FASTCALL
1933 MsqCleanupThreadMsgs(PTHREADINFO pti)
1934 {
1935 PLIST_ENTRY CurrentEntry;
1936 PUSER_MESSAGE CurrentMessage;
1937 PUSER_SENT_MESSAGE CurrentSentMessage;
1938
1939 /* cleanup posted messages */
1940 while (!IsListEmpty(&pti->PostedMessagesListHead))
1941 {
1942 CurrentEntry = RemoveHeadList(&pti->PostedMessagesListHead);
1943 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1944 ListEntry);
1945 MsqDestroyMessage(CurrentMessage);
1946 }
1947
1948 /* remove the messages that have not yet been dispatched */
1949 while (!IsListEmpty(&pti->SentMessagesListHead))
1950 {
1951 CurrentEntry = RemoveHeadList(&pti->SentMessagesListHead);
1952 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1953 ListEntry);
1954
1955 TRACE("Notify the sender and remove a message from the queue that had not been dispatched\n");
1956 /* Only if the message has a sender was the message in the DispatchingList */
1957 if ((CurrentSentMessage->ptiSender)
1958 && (CurrentSentMessage->DispatchingListEntry.Flink != NULL))
1959 {
1960 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
1961 }
1962
1963 /* wake the sender's thread */
1964 if (CurrentSentMessage->CompletionEvent != NULL)
1965 {
1966 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1967 }
1968
1969 if (CurrentSentMessage->HasPackedLParam == TRUE)
1970 {
1971 if (CurrentSentMessage->Msg.lParam)
1972 ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
1973 }
1974
1975 /* free the message */
1976 ExFreePool(CurrentSentMessage);
1977 }
1978
1979 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
1980 ExitThread() was called in a SendMessage() umode callback */
1981 while (!IsListEmpty(&pti->LocalDispatchingMessagesHead))
1982 {
1983 CurrentEntry = RemoveHeadList(&pti->LocalDispatchingMessagesHead);
1984 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1985 ListEntry);
1986
1987 /* remove the message from the dispatching list */
1988 if(CurrentSentMessage->DispatchingListEntry.Flink != NULL)
1989 {
1990 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
1991 }
1992
1993 TRACE("Notify the sender, the thread has been terminated while dispatching a message!\n");
1994
1995 /* wake the sender's thread */
1996 if (CurrentSentMessage->CompletionEvent != NULL)
1997 {
1998 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1999 }
2000
2001 if (CurrentSentMessage->HasPackedLParam == TRUE)
2002 {
2003 if (CurrentSentMessage->Msg.lParam)
2004 ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
2005 }
2006
2007 /* free the message */
2008 ExFreePool(CurrentSentMessage);
2009 }
2010
2011 /* tell other threads not to bother returning any info to us */
2012 while (! IsListEmpty(&pti->DispatchingMessagesHead))
2013 {
2014 CurrentEntry = RemoveHeadList(&pti->DispatchingMessagesHead);
2015 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
2016 DispatchingListEntry);
2017 CurrentSentMessage->CompletionEvent = NULL;
2018 CurrentSentMessage->Result = NULL;
2019
2020 /* do NOT dereference our message queue as it might get attempted to be
2021 locked later */
2022 }
2023
2024 // Clear it all out.
2025 if (pti->pcti)
2026 {
2027 pti->pcti->fsWakeBits = 0;
2028 pti->pcti->fsChangeBits = 0;
2029 }
2030
2031 pti->nCntsQBits[QSRosKey] = 0;
2032 pti->nCntsQBits[QSRosMouseMove] = 0;
2033 pti->nCntsQBits[QSRosMouseButton] = 0;
2034 pti->nCntsQBits[QSRosPostMessage] = 0;
2035 pti->nCntsQBits[QSRosSendMessage] = 0;
2036 pti->nCntsQBits[QSRosHotKey] = 0;
2037 pti->nCntsQBits[QSRosEvent] = 0;
2038 }
2039
2040 VOID FASTCALL
2041 MsqCleanupMessageQueue(PTHREADINFO pti)
2042 {
2043 PUSER_MESSAGE_QUEUE MessageQueue;
2044
2045 MessageQueue = pti->MessageQueue;
2046 MessageQueue->cThreads--;
2047
2048 if (MessageQueue->cThreads)
2049 {
2050 if (MessageQueue->ptiSysLock == pti) MessageQueue->ptiSysLock = NULL;
2051 }
2052
2053 if (MessageQueue->CursorObject)
2054 {
2055 PCURICON_OBJECT pCursor = MessageQueue->CursorObject;
2056
2057 /* Change to another cursor if we going to dereference current one
2058 Note: we can't use UserSetCursor because it uses current thread
2059 message queue instead of queue given for cleanup */
2060 if (IntGetSysCursorInfo()->CurrentCursorObject == pCursor)
2061 {
2062 HDC hdcScreen;
2063
2064 /* Get the screen DC */
2065 hdcScreen = IntGetScreenDC();
2066 if (hdcScreen)
2067 GreMovePointer(hdcScreen, -1, -1);
2068 IntGetSysCursorInfo()->CurrentCursorObject = NULL;
2069 }
2070
2071 ERR("DereferenceObject pCursor\n");
2072 UserDereferenceObject(pCursor);
2073 }
2074
2075 if (gpqForeground == MessageQueue)
2076 {
2077 IntSetFocusMessageQueue(NULL);
2078 }
2079 if (gpqForegroundPrev == MessageQueue)
2080 {
2081 gpqForegroundPrev = NULL;
2082 }
2083 if (gpqCursor == MessageQueue)
2084 {
2085 gpqCursor = NULL;
2086 }
2087 }
2088
2089 PUSER_MESSAGE_QUEUE FASTCALL
2090 MsqCreateMessageQueue(PTHREADINFO pti)
2091 {
2092 PUSER_MESSAGE_QUEUE MessageQueue;
2093
2094 MessageQueue = (PUSER_MESSAGE_QUEUE)ExAllocatePoolWithTag(NonPagedPool,
2095 sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO),
2096 USERTAG_Q);
2097
2098 if (!MessageQueue)
2099 {
2100 return NULL;
2101 }
2102
2103 RtlZeroMemory(MessageQueue, sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO));
2104 /* hold at least one reference until it'll be destroyed */
2105 IntReferenceMessageQueue(MessageQueue);
2106 /* initialize the queue */
2107 if (!MsqInitializeMessageQueue(pti, MessageQueue))
2108 {
2109 IntDereferenceMessageQueue(MessageQueue);
2110 return NULL;
2111 }
2112
2113 return MessageQueue;
2114 }
2115
2116 VOID FASTCALL
2117 MsqDestroyMessageQueue(PTHREADINFO pti)
2118 {
2119 PDESKTOP desk;
2120 PUSER_MESSAGE_QUEUE MessageQueue = pti->MessageQueue;
2121
2122 MessageQueue->QF_flags |= QF_INDESTROY;
2123
2124 /* remove the message queue from any desktops */
2125 if ((desk = InterlockedExchangePointer((PVOID*)&MessageQueue->Desktop, 0)))
2126 {
2127 (void)InterlockedExchangePointer((PVOID*)&desk->ActiveMessageQueue, 0);
2128 IntDereferenceMessageQueue(MessageQueue);
2129 }
2130
2131 /* clean it up */
2132 MsqCleanupMessageQueue(pti);
2133
2134 /* decrease the reference counter, if it hits zero, the queue will be freed */
2135 IntDereferenceMessageQueue(MessageQueue);
2136 }
2137
2138 LPARAM FASTCALL
2139 MsqSetMessageExtraInfo(LPARAM lParam)
2140 {
2141 LPARAM Ret;
2142 PTHREADINFO pti;
2143 PUSER_MESSAGE_QUEUE MessageQueue;
2144
2145 pti = PsGetCurrentThreadWin32Thread();
2146 MessageQueue = pti->MessageQueue;
2147 if(!MessageQueue)
2148 {
2149 return 0;
2150 }
2151
2152 Ret = MessageQueue->ExtraInfo;
2153 MessageQueue->ExtraInfo = lParam;
2154
2155 return Ret;
2156 }
2157
2158 LPARAM FASTCALL
2159 MsqGetMessageExtraInfo(VOID)
2160 {
2161 PTHREADINFO pti;
2162 PUSER_MESSAGE_QUEUE MessageQueue;
2163
2164 pti = PsGetCurrentThreadWin32Thread();
2165 MessageQueue = pti->MessageQueue;
2166 if(!MessageQueue)
2167 {
2168 return 0;
2169 }
2170
2171 return MessageQueue->ExtraInfo;
2172 }
2173
2174 // ReplyMessage is called by the thread receiving the window message.
2175 BOOL FASTCALL
2176 co_MsqReplyMessage( LRESULT lResult )
2177 {
2178 PUSER_SENT_MESSAGE Message;
2179 PTHREADINFO pti;
2180
2181 pti = PsGetCurrentThreadWin32Thread();
2182 Message = pti->pusmCurrent;
2183
2184 if (!Message) return FALSE;
2185
2186 if (Message->QS_Flags & QS_SMRESULT) return FALSE;
2187
2188 // SendMessageXxx || Callback msg and not a notify msg
2189 if (Message->ptiSender || Message->CompletionCallback)
2190 {
2191 Message->lResult = lResult;
2192 Message->QS_Flags |= QS_SMRESULT;
2193 // See co_MsqDispatchOneSentMessage, change bits already accounted for and cleared and this msg is going away..
2194 }
2195 return TRUE;
2196 }
2197
2198 HWND FASTCALL
2199 MsqSetStateWindow(PTHREADINFO pti, ULONG Type, HWND hWnd)
2200 {
2201 HWND Prev;
2202 PUSER_MESSAGE_QUEUE MessageQueue;
2203
2204 MessageQueue = pti->MessageQueue;
2205
2206 switch(Type)
2207 {
2208 case MSQ_STATE_CAPTURE:
2209 Prev = MessageQueue->spwndCapture ? UserHMGetHandle(MessageQueue->spwndCapture) : 0;
2210 MessageQueue->spwndCapture = ValidateHwndNoErr(hWnd);
2211 return Prev;
2212 case MSQ_STATE_ACTIVE:
2213 Prev = MessageQueue->spwndActive ? UserHMGetHandle(MessageQueue->spwndActive) : 0;
2214 MessageQueue->spwndActive = ValidateHwndNoErr(hWnd);
2215 return Prev;
2216 case MSQ_STATE_FOCUS:
2217 Prev = MessageQueue->spwndFocus ? UserHMGetHandle(MessageQueue->spwndFocus) : 0;
2218 MessageQueue->spwndFocus = ValidateHwndNoErr(hWnd);
2219 return Prev;
2220 case MSQ_STATE_MENUOWNER:
2221 Prev = MessageQueue->MenuOwner;
2222 MessageQueue->MenuOwner = hWnd;
2223 return Prev;
2224 case MSQ_STATE_MOVESIZE:
2225 Prev = MessageQueue->MoveSize;
2226 MessageQueue->MoveSize = hWnd;
2227 return Prev;
2228 case MSQ_STATE_CARET:
2229 ASSERT(MessageQueue->CaretInfo);
2230 Prev = MessageQueue->CaretInfo->hWnd;
2231 MessageQueue->CaretInfo->hWnd = hWnd;
2232 return Prev;
2233 }
2234
2235 return NULL;
2236 }
2237
2238 SHORT
2239 APIENTRY
2240 NtUserGetKeyState(INT key)
2241 {
2242 DWORD Ret;
2243
2244 UserEnterShared();
2245
2246 Ret = UserGetKeyState(key);
2247
2248 UserLeave();
2249
2250 return (SHORT)Ret;
2251 }
2252
2253
2254 DWORD
2255 APIENTRY
2256 NtUserGetKeyboardState(LPBYTE lpKeyState)
2257 {
2258 DWORD i, ret = TRUE;
2259 PTHREADINFO pti;
2260 PUSER_MESSAGE_QUEUE MessageQueue;
2261
2262 UserEnterShared();
2263
2264 pti = PsGetCurrentThreadWin32Thread();
2265 MessageQueue = pti->MessageQueue;
2266
2267 _SEH2_TRY
2268 {
2269 /* Probe and copy key state to an array */
2270 ProbeForWrite(lpKeyState, 256 * sizeof(BYTE), 1);
2271 for (i = 0; i < 256; ++i)
2272 {
2273 lpKeyState[i] = 0;
2274 if (IS_KEY_DOWN(MessageQueue->afKeyState, i))
2275 lpKeyState[i] |= KS_DOWN_BIT;
2276 if (IS_KEY_LOCKED(MessageQueue->afKeyState, i))
2277 lpKeyState[i] |= KS_LOCK_BIT;
2278 }
2279 }
2280 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2281 {
2282 SetLastNtError(_SEH2_GetExceptionCode());
2283 ret = FALSE;
2284 }
2285 _SEH2_END;
2286
2287 UserLeave();
2288
2289 return ret;
2290 }
2291
2292 BOOL
2293 APIENTRY
2294 NtUserSetKeyboardState(LPBYTE pKeyState)
2295 {
2296 UINT i;
2297 BOOL bRet = TRUE;
2298 PTHREADINFO pti;
2299 PUSER_MESSAGE_QUEUE MessageQueue;
2300
2301 UserEnterExclusive();
2302
2303 pti = PsGetCurrentThreadWin32Thread();
2304 MessageQueue = pti->MessageQueue;
2305
2306 _SEH2_TRY
2307 {
2308 ProbeForRead(pKeyState, 256 * sizeof(BYTE), 1);
2309 for (i = 0; i < 256; ++i)
2310 {
2311 SET_KEY_DOWN(MessageQueue->afKeyState, i, pKeyState[i] & KS_DOWN_BIT);
2312 SET_KEY_LOCKED(MessageQueue->afKeyState, i, pKeyState[i] & KS_LOCK_BIT);
2313 }
2314 }
2315 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2316 {
2317 SetLastNtError(_SEH2_GetExceptionCode());
2318 bRet = FALSE;
2319 }
2320 _SEH2_END;
2321
2322 UserLeave();
2323
2324 return bRet;
2325 }
2326
2327 /* EOF */