[WIN32SS:NTUSER] Implement "Window Snap" feature (#1226)
[reactos.git] / win32ss / user / ntuser / input.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Win32k subsystem
4 * PURPOSE: General input functions
5 * FILE: win32ss/user/ntuser/input.c
6 * PROGRAMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * Rafal Harabien (rafalh@reactos.org)
8 */
9
10 #include <win32k.h>
11 DBG_DEFAULT_CHANNEL(UserInput);
12
13 /* GLOBALS *******************************************************************/
14
15 PTHREADINFO ptiRawInput;
16 PKTIMER MasterTimer = NULL;
17 PATTACHINFO gpai = NULL;
18 INT paiCount = 0;
19 HANDLE ghKeyboardDevice = NULL;
20
21 static DWORD LastInputTick = 0;
22 static HANDLE ghMouseDevice;
23
24 /* FUNCTIONS *****************************************************************/
25
26 /*
27 * IntLastInputTick
28 *
29 * Updates or gets last input tick count
30 */
31 static DWORD FASTCALL
32 IntLastInputTick(BOOL bUpdate)
33 {
34 if (bUpdate)
35 {
36 LARGE_INTEGER TickCount;
37 KeQueryTickCount(&TickCount);
38 LastInputTick = MsqCalculateMessageTime(&TickCount);
39 if (gpsi) gpsi->dwLastRITEventTickCount = LastInputTick;
40 }
41 return LastInputTick;
42 }
43
44 /*
45 * DoTheScreenSaver
46 *
47 * Check if scrensaver should be started and sends message to SAS window
48 */
49 VOID FASTCALL
50 DoTheScreenSaver(VOID)
51 {
52 LARGE_INTEGER TickCount;
53 DWORD Test, TO;
54
55 if (gspv.iScrSaverTimeout > 0) // Zero means Off.
56 {
57 KeQueryTickCount(&TickCount);
58 Test = MsqCalculateMessageTime(&TickCount);
59 Test = Test - LastInputTick;
60 TO = 1000 * gspv.iScrSaverTimeout;
61 if (Test > TO)
62 {
63 TRACE("Screensaver Message Start! Tick %lu Timeout %d \n", Test, gspv.iScrSaverTimeout);
64
65 if (ppiScrnSaver) // We are or we are not the screensaver, prevent reentry...
66 {
67 if (!(ppiScrnSaver->W32PF_flags & W32PF_IDLESCREENSAVER))
68 {
69 ppiScrnSaver->W32PF_flags |= W32PF_IDLESCREENSAVER;
70 ERR("Screensaver is Idle\n");
71 }
72 }
73 else
74 {
75 PUSER_MESSAGE_QUEUE ForegroundQueue = IntGetFocusMessageQueue();
76 if (ForegroundQueue && ForegroundQueue->spwndActive)
77 UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_START_SCREENSAVE, 1); // lParam 1 == Secure
78 else
79 UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_START_SCREENSAVE, 0);
80 }
81 }
82 }
83 }
84
85 /*
86 * OpenInputDevice
87 *
88 * Opens input device for asynchronous access
89 */
90 static
91 NTSTATUS NTAPI
92 OpenInputDevice(PHANDLE pHandle, PFILE_OBJECT *ppObject, CONST WCHAR *pszDeviceName)
93 {
94 UNICODE_STRING DeviceName;
95 OBJECT_ATTRIBUTES ObjectAttributes;
96 NTSTATUS Status;
97 IO_STATUS_BLOCK Iosb;
98
99 RtlInitUnicodeString(&DeviceName, pszDeviceName);
100
101 InitializeObjectAttributes(&ObjectAttributes,
102 &DeviceName,
103 OBJ_KERNEL_HANDLE,
104 NULL,
105 NULL);
106
107 Status = ZwOpenFile(pHandle,
108 FILE_ALL_ACCESS,
109 &ObjectAttributes,
110 &Iosb,
111 0,
112 0);
113 if (NT_SUCCESS(Status) && ppObject)
114 {
115 Status = ObReferenceObjectByHandle(*pHandle, SYNCHRONIZE, NULL, KernelMode, (PVOID*)ppObject, NULL);
116 ASSERT(NT_SUCCESS(Status));
117 }
118
119 return Status;
120 }
121
122 /*
123 * RawInputThreadMain
124 *
125 * Reads data from input devices and supports win32 timers
126 */
127 VOID NTAPI
128 RawInputThreadMain(VOID)
129 {
130 NTSTATUS MouStatus = STATUS_UNSUCCESSFUL, KbdStatus = STATUS_UNSUCCESSFUL, Status;
131 IO_STATUS_BLOCK MouIosb, KbdIosb;
132 PFILE_OBJECT pKbdDevice = NULL, pMouDevice = NULL;
133 LARGE_INTEGER ByteOffset;
134 //LARGE_INTEGER WaitTimeout;
135 PVOID WaitObjects[4], pSignaledObject = NULL;
136 KWAIT_BLOCK WaitBlockArray[RTL_NUMBER_OF(WaitObjects)];
137 ULONG cWaitObjects = 0, cMaxWaitObjects = 2;
138 MOUSE_INPUT_DATA MouseInput;
139 KEYBOARD_INPUT_DATA KeyInput;
140 PVOID ShutdownEvent;
141 HWINSTA hWinSta;
142
143 ByteOffset.QuadPart = (LONGLONG)0;
144 //WaitTimeout.QuadPart = (LONGLONG)(-10000000);
145
146 ptiRawInput = GetW32ThreadInfo();
147 ptiRawInput->TIF_flags |= TIF_SYSTEMTHREAD;
148 ptiRawInput->pClientInfo->dwTIFlags = ptiRawInput->TIF_flags;
149
150 TRACE("Raw Input Thread %p\n", ptiRawInput);
151
152 KeSetPriorityThread(&PsGetCurrentThread()->Tcb,
153 LOW_REALTIME_PRIORITY + 3);
154
155 Status = ObOpenObjectByPointer(InputWindowStation,
156 0,
157 NULL,
158 MAXIMUM_ALLOWED,
159 ExWindowStationObjectType,
160 UserMode,
161 (PHANDLE)&hWinSta);
162 if (NT_SUCCESS(Status))
163 {
164 UserSetProcessWindowStation(hWinSta);
165 }
166 else
167 {
168 ASSERT(FALSE);
169 /* Failed to open the interactive winsta! What now? */
170 }
171
172 UserEnterExclusive();
173 StartTheTimers();
174 UserLeave();
175
176 NT_ASSERT(ghMouseDevice == NULL);
177 NT_ASSERT(ghKeyboardDevice == NULL);
178
179 PoRequestShutdownEvent(&ShutdownEvent);
180 for (;;)
181 {
182 if (!ghMouseDevice)
183 {
184 /* Check if mouse device already exists */
185 Status = OpenInputDevice(&ghMouseDevice, &pMouDevice, L"\\Device\\PointerClass0" );
186 if (NT_SUCCESS(Status))
187 {
188 ++cMaxWaitObjects;
189 TRACE("Mouse connected!\n");
190 }
191 }
192 if (!ghKeyboardDevice)
193 {
194 /* Check if keyboard device already exists */
195 Status = OpenInputDevice(&ghKeyboardDevice, &pKbdDevice, L"\\Device\\KeyboardClass0");
196 if (NT_SUCCESS(Status))
197 {
198 ++cMaxWaitObjects;
199 TRACE("Keyboard connected!\n");
200 // Get and load keyboard attributes.
201 UserInitKeyboard(ghKeyboardDevice);
202 UserEnterExclusive();
203 // Register the Window hotkey.
204 UserRegisterHotKey(PWND_BOTTOM, IDHK_WINKEY, MOD_WIN, 0);
205 // Register the Window Snap hotkey.
206 UserRegisterHotKey(PWND_BOTTOM, IDHK_SNAP_LEFT, MOD_WIN, VK_LEFT);
207 UserRegisterHotKey(PWND_BOTTOM, IDHK_SNAP_RIGHT, MOD_WIN, VK_RIGHT);
208 UserRegisterHotKey(PWND_BOTTOM, IDHK_SNAP_UP, MOD_WIN, VK_UP);
209 UserRegisterHotKey(PWND_BOTTOM, IDHK_SNAP_DOWN, MOD_WIN, VK_DOWN);
210 // Register the debug hotkeys.
211 StartDebugHotKeys();
212 UserLeave();
213 }
214 }
215
216 /* Reset WaitHandles array */
217 cWaitObjects = 0;
218 WaitObjects[cWaitObjects++] = ShutdownEvent;
219 WaitObjects[cWaitObjects++] = MasterTimer;
220
221 if (ghMouseDevice)
222 {
223 /* Try to read from mouse if previous reading is not pending */
224 if (MouStatus != STATUS_PENDING)
225 {
226 MouStatus = ZwReadFile(ghMouseDevice,
227 NULL,
228 NULL,
229 NULL,
230 &MouIosb,
231 &MouseInput,
232 sizeof(MOUSE_INPUT_DATA),
233 &ByteOffset,
234 NULL);
235 }
236
237 if (MouStatus == STATUS_PENDING)
238 WaitObjects[cWaitObjects++] = &pMouDevice->Event;
239 }
240
241 if (ghKeyboardDevice)
242 {
243 /* Try to read from keyboard if previous reading is not pending */
244 if (KbdStatus != STATUS_PENDING)
245 {
246 KbdStatus = ZwReadFile(ghKeyboardDevice,
247 NULL,
248 NULL,
249 NULL,
250 &KbdIosb,
251 &KeyInput,
252 sizeof(KEYBOARD_INPUT_DATA),
253 &ByteOffset,
254 NULL);
255
256 }
257 if (KbdStatus == STATUS_PENDING)
258 WaitObjects[cWaitObjects++] = &pKbdDevice->Event;
259 }
260
261 /* If all objects are pending, wait for them */
262 if (cWaitObjects == cMaxWaitObjects)
263 {
264 Status = KeWaitForMultipleObjects(cWaitObjects,
265 WaitObjects,
266 WaitAny,
267 UserRequest,
268 KernelMode,
269 TRUE,
270 NULL,//&WaitTimeout,
271 WaitBlockArray);
272
273 if ((Status >= STATUS_WAIT_0) &&
274 (Status < (STATUS_WAIT_0 + (LONG)cWaitObjects)))
275 {
276 /* Some device has finished reading */
277 pSignaledObject = WaitObjects[Status - STATUS_WAIT_0];
278
279 /* Check if it is mouse or keyboard and update status */
280 if ((MouStatus == STATUS_PENDING) &&
281 (pSignaledObject == &pMouDevice->Event))
282 {
283 MouStatus = MouIosb.Status;
284 }
285 else if ((KbdStatus == STATUS_PENDING) &&
286 (pSignaledObject == &pKbdDevice->Event))
287 {
288 KbdStatus = KbdIosb.Status;
289 }
290 else if (pSignaledObject == MasterTimer)
291 {
292 ProcessTimers();
293 }
294 else if (pSignaledObject == ShutdownEvent)
295 {
296 break;
297 }
298 else ASSERT(FALSE);
299 }
300 }
301
302 /* Have we successed reading from mouse? */
303 if (NT_SUCCESS(MouStatus) && MouStatus != STATUS_PENDING)
304 {
305 TRACE("MouseEvent\n");
306
307 /* Set LastInputTick */
308 IntLastInputTick(TRUE);
309
310 /* Process data */
311 UserEnterExclusive();
312 UserProcessMouseInput(&MouseInput);
313 UserLeave();
314 }
315 else if (MouStatus != STATUS_PENDING)
316 ERR("Failed to read from mouse: %x.\n", MouStatus);
317
318 /* Have we successed reading from keyboard? */
319 if (NT_SUCCESS(KbdStatus) && KbdStatus != STATUS_PENDING)
320 {
321 TRACE("KeyboardEvent: %s %04x\n",
322 (KeyInput.Flags & KEY_BREAK) ? "up" : "down",
323 KeyInput.MakeCode);
324
325 /* Set LastInputTick */
326 IntLastInputTick(TRUE);
327
328 /* Process data */
329 UserEnterExclusive();
330 UserProcessKeyboardInput(&KeyInput);
331 UserLeave();
332 }
333 else if (KbdStatus != STATUS_PENDING)
334 ERR("Failed to read from keyboard: %x.\n", KbdStatus);
335 }
336
337 if (ghMouseDevice)
338 {
339 (void)ZwCancelIoFile(ghMouseDevice, &MouIosb);
340 ObCloseHandle(ghMouseDevice, KernelMode);
341 ObDereferenceObject(pMouDevice);
342 ghMouseDevice = NULL;
343 }
344
345 if (ghKeyboardDevice)
346 {
347 (void)ZwCancelIoFile(ghKeyboardDevice, &KbdIosb);
348 ObCloseHandle(ghKeyboardDevice, KernelMode);
349 ObDereferenceObject(pKbdDevice);
350 ghKeyboardDevice = NULL;
351 }
352
353 ERR("Raw Input Thread Exit!\n");
354 }
355
356 /*
357 * InitInputImpl
358 *
359 * Inits input implementation
360 */
361 INIT_FUNCTION
362 NTSTATUS
363 NTAPI
364 InitInputImpl(VOID)
365 {
366 MasterTimer = ExAllocatePoolWithTag(NonPagedPool, sizeof(KTIMER), USERTAG_SYSTEM);
367 if (!MasterTimer)
368 {
369 ERR("Failed to allocate memory\n");
370 ASSERT(FALSE);
371 return STATUS_UNSUCCESSFUL;
372 }
373 KeInitializeTimer(MasterTimer);
374
375 return STATUS_SUCCESS;
376 }
377
378 BOOL FASTCALL
379 IntBlockInput(PTHREADINFO pti, BOOL BlockIt)
380 {
381 PTHREADINFO OldBlock;
382 ASSERT(pti);
383
384 if(!pti->rpdesk || ((pti->TIF_flags & TIF_INCLEANUP) && BlockIt))
385 {
386 /*
387 * Fail blocking if exiting the thread
388 */
389
390 return FALSE;
391 }
392
393 /*
394 * FIXME: Check access rights of the window station
395 * e.g. services running in the service window station cannot block input
396 */
397 if(!ThreadHasInputAccess(pti) ||
398 !IntIsActiveDesktop(pti->rpdesk))
399 {
400 EngSetLastError(ERROR_ACCESS_DENIED);
401 return FALSE;
402 }
403
404 ASSERT(pti->rpdesk);
405 OldBlock = pti->rpdesk->BlockInputThread;
406 if(OldBlock)
407 {
408 if(OldBlock != pti)
409 {
410 EngSetLastError(ERROR_ACCESS_DENIED);
411 return FALSE;
412 }
413 pti->rpdesk->BlockInputThread = (BlockIt ? pti : NULL);
414 return OldBlock == NULL;
415 }
416
417 pti->rpdesk->BlockInputThread = (BlockIt ? pti : NULL);
418 return OldBlock == NULL;
419 }
420
421 BOOL
422 APIENTRY
423 NtUserBlockInput(
424 BOOL BlockIt)
425 {
426 BOOL ret;
427
428 TRACE("Enter NtUserBlockInput\n");
429 UserEnterExclusive();
430
431 ret = IntBlockInput(PsGetCurrentThreadWin32Thread(), BlockIt);
432
433 UserLeave();
434 TRACE("Leave NtUserBlockInput, ret=%i\n", ret);
435
436 return ret;
437 }
438
439 BOOL
440 FASTCALL
441 IsRemoveAttachThread(PTHREADINFO pti)
442 {
443 NTSTATUS Status;
444 PATTACHINFO pai;
445 BOOL Ret = TRUE;
446 PTHREADINFO ptiFrom = NULL, ptiTo = NULL;
447
448 do
449 {
450 if (!gpai) return TRUE;
451
452 pai = gpai; // Bottom of the list.
453
454 do
455 {
456 if (pai->pti2 == pti)
457 {
458 ptiFrom = pai->pti1;
459 ptiTo = pti;
460 break;
461 }
462 if (pai->pti1 == pti)
463 {
464 ptiFrom = pti;
465 ptiTo = pai->pti2;
466 break;
467 }
468 pai = pai->paiNext;
469
470 } while (pai);
471
472 if (!pai && !ptiFrom && !ptiTo) break;
473
474 Status = UserAttachThreadInput(ptiFrom, ptiTo, FALSE);
475 if (!NT_SUCCESS(Status)) Ret = FALSE;
476
477 } while (Ret);
478
479 return Ret;
480 }
481
482 NTSTATUS FASTCALL
483 UserAttachThreadInput(PTHREADINFO ptiFrom, PTHREADINFO ptiTo, BOOL fAttach)
484 {
485 MSG msg;
486 PATTACHINFO pai;
487 PCURICON_OBJECT CurIcon;
488
489 /* Can not be the same thread. */
490 if (ptiFrom == ptiTo) return STATUS_INVALID_PARAMETER;
491
492 /* Do not attach to system threads or between different desktops. */
493 if (ptiFrom->TIF_flags & TIF_DONTATTACHQUEUE ||
494 ptiTo->TIF_flags & TIF_DONTATTACHQUEUE ||
495 ptiFrom->rpdesk != ptiTo->rpdesk)
496 return STATUS_ACCESS_DENIED;
497
498 /* MSDN Note:
499 Keyboard and mouse events received by both threads are processed by the thread specified by the idAttachTo.
500 */
501
502 /* If Attach set, allocate and link. */
503 if (fAttach)
504 {
505 pai = ExAllocatePoolWithTag(PagedPool, sizeof(ATTACHINFO), USERTAG_ATTACHINFO);
506 if (!pai) return STATUS_NO_MEMORY;
507
508 pai->paiNext = gpai;
509 pai->pti1 = ptiFrom;
510 pai->pti2 = ptiTo;
511 gpai = pai;
512 paiCount++;
513 ERR("Attach Allocated! ptiFrom 0x%p ptiTo 0x%p paiCount %d\n",ptiFrom,ptiTo,paiCount);
514
515 if (ptiTo->MessageQueue != ptiFrom->MessageQueue)
516 {
517
518 ptiTo->MessageQueue->iCursorLevel -= ptiFrom->iCursorLevel;
519
520 if (ptiFrom->MessageQueue == gpqForeground)
521 {
522 ERR("ptiFrom is Foreground\n");
523 ptiTo->MessageQueue->spwndActive = ptiFrom->MessageQueue->spwndActive;
524 ptiTo->MessageQueue->spwndFocus = ptiFrom->MessageQueue->spwndFocus;
525 ptiTo->MessageQueue->spwndCapture = ptiFrom->MessageQueue->spwndCapture;
526 ptiTo->MessageQueue->QF_flags ^= ((ptiTo->MessageQueue->QF_flags ^ ptiFrom->MessageQueue->QF_flags) & QF_CAPTURELOCKED);
527 RtlCopyMemory(&ptiTo->MessageQueue->CaretInfo,
528 &ptiFrom->MessageQueue->CaretInfo,
529 sizeof(ptiTo->MessageQueue->CaretInfo));
530 IntSetFocusMessageQueue(NULL);
531 IntSetFocusMessageQueue(ptiTo->MessageQueue);
532 gptiForeground = ptiTo;
533 }
534 else
535 {
536 ERR("ptiFrom NOT Foreground\n");
537 if ( ptiTo->MessageQueue->spwndActive == 0 )
538 ptiTo->MessageQueue->spwndActive = ptiFrom->MessageQueue->spwndActive;
539 if ( ptiTo->MessageQueue->spwndFocus == 0 )
540 ptiTo->MessageQueue->spwndFocus = ptiFrom->MessageQueue->spwndFocus;
541 }
542
543 CurIcon = ptiFrom->MessageQueue->CursorObject;
544
545 MsqDestroyMessageQueue(ptiFrom);
546
547 if (CurIcon)
548 {
549 // Could be global. Keep it above the water line!
550 UserReferenceObject(CurIcon);
551 }
552
553 if (CurIcon && UserObjectInDestroy(UserHMGetHandle(CurIcon)))
554 {
555 UserDereferenceObject(CurIcon);
556 CurIcon = NULL;
557 }
558
559 ptiFrom->MessageQueue = ptiTo->MessageQueue;
560
561 // Pass cursor From if To is null. Pass test_SetCursor parent_id == current pti ID.
562 if (CurIcon && ptiTo->MessageQueue->CursorObject == NULL)
563 {
564 ERR("ptiTo receiving ptiFrom Cursor\n");
565 ptiTo->MessageQueue->CursorObject = CurIcon;
566 }
567
568 ptiFrom->MessageQueue->cThreads++;
569 ERR("ptiTo S Share count %u\n", ptiFrom->MessageQueue->cThreads);
570
571 IntReferenceMessageQueue(ptiTo->MessageQueue);
572 }
573 else
574 {
575 ERR("Attach Threads are already associated!\n");
576 }
577 }
578 else /* If clear, unlink and free it. */
579 {
580 BOOL Hit = FALSE;
581 PATTACHINFO *ppai;
582
583 if (!gpai) return STATUS_INVALID_PARAMETER;
584
585 /* Search list and free if found or return false. */
586 ppai = &gpai;
587 while (*ppai != NULL)
588 {
589 if ( (*ppai)->pti2 == ptiTo && (*ppai)->pti1 == ptiFrom )
590 {
591 pai = *ppai;
592 /* Remove it from the list */
593 *ppai = (*ppai)->paiNext;
594 ExFreePoolWithTag(pai, USERTAG_ATTACHINFO);
595 paiCount--;
596 Hit = TRUE;
597 break;
598 }
599 ppai = &((*ppai)->paiNext);
600 }
601
602 if (!Hit) return STATUS_INVALID_PARAMETER;
603
604 ERR("Attach Free! ptiFrom 0x%p ptiTo 0x%p paiCount %d\n",ptiFrom,ptiTo,paiCount);
605
606 if (ptiTo->MessageQueue == ptiFrom->MessageQueue)
607 {
608 PWND spwndActive = ptiTo->MessageQueue->spwndActive;
609 PWND spwndFocus = ptiTo->MessageQueue->spwndFocus;
610
611 if (gptiForeground == ptiFrom)
612 {
613 ERR("ptiTo is now pti FG.\n");
614 // MessageQueue foreground is set so switch threads.
615 gptiForeground = ptiTo;
616 }
617 ptiTo->MessageQueue->cThreads--;
618 ERR("ptiTo E Share count %u\n", ptiTo->MessageQueue->cThreads);
619 ASSERT(ptiTo->MessageQueue->cThreads >= 1);
620
621 IntDereferenceMessageQueue(ptiTo->MessageQueue);
622
623 ptiFrom->MessageQueue = MsqCreateMessageQueue(ptiFrom);
624
625 if (spwndActive)
626 {
627 if (spwndActive->head.pti == ptiFrom)
628 {
629 ptiFrom->MessageQueue->spwndActive = spwndActive;
630 ptiTo->MessageQueue->spwndActive = 0;
631 }
632 }
633 if (spwndFocus)
634 {
635 if (spwndFocus->head.pti == ptiFrom)
636 {
637 ptiFrom->MessageQueue->spwndFocus = spwndFocus;
638 ptiTo->MessageQueue->spwndFocus = 0;
639 }
640 }
641 ptiTo->MessageQueue->iCursorLevel -= ptiFrom->iCursorLevel;
642 }
643 else
644 {
645 ERR("Detaching Threads are not associated!\n");
646 }
647 }
648 /* Note that key state, which can be ascertained by calls to the GetKeyState
649 or GetKeyboardState function, is reset after a call to AttachThreadInput.
650 ATM which one?
651 */
652 RtlCopyMemory(ptiTo->MessageQueue->afKeyState, gafAsyncKeyState, sizeof(gafAsyncKeyState));
653
654 ptiTo->MessageQueue->msgDblClk.message = 0;
655
656 /* Generate mouse move message */
657 msg.message = WM_MOUSEMOVE;
658 msg.wParam = UserGetMouseButtonsState();
659 msg.lParam = MAKELPARAM(gpsi->ptCursor.x, gpsi->ptCursor.y);
660 msg.pt = gpsi->ptCursor;
661 co_MsqInsertMouseMessage(&msg, 0, 0, TRUE);
662
663 return STATUS_SUCCESS;
664 }
665
666 BOOL
667 APIENTRY
668 NtUserAttachThreadInput(
669 IN DWORD idAttach,
670 IN DWORD idAttachTo,
671 IN BOOL fAttach)
672 {
673 NTSTATUS Status;
674 PTHREADINFO pti, ptiTo;
675 BOOL Ret = FALSE;
676
677 UserEnterExclusive();
678 TRACE("Enter NtUserAttachThreadInput %s\n",(fAttach ? "TRUE" : "FALSE" ));
679
680 pti = IntTID2PTI(UlongToHandle(idAttach));
681 ptiTo = IntTID2PTI(UlongToHandle(idAttachTo));
682
683 if ( !pti || !ptiTo )
684 {
685 TRACE("AttachThreadInput pti or ptiTo NULL.\n");
686 EngSetLastError(ERROR_INVALID_PARAMETER);
687 goto Exit;
688 }
689
690 Status = UserAttachThreadInput( pti, ptiTo, fAttach);
691 if (!NT_SUCCESS(Status))
692 {
693 TRACE("AttachThreadInput Error Status 0x%x. \n",Status);
694 EngSetLastError(RtlNtStatusToDosError(Status));
695 }
696 else Ret = TRUE;
697
698 Exit:
699 TRACE("Leave NtUserAttachThreadInput, ret=%d\n",Ret);
700 UserLeave();
701 return Ret;
702 }
703
704 /*
705 * NtUserSendInput
706 *
707 * Generates input events from software
708 */
709 UINT
710 APIENTRY
711 NtUserSendInput(
712 UINT nInputs,
713 LPINPUT pInput,
714 INT cbSize)
715 {
716 PTHREADINFO pti;
717 UINT uRet = 0;
718
719 TRACE("Enter NtUserSendInput\n");
720 UserEnterExclusive();
721
722 pti = PsGetCurrentThreadWin32Thread();
723 ASSERT(pti);
724
725 if (!pti->rpdesk)
726 {
727 goto cleanup;
728 }
729
730 if (!nInputs || !pInput || cbSize != sizeof(INPUT))
731 {
732 EngSetLastError(ERROR_INVALID_PARAMETER);
733 goto cleanup;
734 }
735
736 /*
737 * FIXME: Check access rights of the window station
738 * e.g. services running in the service window station cannot block input
739 */
740 if (!ThreadHasInputAccess(pti) ||
741 !IntIsActiveDesktop(pti->rpdesk))
742 {
743 EngSetLastError(ERROR_ACCESS_DENIED);
744 goto cleanup;
745 }
746
747 while (nInputs--)
748 {
749 INPUT SafeInput;
750 NTSTATUS Status;
751
752 Status = MmCopyFromCaller(&SafeInput, pInput++, sizeof(INPUT));
753 if (!NT_SUCCESS(Status))
754 {
755 SetLastNtError(Status);
756 goto cleanup;
757 }
758
759 switch (SafeInput.type)
760 {
761 case INPUT_MOUSE:
762 if (UserSendMouseInput(&SafeInput.mi, TRUE))
763 uRet++;
764 break;
765 case INPUT_KEYBOARD:
766 if (UserSendKeyboardInput(&SafeInput.ki, TRUE))
767 uRet++;
768 break;
769 case INPUT_HARDWARE:
770 FIXME("INPUT_HARDWARE not supported!");
771 break;
772 default:
773 ERR("SendInput(): Invalid input type: 0x%x\n", SafeInput.type);
774 break;
775 }
776 }
777
778 cleanup:
779 TRACE("Leave NtUserSendInput, ret=%u\n", uRet);
780 UserLeave();
781 return uRet;
782 }
783
784 /* EOF */