ef090e1b80e0fb8c8d7dc4a9248da71403b57405
[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);
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);
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(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, MSG* Msg, BOOLEAN HardwareMessage,
1256 DWORD MessageBits)
1257 {
1258 PUSER_MESSAGE Message;
1259
1260 if(!(Message = MsqCreateMessage(Msg)))
1261 {
1262 return;
1263 }
1264
1265 if(!HardwareMessage)
1266 {
1267 InsertTailList(&MessageQueue->PostedMessagesListHead,
1268 &Message->ListEntry);
1269 }
1270 else
1271 {
1272 InsertTailList(&MessageQueue->HardwareMessagesListHead,
1273 &Message->ListEntry);
1274 }
1275
1276 Message->QS_Flags = MessageBits;
1277 MsqWakeQueue(MessageQueue, MessageBits, (MessageBits & QS_TIMER ? FALSE : TRUE));
1278 }
1279
1280 VOID FASTCALL
1281 MsqPostQuitMessage(PUSER_MESSAGE_QUEUE MessageQueue, ULONG ExitCode)
1282 {
1283 MessageQueue->QuitPosted = TRUE;
1284 MessageQueue->QuitExitCode = ExitCode;
1285 MsqWakeQueue(MessageQueue, QS_POSTMESSAGE|QS_ALLPOSTMESSAGE, TRUE);
1286 }
1287
1288 /***********************************************************************
1289 * MsqSendParentNotify
1290 *
1291 * Send a WM_PARENTNOTIFY to all ancestors of the given window, unless
1292 * the window has the WS_EX_NOPARENTNOTIFY style.
1293 */
1294 static void MsqSendParentNotify( PWND pwnd, WORD event, WORD idChild, POINT pt )
1295 {
1296 PWND pwndDesktop = UserGetDesktopWindow();
1297
1298 /* pt has to be in the client coordinates of the parent window */
1299 pt.x += pwndDesktop->rcClient.left - pwnd->rcClient.left;
1300 pt.y += pwndDesktop->rcClient.top - pwnd->rcClient.top;
1301
1302 for (;;)
1303 {
1304 PWND pwndParent;
1305
1306 if (!(pwnd->style & WS_CHILD)) break;
1307 if (pwnd->ExStyle & WS_EX_NOPARENTNOTIFY) break;
1308 if (!(pwndParent = IntGetParent(pwnd))) break;
1309 if (pwndParent == pwndDesktop) break;
1310 pt.x += pwnd->rcClient.left - pwndParent->rcClient.left;
1311 pt.y += pwnd->rcClient.top - pwndParent->rcClient.top;
1312
1313 pwnd = pwndParent;
1314 co_IntSendMessage( UserHMGetHandle(pwnd), WM_PARENTNOTIFY,
1315 MAKEWPARAM( event, idChild ), MAKELPARAM( pt.x, pt.y ) );
1316 }
1317 }
1318
1319 VOID
1320 FASTCALL
1321 IntTrackMouseMove(PWND pwndTrack, PDESKTOP pDesk, PMSG msg, USHORT hittest)
1322 {
1323 // PWND pwndTrack = IntChildrenWindowFromPoint(pwndMsg, msg->pt.x, msg->pt.y);
1324 hittest = GetNCHitEx(pwndTrack, msg->pt);
1325
1326 if ( pDesk->spwndTrack != pwndTrack || // Change with tracking window or
1327 msg->message != WM_MOUSEMOVE || // Mouse click changes or
1328 pDesk->htEx != hittest) // Change in current hit test states.
1329 {
1330 TRACE("ITMM: Track Mouse Move!\n");
1331
1332 /* Handle only the changing window track and mouse move across a border. */
1333 if ( pDesk->spwndTrack != pwndTrack ||
1334 (pDesk->htEx == HTCLIENT) ^ (hittest == HTCLIENT) )
1335 {
1336 TRACE("ITMM: Another Wnd %d or Across Border %d\n",
1337 pDesk->spwndTrack != pwndTrack,(pDesk->htEx == HTCLIENT) ^ (hittest == HTCLIENT));
1338
1339 if ( pDesk->dwDTFlags & DF_TME_LEAVE )
1340 UserPostMessage( UserHMGetHandle(pDesk->spwndTrack),
1341 (pDesk->htEx != HTCLIENT) ? WM_NCMOUSELEAVE : WM_MOUSELEAVE,
1342 0, 0);
1343
1344 if ( pDesk->dwDTFlags & DF_TME_HOVER )
1345 IntKillTimer(UserHMGetHandle(pDesk->spwndTrack), ID_EVENT_SYSTIMER_MOUSEHOVER, TRUE);
1346
1347 /* Clear the flags to sign a change. */
1348 pDesk->dwDTFlags &= ~(DF_TME_LEAVE|DF_TME_HOVER);
1349 }
1350 /* Set the Track window and hit test. */
1351 pDesk->spwndTrack = pwndTrack;
1352 pDesk->htEx = hittest;
1353 }
1354
1355 /* Reset, Same Track window, Hover set and Mouse Clicks or Clobbered Hover box. */
1356 if ( pDesk->spwndTrack == pwndTrack &&
1357 ( msg->message != WM_MOUSEMOVE || !RECTL_bPointInRect(&pDesk->rcMouseHover, msg->pt.x, msg->pt.y)) &&
1358 pDesk->dwDTFlags & DF_TME_HOVER )
1359 {
1360 TRACE("ITMM: Reset Hover points!\n");
1361 // Restart timer for the hover period.
1362 IntSetTimer(pDesk->spwndTrack, ID_EVENT_SYSTIMER_MOUSEHOVER, pDesk->dwMouseHoverTime, SystemTimerProc, TMRF_SYSTEM);
1363 // Reset desktop mouse hover from the system default hover rectangle.
1364 RECTL_vSetRect(&pDesk->rcMouseHover,
1365 msg->pt.x - gspv.iMouseHoverWidth / 2,
1366 msg->pt.y - gspv.iMouseHoverHeight / 2,
1367 msg->pt.x + gspv.iMouseHoverWidth / 2,
1368 msg->pt.y + gspv.iMouseHoverHeight / 2);
1369 }
1370 }
1371
1372 BOOL co_IntProcessMouseMessage(MSG* msg, BOOL* RemoveMessages, UINT first, UINT last)
1373 {
1374 MSG clk_msg;
1375 POINT pt;
1376 UINT message;
1377 USHORT hittest;
1378 EVENTMSG event;
1379 MOUSEHOOKSTRUCT hook;
1380 BOOL eatMsg;
1381
1382 PWND pwndMsg, pwndDesktop;
1383 PUSER_MESSAGE_QUEUE MessageQueue;
1384 PTHREADINFO pti;
1385 PSYSTEM_CURSORINFO CurInfo;
1386 PDESKTOP pDesk;
1387 DECLARE_RETURN(BOOL);
1388
1389 pti = PsGetCurrentThreadWin32Thread();
1390 pwndDesktop = UserGetDesktopWindow();
1391 MessageQueue = pti->MessageQueue;
1392 CurInfo = IntGetSysCursorInfo();
1393 pwndMsg = ValidateHwndNoErr(msg->hwnd);
1394 clk_msg = MessageQueue->msgDblClk;
1395 pDesk = pwndDesktop->head.rpdesk;
1396
1397 /* find the window to dispatch this mouse message to */
1398 if (MessageQueue->spwndCapture)
1399 {
1400 hittest = HTCLIENT;
1401 pwndMsg = MessageQueue->spwndCapture;
1402 if (pwndMsg) UserReferenceObject(pwndMsg);
1403 }
1404 else
1405 { // Fix wine Msg test_HTTRANSPARENT. Start with a NULL window.
1406 // http://www.winehq.org/pipermail/wine-patches/2012-August/116776.html
1407 pwndMsg = co_WinPosWindowFromPoint(NULL, &msg->pt, &hittest);
1408 }
1409
1410 TRACE("Got mouse message for 0x%x, hittest: 0x%x\n", msg->hwnd, hittest );
1411
1412 if (pwndMsg == NULL || pwndMsg->head.pti != pti)
1413 {
1414 /* Remove and ignore the message */
1415 *RemoveMessages = TRUE;
1416 RETURN(FALSE);
1417 }
1418
1419 if ( MessageQueue == gpqCursor ) // Cursor must use the same Queue!
1420 {
1421 IntTrackMouseMove(pwndMsg, pDesk, msg, hittest);
1422 }
1423 else
1424 {
1425 ERR("Not the same cursor!\n");
1426 }
1427
1428 msg->hwnd = UserHMGetHandle(pwndMsg);
1429
1430 #if 0
1431 if (!check_hwnd_filter( msg, hwnd_filter )) RETURN(FALSE);
1432 #endif
1433
1434 pt = msg->pt;
1435 message = msg->message;
1436 /* Note: windows has no concept of a non-client wheel message */
1437 if (message != WM_MOUSEWHEEL)
1438 {
1439 if (hittest != HTCLIENT)
1440 {
1441 message += WM_NCMOUSEMOVE - WM_MOUSEMOVE;
1442 msg->wParam = hittest;
1443 }
1444 else
1445 {
1446 /* coordinates don't get translated while tracking a menu */
1447 /* FIXME: should differentiate popups and top-level menus */
1448 if (!(MessageQueue->MenuOwner))
1449 {
1450 pt.x += pwndDesktop->rcClient.left - pwndMsg->rcClient.left;
1451 pt.y += pwndDesktop->rcClient.top - pwndMsg->rcClient.top;
1452 }
1453 }
1454 }
1455 msg->lParam = MAKELONG( pt.x, pt.y );
1456
1457 /* translate double clicks */
1458
1459 if ((msg->message == WM_LBUTTONDOWN) ||
1460 (msg->message == WM_RBUTTONDOWN) ||
1461 (msg->message == WM_MBUTTONDOWN) ||
1462 (msg->message == WM_XBUTTONDOWN))
1463 {
1464 BOOL update = *RemoveMessages;
1465
1466 /* translate double clicks -
1467 * note that ...MOUSEMOVEs can slip in between
1468 * ...BUTTONDOWN and ...BUTTONDBLCLK messages */
1469
1470 if ((MessageQueue->MenuOwner || MessageQueue->MoveSize) ||
1471 hittest != HTCLIENT ||
1472 (pwndMsg->pcls->style & CS_DBLCLKS))
1473 {
1474 if ((msg->message == clk_msg.message) &&
1475 (msg->hwnd == clk_msg.hwnd) &&
1476 (msg->wParam == clk_msg.wParam) &&
1477 ((msg->time - clk_msg.time) < (ULONG)gspv.iDblClickTime) &&
1478 (abs(msg->pt.x - clk_msg.pt.x) < UserGetSystemMetrics(SM_CXDOUBLECLK)/2) &&
1479 (abs(msg->pt.y - clk_msg.pt.y) < UserGetSystemMetrics(SM_CYDOUBLECLK)/2))
1480 {
1481 message += (WM_LBUTTONDBLCLK - WM_LBUTTONDOWN);
1482 if (update)
1483 {
1484 MessageQueue->msgDblClk.message = 0; /* clear the double click conditions */
1485 update = FALSE;
1486 }
1487 }
1488 }
1489
1490 if (!((first == 0 && last == 0) || (message >= first || message <= last)))
1491 {
1492 TRACE("Message out of range!!!\n");
1493 RETURN(FALSE);
1494 }
1495
1496 /* update static double click conditions */
1497 if (update) MessageQueue->msgDblClk = *msg;
1498 }
1499 else
1500 {
1501 if (!((first == 0 && last == 0) || (message >= first || message <= last)))
1502 {
1503 TRACE("Message out of range!!!\n");
1504 RETURN(FALSE);
1505 }
1506 }
1507
1508 if(gspv.bMouseClickLock)
1509 {
1510 BOOL IsClkLck = FALSE;
1511
1512 if(msg->message == WM_LBUTTONUP)
1513 {
1514 IsClkLck = ((msg->time - CurInfo->ClickLockTime) >= gspv.dwMouseClickLockTime);
1515 if (IsClkLck && (!CurInfo->ClickLockActive))
1516 {
1517 CurInfo->ClickLockActive = TRUE;
1518 }
1519 }
1520 else if (msg->message == WM_LBUTTONDOWN)
1521 {
1522 if (CurInfo->ClickLockActive)
1523 {
1524 IsClkLck = TRUE;
1525 CurInfo->ClickLockActive = FALSE;
1526 }
1527
1528 CurInfo->ClickLockTime = msg->time;
1529 }
1530
1531 if(IsClkLck)
1532 {
1533 /* Remove and ignore the message */
1534 *RemoveMessages = TRUE;
1535 RETURN(FALSE);
1536 }
1537 }
1538
1539 /* message is accepted now (but may still get dropped) */
1540
1541 event.message = msg->message;
1542 event.time = msg->time;
1543 event.hwnd = msg->hwnd;
1544 event.paramL = msg->pt.x;
1545 event.paramH = msg->pt.y;
1546 co_HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&event );
1547
1548 hook.pt = msg->pt;
1549 hook.hwnd = msg->hwnd;
1550 hook.wHitTestCode = hittest;
1551 hook.dwExtraInfo = 0 /* extra_info */ ;
1552 if (co_HOOK_CallHooks( WH_MOUSE, *RemoveMessages ? HC_ACTION : HC_NOREMOVE,
1553 message, (LPARAM)&hook ))
1554 {
1555 hook.pt = msg->pt;
1556 hook.hwnd = msg->hwnd;
1557 hook.wHitTestCode = hittest;
1558 hook.dwExtraInfo = 0 /* extra_info */ ;
1559 co_HOOK_CallHooks( WH_CBT, HCBT_CLICKSKIPPED, message, (LPARAM)&hook );
1560
1561 ERR("WH_MOUSE dorpped mouse message!\n");
1562
1563 /* Remove and skip message */
1564 *RemoveMessages = TRUE;
1565 RETURN(FALSE);
1566 }
1567
1568 if ((hittest == HTERROR) || (hittest == HTNOWHERE))
1569 {
1570 co_IntSendMessage( msg->hwnd, WM_SETCURSOR, (WPARAM)msg->hwnd,
1571 MAKELONG( hittest, msg->message ));
1572
1573 /* Remove and skip message */
1574 *RemoveMessages = TRUE;
1575 RETURN(FALSE);
1576 }
1577
1578 if ((*RemoveMessages == FALSE) || MessageQueue->spwndCapture)
1579 {
1580 /* Accept the message */
1581 msg->message = message;
1582 RETURN(TRUE);
1583 }
1584
1585 eatMsg = FALSE;
1586
1587 if ((msg->message == WM_LBUTTONDOWN) ||
1588 (msg->message == WM_RBUTTONDOWN) ||
1589 (msg->message == WM_MBUTTONDOWN) ||
1590 (msg->message == WM_XBUTTONDOWN))
1591 {
1592 /* Send the WM_PARENTNOTIFY,
1593 * note that even for double/nonclient clicks
1594 * notification message is still WM_L/M/RBUTTONDOWN.
1595 */
1596 MsqSendParentNotify(pwndMsg, msg->message, 0, msg->pt );
1597
1598 /* Activate the window if needed */
1599
1600 if (pwndMsg != MessageQueue->spwndActive)
1601 {
1602 PWND pwndTop = pwndMsg;
1603 while (pwndTop && ((pwndTop->style & (WS_POPUP|WS_CHILD)) == WS_CHILD))
1604 {
1605 pwndTop = pwndTop->spwndParent;
1606 }
1607
1608 if (pwndTop && pwndTop != pwndDesktop)
1609 {
1610 LONG ret = co_IntSendMessage( msg->hwnd,
1611 WM_MOUSEACTIVATE,
1612 (WPARAM)UserHMGetHandle(pwndTop),
1613 MAKELONG( hittest, msg->message));
1614 switch(ret)
1615 {
1616 case MA_NOACTIVATEANDEAT:
1617 eatMsg = TRUE;
1618 /* fall through */
1619 case MA_NOACTIVATE:
1620 break;
1621 case MA_ACTIVATEANDEAT:
1622 eatMsg = TRUE;
1623 /* fall through */
1624 case MA_ACTIVATE:
1625 case 0:
1626 if (!co_IntMouseActivateWindow( pwndTop )) eatMsg = TRUE;
1627 break;
1628 default:
1629 ERR( "unknown WM_MOUSEACTIVATE code %d\n", ret );
1630 break;
1631 }
1632 }
1633 }
1634 }
1635
1636 /* send the WM_SETCURSOR message */
1637
1638 /* Windows sends the normal mouse message as the message parameter
1639 in the WM_SETCURSOR message even if it's non-client mouse message */
1640 co_IntSendMessage( msg->hwnd, WM_SETCURSOR, (WPARAM)msg->hwnd, MAKELONG( hittest, msg->message ));
1641
1642 msg->message = message;
1643 RETURN(!eatMsg);
1644
1645 CLEANUP:
1646 if(pwndMsg)
1647 UserDereferenceObject(pwndMsg);
1648
1649 END_CLEANUP;
1650 }
1651
1652 BOOL co_IntProcessKeyboardMessage(MSG* Msg, BOOL* RemoveMessages)
1653 {
1654 EVENTMSG Event;
1655
1656 if (Msg->message == WM_KEYDOWN || Msg->message == WM_SYSKEYDOWN ||
1657 Msg->message == WM_KEYUP || Msg->message == WM_SYSKEYUP)
1658 {
1659 switch (Msg->wParam)
1660 {
1661 case VK_LSHIFT: case VK_RSHIFT:
1662 Msg->wParam = VK_SHIFT;
1663 break;
1664 case VK_LCONTROL: case VK_RCONTROL:
1665 Msg->wParam = VK_CONTROL;
1666 break;
1667 case VK_LMENU: case VK_RMENU:
1668 Msg->wParam = VK_MENU;
1669 break;
1670 }
1671 }
1672
1673 Event.message = Msg->message;
1674 Event.hwnd = Msg->hwnd;
1675 Event.time = Msg->time;
1676 Event.paramL = (Msg->wParam & 0xFF) | (HIWORD(Msg->lParam) << 8);
1677 Event.paramH = Msg->lParam & 0x7FFF;
1678 if (HIWORD(Msg->lParam) & 0x0100) Event.paramH |= 0x8000;
1679 co_HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&Event);
1680
1681 if (co_HOOK_CallHooks( WH_KEYBOARD,
1682 *RemoveMessages ? HC_ACTION : HC_NOREMOVE,
1683 LOWORD(Msg->wParam),
1684 Msg->lParam))
1685 {
1686 /* skip this message */
1687 co_HOOK_CallHooks( WH_CBT,
1688 HCBT_KEYSKIPPED,
1689 LOWORD(Msg->wParam),
1690 Msg->lParam );
1691 ERR("KeyboardMessage WH_CBT Call Hook return!\n");
1692 return FALSE;
1693 }
1694 return TRUE;
1695 }
1696
1697 BOOL co_IntProcessHardwareMessage(MSG* Msg, BOOL* RemoveMessages, UINT first, UINT last)
1698 {
1699 if ( IS_MOUSE_MESSAGE(Msg->message))
1700 {
1701 return co_IntProcessMouseMessage(Msg, RemoveMessages, first, last);
1702 }
1703 else if ( IS_KBD_MESSAGE(Msg->message))
1704 {
1705 return co_IntProcessKeyboardMessage(Msg, RemoveMessages);
1706 }
1707
1708 return TRUE;
1709 }
1710
1711 BOOL APIENTRY
1712 co_MsqPeekMouseMove(IN PUSER_MESSAGE_QUEUE MessageQueue,
1713 IN BOOL Remove,
1714 IN PWND Window,
1715 IN UINT MsgFilterLow,
1716 IN UINT MsgFilterHigh,
1717 OUT MSG* pMsg)
1718 {
1719 BOOL AcceptMessage;
1720 MSG msg;
1721 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
1722
1723 if(!(MessageQueue->MouseMoved))
1724 return FALSE;
1725
1726 if (!MessageQueue->ptiSysLock)
1727 {
1728 MessageQueue->ptiSysLock = pti;
1729 pti->pcti->CTI_flags |= CTI_THREADSYSLOCK;
1730 }
1731
1732 if (MessageQueue->ptiSysLock != pti)
1733 {
1734 ERR("MsqPeekMouseMove: Thread Q is locked to another pti!\n");
1735 return FALSE;
1736 }
1737
1738 msg = MessageQueue->MouseMoveMsg;
1739
1740 AcceptMessage = co_IntProcessMouseMessage(&msg, &Remove, MsgFilterLow, MsgFilterHigh);
1741
1742 if(AcceptMessage)
1743 *pMsg = msg;
1744
1745 if(Remove)
1746 {
1747 ClearMsgBitsMask(MessageQueue, QS_MOUSEMOVE);
1748 MessageQueue->MouseMoved = FALSE;
1749 }
1750
1751 MessageQueue->ptiSysLock = NULL;
1752 pti->pcti->CTI_flags &= ~CTI_THREADSYSLOCK;
1753 return AcceptMessage;
1754 }
1755
1756 /* check whether a message filter contains at least one potential hardware message */
1757 static INT FASTCALL
1758 filter_contains_hw_range( UINT first, UINT last )
1759 {
1760 /* hardware message ranges are (in numerical order):
1761 * WM_NCMOUSEFIRST .. WM_NCMOUSELAST
1762 * WM_KEYFIRST .. WM_KEYLAST
1763 * WM_MOUSEFIRST .. WM_MOUSELAST
1764 */
1765 if (!last) --last;
1766 if (last < WM_NCMOUSEFIRST) return 0;
1767 if (first > WM_NCMOUSELAST && last < WM_KEYFIRST) return 0;
1768 if (first > WM_KEYLAST && last < WM_MOUSEFIRST) return 0;
1769 if (first > WM_MOUSELAST) return 0;
1770 return 1;
1771 }
1772
1773 BOOL APIENTRY
1774 co_MsqPeekHardwareMessage(IN PUSER_MESSAGE_QUEUE MessageQueue,
1775 IN BOOL Remove,
1776 IN PWND Window,
1777 IN UINT MsgFilterLow,
1778 IN UINT MsgFilterHigh,
1779 IN UINT QSflags,
1780 OUT MSG* pMsg)
1781 {
1782
1783 BOOL AcceptMessage;
1784 PUSER_MESSAGE CurrentMessage;
1785 PLIST_ENTRY ListHead, CurrentEntry = NULL;
1786 MSG msg;
1787 BOOL Ret = FALSE;
1788 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
1789
1790 if (!filter_contains_hw_range( MsgFilterLow, MsgFilterHigh )) return FALSE;
1791
1792 ListHead = &MessageQueue->HardwareMessagesListHead;
1793 CurrentEntry = ListHead->Flink;
1794
1795 if (IsListEmpty(CurrentEntry)) return FALSE;
1796
1797 if (!MessageQueue->ptiSysLock)
1798 {
1799 MessageQueue->ptiSysLock = pti;
1800 pti->pcti->CTI_flags |= CTI_THREADSYSLOCK;
1801 }
1802
1803 if (MessageQueue->ptiSysLock != pti)
1804 {
1805 ERR("MsqPeekHardwareMessage: Thread Q is locked to another pti!\n");
1806 return FALSE;
1807 }
1808
1809 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1810 ListEntry);
1811 do
1812 {
1813 if (IsListEmpty(CurrentEntry)) break;
1814 if (!CurrentMessage) break;
1815 CurrentEntry = CurrentMessage->ListEntry.Flink;
1816 /*
1817 MSDN:
1818 1: any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL.
1819 2: retrieves only messages on the current thread's message queue whose hwnd value is NULL.
1820 3: handle to the window whose messages are to be retrieved.
1821 */
1822 if ( ( !Window || // 1
1823 ( Window == PWND_BOTTOM && CurrentMessage->Msg.hwnd == NULL ) || // 2
1824 ( Window != PWND_BOTTOM && Window->head.h == CurrentMessage->Msg.hwnd ) ) && // 3
1825 ( ( ( MsgFilterLow == 0 && MsgFilterHigh == 0 ) && CurrentMessage->QS_Flags & QSflags ) ||
1826 ( MsgFilterLow <= CurrentMessage->Msg.message && MsgFilterHigh >= CurrentMessage->Msg.message ) ) )
1827 {
1828 msg = CurrentMessage->Msg;
1829
1830 UpdateKeyStateFromMsg(MessageQueue, &msg);
1831 AcceptMessage = co_IntProcessHardwareMessage(&msg, &Remove, MsgFilterLow, MsgFilterHigh);
1832
1833 if (Remove)
1834 {
1835 RemoveEntryList(&CurrentMessage->ListEntry);
1836 ClearMsgBitsMask(MessageQueue, CurrentMessage->QS_Flags);
1837 MsqDestroyMessage(CurrentMessage);
1838 }
1839
1840 if (AcceptMessage)
1841 {
1842 *pMsg = msg;
1843 Ret = TRUE;
1844 break;
1845 }
1846 }
1847 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1848 ListEntry);
1849 }
1850 while(CurrentEntry != ListHead);
1851
1852 MessageQueue->ptiSysLock = NULL;
1853 pti->pcti->CTI_flags &= ~CTI_THREADSYSLOCK;
1854 return Ret;
1855 }
1856
1857 BOOLEAN APIENTRY
1858 MsqPeekMessage(IN PUSER_MESSAGE_QUEUE MessageQueue,
1859 IN BOOLEAN Remove,
1860 IN PWND Window,
1861 IN UINT MsgFilterLow,
1862 IN UINT MsgFilterHigh,
1863 IN UINT QSflags,
1864 OUT PMSG Message)
1865 {
1866 PLIST_ENTRY CurrentEntry;
1867 PUSER_MESSAGE CurrentMessage;
1868 PLIST_ENTRY ListHead;
1869 BOOL Ret = FALSE;
1870 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
1871
1872 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
1873 ListHead = &MessageQueue->PostedMessagesListHead;
1874
1875 if (IsListEmpty(CurrentEntry)) return FALSE;
1876
1877 if (!MessageQueue->ptiSysLock)
1878 {
1879 MessageQueue->ptiSysLock = pti;
1880 pti->pcti->CTI_flags |= CTI_THREADSYSLOCK;
1881 }
1882
1883 if (MessageQueue->ptiSysLock != pti)
1884 {
1885 ERR("MsqPeekMessage: Thread Q is locked to another pti!\n");
1886 return FALSE;
1887 }
1888
1889 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1890 ListEntry);
1891 do
1892 {
1893 if (IsListEmpty(CurrentEntry)) break;
1894 if (!CurrentMessage) break;
1895 CurrentEntry = CurrentEntry->Flink;
1896 /*
1897 MSDN:
1898 1: any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL.
1899 2: retrieves only messages on the current thread's message queue whose hwnd value is NULL.
1900 3: handle to the window whose messages are to be retrieved.
1901 */
1902 if ( ( !Window || // 1
1903 ( Window == PWND_BOTTOM && CurrentMessage->Msg.hwnd == NULL ) || // 2
1904 ( Window != PWND_BOTTOM && Window->head.h == CurrentMessage->Msg.hwnd ) ) && // 3
1905 ( ( ( MsgFilterLow == 0 && MsgFilterHigh == 0 ) && CurrentMessage->QS_Flags & QSflags ) ||
1906 ( MsgFilterLow <= CurrentMessage->Msg.message && MsgFilterHigh >= CurrentMessage->Msg.message ) ) )
1907 {
1908 *Message = CurrentMessage->Msg;
1909
1910 if (Remove)
1911 {
1912 RemoveEntryList(&CurrentMessage->ListEntry);
1913 ClearMsgBitsMask(MessageQueue, CurrentMessage->QS_Flags);
1914 MsqDestroyMessage(CurrentMessage);
1915 }
1916 Ret = TRUE;
1917 break;
1918 }
1919 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1920 ListEntry);
1921 }
1922 while (CurrentEntry != ListHead);
1923
1924 MessageQueue->ptiSysLock = NULL;
1925 pti->pcti->CTI_flags &= ~CTI_THREADSYSLOCK;
1926 return Ret;
1927 }
1928
1929 NTSTATUS FASTCALL
1930 co_MsqWaitForNewMessages(PUSER_MESSAGE_QUEUE MessageQueue, PWND WndFilter,
1931 UINT MsgFilterMin, UINT MsgFilterMax)
1932 {
1933 NTSTATUS ret;
1934 UserLeaveCo();
1935 ret = KeWaitForSingleObject( MessageQueue->NewMessages,
1936 UserRequest,
1937 UserMode,
1938 FALSE,
1939 NULL );
1940 UserEnterCo();
1941 return ret;
1942 }
1943
1944 BOOL FASTCALL
1945 MsqIsHung(PUSER_MESSAGE_QUEUE MessageQueue)
1946 {
1947 LARGE_INTEGER LargeTickCount;
1948
1949 KeQueryTickCount(&LargeTickCount);
1950 return ((LargeTickCount.u.LowPart - MessageQueue->LastMsgRead) > MSQ_HUNG);
1951 }
1952
1953 VOID
1954 CALLBACK
1955 HungAppSysTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
1956 {
1957 DoTheScreenSaver();
1958 TRACE("HungAppSysTimerProc\n");
1959 // Process list of windows that are hung and waiting.
1960 }
1961
1962 BOOLEAN FASTCALL
1963 MsqInitializeMessageQueue(PTHREADINFO pti, PUSER_MESSAGE_QUEUE MessageQueue)
1964 {
1965 struct _ETHREAD *Thread;
1966 LARGE_INTEGER LargeTickCount;
1967 NTSTATUS Status;
1968
1969 Thread = pti->pEThread;
1970 MessageQueue->Thread = Thread;
1971 MessageQueue->CaretInfo = (PTHRDCARETINFO)(MessageQueue + 1);
1972 InitializeListHead(&MessageQueue->PostedMessagesListHead);
1973 InitializeListHead(&MessageQueue->SentMessagesListHead);
1974 InitializeListHead(&MessageQueue->HardwareMessagesListHead);
1975 InitializeListHead(&MessageQueue->DispatchingMessagesHead);
1976 InitializeListHead(&MessageQueue->LocalDispatchingMessagesHead);
1977 MessageQueue->QuitPosted = FALSE;
1978 MessageQueue->QuitExitCode = 0;
1979 KeQueryTickCount(&LargeTickCount);
1980 MessageQueue->LastMsgRead = LargeTickCount.u.LowPart;
1981 MessageQueue->spwndFocus = NULL;
1982 MessageQueue->NewMessagesHandle = NULL;
1983 MessageQueue->iCursorLevel = 0;
1984 MessageQueue->CursorObject = NULL;
1985 RtlCopyMemory(MessageQueue->afKeyState, gafAsyncKeyState, sizeof(gafAsyncKeyState));
1986
1987 Status = ZwCreateEvent(&MessageQueue->NewMessagesHandle, EVENT_ALL_ACCESS,
1988 NULL, SynchronizationEvent, FALSE);
1989 if (!NT_SUCCESS(Status))
1990 {
1991 return FALSE;
1992 }
1993
1994 Status = ObReferenceObjectByHandle(MessageQueue->NewMessagesHandle, 0,
1995 ExEventObjectType, KernelMode,
1996 (PVOID*)&MessageQueue->NewMessages, NULL);
1997 if (!NT_SUCCESS(Status))
1998 {
1999 ZwClose(MessageQueue->NewMessagesHandle);
2000 MessageQueue->NewMessagesHandle = NULL;
2001 return FALSE;
2002 }
2003
2004 return TRUE;
2005 }
2006
2007 VOID FASTCALL
2008 MsqCleanupMessageQueue(PTHREADINFO pti)
2009 {
2010 PUSER_MESSAGE_QUEUE MessageQueue;
2011 PLIST_ENTRY CurrentEntry;
2012 PUSER_MESSAGE CurrentMessage;
2013 PUSER_SENT_MESSAGE CurrentSentMessage;
2014
2015 MessageQueue = pti->MessageQueue;
2016
2017 /* cleanup posted messages */
2018 while (!IsListEmpty(&MessageQueue->PostedMessagesListHead))
2019 {
2020 CurrentEntry = RemoveHeadList(&MessageQueue->PostedMessagesListHead);
2021 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
2022 ListEntry);
2023 MsqDestroyMessage(CurrentMessage);
2024 }
2025
2026 /* remove the messages that have not yet been dispatched */
2027 while (!IsListEmpty(&MessageQueue->SentMessagesListHead))
2028 {
2029 CurrentEntry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
2030 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
2031 ListEntry);
2032
2033 /* if it is a callback and this queue is not the sender queue, dereference queue */
2034 if ((CurrentSentMessage->CompletionCallback) && (CurrentSentMessage->CallBackSenderQueue != MessageQueue))
2035 {
2036 IntDereferenceMessageQueue(CurrentSentMessage->CallBackSenderQueue);
2037 }
2038
2039 TRACE("Notify the sender and remove a message from the queue that had not been dispatched\n");
2040 /* Only if the message has a sender was the message in the DispatchingList */
2041 if ((CurrentSentMessage->SenderQueue)
2042 && (CurrentSentMessage->DispatchingListEntry.Flink != NULL))
2043 {
2044 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
2045 }
2046
2047 /* wake the sender's thread */
2048 if (CurrentSentMessage->CompletionEvent != NULL)
2049 {
2050 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
2051 }
2052
2053 if (CurrentSentMessage->HasPackedLParam == TRUE)
2054 {
2055 if (CurrentSentMessage->Msg.lParam)
2056 ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
2057 }
2058
2059 /* if the message has a sender */
2060 if (CurrentSentMessage->SenderQueue)
2061 {
2062 /* dereference our and the sender's message queue */
2063 IntDereferenceMessageQueue(MessageQueue);
2064 IntDereferenceMessageQueue(CurrentSentMessage->SenderQueue);
2065 }
2066
2067 /* free the message */
2068 ExFreePool(CurrentSentMessage);
2069 }
2070
2071 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
2072 ExitThread() was called in a SendMessage() umode callback */
2073 while (!IsListEmpty(&MessageQueue->LocalDispatchingMessagesHead))
2074 {
2075 CurrentEntry = RemoveHeadList(&MessageQueue->LocalDispatchingMessagesHead);
2076 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
2077 ListEntry);
2078
2079 /* if it is a callback and this queue is not the sender queue, dereference queue */
2080 if ((CurrentSentMessage->CompletionCallback) && (CurrentSentMessage->CallBackSenderQueue != MessageQueue))
2081 {
2082 IntDereferenceMessageQueue(CurrentSentMessage->CallBackSenderQueue);
2083 }
2084
2085 /* remove the message from the dispatching list */
2086 if(CurrentSentMessage->DispatchingListEntry.Flink != NULL)
2087 {
2088 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
2089 }
2090
2091 TRACE("Notify the sender, the thread has been terminated while dispatching a message!\n");
2092
2093 /* wake the sender's thread */
2094 if (CurrentSentMessage->CompletionEvent != NULL)
2095 {
2096 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
2097 }
2098
2099 if (CurrentSentMessage->HasPackedLParam == TRUE)
2100 {
2101 if (CurrentSentMessage->Msg.lParam)
2102 ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
2103 }
2104
2105 /* if the message has a sender */
2106 if (CurrentSentMessage->SenderQueue)
2107 {
2108 /* dereference our and the sender's message queue */
2109 IntDereferenceMessageQueue(MessageQueue);
2110 IntDereferenceMessageQueue(CurrentSentMessage->SenderQueue);
2111 }
2112
2113 /* free the message */
2114 ExFreePool(CurrentSentMessage);
2115 }
2116
2117 /* tell other threads not to bother returning any info to us */
2118 while (! IsListEmpty(&MessageQueue->DispatchingMessagesHead))
2119 {
2120 CurrentEntry = RemoveHeadList(&MessageQueue->DispatchingMessagesHead);
2121 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
2122 DispatchingListEntry);
2123 CurrentSentMessage->CompletionEvent = NULL;
2124 CurrentSentMessage->Result = NULL;
2125
2126 /* do NOT dereference our message queue as it might get attempted to be
2127 locked later */
2128 }
2129
2130 // Clear it all out.
2131 if (pti->pcti)
2132 {
2133 pti->pcti->fsWakeBits = 0;
2134 pti->pcti->fsChangeBits = 0;
2135 }
2136
2137 MessageQueue->nCntsQBits[QSRosKey] = 0;
2138 MessageQueue->nCntsQBits[QSRosMouseMove] = 0;
2139 MessageQueue->nCntsQBits[QSRosMouseButton] = 0;
2140 MessageQueue->nCntsQBits[QSRosPostMessage] = 0;
2141 MessageQueue->nCntsQBits[QSRosSendMessage] = 0;
2142 MessageQueue->nCntsQBits[QSRosHotKey] = 0;
2143
2144 if (MessageQueue->CursorObject)
2145 {
2146 PCURICON_OBJECT pCursor = MessageQueue->CursorObject;
2147
2148 /* Change to another cursor if we going to dereference current one
2149 Note: we can't use UserSetCursor because it uses current thread
2150 message queue instead of queue given for cleanup */
2151 if (IntGetSysCursorInfo()->CurrentCursorObject == pCursor)
2152 {
2153 HDC hdcScreen;
2154
2155 /* Get the screen DC */
2156 hdcScreen = IntGetScreenDC();
2157 if (hdcScreen)
2158 GreMovePointer(hdcScreen, -1, -1);
2159 IntGetSysCursorInfo()->CurrentCursorObject = NULL;
2160 }
2161
2162 UserDereferenceObject(pCursor);
2163 }
2164 }
2165
2166 PUSER_MESSAGE_QUEUE FASTCALL
2167 MsqCreateMessageQueue(PTHREADINFO pti)
2168 {
2169 PUSER_MESSAGE_QUEUE MessageQueue;
2170
2171 MessageQueue = (PUSER_MESSAGE_QUEUE)ExAllocatePoolWithTag(NonPagedPool,
2172 sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO),
2173 USERTAG_Q);
2174
2175 if (!MessageQueue)
2176 {
2177 return NULL;
2178 }
2179
2180 RtlZeroMemory(MessageQueue, sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO));
2181 /* hold at least one reference until it'll be destroyed */
2182 IntReferenceMessageQueue(MessageQueue);
2183 /* initialize the queue */
2184 if (!MsqInitializeMessageQueue(pti, MessageQueue))
2185 {
2186 IntDereferenceMessageQueue(MessageQueue);
2187 return NULL;
2188 }
2189
2190 return MessageQueue;
2191 }
2192
2193 VOID FASTCALL
2194 MsqDestroyMessageQueue(PTHREADINFO pti)
2195 {
2196 PDESKTOP desk;
2197 PUSER_MESSAGE_QUEUE MessageQueue = pti->MessageQueue;
2198
2199 MessageQueue->QF_flags |= QF_INDESTROY;
2200
2201 /* remove the message queue from any desktops */
2202 if ((desk = InterlockedExchangePointer((PVOID*)&MessageQueue->Desktop, 0)))
2203 {
2204 (void)InterlockedExchangePointer((PVOID*)&desk->ActiveMessageQueue, 0);
2205 IntDereferenceMessageQueue(MessageQueue);
2206 }
2207
2208 /* clean it up */
2209 MsqCleanupMessageQueue(pti);
2210
2211 if (MessageQueue->NewMessagesHandle != NULL)
2212 ZwClose(MessageQueue->NewMessagesHandle);
2213 MessageQueue->NewMessagesHandle = NULL;
2214 /* decrease the reference counter, if it hits zero, the queue will be freed */
2215 IntDereferenceMessageQueue(MessageQueue);
2216 }
2217
2218 LPARAM FASTCALL
2219 MsqSetMessageExtraInfo(LPARAM lParam)
2220 {
2221 LPARAM Ret;
2222 PTHREADINFO pti;
2223 PUSER_MESSAGE_QUEUE MessageQueue;
2224
2225 pti = PsGetCurrentThreadWin32Thread();
2226 MessageQueue = pti->MessageQueue;
2227 if(!MessageQueue)
2228 {
2229 return 0;
2230 }
2231
2232 Ret = MessageQueue->ExtraInfo;
2233 MessageQueue->ExtraInfo = lParam;
2234
2235 return Ret;
2236 }
2237
2238 LPARAM FASTCALL
2239 MsqGetMessageExtraInfo(VOID)
2240 {
2241 PTHREADINFO pti;
2242 PUSER_MESSAGE_QUEUE MessageQueue;
2243
2244 pti = PsGetCurrentThreadWin32Thread();
2245 MessageQueue = pti->MessageQueue;
2246 if(!MessageQueue)
2247 {
2248 return 0;
2249 }
2250
2251 return MessageQueue->ExtraInfo;
2252 }
2253
2254 // ReplyMessage is called by the thread receiving the window message.
2255 BOOL FASTCALL
2256 co_MsqReplyMessage( LRESULT lResult )
2257 {
2258 PUSER_SENT_MESSAGE Message;
2259 PTHREADINFO pti;
2260
2261 pti = PsGetCurrentThreadWin32Thread();
2262 Message = pti->pusmCurrent;
2263
2264 if (!Message) return FALSE;
2265
2266 if (Message->QS_Flags & QS_SMRESULT) return FALSE;
2267
2268 // SendMessageXxx || Callback msg and not a notify msg
2269 if (Message->SenderQueue || Message->CompletionCallback)
2270 {
2271 Message->lResult = lResult;
2272 Message->QS_Flags |= QS_SMRESULT;
2273 // See co_MsqDispatchOneSentMessage, change bits already accounted for and cleared and this msg is going away..
2274 }
2275 return TRUE;
2276 }
2277
2278 HWND FASTCALL
2279 MsqSetStateWindow(PUSER_MESSAGE_QUEUE MessageQueue, ULONG Type, HWND hWnd)
2280 {
2281 HWND Prev;
2282
2283 switch(Type)
2284 {
2285 case MSQ_STATE_CAPTURE:
2286 Prev = MessageQueue->spwndCapture ? UserHMGetHandle(MessageQueue->spwndCapture) : 0;
2287 MessageQueue->spwndCapture = ValidateHwndNoErr(hWnd);
2288 return Prev;
2289 case MSQ_STATE_ACTIVE:
2290 Prev = MessageQueue->spwndActive ? UserHMGetHandle(MessageQueue->spwndActive) : 0;
2291 MessageQueue->spwndActive = ValidateHwndNoErr(hWnd);
2292 return Prev;
2293 case MSQ_STATE_FOCUS:
2294 Prev = MessageQueue->spwndFocus ? UserHMGetHandle(MessageQueue->spwndFocus) : 0;
2295 MessageQueue->spwndFocus = ValidateHwndNoErr(hWnd);
2296 return Prev;
2297 case MSQ_STATE_MENUOWNER:
2298 Prev = MessageQueue->MenuOwner;
2299 MessageQueue->MenuOwner = hWnd;
2300 return Prev;
2301 case MSQ_STATE_MOVESIZE:
2302 Prev = MessageQueue->MoveSize;
2303 MessageQueue->MoveSize = hWnd;
2304 return Prev;
2305 case MSQ_STATE_CARET:
2306 ASSERT(MessageQueue->CaretInfo);
2307 Prev = MessageQueue->CaretInfo->hWnd;
2308 MessageQueue->CaretInfo->hWnd = hWnd;
2309 return Prev;
2310 }
2311
2312 return NULL;
2313 }
2314
2315 SHORT
2316 APIENTRY
2317 NtUserGetKeyState(INT key)
2318 {
2319 DWORD Ret;
2320
2321 UserEnterShared();
2322
2323 Ret = UserGetKeyState(key);
2324
2325 UserLeave();
2326
2327 return (SHORT)Ret;
2328 }
2329
2330
2331 DWORD
2332 APIENTRY
2333 NtUserGetKeyboardState(LPBYTE lpKeyState)
2334 {
2335 DWORD i, ret = TRUE;
2336 PTHREADINFO pti;
2337 PUSER_MESSAGE_QUEUE MessageQueue;
2338
2339 UserEnterShared();
2340
2341 pti = PsGetCurrentThreadWin32Thread();
2342 MessageQueue = pti->MessageQueue;
2343
2344 _SEH2_TRY
2345 {
2346 /* Probe and copy key state to an array */
2347 ProbeForWrite(lpKeyState, 256 * sizeof(BYTE), 1);
2348 for (i = 0; i < 256; ++i)
2349 {
2350 lpKeyState[i] = 0;
2351 if (IS_KEY_DOWN(MessageQueue->afKeyState, i))
2352 lpKeyState[i] |= KS_DOWN_BIT;
2353 if (IS_KEY_LOCKED(MessageQueue->afKeyState, i))
2354 lpKeyState[i] |= KS_LOCK_BIT;
2355 }
2356 }
2357 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2358 {
2359 SetLastNtError(_SEH2_GetExceptionCode());
2360 ret = FALSE;
2361 }
2362 _SEH2_END;
2363
2364 UserLeave();
2365
2366 return ret;
2367 }
2368
2369 BOOL
2370 APIENTRY
2371 NtUserSetKeyboardState(LPBYTE pKeyState)
2372 {
2373 UINT i;
2374 BOOL bRet = TRUE;
2375 PTHREADINFO pti;
2376 PUSER_MESSAGE_QUEUE MessageQueue;
2377
2378 UserEnterExclusive();
2379
2380 pti = PsGetCurrentThreadWin32Thread();
2381 MessageQueue = pti->MessageQueue;
2382
2383 _SEH2_TRY
2384 {
2385 ProbeForRead(pKeyState, 256 * sizeof(BYTE), 1);
2386 for (i = 0; i < 256; ++i)
2387 {
2388 SET_KEY_DOWN(MessageQueue->afKeyState, i, pKeyState[i] & KS_DOWN_BIT);
2389 SET_KEY_LOCKED(MessageQueue->afKeyState, i, pKeyState[i] & KS_LOCK_BIT);
2390 }
2391 }
2392 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2393 {
2394 SetLastNtError(_SEH2_GetExceptionCode());
2395 bRet = FALSE;
2396 }
2397 _SEH2_END;
2398
2399 UserLeave();
2400
2401 return bRet;
2402 }
2403
2404 /* EOF */