[Win32k]
[reactos.git] / reactos / win32ss / user / ntuser / msgqueue.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Win32k subsystem
4 * PURPOSE: Message queues
5 * FILE: subsystems/win32/win32k/ntuser/msgqueue.c
6 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 Alexandre Julliard
8 Maarten Lankhorst
9 */
10
11 #include <win32k.h>
12 DBG_DEFAULT_CHANNEL(UserMsgQ);
13
14 /* GLOBALS *******************************************************************/
15
16 static PPAGED_LOOKASIDE_LIST pgMessageLookasideList;
17 PUSER_MESSAGE_QUEUE gpqCursor;
18
19 /* FUNCTIONS *****************************************************************/
20
21 INIT_FUNCTION
22 NTSTATUS
23 NTAPI
24 MsqInitializeImpl(VOID)
25 {
26 pgMessageLookasideList = ExAllocatePoolWithTag(NonPagedPool, sizeof(PAGED_LOOKASIDE_LIST), TAG_USRMSG);
27 if(!pgMessageLookasideList)
28 return STATUS_NO_MEMORY;
29 ExInitializePagedLookasideList(pgMessageLookasideList,
30 NULL,
31 NULL,
32 0,
33 sizeof(USER_MESSAGE),
34 TAG_USRMSG,
35 256);
36
37 return(STATUS_SUCCESS);
38 }
39
40 PWND FASTCALL
41 IntChildrenWindowFromPoint(PWND pWndTop, INT x, INT y)
42 {
43 PWND pWnd, pWndChild;
44
45 if ( !pWndTop )
46 {
47 pWndTop = UserGetDesktopWindow();
48 if ( !pWndTop ) return NULL;
49 }
50
51 if (!(pWndTop->style & WS_VISIBLE)) return NULL;
52 if ((pWndTop->style & WS_DISABLED)) return NULL;
53 if (!IntPtInWindow(pWndTop, x, y)) return NULL;
54
55 if (RECTL_bPointInRect(&pWndTop->rcClient, x, y))
56 {
57 for (pWnd = pWndTop->spwndChild;
58 pWnd != NULL;
59 pWnd = pWnd->spwndNext)
60 {
61 if (pWnd->state2 & WNDS2_INDESTROY || pWnd->state & WNDS_DESTROYED )
62 {
63 TRACE("The Window is in DESTROY!\n");
64 continue;
65 }
66
67 pWndChild = IntChildrenWindowFromPoint(pWnd, x, y);
68
69 if (pWndChild)
70 {
71 return pWndChild;
72 }
73 }
74 }
75 return pWndTop;
76 }
77
78 PWND FASTCALL
79 IntTopLevelWindowFromPoint(INT x, INT y)
80 {
81 PWND pWnd, pwndDesktop;
82
83 /* Get the desktop window */
84 pwndDesktop = UserGetDesktopWindow();
85 if (!pwndDesktop)
86 return NULL;
87
88 /* Loop all top level windows */
89 for (pWnd = pwndDesktop->spwndChild;
90 pWnd != NULL;
91 pWnd = pWnd->spwndNext)
92 {
93 if (pWnd->state2 & WNDS2_INDESTROY || pWnd->state & WNDS_DESTROYED)
94 {
95 TRACE("The Window is in DESTROY!\n");
96 continue;
97 }
98
99 if ((pWnd->style & WS_VISIBLE) && IntPtInWindow(pWnd, x, y))
100 return pWnd;
101 }
102
103 /* Window has not been found */
104 return pwndDesktop;
105 }
106
107 PCURICON_OBJECT
108 FASTCALL
109 UserSetCursor(
110 PCURICON_OBJECT NewCursor,
111 BOOL ForceChange)
112 {
113 PCURICON_OBJECT OldCursor;
114 HDC hdcScreen;
115 PTHREADINFO pti;
116 PUSER_MESSAGE_QUEUE MessageQueue;
117 PWND pWnd;
118
119 pti = PsGetCurrentThreadWin32Thread();
120 MessageQueue = pti->MessageQueue;
121
122 /* Get the screen DC */
123 if(!(hdcScreen = IntGetScreenDC()))
124 {
125 return NULL;
126 }
127
128 OldCursor = MessageQueue->CursorObject;
129
130 /* Check if cursors are different */
131 if (OldCursor == NewCursor)
132 return OldCursor;
133
134 /* Update cursor for this message queue */
135 MessageQueue->CursorObject = NewCursor;
136
137 /* If cursor is not visible we have nothing to do */
138 if (MessageQueue->iCursorLevel < 0)
139 return OldCursor;
140
141 /* Update cursor if this message queue controls it */
142 pWnd = IntTopLevelWindowFromPoint(gpsi->ptCursor.x, gpsi->ptCursor.y);
143 if (pWnd && pWnd->head.pti->MessageQueue == MessageQueue)
144 {
145 if (NewCursor)
146 {
147 /* Call GDI to set the new screen cursor */
148 #ifdef NEW_CURSORICON
149 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 /*
1779 MSDN:
1780 1: any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL.
1781 2: retrieves only messages on the current thread's message queue whose hwnd value is NULL.
1782 3: handle to the window whose messages are to be retrieved.
1783 */
1784 if ( ( !Window || // 1
1785 ( Window == PWND_BOTTOM && CurrentMessage->Msg.hwnd == NULL ) || // 2
1786 ( Window != PWND_BOTTOM && Window->head.h == CurrentMessage->Msg.hwnd ) ) && // 3
1787 ( ( ( MsgFilterLow == 0 && MsgFilterHigh == 0 ) && CurrentMessage->QS_Flags & QSflags ) ||
1788 ( MsgFilterLow <= CurrentMessage->Msg.message && MsgFilterHigh >= CurrentMessage->Msg.message ) ) )
1789 {
1790 msg = CurrentMessage->Msg;
1791
1792 UpdateKeyStateFromMsg(MessageQueue, &msg);
1793 AcceptMessage = co_IntProcessHardwareMessage(&msg, &Remove, MsgFilterLow, MsgFilterHigh);
1794
1795 if (Remove)
1796 {
1797 RemoveEntryList(&CurrentMessage->ListEntry);
1798 ClearMsgBitsMask(pti, CurrentMessage->QS_Flags);
1799 MsqDestroyMessage(CurrentMessage);
1800 }
1801
1802 if (AcceptMessage)
1803 {
1804 *pMsg = msg;
1805 Ret = TRUE;
1806 break;
1807 }
1808 }
1809 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1810 ListEntry);
1811 }
1812 while(CurrentEntry != ListHead);
1813
1814 MessageQueue->ptiSysLock = NULL;
1815 pti->pcti->CTI_flags &= ~CTI_THREADSYSLOCK;
1816 return Ret;
1817 }
1818
1819 BOOLEAN APIENTRY
1820 MsqPeekMessage(IN PTHREADINFO pti,
1821 IN BOOLEAN Remove,
1822 IN PWND Window,
1823 IN UINT MsgFilterLow,
1824 IN UINT MsgFilterHigh,
1825 IN UINT QSflags,
1826 OUT PMSG Message)
1827 {
1828 PLIST_ENTRY CurrentEntry;
1829 PUSER_MESSAGE CurrentMessage;
1830 PLIST_ENTRY ListHead;
1831 BOOL Ret = FALSE;
1832
1833 CurrentEntry = pti->PostedMessagesListHead.Flink;
1834 ListHead = &pti->PostedMessagesListHead;
1835
1836 if (IsListEmpty(CurrentEntry)) return FALSE;
1837
1838 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1839 ListEntry);
1840 do
1841 {
1842 if (IsListEmpty(CurrentEntry)) break;
1843 if (!CurrentMessage) break;
1844 CurrentEntry = CurrentEntry->Flink;
1845 /*
1846 MSDN:
1847 1: any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL.
1848 2: retrieves only messages on the current thread's message queue whose hwnd value is NULL.
1849 3: handle to the window whose messages are to be retrieved.
1850 */
1851 if ( ( !Window || // 1
1852 ( Window == PWND_BOTTOM && CurrentMessage->Msg.hwnd == NULL ) || // 2
1853 ( Window != PWND_BOTTOM && Window->head.h == CurrentMessage->Msg.hwnd ) ) && // 3
1854 ( ( ( MsgFilterLow == 0 && MsgFilterHigh == 0 ) && CurrentMessage->QS_Flags & QSflags ) ||
1855 ( MsgFilterLow <= CurrentMessage->Msg.message && MsgFilterHigh >= CurrentMessage->Msg.message ) ) )
1856 {
1857 *Message = CurrentMessage->Msg;
1858
1859 if (Remove)
1860 {
1861 RemoveEntryList(&CurrentMessage->ListEntry);
1862 ClearMsgBitsMask(pti, CurrentMessage->QS_Flags);
1863 MsqDestroyMessage(CurrentMessage);
1864 }
1865 Ret = TRUE;
1866 break;
1867 }
1868 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1869 ListEntry);
1870 }
1871 while (CurrentEntry != ListHead);
1872
1873 return Ret;
1874 }
1875
1876 NTSTATUS FASTCALL
1877 co_MsqWaitForNewMessages(PTHREADINFO pti, PWND WndFilter,
1878 UINT MsgFilterMin, UINT MsgFilterMax)
1879 {
1880 NTSTATUS ret;
1881 UserLeaveCo();
1882 ret = KeWaitForSingleObject( pti->pEventQueueServer,
1883 UserRequest,
1884 UserMode,
1885 FALSE,
1886 NULL );
1887 UserEnterCo();
1888 return ret;
1889 }
1890
1891 BOOL FASTCALL
1892 MsqIsHung(PTHREADINFO pti)
1893 {
1894 LARGE_INTEGER LargeTickCount;
1895
1896 KeQueryTickCount(&LargeTickCount);
1897 return ((LargeTickCount.u.LowPart - pti->timeLast) > MSQ_HUNG);
1898 }
1899
1900 VOID
1901 CALLBACK
1902 HungAppSysTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
1903 {
1904 DoTheScreenSaver();
1905 TRACE("HungAppSysTimerProc\n");
1906 // Process list of windows that are hung and waiting.
1907 }
1908
1909 BOOLEAN FASTCALL
1910 MsqInitializeMessageQueue(PTHREADINFO pti, PUSER_MESSAGE_QUEUE MessageQueue)
1911 {
1912 MessageQueue->CaretInfo = (PTHRDCARETINFO)(MessageQueue + 1);
1913 InitializeListHead(&MessageQueue->HardwareMessagesListHead); // Keep here!
1914 MessageQueue->spwndFocus = NULL;
1915 MessageQueue->iCursorLevel = 0;
1916 MessageQueue->CursorObject = NULL;
1917 RtlCopyMemory(MessageQueue->afKeyState, gafAsyncKeyState, sizeof(gafAsyncKeyState));
1918 MessageQueue->ptiMouse = pti;
1919 MessageQueue->ptiKeyboard = pti;
1920 MessageQueue->cThreads++;
1921
1922 return TRUE;
1923 }
1924
1925 VOID FASTCALL
1926 MsqCleanupThreadMsgs(PTHREADINFO pti)
1927 {
1928 PLIST_ENTRY CurrentEntry;
1929 PUSER_MESSAGE CurrentMessage;
1930 PUSER_SENT_MESSAGE CurrentSentMessage;
1931
1932 /* cleanup posted messages */
1933 while (!IsListEmpty(&pti->PostedMessagesListHead))
1934 {
1935 CurrentEntry = RemoveHeadList(&pti->PostedMessagesListHead);
1936 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1937 ListEntry);
1938 MsqDestroyMessage(CurrentMessage);
1939 }
1940
1941 /* remove the messages that have not yet been dispatched */
1942 while (!IsListEmpty(&pti->SentMessagesListHead))
1943 {
1944 CurrentEntry = RemoveHeadList(&pti->SentMessagesListHead);
1945 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1946 ListEntry);
1947
1948 TRACE("Notify the sender and remove a message from the queue that had not been dispatched\n");
1949 /* Only if the message has a sender was the message in the DispatchingList */
1950 if ((CurrentSentMessage->ptiSender)
1951 && (CurrentSentMessage->DispatchingListEntry.Flink != NULL))
1952 {
1953 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
1954 }
1955
1956 /* wake the sender's thread */
1957 if (CurrentSentMessage->CompletionEvent != NULL)
1958 {
1959 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1960 }
1961
1962 if (CurrentSentMessage->HasPackedLParam == TRUE)
1963 {
1964 if (CurrentSentMessage->Msg.lParam)
1965 ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
1966 }
1967
1968 /* free the message */
1969 ExFreePool(CurrentSentMessage);
1970 }
1971
1972 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
1973 ExitThread() was called in a SendMessage() umode callback */
1974 while (!IsListEmpty(&pti->LocalDispatchingMessagesHead))
1975 {
1976 CurrentEntry = RemoveHeadList(&pti->LocalDispatchingMessagesHead);
1977 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1978 ListEntry);
1979
1980 /* remove the message from the dispatching list */
1981 if(CurrentSentMessage->DispatchingListEntry.Flink != NULL)
1982 {
1983 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
1984 }
1985
1986 TRACE("Notify the sender, the thread has been terminated while dispatching a message!\n");
1987
1988 /* wake the sender's thread */
1989 if (CurrentSentMessage->CompletionEvent != NULL)
1990 {
1991 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1992 }
1993
1994 if (CurrentSentMessage->HasPackedLParam == TRUE)
1995 {
1996 if (CurrentSentMessage->Msg.lParam)
1997 ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
1998 }
1999
2000 /* free the message */
2001 ExFreePool(CurrentSentMessage);
2002 }
2003
2004 /* tell other threads not to bother returning any info to us */
2005 while (! IsListEmpty(&pti->DispatchingMessagesHead))
2006 {
2007 CurrentEntry = RemoveHeadList(&pti->DispatchingMessagesHead);
2008 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
2009 DispatchingListEntry);
2010 CurrentSentMessage->CompletionEvent = NULL;
2011 CurrentSentMessage->Result = NULL;
2012
2013 /* do NOT dereference our message queue as it might get attempted to be
2014 locked later */
2015 }
2016
2017 // Clear it all out.
2018 if (pti->pcti)
2019 {
2020 pti->pcti->fsWakeBits = 0;
2021 pti->pcti->fsChangeBits = 0;
2022 }
2023
2024 pti->nCntsQBits[QSRosKey] = 0;
2025 pti->nCntsQBits[QSRosMouseMove] = 0;
2026 pti->nCntsQBits[QSRosMouseButton] = 0;
2027 pti->nCntsQBits[QSRosPostMessage] = 0;
2028 pti->nCntsQBits[QSRosSendMessage] = 0;
2029 pti->nCntsQBits[QSRosHotKey] = 0;
2030 pti->nCntsQBits[QSRosEvent] = 0;
2031 }
2032
2033 VOID FASTCALL
2034 MsqCleanupMessageQueue(PTHREADINFO pti)
2035 {
2036 PUSER_MESSAGE_QUEUE MessageQueue;
2037
2038 MessageQueue = pti->MessageQueue;
2039 MessageQueue->cThreads--;
2040
2041 if (MessageQueue->cThreads)
2042 {
2043 if (MessageQueue->ptiSysLock == pti) MessageQueue->ptiSysLock = NULL;
2044 }
2045
2046 if (MessageQueue->CursorObject)
2047 {
2048 PCURICON_OBJECT pCursor = MessageQueue->CursorObject;
2049
2050 /* Change to another cursor if we going to dereference current one
2051 Note: we can't use UserSetCursor because it uses current thread
2052 message queue instead of queue given for cleanup */
2053 if (IntGetSysCursorInfo()->CurrentCursorObject == pCursor)
2054 {
2055 HDC hdcScreen;
2056
2057 /* Get the screen DC */
2058 hdcScreen = IntGetScreenDC();
2059 if (hdcScreen)
2060 GreMovePointer(hdcScreen, -1, -1);
2061 IntGetSysCursorInfo()->CurrentCursorObject = NULL;
2062 }
2063
2064 ERR("DereferenceObject pCursor\n");
2065 UserDereferenceObject(pCursor);
2066 }
2067
2068 if (gpqForeground == MessageQueue)
2069 {
2070 IntSetFocusMessageQueue(NULL);
2071 }
2072 if (gpqForegroundPrev == MessageQueue)
2073 {
2074 gpqForegroundPrev = NULL;
2075 }
2076 if (gpqCursor == MessageQueue)
2077 {
2078 gpqCursor = NULL;
2079 }
2080 }
2081
2082 PUSER_MESSAGE_QUEUE FASTCALL
2083 MsqCreateMessageQueue(PTHREADINFO pti)
2084 {
2085 PUSER_MESSAGE_QUEUE MessageQueue;
2086
2087 MessageQueue = (PUSER_MESSAGE_QUEUE)ExAllocatePoolWithTag(NonPagedPool,
2088 sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO),
2089 USERTAG_Q);
2090
2091 if (!MessageQueue)
2092 {
2093 return NULL;
2094 }
2095
2096 RtlZeroMemory(MessageQueue, sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO));
2097 /* hold at least one reference until it'll be destroyed */
2098 IntReferenceMessageQueue(MessageQueue);
2099 /* initialize the queue */
2100 if (!MsqInitializeMessageQueue(pti, MessageQueue))
2101 {
2102 IntDereferenceMessageQueue(MessageQueue);
2103 return NULL;
2104 }
2105
2106 return MessageQueue;
2107 }
2108
2109 VOID FASTCALL
2110 MsqDestroyMessageQueue(PTHREADINFO pti)
2111 {
2112 PDESKTOP desk;
2113 PUSER_MESSAGE_QUEUE MessageQueue = pti->MessageQueue;
2114
2115 MessageQueue->QF_flags |= QF_INDESTROY;
2116
2117 /* remove the message queue from any desktops */
2118 if ((desk = InterlockedExchangePointer((PVOID*)&MessageQueue->Desktop, 0)))
2119 {
2120 (void)InterlockedExchangePointer((PVOID*)&desk->ActiveMessageQueue, 0);
2121 IntDereferenceMessageQueue(MessageQueue);
2122 }
2123
2124 /* clean it up */
2125 MsqCleanupMessageQueue(pti);
2126
2127 /* decrease the reference counter, if it hits zero, the queue will be freed */
2128 IntDereferenceMessageQueue(MessageQueue);
2129 }
2130
2131 LPARAM FASTCALL
2132 MsqSetMessageExtraInfo(LPARAM lParam)
2133 {
2134 LPARAM Ret;
2135 PTHREADINFO pti;
2136 PUSER_MESSAGE_QUEUE MessageQueue;
2137
2138 pti = PsGetCurrentThreadWin32Thread();
2139 MessageQueue = pti->MessageQueue;
2140 if(!MessageQueue)
2141 {
2142 return 0;
2143 }
2144
2145 Ret = MessageQueue->ExtraInfo;
2146 MessageQueue->ExtraInfo = lParam;
2147
2148 return Ret;
2149 }
2150
2151 LPARAM FASTCALL
2152 MsqGetMessageExtraInfo(VOID)
2153 {
2154 PTHREADINFO pti;
2155 PUSER_MESSAGE_QUEUE MessageQueue;
2156
2157 pti = PsGetCurrentThreadWin32Thread();
2158 MessageQueue = pti->MessageQueue;
2159 if(!MessageQueue)
2160 {
2161 return 0;
2162 }
2163
2164 return MessageQueue->ExtraInfo;
2165 }
2166
2167 // ReplyMessage is called by the thread receiving the window message.
2168 BOOL FASTCALL
2169 co_MsqReplyMessage( LRESULT lResult )
2170 {
2171 PUSER_SENT_MESSAGE Message;
2172 PTHREADINFO pti;
2173
2174 pti = PsGetCurrentThreadWin32Thread();
2175 Message = pti->pusmCurrent;
2176
2177 if (!Message) return FALSE;
2178
2179 if (Message->QS_Flags & QS_SMRESULT) return FALSE;
2180
2181 // SendMessageXxx || Callback msg and not a notify msg
2182 if (Message->ptiSender || Message->CompletionCallback)
2183 {
2184 Message->lResult = lResult;
2185 Message->QS_Flags |= QS_SMRESULT;
2186 // See co_MsqDispatchOneSentMessage, change bits already accounted for and cleared and this msg is going away..
2187 }
2188 return TRUE;
2189 }
2190
2191 HWND FASTCALL
2192 MsqSetStateWindow(PTHREADINFO pti, ULONG Type, HWND hWnd)
2193 {
2194 HWND Prev;
2195 PUSER_MESSAGE_QUEUE MessageQueue;
2196
2197 MessageQueue = pti->MessageQueue;
2198
2199 switch(Type)
2200 {
2201 case MSQ_STATE_CAPTURE:
2202 Prev = MessageQueue->spwndCapture ? UserHMGetHandle(MessageQueue->spwndCapture) : 0;
2203 MessageQueue->spwndCapture = ValidateHwndNoErr(hWnd);
2204 return Prev;
2205 case MSQ_STATE_ACTIVE:
2206 Prev = MessageQueue->spwndActive ? UserHMGetHandle(MessageQueue->spwndActive) : 0;
2207 MessageQueue->spwndActive = ValidateHwndNoErr(hWnd);
2208 return Prev;
2209 case MSQ_STATE_FOCUS:
2210 Prev = MessageQueue->spwndFocus ? UserHMGetHandle(MessageQueue->spwndFocus) : 0;
2211 MessageQueue->spwndFocus = ValidateHwndNoErr(hWnd);
2212 return Prev;
2213 case MSQ_STATE_MENUOWNER:
2214 Prev = MessageQueue->MenuOwner;
2215 MessageQueue->MenuOwner = hWnd;
2216 return Prev;
2217 case MSQ_STATE_MOVESIZE:
2218 Prev = MessageQueue->MoveSize;
2219 MessageQueue->MoveSize = hWnd;
2220 return Prev;
2221 case MSQ_STATE_CARET:
2222 ASSERT(MessageQueue->CaretInfo);
2223 Prev = MessageQueue->CaretInfo->hWnd;
2224 MessageQueue->CaretInfo->hWnd = hWnd;
2225 return Prev;
2226 }
2227
2228 return NULL;
2229 }
2230
2231 SHORT
2232 APIENTRY
2233 NtUserGetKeyState(INT key)
2234 {
2235 DWORD Ret;
2236
2237 UserEnterShared();
2238
2239 Ret = UserGetKeyState(key);
2240
2241 UserLeave();
2242
2243 return (SHORT)Ret;
2244 }
2245
2246
2247 DWORD
2248 APIENTRY
2249 NtUserGetKeyboardState(LPBYTE lpKeyState)
2250 {
2251 DWORD i, ret = TRUE;
2252 PTHREADINFO pti;
2253 PUSER_MESSAGE_QUEUE MessageQueue;
2254
2255 UserEnterShared();
2256
2257 pti = PsGetCurrentThreadWin32Thread();
2258 MessageQueue = pti->MessageQueue;
2259
2260 _SEH2_TRY
2261 {
2262 /* Probe and copy key state to an array */
2263 ProbeForWrite(lpKeyState, 256 * sizeof(BYTE), 1);
2264 for (i = 0; i < 256; ++i)
2265 {
2266 lpKeyState[i] = 0;
2267 if (IS_KEY_DOWN(MessageQueue->afKeyState, i))
2268 lpKeyState[i] |= KS_DOWN_BIT;
2269 if (IS_KEY_LOCKED(MessageQueue->afKeyState, i))
2270 lpKeyState[i] |= KS_LOCK_BIT;
2271 }
2272 }
2273 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2274 {
2275 SetLastNtError(_SEH2_GetExceptionCode());
2276 ret = FALSE;
2277 }
2278 _SEH2_END;
2279
2280 UserLeave();
2281
2282 return ret;
2283 }
2284
2285 BOOL
2286 APIENTRY
2287 NtUserSetKeyboardState(LPBYTE pKeyState)
2288 {
2289 UINT i;
2290 BOOL bRet = TRUE;
2291 PTHREADINFO pti;
2292 PUSER_MESSAGE_QUEUE MessageQueue;
2293
2294 UserEnterExclusive();
2295
2296 pti = PsGetCurrentThreadWin32Thread();
2297 MessageQueue = pti->MessageQueue;
2298
2299 _SEH2_TRY
2300 {
2301 ProbeForRead(pKeyState, 256 * sizeof(BYTE), 1);
2302 for (i = 0; i < 256; ++i)
2303 {
2304 SET_KEY_DOWN(MessageQueue->afKeyState, i, pKeyState[i] & KS_DOWN_BIT);
2305 SET_KEY_LOCKED(MessageQueue->afKeyState, i, pKeyState[i] & KS_LOCK_BIT);
2306 }
2307 }
2308 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2309 {
2310 SetLastNtError(_SEH2_GetExceptionCode());
2311 bRet = FALSE;
2312 }
2313 _SEH2_END;
2314
2315 UserLeave();
2316
2317 return bRet;
2318 }
2319
2320 /* EOF */