[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 while (pwndTop && ((pwndTop->style & (WS_POPUP|WS_CHILD)) == WS_CHILD))
1569 {
1570 pwndTop = pwndTop->spwndParent;
1571 }
1572
1573 if (pwndTop && pwndTop != pwndDesktop)
1574 {
1575 LONG ret = co_IntSendMessage( msg->hwnd,
1576 WM_MOUSEACTIVATE,
1577 (WPARAM)UserHMGetHandle(pwndTop),
1578 MAKELONG( hittest, msg->message));
1579 switch(ret)
1580 {
1581 case MA_NOACTIVATEANDEAT:
1582 eatMsg = TRUE;
1583 /* fall through */
1584 case MA_NOACTIVATE:
1585 break;
1586 case MA_ACTIVATEANDEAT:
1587 eatMsg = TRUE;
1588 /* fall through */
1589 case MA_ACTIVATE:
1590 case 0:
1591 if (!co_IntMouseActivateWindow( pwndTop )) eatMsg = TRUE;
1592 break;
1593 default:
1594 ERR( "unknown WM_MOUSEACTIVATE code %d\n", ret );
1595 break;
1596 }
1597 }
1598 }
1599 }
1600
1601 /* send the WM_SETCURSOR message */
1602
1603 /* Windows sends the normal mouse message as the message parameter
1604 in the WM_SETCURSOR message even if it's non-client mouse message */
1605 co_IntSendMessage( msg->hwnd, WM_SETCURSOR, (WPARAM)msg->hwnd, MAKELONG( hittest, msg->message ));
1606
1607 msg->message = message;
1608 RETURN(!eatMsg);
1609
1610 CLEANUP:
1611 if(pwndMsg)
1612 UserDereferenceObject(pwndMsg);
1613
1614 END_CLEANUP;
1615 }
1616
1617 BOOL co_IntProcessKeyboardMessage(MSG* Msg, BOOL* RemoveMessages)
1618 {
1619 EVENTMSG Event;
1620
1621 if (Msg->message == WM_KEYDOWN || Msg->message == WM_SYSKEYDOWN ||
1622 Msg->message == WM_KEYUP || Msg->message == WM_SYSKEYUP)
1623 {
1624 switch (Msg->wParam)
1625 {
1626 case VK_LSHIFT: case VK_RSHIFT:
1627 Msg->wParam = VK_SHIFT;
1628 break;
1629 case VK_LCONTROL: case VK_RCONTROL:
1630 Msg->wParam = VK_CONTROL;
1631 break;
1632 case VK_LMENU: case VK_RMENU:
1633 Msg->wParam = VK_MENU;
1634 break;
1635 }
1636 }
1637
1638 Event.message = Msg->message;
1639 Event.hwnd = Msg->hwnd;
1640 Event.time = Msg->time;
1641 Event.paramL = (Msg->wParam & 0xFF) | (HIWORD(Msg->lParam) << 8);
1642 Event.paramH = Msg->lParam & 0x7FFF;
1643 if (HIWORD(Msg->lParam) & 0x0100) Event.paramH |= 0x8000;
1644 co_HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&Event);
1645
1646 if (co_HOOK_CallHooks( WH_KEYBOARD,
1647 *RemoveMessages ? HC_ACTION : HC_NOREMOVE,
1648 LOWORD(Msg->wParam),
1649 Msg->lParam))
1650 {
1651 /* skip this message */
1652 co_HOOK_CallHooks( WH_CBT,
1653 HCBT_KEYSKIPPED,
1654 LOWORD(Msg->wParam),
1655 Msg->lParam );
1656 ERR("KeyboardMessage WH_CBT Call Hook return!\n");
1657 return FALSE;
1658 }
1659 return TRUE;
1660 }
1661
1662 BOOL co_IntProcessHardwareMessage(MSG* Msg, BOOL* RemoveMessages, UINT first, UINT last)
1663 {
1664 if ( IS_MOUSE_MESSAGE(Msg->message))
1665 {
1666 return co_IntProcessMouseMessage(Msg, RemoveMessages, first, last);
1667 }
1668 else if ( IS_KBD_MESSAGE(Msg->message))
1669 {
1670 return co_IntProcessKeyboardMessage(Msg, RemoveMessages);
1671 }
1672
1673 return TRUE;
1674 }
1675
1676 BOOL APIENTRY
1677 co_MsqPeekMouseMove(IN PTHREADINFO pti,
1678 IN BOOL Remove,
1679 IN PWND Window,
1680 IN UINT MsgFilterLow,
1681 IN UINT MsgFilterHigh,
1682 OUT MSG* pMsg)
1683 {
1684 BOOL AcceptMessage;
1685 MSG msg;
1686 PUSER_MESSAGE_QUEUE MessageQueue = pti->MessageQueue;
1687
1688 if(!(MessageQueue->MouseMoved))
1689 return FALSE;
1690
1691 if (!MessageQueue->ptiSysLock)
1692 {
1693 MessageQueue->ptiSysLock = pti;
1694 pti->pcti->CTI_flags |= CTI_THREADSYSLOCK;
1695 }
1696
1697 if (MessageQueue->ptiSysLock != pti)
1698 {
1699 ERR("MsqPeekMouseMove: Thread Q is locked to another pti!\n");
1700 return FALSE;
1701 }
1702
1703 msg = MessageQueue->MouseMoveMsg;
1704
1705 AcceptMessage = co_IntProcessMouseMessage(&msg, &Remove, MsgFilterLow, MsgFilterHigh);
1706
1707 if(AcceptMessage)
1708 *pMsg = msg;
1709
1710 if(Remove)
1711 {
1712 ClearMsgBitsMask(pti, QS_MOUSEMOVE);
1713 MessageQueue->MouseMoved = FALSE;
1714 }
1715
1716 MessageQueue->ptiSysLock = NULL;
1717 pti->pcti->CTI_flags &= ~CTI_THREADSYSLOCK;
1718 return AcceptMessage;
1719 }
1720
1721 /* check whether a message filter contains at least one potential hardware message */
1722 static INT FASTCALL
1723 filter_contains_hw_range( UINT first, UINT last )
1724 {
1725 /* hardware message ranges are (in numerical order):
1726 * WM_NCMOUSEFIRST .. WM_NCMOUSELAST
1727 * WM_KEYFIRST .. WM_KEYLAST
1728 * WM_MOUSEFIRST .. WM_MOUSELAST
1729 */
1730 if (!last) --last;
1731 if (last < WM_NCMOUSEFIRST) return 0;
1732 if (first > WM_NCMOUSELAST && last < WM_KEYFIRST) return 0;
1733 if (first > WM_KEYLAST && last < WM_MOUSEFIRST) return 0;
1734 if (first > WM_MOUSELAST) return 0;
1735 return 1;
1736 }
1737
1738 BOOL APIENTRY
1739 co_MsqPeekHardwareMessage(IN PTHREADINFO pti,
1740 IN BOOL Remove,
1741 IN PWND Window,
1742 IN UINT MsgFilterLow,
1743 IN UINT MsgFilterHigh,
1744 IN UINT QSflags,
1745 OUT MSG* pMsg)
1746 {
1747
1748 BOOL AcceptMessage;
1749 PUSER_MESSAGE CurrentMessage;
1750 PLIST_ENTRY ListHead, CurrentEntry = NULL;
1751 MSG msg;
1752 BOOL Ret = FALSE;
1753 PUSER_MESSAGE_QUEUE MessageQueue = pti->MessageQueue;
1754
1755 if (!filter_contains_hw_range( MsgFilterLow, MsgFilterHigh )) return FALSE;
1756
1757 ListHead = &MessageQueue->HardwareMessagesListHead;
1758 CurrentEntry = ListHead->Flink;
1759
1760 if (IsListEmpty(CurrentEntry)) return FALSE;
1761
1762 if (!MessageQueue->ptiSysLock)
1763 {
1764 MessageQueue->ptiSysLock = pti;
1765 pti->pcti->CTI_flags |= CTI_THREADSYSLOCK;
1766 }
1767
1768 if (MessageQueue->ptiSysLock != pti)
1769 {
1770 ERR("MsqPeekHardwareMessage: Thread Q is locked to another pti!\n");
1771 return FALSE;
1772 }
1773
1774 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1775 ListEntry);
1776 do
1777 {
1778 if (IsListEmpty(CurrentEntry)) break;
1779 if (!CurrentMessage) break;
1780 CurrentEntry = CurrentMessage->ListEntry.Flink;
1781 /*
1782 MSDN:
1783 1: any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL.
1784 2: retrieves only messages on the current thread's message queue whose hwnd value is NULL.
1785 3: handle to the window whose messages are to be retrieved.
1786 */
1787 if ( ( !Window || // 1
1788 ( Window == PWND_BOTTOM && CurrentMessage->Msg.hwnd == NULL ) || // 2
1789 ( Window != PWND_BOTTOM && Window->head.h == CurrentMessage->Msg.hwnd ) ) && // 3
1790 ( ( ( MsgFilterLow == 0 && MsgFilterHigh == 0 ) && CurrentMessage->QS_Flags & QSflags ) ||
1791 ( MsgFilterLow <= CurrentMessage->Msg.message && MsgFilterHigh >= CurrentMessage->Msg.message ) ) )
1792 {
1793 msg = CurrentMessage->Msg;
1794
1795 UpdateKeyStateFromMsg(MessageQueue, &msg);
1796 AcceptMessage = co_IntProcessHardwareMessage(&msg, &Remove, MsgFilterLow, MsgFilterHigh);
1797
1798 if (Remove)
1799 {
1800 RemoveEntryList(&CurrentMessage->ListEntry);
1801 ClearMsgBitsMask(pti, CurrentMessage->QS_Flags);
1802 MsqDestroyMessage(CurrentMessage);
1803 }
1804
1805 if (AcceptMessage)
1806 {
1807 *pMsg = msg;
1808 Ret = TRUE;
1809 break;
1810 }
1811 }
1812 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1813 ListEntry);
1814 }
1815 while(CurrentEntry != ListHead);
1816
1817 MessageQueue->ptiSysLock = NULL;
1818 pti->pcti->CTI_flags &= ~CTI_THREADSYSLOCK;
1819 return Ret;
1820 }
1821
1822 BOOLEAN APIENTRY
1823 MsqPeekMessage(IN PTHREADINFO pti,
1824 IN BOOLEAN Remove,
1825 IN PWND Window,
1826 IN UINT MsgFilterLow,
1827 IN UINT MsgFilterHigh,
1828 IN UINT QSflags,
1829 OUT PMSG Message)
1830 {
1831 PLIST_ENTRY CurrentEntry;
1832 PUSER_MESSAGE CurrentMessage;
1833 PLIST_ENTRY ListHead;
1834 BOOL Ret = FALSE;
1835
1836 CurrentEntry = pti->PostedMessagesListHead.Flink;
1837 ListHead = &pti->PostedMessagesListHead;
1838
1839 if (IsListEmpty(CurrentEntry)) return FALSE;
1840
1841 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1842 ListEntry);
1843 do
1844 {
1845 if (IsListEmpty(CurrentEntry)) break;
1846 if (!CurrentMessage) break;
1847 CurrentEntry = CurrentEntry->Flink;
1848 /*
1849 MSDN:
1850 1: any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL.
1851 2: retrieves only messages on the current thread's message queue whose hwnd value is NULL.
1852 3: handle to the window whose messages are to be retrieved.
1853 */
1854 if ( ( !Window || // 1
1855 ( Window == PWND_BOTTOM && CurrentMessage->Msg.hwnd == NULL ) || // 2
1856 ( Window != PWND_BOTTOM && Window->head.h == CurrentMessage->Msg.hwnd ) ) && // 3
1857 ( ( ( MsgFilterLow == 0 && MsgFilterHigh == 0 ) && CurrentMessage->QS_Flags & QSflags ) ||
1858 ( MsgFilterLow <= CurrentMessage->Msg.message && MsgFilterHigh >= CurrentMessage->Msg.message ) ) )
1859 {
1860 *Message = CurrentMessage->Msg;
1861
1862 if (Remove)
1863 {
1864 RemoveEntryList(&CurrentMessage->ListEntry);
1865 ClearMsgBitsMask(pti, CurrentMessage->QS_Flags);
1866 MsqDestroyMessage(CurrentMessage);
1867 }
1868 Ret = TRUE;
1869 break;
1870 }
1871 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1872 ListEntry);
1873 }
1874 while (CurrentEntry != ListHead);
1875
1876 return Ret;
1877 }
1878
1879 NTSTATUS FASTCALL
1880 co_MsqWaitForNewMessages(PTHREADINFO pti, PWND WndFilter,
1881 UINT MsgFilterMin, UINT MsgFilterMax)
1882 {
1883 NTSTATUS ret;
1884 UserLeaveCo();
1885 ret = KeWaitForSingleObject( pti->pEventQueueServer,
1886 UserRequest,
1887 UserMode,
1888 FALSE,
1889 NULL );
1890 UserEnterCo();
1891 return ret;
1892 }
1893
1894 BOOL FASTCALL
1895 MsqIsHung(PTHREADINFO pti)
1896 {
1897 LARGE_INTEGER LargeTickCount;
1898
1899 KeQueryTickCount(&LargeTickCount);
1900 return ((LargeTickCount.u.LowPart - pti->timeLast) > MSQ_HUNG);
1901 }
1902
1903 VOID
1904 CALLBACK
1905 HungAppSysTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
1906 {
1907 DoTheScreenSaver();
1908 TRACE("HungAppSysTimerProc\n");
1909 // Process list of windows that are hung and waiting.
1910 }
1911
1912 BOOLEAN FASTCALL
1913 MsqInitializeMessageQueue(PTHREADINFO pti, PUSER_MESSAGE_QUEUE MessageQueue)
1914 {
1915 MessageQueue->CaretInfo = (PTHRDCARETINFO)(MessageQueue + 1);
1916 InitializeListHead(&MessageQueue->HardwareMessagesListHead); // Keep here!
1917 MessageQueue->spwndFocus = NULL;
1918 MessageQueue->iCursorLevel = 0;
1919 MessageQueue->CursorObject = NULL;
1920 RtlCopyMemory(MessageQueue->afKeyState, gafAsyncKeyState, sizeof(gafAsyncKeyState));
1921 MessageQueue->ptiMouse = pti;
1922 MessageQueue->ptiKeyboard = pti;
1923 MessageQueue->cThreads++;
1924
1925 return TRUE;
1926 }
1927
1928 VOID FASTCALL
1929 MsqCleanupThreadMsgs(PTHREADINFO pti)
1930 {
1931 PLIST_ENTRY CurrentEntry;
1932 PUSER_MESSAGE CurrentMessage;
1933 PUSER_SENT_MESSAGE CurrentSentMessage;
1934
1935 /* cleanup posted messages */
1936 while (!IsListEmpty(&pti->PostedMessagesListHead))
1937 {
1938 CurrentEntry = RemoveHeadList(&pti->PostedMessagesListHead);
1939 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1940 ListEntry);
1941 MsqDestroyMessage(CurrentMessage);
1942 }
1943
1944 /* remove the messages that have not yet been dispatched */
1945 while (!IsListEmpty(&pti->SentMessagesListHead))
1946 {
1947 CurrentEntry = RemoveHeadList(&pti->SentMessagesListHead);
1948 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1949 ListEntry);
1950
1951 TRACE("Notify the sender and remove a message from the queue that had not been dispatched\n");
1952 /* Only if the message has a sender was the message in the DispatchingList */
1953 if ((CurrentSentMessage->ptiSender)
1954 && (CurrentSentMessage->DispatchingListEntry.Flink != NULL))
1955 {
1956 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
1957 }
1958
1959 /* wake the sender's thread */
1960 if (CurrentSentMessage->CompletionEvent != NULL)
1961 {
1962 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1963 }
1964
1965 if (CurrentSentMessage->HasPackedLParam == TRUE)
1966 {
1967 if (CurrentSentMessage->Msg.lParam)
1968 ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
1969 }
1970
1971 /* free the message */
1972 ExFreePool(CurrentSentMessage);
1973 }
1974
1975 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
1976 ExitThread() was called in a SendMessage() umode callback */
1977 while (!IsListEmpty(&pti->LocalDispatchingMessagesHead))
1978 {
1979 CurrentEntry = RemoveHeadList(&pti->LocalDispatchingMessagesHead);
1980 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1981 ListEntry);
1982
1983 /* remove the message from the dispatching list */
1984 if(CurrentSentMessage->DispatchingListEntry.Flink != NULL)
1985 {
1986 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
1987 }
1988
1989 TRACE("Notify the sender, the thread has been terminated while dispatching a message!\n");
1990
1991 /* wake the sender's thread */
1992 if (CurrentSentMessage->CompletionEvent != NULL)
1993 {
1994 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1995 }
1996
1997 if (CurrentSentMessage->HasPackedLParam == TRUE)
1998 {
1999 if (CurrentSentMessage->Msg.lParam)
2000 ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
2001 }
2002
2003 /* free the message */
2004 ExFreePool(CurrentSentMessage);
2005 }
2006
2007 /* tell other threads not to bother returning any info to us */
2008 while (! IsListEmpty(&pti->DispatchingMessagesHead))
2009 {
2010 CurrentEntry = RemoveHeadList(&pti->DispatchingMessagesHead);
2011 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
2012 DispatchingListEntry);
2013 CurrentSentMessage->CompletionEvent = NULL;
2014 CurrentSentMessage->Result = NULL;
2015
2016 /* do NOT dereference our message queue as it might get attempted to be
2017 locked later */
2018 }
2019
2020 // Clear it all out.
2021 if (pti->pcti)
2022 {
2023 pti->pcti->fsWakeBits = 0;
2024 pti->pcti->fsChangeBits = 0;
2025 }
2026
2027 pti->nCntsQBits[QSRosKey] = 0;
2028 pti->nCntsQBits[QSRosMouseMove] = 0;
2029 pti->nCntsQBits[QSRosMouseButton] = 0;
2030 pti->nCntsQBits[QSRosPostMessage] = 0;
2031 pti->nCntsQBits[QSRosSendMessage] = 0;
2032 pti->nCntsQBits[QSRosHotKey] = 0;
2033 pti->nCntsQBits[QSRosEvent] = 0;
2034 }
2035
2036 VOID FASTCALL
2037 MsqCleanupMessageQueue(PTHREADINFO pti)
2038 {
2039 PUSER_MESSAGE_QUEUE MessageQueue;
2040
2041 MessageQueue = pti->MessageQueue;
2042 MessageQueue->cThreads--;
2043
2044 if (MessageQueue->cThreads)
2045 {
2046 if (MessageQueue->ptiSysLock == pti) MessageQueue->ptiSysLock = NULL;
2047 }
2048
2049 if (MessageQueue->CursorObject)
2050 {
2051 PCURICON_OBJECT pCursor = MessageQueue->CursorObject;
2052
2053 /* Change to another cursor if we going to dereference current one
2054 Note: we can't use UserSetCursor because it uses current thread
2055 message queue instead of queue given for cleanup */
2056 if (IntGetSysCursorInfo()->CurrentCursorObject == pCursor)
2057 {
2058 HDC hdcScreen;
2059
2060 /* Get the screen DC */
2061 hdcScreen = IntGetScreenDC();
2062 if (hdcScreen)
2063 GreMovePointer(hdcScreen, -1, -1);
2064 IntGetSysCursorInfo()->CurrentCursorObject = NULL;
2065 }
2066
2067 ERR("DereferenceObject pCursor\n");
2068 UserDereferenceObject(pCursor);
2069 }
2070
2071 if (gpqForeground == MessageQueue)
2072 {
2073 IntSetFocusMessageQueue(NULL);
2074 }
2075 if (gpqForegroundPrev == MessageQueue)
2076 {
2077 gpqForegroundPrev = NULL;
2078 }
2079 if (gpqCursor == MessageQueue)
2080 {
2081 gpqCursor = NULL;
2082 }
2083 }
2084
2085 PUSER_MESSAGE_QUEUE FASTCALL
2086 MsqCreateMessageQueue(PTHREADINFO pti)
2087 {
2088 PUSER_MESSAGE_QUEUE MessageQueue;
2089
2090 MessageQueue = (PUSER_MESSAGE_QUEUE)ExAllocatePoolWithTag(NonPagedPool,
2091 sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO),
2092 USERTAG_Q);
2093
2094 if (!MessageQueue)
2095 {
2096 return NULL;
2097 }
2098
2099 RtlZeroMemory(MessageQueue, sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO));
2100 /* hold at least one reference until it'll be destroyed */
2101 IntReferenceMessageQueue(MessageQueue);
2102 /* initialize the queue */
2103 if (!MsqInitializeMessageQueue(pti, MessageQueue))
2104 {
2105 IntDereferenceMessageQueue(MessageQueue);
2106 return NULL;
2107 }
2108
2109 return MessageQueue;
2110 }
2111
2112 VOID FASTCALL
2113 MsqDestroyMessageQueue(PTHREADINFO pti)
2114 {
2115 PDESKTOP desk;
2116 PUSER_MESSAGE_QUEUE MessageQueue = pti->MessageQueue;
2117
2118 MessageQueue->QF_flags |= QF_INDESTROY;
2119
2120 /* remove the message queue from any desktops */
2121 if ((desk = InterlockedExchangePointer((PVOID*)&MessageQueue->Desktop, 0)))
2122 {
2123 (void)InterlockedExchangePointer((PVOID*)&desk->ActiveMessageQueue, 0);
2124 IntDereferenceMessageQueue(MessageQueue);
2125 }
2126
2127 /* clean it up */
2128 MsqCleanupMessageQueue(pti);
2129
2130 /* decrease the reference counter, if it hits zero, the queue will be freed */
2131 IntDereferenceMessageQueue(MessageQueue);
2132 }
2133
2134 LPARAM FASTCALL
2135 MsqSetMessageExtraInfo(LPARAM lParam)
2136 {
2137 LPARAM Ret;
2138 PTHREADINFO pti;
2139 PUSER_MESSAGE_QUEUE MessageQueue;
2140
2141 pti = PsGetCurrentThreadWin32Thread();
2142 MessageQueue = pti->MessageQueue;
2143 if(!MessageQueue)
2144 {
2145 return 0;
2146 }
2147
2148 Ret = MessageQueue->ExtraInfo;
2149 MessageQueue->ExtraInfo = lParam;
2150
2151 return Ret;
2152 }
2153
2154 LPARAM FASTCALL
2155 MsqGetMessageExtraInfo(VOID)
2156 {
2157 PTHREADINFO pti;
2158 PUSER_MESSAGE_QUEUE MessageQueue;
2159
2160 pti = PsGetCurrentThreadWin32Thread();
2161 MessageQueue = pti->MessageQueue;
2162 if(!MessageQueue)
2163 {
2164 return 0;
2165 }
2166
2167 return MessageQueue->ExtraInfo;
2168 }
2169
2170 // ReplyMessage is called by the thread receiving the window message.
2171 BOOL FASTCALL
2172 co_MsqReplyMessage( LRESULT lResult )
2173 {
2174 PUSER_SENT_MESSAGE Message;
2175 PTHREADINFO pti;
2176
2177 pti = PsGetCurrentThreadWin32Thread();
2178 Message = pti->pusmCurrent;
2179
2180 if (!Message) return FALSE;
2181
2182 if (Message->QS_Flags & QS_SMRESULT) return FALSE;
2183
2184 // SendMessageXxx || Callback msg and not a notify msg
2185 if (Message->ptiSender || Message->CompletionCallback)
2186 {
2187 Message->lResult = lResult;
2188 Message->QS_Flags |= QS_SMRESULT;
2189 // See co_MsqDispatchOneSentMessage, change bits already accounted for and cleared and this msg is going away..
2190 }
2191 return TRUE;
2192 }
2193
2194 HWND FASTCALL
2195 MsqSetStateWindow(PTHREADINFO pti, ULONG Type, HWND hWnd)
2196 {
2197 HWND Prev;
2198 PUSER_MESSAGE_QUEUE MessageQueue;
2199
2200 MessageQueue = pti->MessageQueue;
2201
2202 switch(Type)
2203 {
2204 case MSQ_STATE_CAPTURE:
2205 Prev = MessageQueue->spwndCapture ? UserHMGetHandle(MessageQueue->spwndCapture) : 0;
2206 MessageQueue->spwndCapture = ValidateHwndNoErr(hWnd);
2207 return Prev;
2208 case MSQ_STATE_ACTIVE:
2209 Prev = MessageQueue->spwndActive ? UserHMGetHandle(MessageQueue->spwndActive) : 0;
2210 MessageQueue->spwndActive = ValidateHwndNoErr(hWnd);
2211 return Prev;
2212 case MSQ_STATE_FOCUS:
2213 Prev = MessageQueue->spwndFocus ? UserHMGetHandle(MessageQueue->spwndFocus) : 0;
2214 MessageQueue->spwndFocus = ValidateHwndNoErr(hWnd);
2215 return Prev;
2216 case MSQ_STATE_MENUOWNER:
2217 Prev = MessageQueue->MenuOwner;
2218 MessageQueue->MenuOwner = hWnd;
2219 return Prev;
2220 case MSQ_STATE_MOVESIZE:
2221 Prev = MessageQueue->MoveSize;
2222 MessageQueue->MoveSize = hWnd;
2223 return Prev;
2224 case MSQ_STATE_CARET:
2225 ASSERT(MessageQueue->CaretInfo);
2226 Prev = MessageQueue->CaretInfo->hWnd;
2227 MessageQueue->CaretInfo->hWnd = hWnd;
2228 return Prev;
2229 }
2230
2231 return NULL;
2232 }
2233
2234 SHORT
2235 APIENTRY
2236 NtUserGetKeyState(INT key)
2237 {
2238 DWORD Ret;
2239
2240 UserEnterShared();
2241
2242 Ret = UserGetKeyState(key);
2243
2244 UserLeave();
2245
2246 return (SHORT)Ret;
2247 }
2248
2249
2250 DWORD
2251 APIENTRY
2252 NtUserGetKeyboardState(LPBYTE lpKeyState)
2253 {
2254 DWORD i, ret = TRUE;
2255 PTHREADINFO pti;
2256 PUSER_MESSAGE_QUEUE MessageQueue;
2257
2258 UserEnterShared();
2259
2260 pti = PsGetCurrentThreadWin32Thread();
2261 MessageQueue = pti->MessageQueue;
2262
2263 _SEH2_TRY
2264 {
2265 /* Probe and copy key state to an array */
2266 ProbeForWrite(lpKeyState, 256 * sizeof(BYTE), 1);
2267 for (i = 0; i < 256; ++i)
2268 {
2269 lpKeyState[i] = 0;
2270 if (IS_KEY_DOWN(MessageQueue->afKeyState, i))
2271 lpKeyState[i] |= KS_DOWN_BIT;
2272 if (IS_KEY_LOCKED(MessageQueue->afKeyState, i))
2273 lpKeyState[i] |= KS_LOCK_BIT;
2274 }
2275 }
2276 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2277 {
2278 SetLastNtError(_SEH2_GetExceptionCode());
2279 ret = FALSE;
2280 }
2281 _SEH2_END;
2282
2283 UserLeave();
2284
2285 return ret;
2286 }
2287
2288 BOOL
2289 APIENTRY
2290 NtUserSetKeyboardState(LPBYTE pKeyState)
2291 {
2292 UINT i;
2293 BOOL bRet = TRUE;
2294 PTHREADINFO pti;
2295 PUSER_MESSAGE_QUEUE MessageQueue;
2296
2297 UserEnterExclusive();
2298
2299 pti = PsGetCurrentThreadWin32Thread();
2300 MessageQueue = pti->MessageQueue;
2301
2302 _SEH2_TRY
2303 {
2304 ProbeForRead(pKeyState, 256 * sizeof(BYTE), 1);
2305 for (i = 0; i < 256; ++i)
2306 {
2307 SET_KEY_DOWN(MessageQueue->afKeyState, i, pKeyState[i] & KS_DOWN_BIT);
2308 SET_KEY_LOCKED(MessageQueue->afKeyState, i, pKeyState[i] & KS_LOCK_BIT);
2309 }
2310 }
2311 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2312 {
2313 SetLastNtError(_SEH2_GetExceptionCode());
2314 bRet = FALSE;
2315 }
2316 _SEH2_END;
2317
2318 UserLeave();
2319
2320 return bRet;
2321 }
2322
2323 /* EOF */