Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS Win32k subsystem * PURPOSE: General input functions * FILE: win32ss/user/ntuser/input.c * PROGRAMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) * Rafal Harabien (rafalh@reactos.org) */
#include <win32k.h>DBG_DEFAULT_CHANNEL(UserInput);
/* GLOBALS *******************************************************************/
PTHREADINFO ptiRawInput;PKTIMER MasterTimer = NULL;PATTACHINFO gpai = NULL;INT paiCount = 0;HANDLE ghKeyboardDevice = NULL;
static DWORD LastInputTick = 0;static HANDLE ghMouseDevice;
/* FUNCTIONS *****************************************************************/
/* * IntLastInputTick * * Updates or gets last input tick count */static DWORD FASTCALLIntLastInputTick(BOOL bUpdate){ if (bUpdate) { LastInputTick = EngGetTickCount32(); if (gpsi) gpsi->dwLastRITEventTickCount = LastInputTick; } return LastInputTick;}
/* * DoTheScreenSaver * * Check if screensaver should be started and sends message to SAS window */VOID FASTCALLDoTheScreenSaver(VOID){ DWORD Test, TO;
if (gspv.iScrSaverTimeout > 0) // Zero means Off. { Test = EngGetTickCount32(); Test = Test - LastInputTick; TO = 1000 * gspv.iScrSaverTimeout; if (Test > TO) { TRACE("Screensaver Message Start! Tick %lu Timeout %d \n", Test, gspv.iScrSaverTimeout);
if (ppiScrnSaver) // We are or we are not the screensaver, prevent reentry... { if (!(ppiScrnSaver->W32PF_flags & W32PF_IDLESCREENSAVER)) { ppiScrnSaver->W32PF_flags |= W32PF_IDLESCREENSAVER; ERR("Screensaver is Idle\n"); } } else { PUSER_MESSAGE_QUEUE ForegroundQueue = IntGetFocusMessageQueue(); if (ForegroundQueue && ForegroundQueue->spwndActive) UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_START_SCREENSAVE, 1); // lParam 1 == Secure else UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_START_SCREENSAVE, 0); } } }}
/* * OpenInputDevice * * Opens input device for asynchronous access */staticNTSTATUS NTAPIOpenInputDevice(PHANDLE pHandle, PFILE_OBJECT *ppObject, CONST WCHAR *pszDeviceName){ UNICODE_STRING DeviceName; OBJECT_ATTRIBUTES ObjectAttributes; NTSTATUS Status; IO_STATUS_BLOCK Iosb;
RtlInitUnicodeString(&DeviceName, pszDeviceName);
InitializeObjectAttributes(&ObjectAttributes, &DeviceName, OBJ_KERNEL_HANDLE, NULL, NULL);
Status = ZwOpenFile(pHandle, FILE_ALL_ACCESS, &ObjectAttributes, &Iosb, 0, 0); if (NT_SUCCESS(Status) && ppObject) { Status = ObReferenceObjectByHandle(*pHandle, SYNCHRONIZE, NULL, KernelMode, (PVOID*)ppObject, NULL); ASSERT(NT_SUCCESS(Status)); }
return Status;}
/* * RawInputThreadMain * * Reads data from input devices and supports win32 timers */VOID NTAPIRawInputThreadMain(VOID){ NTSTATUS MouStatus = STATUS_UNSUCCESSFUL, KbdStatus = STATUS_UNSUCCESSFUL, Status; IO_STATUS_BLOCK MouIosb, KbdIosb; PFILE_OBJECT pKbdDevice = NULL, pMouDevice = NULL; LARGE_INTEGER ByteOffset; //LARGE_INTEGER WaitTimeout; PVOID WaitObjects[4], pSignaledObject = NULL; KWAIT_BLOCK WaitBlockArray[RTL_NUMBER_OF(WaitObjects)]; ULONG cWaitObjects = 0, cMaxWaitObjects = 2; MOUSE_INPUT_DATA MouseInput; KEYBOARD_INPUT_DATA KeyInput; PVOID ShutdownEvent; HWINSTA hWinSta;
ByteOffset.QuadPart = (LONGLONG)0; //WaitTimeout.QuadPart = (LONGLONG)(-10000000);
ptiRawInput = GetW32ThreadInfo(); ptiRawInput->TIF_flags |= TIF_SYSTEMTHREAD; ptiRawInput->pClientInfo->dwTIFlags = ptiRawInput->TIF_flags;
TRACE("Raw Input Thread %p\n", ptiRawInput);
KeSetPriorityThread(&PsGetCurrentThread()->Tcb, LOW_REALTIME_PRIORITY + 3);
Status = ObOpenObjectByPointer(InputWindowStation, 0, NULL, MAXIMUM_ALLOWED, ExWindowStationObjectType, UserMode, (PHANDLE)&hWinSta); if (NT_SUCCESS(Status)) { UserSetProcessWindowStation(hWinSta); } else { ASSERT(FALSE); /* Failed to open the interactive winsta! What now? */ }
UserEnterExclusive(); StartTheTimers(); UserLeave();
NT_ASSERT(ghMouseDevice == NULL); NT_ASSERT(ghKeyboardDevice == NULL);
PoRequestShutdownEvent(&ShutdownEvent); for (;;) { if (!ghMouseDevice) { /* Check if mouse device already exists */ Status = OpenInputDevice(&ghMouseDevice, &pMouDevice, L"\\Device\\PointerClass0" ); if (NT_SUCCESS(Status)) { ++cMaxWaitObjects; TRACE("Mouse connected!\n"); } } if (!ghKeyboardDevice) { /* Check if keyboard device already exists */ Status = OpenInputDevice(&ghKeyboardDevice, &pKbdDevice, L"\\Device\\KeyboardClass0"); if (NT_SUCCESS(Status)) { ++cMaxWaitObjects; TRACE("Keyboard connected!\n"); // Get and load keyboard attributes. UserInitKeyboard(ghKeyboardDevice); UserEnterExclusive(); // Register the Window hotkey. UserRegisterHotKey(PWND_BOTTOM, IDHK_WINKEY, MOD_WIN, 0); // Register the Window Snap hotkey. UserRegisterHotKey(PWND_BOTTOM, IDHK_SNAP_LEFT, MOD_WIN, VK_LEFT); UserRegisterHotKey(PWND_BOTTOM, IDHK_SNAP_RIGHT, MOD_WIN, VK_RIGHT); UserRegisterHotKey(PWND_BOTTOM, IDHK_SNAP_UP, MOD_WIN, VK_UP); UserRegisterHotKey(PWND_BOTTOM, IDHK_SNAP_DOWN, MOD_WIN, VK_DOWN); // Register the debug hotkeys. SetDebugHotKeys(); UserLeave(); } }
/* Reset WaitHandles array */ cWaitObjects = 0; WaitObjects[cWaitObjects++] = ShutdownEvent; WaitObjects[cWaitObjects++] = MasterTimer;
if (ghMouseDevice) { /* Try to read from mouse if previous reading is not pending */ if (MouStatus != STATUS_PENDING) { MouStatus = ZwReadFile(ghMouseDevice, NULL, NULL, NULL, &MouIosb, &MouseInput, sizeof(MOUSE_INPUT_DATA), &ByteOffset, NULL); }
if (MouStatus == STATUS_PENDING) WaitObjects[cWaitObjects++] = &pMouDevice->Event; }
if (ghKeyboardDevice) { /* Try to read from keyboard if previous reading is not pending */ if (KbdStatus != STATUS_PENDING) { KbdStatus = ZwReadFile(ghKeyboardDevice, NULL, NULL, NULL, &KbdIosb, &KeyInput, sizeof(KEYBOARD_INPUT_DATA), &ByteOffset, NULL);
} if (KbdStatus == STATUS_PENDING) WaitObjects[cWaitObjects++] = &pKbdDevice->Event; }
/* If all objects are pending, wait for them */ if (cWaitObjects == cMaxWaitObjects) { Status = KeWaitForMultipleObjects(cWaitObjects, WaitObjects, WaitAny, UserRequest, KernelMode, TRUE, NULL,//&WaitTimeout, WaitBlockArray);
if ((Status >= STATUS_WAIT_0) && (Status < (STATUS_WAIT_0 + (LONG)cWaitObjects))) { /* Some device has finished reading */ pSignaledObject = WaitObjects[Status - STATUS_WAIT_0];
/* Check if it is mouse or keyboard and update status */ if ((MouStatus == STATUS_PENDING) && (pSignaledObject == &pMouDevice->Event)) { MouStatus = MouIosb.Status; } else if ((KbdStatus == STATUS_PENDING) && (pSignaledObject == &pKbdDevice->Event)) { KbdStatus = KbdIosb.Status; } else if (pSignaledObject == MasterTimer) { ProcessTimers(); } else if (pSignaledObject == ShutdownEvent) { break; } else ASSERT(FALSE); } }
/* Have we successed reading from mouse? */ if (NT_SUCCESS(MouStatus) && MouStatus != STATUS_PENDING) { TRACE("MouseEvent\n");
/* Set LastInputTick */ IntLastInputTick(TRUE);
/* Process data */ UserEnterExclusive(); UserProcessMouseInput(&MouseInput); UserLeave(); } else if (MouStatus != STATUS_PENDING) ERR("Failed to read from mouse: %x.\n", MouStatus);
/* Have we successed reading from keyboard? */ if (NT_SUCCESS(KbdStatus) && KbdStatus != STATUS_PENDING) { TRACE("KeyboardEvent: %s %04x\n", (KeyInput.Flags & KEY_BREAK) ? "up" : "down", KeyInput.MakeCode);
/* Set LastInputTick */ IntLastInputTick(TRUE);
/* Process data */ UserEnterExclusive(); UserProcessKeyboardInput(&KeyInput); UserLeave(); } else if (KbdStatus != STATUS_PENDING) ERR("Failed to read from keyboard: %x.\n", KbdStatus); }
if (ghMouseDevice) { (void)ZwCancelIoFile(ghMouseDevice, &MouIosb); ObCloseHandle(ghMouseDevice, KernelMode); ObDereferenceObject(pMouDevice); ghMouseDevice = NULL; }
if (ghKeyboardDevice) { (void)ZwCancelIoFile(ghKeyboardDevice, &KbdIosb); ObCloseHandle(ghKeyboardDevice, KernelMode); ObDereferenceObject(pKbdDevice); ghKeyboardDevice = NULL; }
ERR("Raw Input Thread Exit!\n");}
/* * InitInputImpl * * Inits input implementation */CODE_SEG("INIT")NTSTATUSNTAPIInitInputImpl(VOID){ MasterTimer = ExAllocatePoolWithTag(NonPagedPool, sizeof(KTIMER), USERTAG_SYSTEM); if (!MasterTimer) { ERR("Failed to allocate memory\n"); ASSERT(FALSE); return STATUS_UNSUCCESSFUL; } KeInitializeTimer(MasterTimer);
return STATUS_SUCCESS;}
BOOL FASTCALLIntBlockInput(PTHREADINFO pti, BOOL BlockIt){ PTHREADINFO OldBlock; ASSERT(pti);
if(!pti->rpdesk || ((pti->TIF_flags & TIF_INCLEANUP) && BlockIt)) { /* * Fail blocking if exiting the thread */
return FALSE; }
/* * FIXME: Check access rights of the window station * e.g. services running in the service window station cannot block input */ if(!ThreadHasInputAccess(pti) || !IntIsActiveDesktop(pti->rpdesk)) { EngSetLastError(ERROR_ACCESS_DENIED); return FALSE; }
ASSERT(pti->rpdesk); OldBlock = pti->rpdesk->BlockInputThread; if(OldBlock) { if(OldBlock != pti) { EngSetLastError(ERROR_ACCESS_DENIED); return FALSE; } pti->rpdesk->BlockInputThread = (BlockIt ? pti : NULL); return OldBlock == NULL; }
pti->rpdesk->BlockInputThread = (BlockIt ? pti : NULL); return OldBlock == NULL;}
BOOLAPIENTRYNtUserBlockInput( BOOL BlockIt){ BOOL ret;
TRACE("Enter NtUserBlockInput\n"); UserEnterExclusive();
ret = IntBlockInput(PsGetCurrentThreadWin32Thread(), BlockIt);
UserLeave(); TRACE("Leave NtUserBlockInput, ret=%i\n", ret);
return ret;}
BOOLFASTCALLIsRemoveAttachThread(PTHREADINFO pti){ NTSTATUS Status; PATTACHINFO pai; BOOL Ret = TRUE; PTHREADINFO ptiFrom = NULL, ptiTo = NULL;
do { if (!gpai) return TRUE;
pai = gpai; // Bottom of the list.
do { if (pai->pti2 == pti) { ptiFrom = pai->pti1; ptiTo = pti; break; } if (pai->pti1 == pti) { ptiFrom = pti; ptiTo = pai->pti2; break; } pai = pai->paiNext;
} while (pai);
if (!pai && !ptiFrom && !ptiTo) break;
Status = UserAttachThreadInput(ptiFrom, ptiTo, FALSE); if (!NT_SUCCESS(Status)) Ret = FALSE;
} while (Ret);
return Ret;}
// Win: zzzAttachThreadInputNTSTATUS FASTCALLUserAttachThreadInput(PTHREADINFO ptiFrom, PTHREADINFO ptiTo, BOOL fAttach){ MSG msg; PATTACHINFO pai; PCURICON_OBJECT CurIcon;
/* Can not be the same thread. */ if (ptiFrom == ptiTo) return STATUS_INVALID_PARAMETER;
/* Do not attach to system threads or between different desktops. */ if (ptiFrom->TIF_flags & TIF_DONTATTACHQUEUE || ptiTo->TIF_flags & TIF_DONTATTACHQUEUE || ptiFrom->rpdesk != ptiTo->rpdesk) return STATUS_ACCESS_DENIED;
/* MSDN Note: Keyboard and mouse events received by both threads are processed by the thread specified by the idAttachTo. */
/* If Attach set, allocate and link. */ if (fAttach) { pai = ExAllocatePoolWithTag(PagedPool, sizeof(ATTACHINFO), USERTAG_ATTACHINFO); if (!pai) return STATUS_NO_MEMORY;
pai->paiNext = gpai; pai->pti1 = ptiFrom; pai->pti2 = ptiTo; gpai = pai; paiCount++; ERR("Attach Allocated! ptiFrom 0x%p ptiTo 0x%p paiCount %d\n",ptiFrom,ptiTo,paiCount);
if (ptiTo->MessageQueue != ptiFrom->MessageQueue) {
ptiTo->MessageQueue->iCursorLevel -= ptiFrom->iCursorLevel;
if (ptiFrom->MessageQueue == gpqForeground) { ERR("ptiFrom is Foreground\n"); ptiTo->MessageQueue->spwndActive = ptiFrom->MessageQueue->spwndActive; ptiTo->MessageQueue->spwndFocus = ptiFrom->MessageQueue->spwndFocus; ptiTo->MessageQueue->spwndCapture = ptiFrom->MessageQueue->spwndCapture; ptiTo->MessageQueue->QF_flags ^= ((ptiTo->MessageQueue->QF_flags ^ ptiFrom->MessageQueue->QF_flags) & QF_CAPTURELOCKED); RtlCopyMemory(&ptiTo->MessageQueue->CaretInfo, &ptiFrom->MessageQueue->CaretInfo, sizeof(ptiTo->MessageQueue->CaretInfo)); IntSetFocusMessageQueue(NULL); IntSetFocusMessageQueue(ptiTo->MessageQueue); gptiForeground = ptiTo; } else { ERR("ptiFrom NOT Foreground\n"); if ( ptiTo->MessageQueue->spwndActive == 0 ) ptiTo->MessageQueue->spwndActive = ptiFrom->MessageQueue->spwndActive; if ( ptiTo->MessageQueue->spwndFocus == 0 ) ptiTo->MessageQueue->spwndFocus = ptiFrom->MessageQueue->spwndFocus; }
CurIcon = ptiFrom->MessageQueue->CursorObject;
MsqDestroyMessageQueue(ptiFrom);
if (CurIcon) { // Could be global. Keep it above the water line! UserReferenceObject(CurIcon); }
if (CurIcon && UserObjectInDestroy(UserHMGetHandle(CurIcon))) { UserDereferenceObject(CurIcon); CurIcon = NULL; }
ptiFrom->MessageQueue = ptiTo->MessageQueue;
// Pass cursor From if To is null. Pass test_SetCursor parent_id == current pti ID. if (CurIcon && ptiTo->MessageQueue->CursorObject == NULL) { ERR("ptiTo receiving ptiFrom Cursor\n"); ptiTo->MessageQueue->CursorObject = CurIcon; }
ptiFrom->MessageQueue->cThreads++; ERR("ptiTo S Share count %u\n", ptiFrom->MessageQueue->cThreads);
IntReferenceMessageQueue(ptiTo->MessageQueue); } else { ERR("Attach Threads are already associated!\n"); } } else /* If clear, unlink and free it. */ { BOOL Hit = FALSE; PATTACHINFO *ppai;
if (!gpai) return STATUS_INVALID_PARAMETER;
/* Search list and free if found or return false. */ ppai = &gpai; while (*ppai != NULL) { if ( (*ppai)->pti2 == ptiTo && (*ppai)->pti1 == ptiFrom ) { pai = *ppai; /* Remove it from the list */ *ppai = (*ppai)->paiNext; ExFreePoolWithTag(pai, USERTAG_ATTACHINFO); paiCount--; Hit = TRUE; break; } ppai = &((*ppai)->paiNext); }
if (!Hit) return STATUS_INVALID_PARAMETER;
ERR("Attach Free! ptiFrom 0x%p ptiTo 0x%p paiCount %d\n",ptiFrom,ptiTo,paiCount);
if (ptiTo->MessageQueue == ptiFrom->MessageQueue) { PWND spwndActive = ptiTo->MessageQueue->spwndActive; PWND spwndFocus = ptiTo->MessageQueue->spwndFocus;
if (gptiForeground == ptiFrom) { ERR("ptiTo is now pti FG.\n"); // MessageQueue foreground is set so switch threads. gptiForeground = ptiTo; } ptiTo->MessageQueue->cThreads--; ERR("ptiTo E Share count %u\n", ptiTo->MessageQueue->cThreads); ASSERT(ptiTo->MessageQueue->cThreads >= 1);
IntDereferenceMessageQueue(ptiTo->MessageQueue);
ptiFrom->MessageQueue = MsqCreateMessageQueue(ptiFrom);
if (spwndActive) { if (spwndActive->head.pti == ptiFrom) { ptiFrom->MessageQueue->spwndActive = spwndActive; ptiTo->MessageQueue->spwndActive = 0; } } if (spwndFocus) { if (spwndFocus->head.pti == ptiFrom) { ptiFrom->MessageQueue->spwndFocus = spwndFocus; ptiTo->MessageQueue->spwndFocus = 0; } } ptiTo->MessageQueue->iCursorLevel -= ptiFrom->iCursorLevel; } else { ERR("Detaching Threads are not associated!\n"); } } /* Note that key state, which can be ascertained by calls to the GetKeyState or GetKeyboardState function, is reset after a call to AttachThreadInput. ATM which one? */ RtlCopyMemory(ptiTo->MessageQueue->afKeyState, gafAsyncKeyState, sizeof(gafAsyncKeyState));
ptiTo->MessageQueue->msgDblClk.message = 0;
/* Generate mouse move message */ msg.message = WM_MOUSEMOVE; msg.wParam = UserGetMouseButtonsState(); msg.lParam = MAKELPARAM(gpsi->ptCursor.x, gpsi->ptCursor.y); msg.pt = gpsi->ptCursor; co_MsqInsertMouseMessage(&msg, 0, 0, TRUE);
return STATUS_SUCCESS;}
BOOLAPIENTRYNtUserAttachThreadInput( IN DWORD idAttach, IN DWORD idAttachTo, IN BOOL fAttach){ NTSTATUS Status; PTHREADINFO pti, ptiTo; BOOL Ret = FALSE;
UserEnterExclusive(); TRACE("Enter NtUserAttachThreadInput %s\n",(fAttach ? "TRUE" : "FALSE" ));
pti = IntTID2PTI(UlongToHandle(idAttach)); ptiTo = IntTID2PTI(UlongToHandle(idAttachTo));
if ( !pti || !ptiTo ) { TRACE("AttachThreadInput pti or ptiTo NULL.\n"); EngSetLastError(ERROR_INVALID_PARAMETER); goto Exit; }
Status = UserAttachThreadInput( pti, ptiTo, fAttach); if (!NT_SUCCESS(Status)) { TRACE("AttachThreadInput Error Status 0x%x. \n",Status); EngSetLastError(RtlNtStatusToDosError(Status)); } else Ret = TRUE;
Exit: TRACE("Leave NtUserAttachThreadInput, ret=%d\n",Ret); UserLeave(); return Ret;}
/* * NtUserSendInput * * Generates input events from software */UINTAPIENTRYNtUserSendInput( UINT nInputs, LPINPUT pInput, INT cbSize){ PTHREADINFO pti; UINT uRet = 0;
TRACE("Enter NtUserSendInput\n"); UserEnterExclusive();
pti = PsGetCurrentThreadWin32Thread(); ASSERT(pti);
if (!pti->rpdesk) { goto cleanup; }
if (!nInputs || !pInput || cbSize != sizeof(INPUT)) { EngSetLastError(ERROR_INVALID_PARAMETER); goto cleanup; }
/* * FIXME: Check access rights of the window station * e.g. services running in the service window station cannot block input */ if (!ThreadHasInputAccess(pti) || !IntIsActiveDesktop(pti->rpdesk)) { EngSetLastError(ERROR_ACCESS_DENIED); goto cleanup; }
while (nInputs--) { INPUT SafeInput; NTSTATUS Status;
Status = MmCopyFromCaller(&SafeInput, pInput++, sizeof(INPUT)); if (!NT_SUCCESS(Status)) { SetLastNtError(Status); goto cleanup; }
switch (SafeInput.type) { case INPUT_MOUSE: if (UserSendMouseInput(&SafeInput.mi, TRUE)) uRet++; break; case INPUT_KEYBOARD: if (UserSendKeyboardInput(&SafeInput.ki, TRUE)) uRet++; break; case INPUT_HARDWARE: FIXME("INPUT_HARDWARE not supported!\n"); break; default: ERR("SendInput(): Invalid input type: 0x%x\n", SafeInput.type); break; } }
cleanup: TRACE("Leave NtUserSendInput, ret=%u\n", uRet); UserLeave(); return uRet;}
/* EOF */