reactos/reactos/subsystems/win32/win32k/ntuser/input.c
Rafal Harabien 2a799f8ad8 [WIN32K]
- Use 64 byte bitmap for keystate instead of 256 elements array (like Windows)
- Fix GetAsyncKeyState lowest bit meaning. See MSDN
- Call low level keyboard hook before setting async key state for keys which distinguish between left and right hand. Fixes user32:GetKeyState apitest failure
- Improve IsHotKey and delete GetHotKey function to avoid code duplication
- Support WIN key in hotkeys code instead of keyboard.c

svn path=/trunk/; revision=54063
2011-10-09 20:12:12 +00:00

1195 lines
34 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* PURPOSE: Window classes
* FILE: subsystems/win32/win32k/ntuser/input.c
* PROGRAMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
* Rafal Harabien (rafalh@reactos.org)
*/
#include <win32k.h>
DBG_DEFAULT_CHANNEL(UserInput);
extern NTSTATUS Win32kInitWin32Thread(PETHREAD Thread);
extern PPROCESSINFO ppiScrnSaver;
/* GLOBALS *******************************************************************/
PTHREADINFO ptiRawInput;
PTHREADINFO ptiKeyboard;
PTHREADINFO ptiMouse;
PKTIMER MasterTimer = NULL;
PATTACHINFO gpai = NULL;
HANDLE ghKeyboardDevice;
static DWORD LastInputTick = 0;
static HANDLE MouseDeviceHandle;
static HANDLE MouseThreadHandle;
static CLIENT_ID MouseThreadId;
static HANDLE KeyboardThreadHandle;
static CLIENT_ID KeyboardThreadId;
static HANDLE RawInputThreadHandle;
static CLIENT_ID RawInputThreadId;
static KEVENT InputThreadsStart;
static BOOLEAN InputThreadsRunning = FALSE;
/* FUNCTIONS *****************************************************************/
#define ClearMouseInput(mi) \
mi.dx = 0; \
mi.dy = 0; \
mi.mouseData = 0; \
mi.dwFlags = 0;
#define SendMouseEvent(mi) \
if(mi.dx != 0 || mi.dy != 0) \
mi.dwFlags |= MOUSEEVENTF_MOVE; \
if(mi.dwFlags) \
IntMouseInput(&mi,FALSE); \
ClearMouseInput(mi);
static DWORD FASTCALL
IntLastInputTick(BOOL bUpdate)
{
if (bUpdate)
{
LARGE_INTEGER TickCount;
KeQueryTickCount(&TickCount);
LastInputTick = MsqCalculateMessageTime(&TickCount);
if (gpsi) gpsi->dwLastRITEventTickCount = LastInputTick;
}
return LastInputTick;
}
VOID FASTCALL
DoTheScreenSaver(VOID)
{
LARGE_INTEGER TickCount;
DWORD Test, TO;
if (gspv.iScrSaverTimeout > 0) // Zero means Off.
{
KeQueryTickCount(&TickCount);
Test = MsqCalculateMessageTime(&TickCount);
Test = Test - LastInputTick;
TO = 1000 * gspv.iScrSaverTimeout;
if (Test > TO)
{
TRACE("Screensaver Message Start! Tick %d 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->ActiveWindow)
UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_START_SCREENSAVE, 1); // lParam 1 == Secure
else
UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_START_SCREENSAVE, 0);
}
}
}
}
static VOID NTAPI
IntProcessMouseInputData(PMOUSE_INPUT_DATA Data, ULONG InputCount)
{
PMOUSE_INPUT_DATA mid;
MOUSEINPUT mi;
ULONG i;
ClearMouseInput(mi);
mi.time = 0;
mi.dwExtraInfo = 0;
for(i = 0; i < InputCount; i++)
{
mid = (Data + i);
mi.dx += mid->LastX;
mi.dy += mid->LastY;
/* Check if the mouse move is absolute */
if (mid->Flags == MOUSE_MOVE_ABSOLUTE)
{
/* Set flag to convert to screen location */
mi.dwFlags |= MOUSEEVENTF_ABSOLUTE;
}
if(mid->ButtonFlags)
{
if(mid->ButtonFlags & MOUSE_LEFT_BUTTON_DOWN)
{
mi.dwFlags |= MOUSEEVENTF_LEFTDOWN;
SendMouseEvent(mi);
}
if(mid->ButtonFlags & MOUSE_LEFT_BUTTON_UP)
{
mi.dwFlags |= MOUSEEVENTF_LEFTUP;
SendMouseEvent(mi);
}
if(mid->ButtonFlags & MOUSE_MIDDLE_BUTTON_DOWN)
{
mi.dwFlags |= MOUSEEVENTF_MIDDLEDOWN;
SendMouseEvent(mi);
}
if(mid->ButtonFlags & MOUSE_MIDDLE_BUTTON_UP)
{
mi.dwFlags |= MOUSEEVENTF_MIDDLEUP;
SendMouseEvent(mi);
}
if(mid->ButtonFlags & MOUSE_RIGHT_BUTTON_DOWN)
{
mi.dwFlags |= MOUSEEVENTF_RIGHTDOWN;
SendMouseEvent(mi);
}
if(mid->ButtonFlags & MOUSE_RIGHT_BUTTON_UP)
{
mi.dwFlags |= MOUSEEVENTF_RIGHTUP;
SendMouseEvent(mi);
}
if(mid->ButtonFlags & MOUSE_BUTTON_4_DOWN)
{
mi.mouseData |= XBUTTON1;
mi.dwFlags |= MOUSEEVENTF_XDOWN;
SendMouseEvent(mi);
}
if(mid->ButtonFlags & MOUSE_BUTTON_4_UP)
{
mi.mouseData |= XBUTTON1;
mi.dwFlags |= MOUSEEVENTF_XUP;
SendMouseEvent(mi);
}
if(mid->ButtonFlags & MOUSE_BUTTON_5_DOWN)
{
mi.mouseData |= XBUTTON2;
mi.dwFlags |= MOUSEEVENTF_XDOWN;
SendMouseEvent(mi);
}
if(mid->ButtonFlags & MOUSE_BUTTON_5_UP)
{
mi.mouseData |= XBUTTON2;
mi.dwFlags |= MOUSEEVENTF_XUP;
SendMouseEvent(mi);
}
if(mid->ButtonFlags & MOUSE_WHEEL)
{
mi.mouseData = mid->ButtonData;
mi.dwFlags |= MOUSEEVENTF_WHEEL;
SendMouseEvent(mi);
}
}
}
SendMouseEvent(mi);
}
static VOID APIENTRY
MouseThreadMain(PVOID StartContext)
{
UNICODE_STRING MouseDeviceName = RTL_CONSTANT_STRING(L"\\Device\\PointerClass0");
OBJECT_ATTRIBUTES MouseObjectAttributes;
IO_STATUS_BLOCK Iosb;
NTSTATUS Status;
MOUSE_ATTRIBUTES MouseAttr;
KeSetPriorityThread(&PsGetCurrentThread()->Tcb,
LOW_REALTIME_PRIORITY + 3);
InitializeObjectAttributes(&MouseObjectAttributes,
&MouseDeviceName,
0,
NULL,
NULL);
do
{
LARGE_INTEGER DueTime;
KEVENT Event;
DueTime.QuadPart = (LONGLONG)(-10000000);
KeInitializeEvent(&Event, NotificationEvent, FALSE);
Status = KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, &DueTime);
Status = NtOpenFile(&MouseDeviceHandle,
FILE_ALL_ACCESS,
&MouseObjectAttributes,
&Iosb,
0,
FILE_SYNCHRONOUS_IO_ALERT);
} while (!NT_SUCCESS(Status));
/* Need to setup basic win32k for this thread to process WH_MOUSE_LL messages. */
Status = Win32kInitWin32Thread(PsGetCurrentThread());
if (!NT_SUCCESS(Status))
{
ERR("Win32K: Failed making mouse thread a win32 thread.\n");
return; //(Status);
}
ptiMouse = PsGetCurrentThreadWin32Thread();
ptiMouse->TIF_flags |= TIF_SYSTEMTHREAD;
TRACE("Mouse Thread 0x%x \n", ptiMouse);
KeSetPriorityThread(&PsGetCurrentThread()->Tcb,
LOW_REALTIME_PRIORITY + 3);
for(;;)
{
/*
* Wait to start input.
*/
TRACE("Mouse Input Thread Waiting for start event\n");
Status = KeWaitForSingleObject(&InputThreadsStart,
0,
KernelMode,
TRUE,
NULL);
TRACE("Mouse Input Thread Starting...\n");
/*FIXME: Does mouse attributes need to be used for anything */
Status = NtDeviceIoControlFile(MouseDeviceHandle,
NULL,
NULL,
NULL,
&Iosb,
IOCTL_MOUSE_QUERY_ATTRIBUTES,
&MouseAttr, sizeof(MOUSE_ATTRIBUTES),
NULL, 0);
if(!NT_SUCCESS(Status))
{
TRACE("Failed to get mouse attributes\n");
}
/*
* Receive and process mouse input.
*/
while(InputThreadsRunning)
{
MOUSE_INPUT_DATA MouseInput;
Status = NtReadFile(MouseDeviceHandle,
NULL,
NULL,
NULL,
&Iosb,
&MouseInput,
sizeof(MOUSE_INPUT_DATA),
NULL,
NULL);
if(Status == STATUS_ALERTED && !InputThreadsRunning)
{
break;
}
if(Status == STATUS_PENDING)
{
NtWaitForSingleObject(MouseDeviceHandle, FALSE, NULL);
Status = Iosb.Status;
}
if(!NT_SUCCESS(Status))
{
ERR("Win32K: Failed to read from mouse.\n");
return; //(Status);
}
TRACE("MouseEvent\n");
IntLastInputTick(TRUE);
UserEnterExclusive();
IntProcessMouseInputData(&MouseInput, Iosb.Information / sizeof(MOUSE_INPUT_DATA));
UserLeave();
}
TRACE("Mouse Input Thread Stopped...\n");
}
}
static VOID APIENTRY
KeyboardThreadMain(PVOID StartContext)
{
UNICODE_STRING KeyboardDeviceName = RTL_CONSTANT_STRING(L"\\Device\\KeyboardClass0");
OBJECT_ATTRIBUTES KeyboardObjectAttributes;
IO_STATUS_BLOCK Iosb;
NTSTATUS Status;
InitializeObjectAttributes(&KeyboardObjectAttributes,
&KeyboardDeviceName,
0,
NULL,
NULL);
do
{
LARGE_INTEGER DueTime;
KEVENT Event;
DueTime.QuadPart = (LONGLONG)(-10000000);
KeInitializeEvent(&Event, NotificationEvent, FALSE);
Status = KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, &DueTime);
Status = NtOpenFile(&ghKeyboardDevice,
FILE_ALL_ACCESS,
&KeyboardObjectAttributes,
&Iosb,
0,
FILE_SYNCHRONOUS_IO_ALERT);
} while (!NT_SUCCESS(Status));
/* Not sure if converting this thread to a win32 thread is such
a great idea. Since we're posting keyboard messages to the focus
window message queue, we'll be (indirectly) doing sendmessage
stuff from this thread (for WH_KEYBOARD_LL processing), which
means we need our own message queue. If keyboard messages were
instead queued to the system message queue, the thread removing
the message from the system message queue would be responsible
for WH_KEYBOARD_LL processing and we wouldn't need this thread
to be a win32 thread. */
Status = Win32kInitWin32Thread(PsGetCurrentThread());
if (!NT_SUCCESS(Status))
{
ERR("Win32K: Failed making keyboard thread a win32 thread.\n");
return; //(Status);
}
ptiKeyboard = PsGetCurrentThreadWin32Thread();
ptiKeyboard->TIF_flags |= TIF_SYSTEMTHREAD;
TRACE("Keyboard Thread 0x%x \n", ptiKeyboard);
KeSetPriorityThread(&PsGetCurrentThread()->Tcb,
LOW_REALTIME_PRIORITY + 3);
//IntKeyboardGetIndicatorTrans(ghKeyboardDevice,
// &IndicatorTrans);
for (;;)
{
/*
* Wait to start input.
*/
TRACE( "Keyboard Input Thread Waiting for start event\n" );
Status = KeWaitForSingleObject(&InputThreadsStart,
0,
KernelMode,
TRUE,
NULL);
TRACE( "Keyboard Input Thread Starting...\n" );
/*
* Receive and process keyboard input.
*/
while (InputThreadsRunning)
{
KEYBOARD_INPUT_DATA KeyInput;
TRACE("KeyInput @ %08x\n", &KeyInput);
Status = NtReadFile (ghKeyboardDevice,
NULL,
NULL,
NULL,
&Iosb,
&KeyInput,
sizeof(KEYBOARD_INPUT_DATA),
NULL,
NULL);
if(Status == STATUS_ALERTED && !InputThreadsRunning)
{
break;
}
if(Status == STATUS_PENDING)
{
NtWaitForSingleObject(ghKeyboardDevice, FALSE, NULL);
Status = Iosb.Status;
}
if(!NT_SUCCESS(Status))
{
ERR("Win32K: Failed to read from keyboard.\n");
return; //(Status);
}
TRACE("KeyRaw: %s %04x\n",
(KeyInput.Flags & KEY_BREAK) ? "up" : "down",
KeyInput.MakeCode );
if (Status == STATUS_ALERTED && !InputThreadsRunning)
break;
if (!NT_SUCCESS(Status))
{
ERR("Win32K: Failed to read from keyboard.\n");
return; //(Status);
}
/* Set LastInputTick */
IntLastInputTick(TRUE);
/* Process data */
UserEnterExclusive();
UserProcessKeyboardInput(&KeyInput);
UserLeave();
}
TRACE( "KeyboardInput Thread Stopped...\n" );
}
}
static PVOID Objects[2];
/*
Raw Input Thread.
Since this relies on InputThreadsStart, just fake it.
*/
static VOID APIENTRY
RawInputThreadMain(PVOID StartContext)
{
NTSTATUS Status;
LARGE_INTEGER DueTime;
DueTime.QuadPart = (LONGLONG)(-10000000);
do
{
KEVENT Event;
KeInitializeEvent(&Event, NotificationEvent, FALSE);
Status = KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, &DueTime);
} while (!NT_SUCCESS(Status));
Objects[0] = &InputThreadsStart;
Objects[1] = MasterTimer;
// This thread requires win32k!
Status = Win32kInitWin32Thread(PsGetCurrentThread());
if (!NT_SUCCESS(Status))
{
ERR("Win32K: Failed making Raw Input thread a win32 thread.\n");
return; //(Status);
}
ptiRawInput = PsGetCurrentThreadWin32Thread();
ptiRawInput->TIF_flags |= TIF_SYSTEMTHREAD;
TRACE("Raw Input Thread 0x%x \n", ptiRawInput);
KeSetPriorityThread(&PsGetCurrentThread()->Tcb,
LOW_REALTIME_PRIORITY + 3);
UserEnterExclusive();
StartTheTimers();
UserLeave();
//
// ATM, we just have one job to handle, merge the other two later.
//
for(;;)
{
TRACE( "Raw Input Thread Waiting for start event\n" );
Status = KeWaitForMultipleObjects( 2,
Objects,
WaitAll, //WaitAny,
WrUserRequest,
KernelMode,
TRUE,
NULL,
NULL);
TRACE( "Raw Input Thread Starting...\n" );
ProcessTimers();
}
ERR("Raw Input Thread Exit!\n");
}
INIT_FUNCTION
NTSTATUS
NTAPI
InitInputImpl(VOID)
{
NTSTATUS Status;
KeInitializeEvent(&InputThreadsStart, NotificationEvent, FALSE);
MasterTimer = ExAllocatePoolWithTag(NonPagedPool, sizeof(KTIMER), USERTAG_SYSTEM);
if (!MasterTimer)
{
ERR("Win32K: Failed making Raw Input thread a win32 thread.\n");
ASSERT(FALSE);
return STATUS_UNSUCCESSFUL;
}
KeInitializeTimer(MasterTimer);
/* Initialize the default keyboard layout */
if(!UserInitDefaultKeyboardLayout())
{
ERR("Failed to initialize default keyboard layout!\n");
}
Status = PsCreateSystemThread(&RawInputThreadHandle,
THREAD_ALL_ACCESS,
NULL,
NULL,
&RawInputThreadId,
RawInputThreadMain,
NULL);
if (!NT_SUCCESS(Status))
{
ERR("Win32K: Failed to create raw thread.\n");
}
Status = PsCreateSystemThread(&KeyboardThreadHandle,
THREAD_ALL_ACCESS,
NULL,
NULL,
&KeyboardThreadId,
KeyboardThreadMain,
NULL);
if (!NT_SUCCESS(Status))
{
ERR("Win32K: Failed to create keyboard thread.\n");
}
Status = PsCreateSystemThread(&MouseThreadHandle,
THREAD_ALL_ACCESS,
NULL,
NULL,
&MouseThreadId,
MouseThreadMain,
NULL);
if (!NT_SUCCESS(Status))
{
ERR("Win32K: Failed to create mouse thread.\n");
}
InputThreadsRunning = TRUE;
KeSetEvent(&InputThreadsStart, IO_NO_INCREMENT, FALSE);
return STATUS_SUCCESS;
}
NTSTATUS FASTCALL
CleanupInputImp(VOID)
{
return(STATUS_SUCCESS);
}
BOOL FASTCALL
IntBlockInput(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;
}
BOOL
APIENTRY
NtUserBlockInput(
BOOL BlockIt)
{
DECLARE_RETURN(BOOLEAN);
TRACE("Enter NtUserBlockInput\n");
UserEnterExclusive();
RETURN( IntBlockInput(PsGetCurrentThreadWin32Thread(), BlockIt));
CLEANUP:
TRACE("Leave NtUserBlockInput, ret=%i\n", _ret_);
UserLeave();
END_CLEANUP;
}
BOOL FASTCALL
IntMouseInput(MOUSEINPUT *mi, BOOL Injected)
{
const UINT SwapBtnMsg[2][2] =
{
{WM_LBUTTONDOWN, WM_RBUTTONDOWN},
{WM_LBUTTONUP, WM_RBUTTONUP}
};
const WPARAM SwapBtn[2] =
{
MK_LBUTTON, MK_RBUTTON
};
POINT MousePos;
PSYSTEM_CURSORINFO CurInfo;
BOOL SwapButtons;
MSG Msg;
ASSERT(mi);
CurInfo = IntGetSysCursorInfo();
if(!mi->time)
{
LARGE_INTEGER LargeTickCount;
KeQueryTickCount(&LargeTickCount);
mi->time = MsqCalculateMessageTime(&LargeTickCount);
}
SwapButtons = gspv.bMouseBtnSwap;
MousePos = gpsi->ptCursor;
if(mi->dwFlags & MOUSEEVENTF_MOVE)
{
if(mi->dwFlags & MOUSEEVENTF_ABSOLUTE)
{
MousePos.x = mi->dx * UserGetSystemMetrics(SM_CXVIRTUALSCREEN) >> 16;
MousePos.y = mi->dy * UserGetSystemMetrics(SM_CYVIRTUALSCREEN) >> 16;
}
else
{
MousePos.x += mi->dx;
MousePos.y += mi->dy;
}
}
/*
* Insert the messages into the system queue
*/
Msg.wParam = 0;
Msg.lParam = MAKELPARAM(MousePos.x, MousePos.y);
Msg.pt = MousePos;
if (IS_KEY_DOWN(gafAsyncKeyState, VK_SHIFT))
{
Msg.wParam |= MK_SHIFT;
}
if (IS_KEY_DOWN(gafAsyncKeyState, VK_CONTROL))
{
Msg.wParam |= MK_CONTROL;
}
if(mi->dwFlags & MOUSEEVENTF_MOVE)
{
UserSetCursorPos(MousePos.x, MousePos.y, Injected, mi->dwExtraInfo, TRUE);
}
if(mi->dwFlags & MOUSEEVENTF_LEFTDOWN)
{
SET_KEY_DOWN(gafAsyncKeyState, VK_LBUTTON, TRUE);
Msg.message = SwapBtnMsg[0][SwapButtons];
CurInfo->ButtonsDown |= SwapBtn[SwapButtons];
Msg.wParam |= CurInfo->ButtonsDown;
co_MsqInsertMouseMessage(&Msg, Injected, mi->dwExtraInfo, TRUE);
}
else if(mi->dwFlags & MOUSEEVENTF_LEFTUP)
{
SET_KEY_DOWN(gafAsyncKeyState, VK_LBUTTON, FALSE);
Msg.message = SwapBtnMsg[1][SwapButtons];
CurInfo->ButtonsDown &= ~SwapBtn[SwapButtons];
Msg.wParam |= CurInfo->ButtonsDown;
co_MsqInsertMouseMessage(&Msg, Injected, mi->dwExtraInfo, TRUE);
}
if(mi->dwFlags & MOUSEEVENTF_MIDDLEDOWN)
{
SET_KEY_DOWN(gafAsyncKeyState, VK_MBUTTON, TRUE);
Msg.message = WM_MBUTTONDOWN;
CurInfo->ButtonsDown |= MK_MBUTTON;
Msg.wParam |= CurInfo->ButtonsDown;
co_MsqInsertMouseMessage(&Msg, Injected, mi->dwExtraInfo, TRUE);
}
else if(mi->dwFlags & MOUSEEVENTF_MIDDLEUP)
{
SET_KEY_DOWN(gafAsyncKeyState, VK_MBUTTON, FALSE);
Msg.message = WM_MBUTTONUP;
CurInfo->ButtonsDown &= ~MK_MBUTTON;
Msg.wParam |= CurInfo->ButtonsDown;
co_MsqInsertMouseMessage(&Msg, Injected, mi->dwExtraInfo, TRUE);
}
if(mi->dwFlags & MOUSEEVENTF_RIGHTDOWN)
{
SET_KEY_DOWN(gafAsyncKeyState, VK_RBUTTON, TRUE);
Msg.message = SwapBtnMsg[0][!SwapButtons];
CurInfo->ButtonsDown |= SwapBtn[!SwapButtons];
Msg.wParam |= CurInfo->ButtonsDown;
co_MsqInsertMouseMessage(&Msg, Injected, mi->dwExtraInfo, TRUE);
}
else if(mi->dwFlags & MOUSEEVENTF_RIGHTUP)
{
SET_KEY_DOWN(gafAsyncKeyState, VK_RBUTTON, FALSE);
Msg.message = SwapBtnMsg[1][!SwapButtons];
CurInfo->ButtonsDown &= ~SwapBtn[!SwapButtons];
Msg.wParam |= CurInfo->ButtonsDown;
co_MsqInsertMouseMessage(&Msg, Injected, mi->dwExtraInfo, TRUE);
}
if((mi->dwFlags & (MOUSEEVENTF_XDOWN | MOUSEEVENTF_XUP)) &&
(mi->dwFlags & MOUSEEVENTF_WHEEL))
{
/* fail because both types of events use the mouseData field */
return FALSE;
}
if(mi->dwFlags & MOUSEEVENTF_XDOWN)
{
Msg.message = WM_XBUTTONDOWN;
if(mi->mouseData & XBUTTON1)
{
SET_KEY_DOWN(gafAsyncKeyState, VK_XBUTTON1, TRUE);
CurInfo->ButtonsDown |= MK_XBUTTON1;
Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, XBUTTON1);
co_MsqInsertMouseMessage(&Msg, Injected, mi->dwExtraInfo, TRUE);
}
if(mi->mouseData & XBUTTON2)
{
SET_KEY_DOWN(gafAsyncKeyState, VK_XBUTTON2, TRUE);
CurInfo->ButtonsDown |= MK_XBUTTON2;
Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, XBUTTON2);
co_MsqInsertMouseMessage(&Msg, Injected, mi->dwExtraInfo, TRUE);
}
}
else if(mi->dwFlags & MOUSEEVENTF_XUP)
{
Msg.message = WM_XBUTTONUP;
if(mi->mouseData & XBUTTON1)
{
SET_KEY_DOWN(gafAsyncKeyState, VK_XBUTTON1, FALSE);
CurInfo->ButtonsDown &= ~MK_XBUTTON1;
Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, XBUTTON1);
co_MsqInsertMouseMessage(&Msg, Injected, mi->dwExtraInfo, TRUE);
}
if(mi->mouseData & XBUTTON2)
{
SET_KEY_DOWN(gafAsyncKeyState, VK_XBUTTON2, FALSE);
CurInfo->ButtonsDown &= ~MK_XBUTTON2;
Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, XBUTTON2);
co_MsqInsertMouseMessage(&Msg, Injected, mi->dwExtraInfo, TRUE);
}
}
if(mi->dwFlags & MOUSEEVENTF_WHEEL)
{
Msg.message = WM_MOUSEWHEEL;
Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, mi->mouseData);
co_MsqInsertMouseMessage(&Msg, Injected, mi->dwExtraInfo, TRUE);
}
return TRUE;
}
BOOL FASTCALL
UserAttachThreadInput(PTHREADINFO pti, PTHREADINFO ptiTo, BOOL fAttach)
{
PATTACHINFO pai;
/* Can not be the same thread.*/
if (pti == ptiTo) return FALSE;
/* Do not attach to system threads or between different desktops. */
if ( pti->TIF_flags & TIF_DONTATTACHQUEUE ||
ptiTo->TIF_flags & TIF_DONTATTACHQUEUE ||
pti->rpdesk != ptiTo->rpdesk )
return FALSE;
/* If Attach set, allocate and link. */
if ( fAttach )
{
pai = ExAllocatePoolWithTag(PagedPool, sizeof(ATTACHINFO), USERTAG_ATTACHINFO);
if ( !pai ) return FALSE;
pai->paiNext = gpai;
pai->pti1 = pti;
pai->pti2 = ptiTo;
gpai = pai;
}
else /* If clear, unlink and free it. */
{
PATTACHINFO paiprev = NULL;
if ( !gpai ) return FALSE;
pai = gpai;
/* Search list and free if found or return false. */
do
{
if ( pai->pti2 == ptiTo && pai->pti1 == pti ) break;
paiprev = pai;
pai = pai->paiNext;
} while (pai);
if ( !pai ) return FALSE;
if (paiprev) paiprev->paiNext = pai->paiNext;
ExFreePoolWithTag(pai, USERTAG_ATTACHINFO);
}
return TRUE;
}
UINT
APIENTRY
NtUserSendInput(
UINT nInputs,
LPINPUT pInput,
INT cbSize)
{
PTHREADINFO pti;
UINT cnt;
DECLARE_RETURN(UINT);
TRACE("Enter NtUserSendInput\n");
UserEnterExclusive();
pti = PsGetCurrentThreadWin32Thread();
ASSERT(pti);
if (!pti->rpdesk)
{
RETURN( 0);
}
if (!nInputs || !pInput || cbSize != sizeof(INPUT))
{
EngSetLastError(ERROR_INVALID_PARAMETER);
RETURN( 0);
}
/*
* 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( 0);
}
cnt = 0;
while (nInputs--)
{
INPUT SafeInput;
NTSTATUS Status;
Status = MmCopyFromCaller(&SafeInput, pInput++, sizeof(INPUT));
if (!NT_SUCCESS(Status))
{
SetLastNtError(Status);
RETURN( cnt);
}
switch (SafeInput.type)
{
case INPUT_MOUSE:
if (IntMouseInput(&SafeInput.mi, TRUE))
cnt++;
break;
case INPUT_KEYBOARD:
if (UserSendKeyboardInput(&SafeInput.ki, TRUE))
cnt++;
break;
case INPUT_HARDWARE:
break;
default:
ERR("SendInput(): Invalid input type: 0x%x\n", SafeInput.type);
break;
}
}
RETURN( cnt);
CLEANUP:
TRACE("Leave NtUserSendInput, ret=%i\n", _ret_);
UserLeave();
END_CLEANUP;
}
BOOL
FASTCALL
IntQueryTrackMouseEvent(
LPTRACKMOUSEEVENT lpEventTrack)
{
PDESKTOP pDesk;
PTHREADINFO pti;
pti = PsGetCurrentThreadWin32Thread();
pDesk = pti->rpdesk;
/* Always cleared with size set and return true. */
RtlZeroMemory(lpEventTrack , sizeof(TRACKMOUSEEVENT));
lpEventTrack->cbSize = sizeof(TRACKMOUSEEVENT);
if ( pDesk->dwDTFlags & (DF_TME_LEAVE | DF_TME_HOVER) &&
pDesk->spwndTrack &&
pti->MessageQueue == pDesk->spwndTrack->head.pti->MessageQueue )
{
if ( pDesk->htEx != HTCLIENT )
lpEventTrack->dwFlags |= TME_NONCLIENT;
if ( pDesk->dwDTFlags & DF_TME_LEAVE )
lpEventTrack->dwFlags |= TME_LEAVE;
if ( pDesk->dwDTFlags & DF_TME_HOVER )
{
lpEventTrack->dwFlags |= TME_HOVER;
lpEventTrack->dwHoverTime = pDesk->dwMouseHoverTime;
}
lpEventTrack->hwndTrack = UserHMGetHandle(pDesk->spwndTrack);
}
return TRUE;
}
BOOL
FASTCALL
IntTrackMouseEvent(
LPTRACKMOUSEEVENT lpEventTrack)
{
PDESKTOP pDesk;
PTHREADINFO pti;
PWND pWnd;
POINT point;
pti = PsGetCurrentThreadWin32Thread();
pDesk = pti->rpdesk;
if (!(pWnd = UserGetWindowObject(lpEventTrack->hwndTrack)))
return FALSE;
/* Tracking spwndTrack same as pWnd */
if ( lpEventTrack->dwFlags & TME_CANCEL ) // Canceled mode.
{
if ( lpEventTrack->dwFlags & TME_LEAVE )
pDesk->dwDTFlags &= ~DF_TME_LEAVE;
if ( lpEventTrack->dwFlags & TME_HOVER )
{
if ( pDesk->dwDTFlags & DF_TME_HOVER )
{ // Kill hover timer.
IntKillTimer(pWnd, ID_EVENT_SYSTIMER_MOUSEHOVER, TRUE);
pDesk->dwDTFlags &= ~DF_TME_HOVER;
}
}
}
else // Not Canceled.
{
pDesk->spwndTrack = pWnd;
if ( lpEventTrack->dwFlags & TME_LEAVE )
pDesk->dwDTFlags |= DF_TME_LEAVE;
if ( lpEventTrack->dwFlags & TME_HOVER )
{
pDesk->dwDTFlags |= DF_TME_HOVER;
if ( !lpEventTrack->dwHoverTime || lpEventTrack->dwHoverTime == HOVER_DEFAULT )
pDesk->dwMouseHoverTime = gspv.iMouseHoverTime; // use the system default hover time-out.
else
pDesk->dwMouseHoverTime = lpEventTrack->dwHoverTime;
// Start timer for the hover period.
IntSetTimer( pWnd, ID_EVENT_SYSTIMER_MOUSEHOVER, pDesk->dwMouseHoverTime, SystemTimerProc, TMRF_SYSTEM);
// Get windows thread message points.
point = pWnd->head.pti->ptLast;
// Set desktop mouse hover from the system default hover rectangle.
RECTL_vSetRect(&pDesk->rcMouseHover,
point.x - gspv.iMouseHoverWidth / 2,
point.y - gspv.iMouseHoverHeight / 2,
point.x + gspv.iMouseHoverWidth / 2,
point.y + gspv.iMouseHoverHeight / 2);
}
}
return TRUE;
}
BOOL
APIENTRY
NtUserTrackMouseEvent(
LPTRACKMOUSEEVENT lpEventTrack)
{
TRACKMOUSEEVENT saveTME;
BOOL Ret = FALSE;
TRACE("Enter NtUserTrackMouseEvent\n");
UserEnterExclusive();
_SEH2_TRY
{
ProbeForRead(lpEventTrack, sizeof(TRACKMOUSEEVENT), 1);
RtlCopyMemory(&saveTME, lpEventTrack, sizeof(TRACKMOUSEEVENT));
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
SetLastNtError(_SEH2_GetExceptionCode());
_SEH2_YIELD(goto Exit;)
}
_SEH2_END;
if ( saveTME.cbSize != sizeof(TRACKMOUSEEVENT) )
{
EngSetLastError(ERROR_INVALID_PARAMETER);
goto Exit;
}
if (saveTME.dwFlags & ~(TME_CANCEL | TME_QUERY | TME_NONCLIENT | TME_LEAVE | TME_HOVER) )
{
EngSetLastError(ERROR_INVALID_FLAGS);
goto Exit;
}
if ( saveTME.dwFlags & TME_QUERY )
{
Ret = IntQueryTrackMouseEvent(&saveTME);
_SEH2_TRY
{
ProbeForWrite(lpEventTrack, sizeof(TRACKMOUSEEVENT), 1);
RtlCopyMemory(lpEventTrack, &saveTME, sizeof(TRACKMOUSEEVENT));
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
SetLastNtError(_SEH2_GetExceptionCode());
Ret = FALSE;
}
_SEH2_END;
}
else
{
Ret = IntTrackMouseEvent(&saveTME);
}
Exit:
TRACE("Leave NtUserTrackMouseEvent, ret=%i\n", Ret);
UserLeave();
return Ret;
}
extern MOUSEMOVEPOINT MouseHistoryOfMoves[];
extern INT gcur_count;
DWORD
APIENTRY
NtUserGetMouseMovePointsEx(
UINT cbSize,
LPMOUSEMOVEPOINT lpptIn,
LPMOUSEMOVEPOINT lpptOut,
int nBufPoints,
DWORD resolution)
{
MOUSEMOVEPOINT Safeppt;
//BOOL Hit;
INT Count = -1;
DECLARE_RETURN(DWORD);
TRACE("Enter NtUserGetMouseMovePointsEx\n");
UserEnterExclusive();
if ((cbSize != sizeof(MOUSEMOVEPOINT)) || (nBufPoints < 0) || (nBufPoints > 64))
{
EngSetLastError(ERROR_INVALID_PARAMETER);
RETURN( -1);
}
if (!lpptIn || (!lpptOut && nBufPoints))
{
EngSetLastError(ERROR_NOACCESS);
RETURN( -1);
}
_SEH2_TRY
{
ProbeForRead( lpptIn, cbSize, 1);
RtlCopyMemory(&Safeppt, lpptIn, cbSize);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
SetLastNtError(_SEH2_GetExceptionCode());
_SEH2_YIELD(RETURN( -1))
}
_SEH2_END;
// http://msdn.microsoft.com/en-us/library/ms646259(v=vs.85).aspx
// This explains the math issues in transforming points.
Count = gcur_count; // FIFO is forward so retrieve backward.
//Hit = FALSE;
do
{
if (Safeppt.x == 0 && Safeppt.y == 0)
break; // No test.
// Finds the point, it returns the last nBufPoints prior to and including the supplied point.
if (MouseHistoryOfMoves[Count].x == Safeppt.x && MouseHistoryOfMoves[Count].y == Safeppt.y)
{
if ( Safeppt.time ) // Now test time and it seems to be absolute.
{
if (Safeppt.time == MouseHistoryOfMoves[Count].time)
{
//Hit = TRUE;
break;
}
else
{
if (--Count < 0) Count = 63;
continue;
}
}
//Hit = TRUE;
break;
}
if (--Count < 0) Count = 63;
}
while ( Count != gcur_count);
switch(resolution)
{
case GMMP_USE_DISPLAY_POINTS:
if (nBufPoints)
{
_SEH2_TRY
{
ProbeForWrite(lpptOut, cbSize, 1);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
SetLastNtError(_SEH2_GetExceptionCode());
_SEH2_YIELD(RETURN( -1))
}
_SEH2_END;
}
Count = nBufPoints;
break;
case GMMP_USE_HIGH_RESOLUTION_POINTS:
break;
default:
EngSetLastError(ERROR_POINT_NOT_FOUND);
RETURN( -1);
}
RETURN( Count);
CLEANUP:
TRACE("Leave NtUserGetMouseMovePointsEx, ret=%i\n", _ret_);
UserLeave();
END_CLEANUP;
}
/* EOF */