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