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