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