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