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