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