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