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 screensaver 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 CODE_SEG("INIT") 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 // Win: zzzAttachThreadInput 479 NTSTATUS FASTCALL 480 UserAttachThreadInput(PTHREADINFO ptiFrom, PTHREADINFO ptiTo, BOOL fAttach) 481 { 482 MSG msg; 483 PATTACHINFO pai; 484 PCURICON_OBJECT CurIcon; 485 486 /* Can not be the same thread. */ 487 if (ptiFrom == ptiTo) return STATUS_INVALID_PARAMETER; 488 489 /* Do not attach to system threads or between different desktops. */ 490 if (ptiFrom->TIF_flags & TIF_DONTATTACHQUEUE || 491 ptiTo->TIF_flags & TIF_DONTATTACHQUEUE || 492 ptiFrom->rpdesk != ptiTo->rpdesk) 493 return STATUS_ACCESS_DENIED; 494 495 /* MSDN Note: 496 Keyboard and mouse events received by both threads are processed by the thread specified by the idAttachTo. 497 */ 498 499 /* If Attach set, allocate and link. */ 500 if (fAttach) 501 { 502 pai = ExAllocatePoolWithTag(PagedPool, sizeof(ATTACHINFO), USERTAG_ATTACHINFO); 503 if (!pai) return STATUS_NO_MEMORY; 504 505 pai->paiNext = gpai; 506 pai->pti1 = ptiFrom; 507 pai->pti2 = ptiTo; 508 gpai = pai; 509 paiCount++; 510 ERR("Attach Allocated! ptiFrom 0x%p ptiTo 0x%p paiCount %d\n",ptiFrom,ptiTo,paiCount); 511 512 if (ptiTo->MessageQueue != ptiFrom->MessageQueue) 513 { 514 515 ptiTo->MessageQueue->iCursorLevel -= ptiFrom->iCursorLevel; 516 517 if (ptiFrom->MessageQueue == gpqForeground) 518 { 519 ERR("ptiFrom is Foreground\n"); 520 ptiTo->MessageQueue->spwndActive = ptiFrom->MessageQueue->spwndActive; 521 ptiTo->MessageQueue->spwndFocus = ptiFrom->MessageQueue->spwndFocus; 522 ptiTo->MessageQueue->spwndCapture = ptiFrom->MessageQueue->spwndCapture; 523 ptiTo->MessageQueue->QF_flags ^= ((ptiTo->MessageQueue->QF_flags ^ ptiFrom->MessageQueue->QF_flags) & QF_CAPTURELOCKED); 524 RtlCopyMemory(&ptiTo->MessageQueue->CaretInfo, 525 &ptiFrom->MessageQueue->CaretInfo, 526 sizeof(ptiTo->MessageQueue->CaretInfo)); 527 IntSetFocusMessageQueue(NULL); 528 IntSetFocusMessageQueue(ptiTo->MessageQueue); 529 gptiForeground = ptiTo; 530 } 531 else 532 { 533 ERR("ptiFrom NOT Foreground\n"); 534 if ( ptiTo->MessageQueue->spwndActive == 0 ) 535 ptiTo->MessageQueue->spwndActive = ptiFrom->MessageQueue->spwndActive; 536 if ( ptiTo->MessageQueue->spwndFocus == 0 ) 537 ptiTo->MessageQueue->spwndFocus = ptiFrom->MessageQueue->spwndFocus; 538 } 539 540 CurIcon = ptiFrom->MessageQueue->CursorObject; 541 542 MsqDestroyMessageQueue(ptiFrom); 543 544 if (CurIcon) 545 { 546 // Could be global. Keep it above the water line! 547 UserReferenceObject(CurIcon); 548 } 549 550 if (CurIcon && UserObjectInDestroy(UserHMGetHandle(CurIcon))) 551 { 552 UserDereferenceObject(CurIcon); 553 CurIcon = NULL; 554 } 555 556 ptiFrom->MessageQueue = ptiTo->MessageQueue; 557 558 // Pass cursor From if To is null. Pass test_SetCursor parent_id == current pti ID. 559 if (CurIcon && ptiTo->MessageQueue->CursorObject == NULL) 560 { 561 ERR("ptiTo receiving ptiFrom Cursor\n"); 562 ptiTo->MessageQueue->CursorObject = CurIcon; 563 } 564 565 ptiFrom->MessageQueue->cThreads++; 566 ERR("ptiTo S Share count %u\n", ptiFrom->MessageQueue->cThreads); 567 568 IntReferenceMessageQueue(ptiTo->MessageQueue); 569 } 570 else 571 { 572 ERR("Attach Threads are already associated!\n"); 573 } 574 } 575 else /* If clear, unlink and free it. */ 576 { 577 BOOL Hit = FALSE; 578 PATTACHINFO *ppai; 579 580 if (!gpai) return STATUS_INVALID_PARAMETER; 581 582 /* Search list and free if found or return false. */ 583 ppai = &gpai; 584 while (*ppai != NULL) 585 { 586 if ( (*ppai)->pti2 == ptiTo && (*ppai)->pti1 == ptiFrom ) 587 { 588 pai = *ppai; 589 /* Remove it from the list */ 590 *ppai = (*ppai)->paiNext; 591 ExFreePoolWithTag(pai, USERTAG_ATTACHINFO); 592 paiCount--; 593 Hit = TRUE; 594 break; 595 } 596 ppai = &((*ppai)->paiNext); 597 } 598 599 if (!Hit) return STATUS_INVALID_PARAMETER; 600 601 ERR("Attach Free! ptiFrom 0x%p ptiTo 0x%p paiCount %d\n",ptiFrom,ptiTo,paiCount); 602 603 if (ptiTo->MessageQueue == ptiFrom->MessageQueue) 604 { 605 PWND spwndActive = ptiTo->MessageQueue->spwndActive; 606 PWND spwndFocus = ptiTo->MessageQueue->spwndFocus; 607 608 if (gptiForeground == ptiFrom) 609 { 610 ERR("ptiTo is now pti FG.\n"); 611 // MessageQueue foreground is set so switch threads. 612 gptiForeground = ptiTo; 613 } 614 ptiTo->MessageQueue->cThreads--; 615 ERR("ptiTo E Share count %u\n", ptiTo->MessageQueue->cThreads); 616 ASSERT(ptiTo->MessageQueue->cThreads >= 1); 617 618 IntDereferenceMessageQueue(ptiTo->MessageQueue); 619 620 ptiFrom->MessageQueue = MsqCreateMessageQueue(ptiFrom); 621 622 if (spwndActive) 623 { 624 if (spwndActive->head.pti == ptiFrom) 625 { 626 ptiFrom->MessageQueue->spwndActive = spwndActive; 627 ptiTo->MessageQueue->spwndActive = 0; 628 } 629 } 630 if (spwndFocus) 631 { 632 if (spwndFocus->head.pti == ptiFrom) 633 { 634 ptiFrom->MessageQueue->spwndFocus = spwndFocus; 635 ptiTo->MessageQueue->spwndFocus = 0; 636 } 637 } 638 ptiTo->MessageQueue->iCursorLevel -= ptiFrom->iCursorLevel; 639 } 640 else 641 { 642 ERR("Detaching Threads are not associated!\n"); 643 } 644 } 645 /* Note that key state, which can be ascertained by calls to the GetKeyState 646 or GetKeyboardState function, is reset after a call to AttachThreadInput. 647 ATM which one? 648 */ 649 RtlCopyMemory(ptiTo->MessageQueue->afKeyState, gafAsyncKeyState, sizeof(gafAsyncKeyState)); 650 651 ptiTo->MessageQueue->msgDblClk.message = 0; 652 653 /* Generate mouse move message */ 654 msg.message = WM_MOUSEMOVE; 655 msg.wParam = UserGetMouseButtonsState(); 656 msg.lParam = MAKELPARAM(gpsi->ptCursor.x, gpsi->ptCursor.y); 657 msg.pt = gpsi->ptCursor; 658 co_MsqInsertMouseMessage(&msg, 0, 0, TRUE); 659 660 return STATUS_SUCCESS; 661 } 662 663 BOOL 664 APIENTRY 665 NtUserAttachThreadInput( 666 IN DWORD idAttach, 667 IN DWORD idAttachTo, 668 IN BOOL fAttach) 669 { 670 NTSTATUS Status; 671 PTHREADINFO pti, ptiTo; 672 BOOL Ret = FALSE; 673 674 UserEnterExclusive(); 675 TRACE("Enter NtUserAttachThreadInput %s\n",(fAttach ? "TRUE" : "FALSE" )); 676 677 pti = IntTID2PTI(UlongToHandle(idAttach)); 678 ptiTo = IntTID2PTI(UlongToHandle(idAttachTo)); 679 680 if ( !pti || !ptiTo ) 681 { 682 TRACE("AttachThreadInput pti or ptiTo NULL.\n"); 683 EngSetLastError(ERROR_INVALID_PARAMETER); 684 goto Exit; 685 } 686 687 Status = UserAttachThreadInput( pti, ptiTo, fAttach); 688 if (!NT_SUCCESS(Status)) 689 { 690 TRACE("AttachThreadInput Error Status 0x%x. \n",Status); 691 EngSetLastError(RtlNtStatusToDosError(Status)); 692 } 693 else Ret = TRUE; 694 695 Exit: 696 TRACE("Leave NtUserAttachThreadInput, ret=%d\n",Ret); 697 UserLeave(); 698 return Ret; 699 } 700 701 /* 702 * NtUserSendInput 703 * 704 * Generates input events from software 705 */ 706 UINT 707 APIENTRY 708 NtUserSendInput( 709 UINT nInputs, 710 LPINPUT pInput, 711 INT cbSize) 712 { 713 PTHREADINFO pti; 714 UINT uRet = 0; 715 716 TRACE("Enter NtUserSendInput\n"); 717 UserEnterExclusive(); 718 719 pti = PsGetCurrentThreadWin32Thread(); 720 ASSERT(pti); 721 722 if (!pti->rpdesk) 723 { 724 goto cleanup; 725 } 726 727 if (!nInputs || !pInput || cbSize != sizeof(INPUT)) 728 { 729 EngSetLastError(ERROR_INVALID_PARAMETER); 730 goto cleanup; 731 } 732 733 /* 734 * FIXME: Check access rights of the window station 735 * e.g. services running in the service window station cannot block input 736 */ 737 if (!ThreadHasInputAccess(pti) || 738 !IntIsActiveDesktop(pti->rpdesk)) 739 { 740 EngSetLastError(ERROR_ACCESS_DENIED); 741 goto cleanup; 742 } 743 744 while (nInputs--) 745 { 746 INPUT SafeInput; 747 NTSTATUS Status; 748 749 Status = MmCopyFromCaller(&SafeInput, pInput++, sizeof(INPUT)); 750 if (!NT_SUCCESS(Status)) 751 { 752 SetLastNtError(Status); 753 goto cleanup; 754 } 755 756 switch (SafeInput.type) 757 { 758 case INPUT_MOUSE: 759 if (UserSendMouseInput(&SafeInput.mi, TRUE)) 760 uRet++; 761 break; 762 case INPUT_KEYBOARD: 763 if (UserSendKeyboardInput(&SafeInput.ki, TRUE)) 764 uRet++; 765 break; 766 case INPUT_HARDWARE: 767 FIXME("INPUT_HARDWARE not supported!\n"); 768 break; 769 default: 770 ERR("SendInput(): Invalid input type: 0x%x\n", SafeInput.type); 771 break; 772 } 773 } 774 775 cleanup: 776 TRACE("Leave NtUserSendInput, ret=%u\n", uRet); 777 UserLeave(); 778 return uRet; 779 } 780 781 /* EOF */ 782