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