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 */ 785