You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
6501 lines
206 KiB
6501 lines
206 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: ntinput.c
|
|
*
|
|
* Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
*
|
|
* This module contains low-level input code specific to the NT
|
|
* implementation of Win32 USER, which is mostly the interfaces to the
|
|
* keyboard and mouse device drivers.
|
|
*
|
|
* History:
|
|
* 11-26-90 DavidPe Created
|
|
\***************************************************************************/
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
#include <ntddmou.h>
|
|
|
|
|
|
PKWAIT_BLOCK gWaitBlockArray;
|
|
|
|
KEYBOARD_UNIT_ID_PARAMETER kuid;
|
|
MOUSE_UNIT_ID_PARAMETER muid;
|
|
|
|
typedef struct tagSCANCODEFLEXIBLEMAP {
|
|
struct {
|
|
BYTE bScanCode;
|
|
BYTE bPrefix;
|
|
BYTE abModifiers[6];
|
|
} Orig;
|
|
struct {
|
|
BYTE bScanCode;
|
|
BYTE bPrefix;
|
|
BYTE abModifiers[6];
|
|
} Target;
|
|
} SCANCODEFLEXIBLEMAP, FAR *LPSCANCODEFLEXIBLEMAP;
|
|
|
|
BYTE bLastVKDown = 0;
|
|
int iLastMatchedTarget = -1;
|
|
|
|
|
|
SCANCODEFLEXIBLEMAP* gpFlexMap;
|
|
DWORD gdwFlexMapSize;
|
|
|
|
|
|
|
|
VOID ProcessQueuedMouseEvents(VOID);
|
|
#ifndef SUBPIXEL_MOUSE
|
|
LONG DoMouseAccel(LONG delta);
|
|
#endif
|
|
VOID GetMouseCoord(LONG dx, LONG dy, DWORD dwFlags, LONG time, ULONG_PTR ExtraInfo, PPOINT ppt);
|
|
VOID xxxMoveEventAbsolute(LONG x, LONG y, ULONG_PTR dwExtraInfo,
|
|
#ifdef GENERIC_INPUT
|
|
HANDLE hDevice,
|
|
PMOUSE_INPUT_DATA pmei,
|
|
#endif
|
|
DWORD time,
|
|
BOOL bInjected);
|
|
|
|
VOID ProcessKeyboardInputWorker(PKEYBOARD_INPUT_DATA pkei,
|
|
#ifdef GENERIC_INPUT
|
|
PDEVICEINFO pDeviceInfo,
|
|
#endif
|
|
BOOL fProcessRemap);
|
|
|
|
|
|
INT idxRemainder, idyRemainder;
|
|
|
|
BYTE gbVKLastDown;
|
|
|
|
/*
|
|
* Mouse/Kbd diagnostics for #136483 etc. Remove when PNP is stable (IanJa)
|
|
*/
|
|
#ifdef DIAGNOSE_IO
|
|
ULONG gMouseProcessMiceInputTime = 0; // tick at start of ProcessMiceInput
|
|
ULONG gMouseQueueMouseEventTime = 0; // tick at start of QueueMouseEvent
|
|
ULONG gMouseUnqueueMouseEventTime = 0; // tick at start of UnqueueMouseEvent
|
|
|
|
// Return a value as close as possible to the system's tick count,
|
|
// yet guaranteed to be larger than the value returned last time.
|
|
// (useful for sequencing events)
|
|
// BUG: when NtGetTickCount overflows, the value returned by MonotonicTick
|
|
// will not track system tick count well: instead it will increase by one
|
|
// each time until it too overflows. (considerd harmless for IO DIAGNOSTICS)
|
|
ULONG MonotonicTick()
|
|
{
|
|
static ULONG lasttick = 0;
|
|
ULONG newtick;
|
|
|
|
newtick = NtGetTickCount();
|
|
if (newtick > lasttick) {
|
|
lasttick = newtick; // use the new tick since it is larger
|
|
} else {
|
|
lasttick++; // artificially bump the tick up one.
|
|
}
|
|
return lasttick;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
* Parameter Constants for xxxButtonEvent()
|
|
*/
|
|
#define MOUSE_BUTTON_LEFT 0x0001
|
|
#define MOUSE_BUTTON_RIGHT 0x0002
|
|
#define MOUSE_BUTTON_MIDDLE 0x0004
|
|
#define MOUSE_BUTTON_X1 0x0008
|
|
#define MOUSE_BUTTON_X2 0x0010
|
|
|
|
#define ID_INPUT 0
|
|
#define ID_MOUSE 1
|
|
|
|
#define ID_TIMER 2
|
|
#define ID_HIDCHANGE 3
|
|
#define ID_SHUTDOWN 4
|
|
|
|
#ifdef GENERIC_INPUT
|
|
#define ID_TRUEHIDCHANGE 5
|
|
#define ID_WDTIMER 6
|
|
#define ID_NUMBER_HYDRA_REMOTE_HANDLES 7
|
|
#else // GENERIC_INPUT
|
|
#define ID_WDTIMER 5
|
|
#define ID_NUMBER_HYDRA_REMOTE_HANDLES 6
|
|
#endif // GENERIC_INPUT
|
|
|
|
PKTIMER gptmrWD;
|
|
|
|
PVOID *apObjects;
|
|
|
|
|
|
/***************************************************************************\
|
|
* fAbsoluteMouse
|
|
*
|
|
* Returns TRUE if the mouse event has absolute coordinates (as apposed to the
|
|
* standard delta values we get from MS and PS2 mice)
|
|
*
|
|
* History:
|
|
* 23-Jul-1992 JonPa Created.
|
|
\***************************************************************************/
|
|
#define fAbsoluteMouse( pmei ) \
|
|
(((pmei)->Flags & MOUSE_MOVE_ABSOLUTE) != 0)
|
|
|
|
/***************************************************************************\
|
|
* ConvertToMouseDriverFlags
|
|
*
|
|
* Converts SendInput kind of flags to mouse driver flags as GetMouseCoord
|
|
* needs them.
|
|
* As mouse inputs are more frequent than send inputs, we penalize the later.
|
|
*
|
|
* History:
|
|
* 17-dec-1997 MCostea Created.
|
|
\***************************************************************************/
|
|
#if ((MOUSEEVENTF_ABSOLUTE >> 15) ^ MOUSE_MOVE_ABSOLUTE) || \
|
|
((MOUSEEVENTF_VIRTUALDESK >> 13) ^ MOUSE_VIRTUAL_DESKTOP)
|
|
# error("Bit mapping broken: fix ConvertToMouseDriverFlags")
|
|
#endif
|
|
|
|
#define ConvertToMouseDriverFlags( Flags ) \
|
|
(((Flags) & MOUSEEVENTF_ABSOLUTE) >> 15 | \
|
|
((Flags) & MOUSEEVENTF_VIRTUALDESK) >> 13)
|
|
|
|
#define VKTOMODIFIERS(Vk) ((((Vk) >= VK_SHIFT) && ((Vk) <= VK_MENU)) ? \
|
|
(MOD_SHIFT >> ((Vk) - VK_SHIFT)) : \
|
|
0)
|
|
#if (VKTOMODIFIERS(VK_SHIFT) != MOD_SHIFT) || \
|
|
(VKTOMODIFIERS(VK_CONTROL) != MOD_CONTROL) || \
|
|
(VKTOMODIFIERS(VK_MENU) != MOD_ALT)
|
|
# error("VKTOMODIFIERS broken")
|
|
#endif
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxInitInput
|
|
*
|
|
* This function is called from CreateTerminalInput() and gets USER setup to
|
|
* process keyboard and mouse input. It starts the RIT for that terminal.
|
|
* History:
|
|
* 11-26-90 DavidPe Created.
|
|
\***************************************************************************/
|
|
BOOL xxxInitInput(
|
|
PTERMINAL pTerm)
|
|
{
|
|
NTSTATUS Status;
|
|
USER_API_MSG m;
|
|
RIT_INIT initData;
|
|
|
|
UserAssert(pTerm != NULL);
|
|
|
|
#ifdef MOUSE_LOCK_CODE
|
|
/*
|
|
* Lock RIT pages into memory
|
|
*/
|
|
LockMouseInputCodePages();
|
|
#endif
|
|
|
|
initData.pTerm = pTerm;
|
|
initData.pRitReadyEvent = CreateKernelEvent(SynchronizationEvent, FALSE);
|
|
if (initData.pRitReadyEvent == NULL) {
|
|
return FALSE;
|
|
}
|
|
/*
|
|
* Create the RIT and let it run.
|
|
*/
|
|
|
|
if (!InitCreateSystemThreadsMsg(&m, CST_RIT, &initData, 0, FALSE)) {
|
|
FreeKernelEvent(&initData.pRitReadyEvent);
|
|
return FALSE;
|
|
}
|
|
/*
|
|
* Be sure that we are not in CSRSS context.
|
|
* WARNING: If for any reason we changed this to run in CSRSS context then we have to use
|
|
* LpcRequestPort instead of LpcRequestWaitReplyPort.
|
|
*/
|
|
UserAssert (!ISCSRSS());
|
|
|
|
LeaveCrit();
|
|
Status = LpcRequestWaitReplyPort(CsrApiPort, (PPORT_MESSAGE)&m, (PPORT_MESSAGE)&m);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Exit;
|
|
}
|
|
|
|
KeWaitForSingleObject(initData.pRitReadyEvent, WrUserRequest,
|
|
KernelMode, FALSE, NULL);
|
|
Exit:
|
|
FreeKernelEvent(&initData.pRitReadyEvent);
|
|
EnterCrit();
|
|
|
|
return (gptiRit != NULL);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* InitScancodeMap
|
|
*
|
|
* Fetches the scancode map from the registry, allocating space as required.
|
|
*
|
|
* A scancode map is used to convert unusual OEM scancodes into standard
|
|
* "Scan Code Set 1" values. This is to support KB3270 keyboards, but can
|
|
* be used for other types too.
|
|
*
|
|
* History:
|
|
* 96-04-18 IanJa Created.
|
|
\***************************************************************************/
|
|
|
|
|
|
|
|
const WCHAR gwszScancodeMap[] = L"Scancode Map";
|
|
const WCHAR gwszScancodeMapEx[] = L"Scancode Map Ex";
|
|
|
|
|
|
VOID InitScancodeMap(
|
|
PUNICODE_STRING pProfileName)
|
|
{
|
|
DWORD dwBytes;
|
|
UINT idSection;
|
|
PUNICODE_STRING pPN;
|
|
LPBYTE pb;
|
|
|
|
TAGMSG2(DBGTAG_KBD, "InitScancodeMap with pProfileName=%#p, \"%S\"", pProfileName,
|
|
pProfileName ? pProfileName->Buffer : L"");
|
|
|
|
if (gpScancodeMap) {
|
|
UserFreePool(gpScancodeMap);
|
|
gpScancodeMap = NULL;
|
|
}
|
|
|
|
/*
|
|
* Read basic scancode mapping information.
|
|
* Firstly try per user, and then per system.
|
|
*/
|
|
idSection = PMAP_UKBDLAYOUT;
|
|
pPN = pProfileName;
|
|
dwBytes = FastGetProfileValue(pPN, idSection, gwszScancodeMap, NULL, NULL, 0, 0);
|
|
if (dwBytes == 0) {
|
|
idSection = PMAP_KBDLAYOUT;
|
|
pPN = NULL;
|
|
dwBytes = FastGetProfileValue(pPN, idSection, gwszScancodeMap, NULL, NULL, 0, 0);
|
|
}
|
|
if (dwBytes > sizeof(SCANCODEMAP)) {
|
|
pb = UserAllocPoolZInit(dwBytes, TAG_SCANCODEMAP);
|
|
if (pb) {
|
|
dwBytes = FastGetProfileValue(pPN, idSection, gwszScancodeMap, NULL, pb, dwBytes, 0);
|
|
gpScancodeMap = (SCANCODEMAP*)pb;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Read extended scancode mapping information.
|
|
* Firstly try per user, then per system.
|
|
*/
|
|
if (gpFlexMap) {
|
|
UserFreePool(gpFlexMap);
|
|
gpFlexMap = NULL;
|
|
gdwFlexMapSize = 0;
|
|
}
|
|
|
|
idSection = PMAP_UKBDLAYOUT;
|
|
pPN = pProfileName;
|
|
dwBytes = FastGetProfileValue(pPN, idSection, gwszScancodeMapEx, NULL, NULL, 0, 0);
|
|
if (dwBytes == 0) {
|
|
TAGMSG0(DBGTAG_KBD, "InitScancodeMap: mapex is not in per-user profile. will use the system's");
|
|
idSection = PMAP_KBDLAYOUT;
|
|
pPN = NULL;
|
|
dwBytes = FastGetProfileValue(pPN, idSection, gwszScancodeMapEx, NULL, NULL, 0, 0);
|
|
}
|
|
if (dwBytes >= sizeof(SCANCODEFLEXIBLEMAP) && dwBytes % sizeof(SCANCODEFLEXIBLEMAP) == 0) {
|
|
if ((pb = UserAllocPoolZInit(dwBytes, TAG_SCANCODEMAP)) != NULL) {
|
|
dwBytes = FastGetProfileValue(pPN, idSection, gwszScancodeMapEx, NULL, pb, dwBytes, 0);
|
|
gpFlexMap = (SCANCODEFLEXIBLEMAP*)pb;
|
|
gdwFlexMapSize = dwBytes / sizeof(SCANCODEFLEXIBLEMAP);
|
|
}
|
|
}
|
|
#if DBG
|
|
else if (dwBytes != 0) {
|
|
TAGMSG1(DBGTAG_KBD, "InitScancodeMap: incorrect dwSize(0x%x) specified.", dwBytes);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* MapScancode
|
|
*
|
|
* Converts a scancode (and it's prefix, if any) to a different scancode
|
|
* and prefix.
|
|
*
|
|
* Parameters:
|
|
* pbScanCode = address of Scancode byte, the scancode may be changed
|
|
* pbPrefix = address of Prefix byte, The prefix may be changed
|
|
*
|
|
* Return value:
|
|
* TRUE - mapping was found, scancode was altered.
|
|
* FALSE - no mapping found, scancode was not altered.
|
|
*
|
|
* Note on scancode map table format:
|
|
* A table entry DWORD of 0xE0450075 means scancode 0x45, prefix 0xE0
|
|
* gets mapped to scancode 0x75, no prefix
|
|
*
|
|
* History:
|
|
* 96-04-18 IanJa Created.
|
|
\***************************************************************************/
|
|
|
|
PKBDTABLES GetCurrentKbdTables()
|
|
{
|
|
PKBDTABLES pKbdTbl;
|
|
PTHREADINFO pti;
|
|
|
|
CheckCritIn();
|
|
if (gpqForeground == NULL) {
|
|
TAGMSG0(DBGTAG_KBD, "GetCurrentKbdTables: NULL gpqForeground\n");
|
|
return NULL;
|
|
}
|
|
|
|
pti = PtiKbdFromQ(gpqForeground);
|
|
UserAssert(pti);
|
|
if (pti->spklActive) {
|
|
pKbdTbl = pti->spklActive->spkf->pKbdTbl;
|
|
} else {
|
|
RIPMSG0(RIP_WARNING, "SendKeyUpDown: NULL spklActive\n");
|
|
pKbdTbl = gpKbdTbl;
|
|
}
|
|
UserAssert(pKbdTbl);
|
|
|
|
return pKbdTbl;
|
|
}
|
|
|
|
VOID SendKeyUpDown(
|
|
CONST BYTE bVK,
|
|
CONST BOOLEAN fBreak)
|
|
{
|
|
KE ke;
|
|
PKBDTABLES pKbdTbl;
|
|
|
|
CheckCritIn();
|
|
|
|
ke.dwTime = 0;
|
|
ke.usFlaggedVk = bVK | KBDMAPPEDVK;
|
|
if (fBreak) {
|
|
ke.usFlaggedVk |= KBDBREAK;
|
|
}
|
|
|
|
//
|
|
// If scancode is not specified (==0), we need to
|
|
// find the scancode value from the virtual key code.
|
|
//
|
|
pKbdTbl = GetCurrentKbdTables();
|
|
if (pKbdTbl) {
|
|
ke.bScanCode = (BYTE)InternalMapVirtualKeyEx(bVK, 0, pKbdTbl);
|
|
}
|
|
|
|
TAGMSG1(DBGTAG_KBD, "Sending Key for VK=%04x", ke.usFlaggedVk);
|
|
|
|
xxxProcessKeyEvent(&ke, 0, TRUE);
|
|
}
|
|
|
|
__inline VOID SendKeyDown(
|
|
CONST BYTE bVK)
|
|
{
|
|
SendKeyUpDown(bVK, FALSE);
|
|
}
|
|
|
|
__inline VOID SendKeyUp(
|
|
CONST BYTE bVK)
|
|
{
|
|
SendKeyUpDown(bVK, TRUE);
|
|
}
|
|
|
|
BOOL IsKeyDownSpecified(CONST BYTE bVK, CONST BYTE* pbMod)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < sizeof((SCANCODEFLEXIBLEMAP*)NULL)->Orig.abModifiers && pbMod[i]; ++i) {
|
|
if (bVK == pbMod[i]) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL MapFlexibleKeys(PKE pke, CONST BYTE bPrefix
|
|
#ifdef GENERIC_INPUT
|
|
, PDEVICEINFO pDeviceInfo
|
|
#endif
|
|
)
|
|
{
|
|
UINT i;
|
|
static const BYTE abModifiers[] = {
|
|
VK_LCONTROL, VK_RCONTROL,
|
|
VK_LSHIFT, VK_RSHIFT,
|
|
VK_LMENU, VK_RMENU,
|
|
VK_LWIN, VK_RWIN,
|
|
VK_APPS, VK_CAPITAL,
|
|
};
|
|
|
|
for (i = 0; i < gdwFlexMapSize; ++i) {
|
|
if (gpFlexMap[i].Orig.bPrefix == bPrefix && gpFlexMap[i].Orig.bScanCode == pke->bScanCode) {
|
|
UINT j;
|
|
|
|
if ((pke->usFlaggedVk & KBDBREAK) && i == (UINT)iLastMatchedTarget) {
|
|
//
|
|
// If this is a keyup event, and if it matches the last substituted
|
|
// key, we want to send keyup event right away.
|
|
//
|
|
iLastMatchedTarget = -1;
|
|
break;
|
|
}
|
|
|
|
for (j = 0; j < ARRAY_SIZE(abModifiers); ++j) {
|
|
BYTE bVK = abModifiers[j];
|
|
|
|
if (bVK == bLastVKDown) {
|
|
//
|
|
// Ignore the key if it's previously substituted by us.
|
|
//
|
|
bLastVKDown = 0;
|
|
continue;
|
|
}
|
|
if (!TestKeyDownBit(gafRawKeyState, bVK) == IsKeyDownSpecified(bVK, gpFlexMap[i].Orig.abModifiers)) {
|
|
TAGMSG1(DBGTAG_KBD, "MapFlexibleKeys: not match by vk=%02x", bVK);
|
|
// No match!
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (j >= ARRAY_SIZE(abModifiers)) {
|
|
// We found the match. Now break the loop.
|
|
TAGMSG1(DBGTAG_KBD, "MapFlexibleKeys: found a match for sc=%02x", gpFlexMap[i].Orig.bScanCode);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (i < gdwFlexMapSize) {
|
|
KEYBOARD_INPUT_DATA kei;
|
|
UINT j, nUp = 0, nDown = 0;
|
|
BYTE bVKModUp[ARRAY_SIZE(((SCANCODEFLEXIBLEMAP*)NULL)->Orig.abModifiers)];
|
|
BYTE bVKModDown[ARRAY_SIZE(((SCANCODEFLEXIBLEMAP*)NULL)->Target.abModifiers)];
|
|
|
|
// We found it.
|
|
// Yes, this key.
|
|
TAGMSG3(DBGTAG_KBD, "MapFlexibleKeys: found a match %d (prefix=%x, sc=%x).", i, gpFlexMap[i].Orig.bPrefix, gpFlexMap[i].Orig.bScanCode);
|
|
|
|
//
|
|
// If this is a keydown event, we want to simulate
|
|
// the modifier keys.
|
|
//
|
|
if ((pke->usFlaggedVk & KBDBREAK) == 0) {
|
|
//
|
|
// Now we need to adjust the down state of the modifieres, which is currently
|
|
// pressed but not specified in the substitute.
|
|
// For instance, if CTRL key is pressed now, but if CTRL is not specified in the substitute
|
|
// modifiers list, we need to make an artificial keyup so that we'll be able to fake the
|
|
// situation. Of cource, we need to push CTRL key after we finish remapping.
|
|
//
|
|
for (j = 0; j < ARRAY_SIZE(gpFlexMap[i].Orig.abModifiers) && gpFlexMap[i].Orig.abModifiers[j]; ++j) {
|
|
if (!IsKeyDownSpecified(gpFlexMap[i].Orig.abModifiers[j], gpFlexMap[i].Target.abModifiers)) {
|
|
//
|
|
// We need to send UP key for this one.
|
|
//
|
|
bVKModUp[nUp++] = gpFlexMap[i].Orig.abModifiers[j];
|
|
SendKeyUp(gpFlexMap[i].Orig.abModifiers[j]);
|
|
}
|
|
}
|
|
for (j = 0; j < ARRAY_SIZE(gpFlexMap[i].Target.abModifiers) && gpFlexMap[i].Target.abModifiers[i]; ++j) {
|
|
if (!IsKeyDownSpecified(gpFlexMap[i].Target.abModifiers[j], gpFlexMap[i].Orig.abModifiers)) {
|
|
//
|
|
// We need to send DOWN key for this one.
|
|
//
|
|
bVKModDown[nDown++] = gpFlexMap[i].Target.abModifiers[j];
|
|
SendKeyDown(gpFlexMap[i].Target.abModifiers[j]);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now we are ready to send the substituted key.
|
|
//
|
|
kei.ExtraInformation = 0;
|
|
kei.Flags = 0;
|
|
if (gpFlexMap[i].Target.bPrefix == 0xE0) {
|
|
kei.Flags |= KEY_E0;
|
|
} else if (gpFlexMap[i].Target.bPrefix == 0xE1) {
|
|
kei.Flags |= KEY_E1;
|
|
}
|
|
if (pke->usFlaggedVk & KBDBREAK) {
|
|
kei.Flags |= KEY_BREAK;
|
|
}
|
|
kei.MakeCode = gpFlexMap[i].Target.bScanCode;
|
|
|
|
kei.UnitId = 0; // LATER:
|
|
|
|
TAGMSG2(DBGTAG_KBD, "MapFlexibleKeys: injecting sc=%02x (flag=%x)",
|
|
kei.MakeCode, kei.Flags);
|
|
|
|
ProcessKeyboardInputWorker(&kei,
|
|
#ifdef GENERIC_INPUT
|
|
pDeviceInfo,
|
|
#endif
|
|
FALSE);
|
|
|
|
if ((pke->usFlaggedVk & KBDBREAK) == 0) {
|
|
//
|
|
// Remember the last down key generated by me.
|
|
// This will be used when matching the UP key.
|
|
//
|
|
bLastVKDown = gbVKLastDown;
|
|
iLastMatchedTarget = i;
|
|
}
|
|
|
|
|
|
//
|
|
// Restore the orignial modifier state.
|
|
//
|
|
for (j = 0; j < nUp; ++j) {
|
|
SendKeyDown(bVKModUp[j]);
|
|
}
|
|
for (j = 0; j < nDown; ++j) {
|
|
SendKeyUp(bVKModDown[j]);
|
|
}
|
|
|
|
//
|
|
// Tell the caller we processed this key. The caller should
|
|
// not continue handling this key if this function returns FALSE.
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
MapScancode(
|
|
PKE pke,
|
|
PBYTE pbPrefix
|
|
#ifdef GENERIC_INPUT
|
|
,
|
|
PDEVICEINFO pDeviceInfo
|
|
#endif
|
|
)
|
|
{
|
|
if (gpScancodeMap) {
|
|
DWORD *pdw;
|
|
WORD wT = MAKEWORD(pke->bScanCode, *pbPrefix);
|
|
|
|
CheckCritIn();
|
|
UserAssert(gpScancodeMap != NULL);
|
|
|
|
for (pdw = &(gpScancodeMap->dwMap[0]); *pdw; pdw++) {
|
|
if (HIWORD(*pdw) == wT) {
|
|
wT = LOWORD(*pdw);
|
|
pke->bScanCode = LOBYTE(wT);
|
|
*pbPrefix = HIBYTE(wT);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return IsRemoteConnection() ||
|
|
MapFlexibleKeys(pke, *pbPrefix
|
|
#ifdef GENERIC_INPUT
|
|
, pDeviceInfo
|
|
#endif
|
|
);
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************\
|
|
* InitMice
|
|
*
|
|
* This function initializes the data and settings before we start enumerating
|
|
* the mice.
|
|
*
|
|
* History:
|
|
* 11-18-97 IanJa Created.
|
|
\***************************************************************************/
|
|
|
|
VOID InitMice()
|
|
{
|
|
CLEAR_ACCF(ACCF_MKVIRTUALMOUSE);
|
|
CLEAR_GTERMF(GTERMF_MOUSE);
|
|
SYSMET(MOUSEPRESENT) = FALSE;
|
|
SYSMET(CMOUSEBUTTONS) = 0;
|
|
SYSMET(MOUSEWHEELPRESENT) = FALSE;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* FreeDeviceInfo
|
|
*
|
|
* Unlinks a DEVICEINFO struct from the gpDeviceInfoList list and frees the
|
|
* allocated memory UNLESS the device is actively being read (GDIF_READING) or
|
|
* has a PnP thread waiting for it in RequestDeviceChange() (GDIAF_PNPWAITING)
|
|
* If the latter, then wake the PnP thread via pkeHidChangeCompleted so that it
|
|
* can free the structure itself.
|
|
*
|
|
* Returns a pointer to the next DEVICEINFO struct, or NULL if the device was
|
|
* not found in the gpDeviceInfoList.
|
|
*
|
|
* History:
|
|
* 11-18-97 IanJa Created.
|
|
\***************************************************************************/
|
|
PDEVICEINFO FreeDeviceInfo(PDEVICEINFO pDeviceInfo)
|
|
{
|
|
PDEVICEINFO *ppDeviceInfo;
|
|
|
|
CheckDeviceInfoListCritIn();
|
|
|
|
TAGMSG1(DBGTAG_PNP, "FreeDeviceInfo(%#p)", pDeviceInfo);
|
|
|
|
/*
|
|
* We cannot free the device since we still have a read pending.
|
|
* Mark it GDIAF_FREEME so that it will be freed when the APC is made
|
|
* (see InputApc), or when the next read request is about to be issued
|
|
* (see StartDeviceRead).
|
|
*/
|
|
if (pDeviceInfo->bFlags & GDIF_READING) {
|
|
#if DIAGNOSE_IO
|
|
pDeviceInfo->bFlags |= GDIF_READERMUSTFREE;
|
|
#endif
|
|
TAGMSG1(DBGTAG_PNP, "** FreeDeviceInfo(%#p) DEFERRED : reader must free", pDeviceInfo);
|
|
pDeviceInfo->usActions |= GDIAF_FREEME;
|
|
#ifdef TRACK_PNP_NOTIFICATION
|
|
if (gfRecordPnpNotification) {
|
|
RecordPnpNotification(PNP_NTF_FREEDEVICEINFO_DEFERRED, pDeviceInfo, pDeviceInfo->usActions);
|
|
}
|
|
#endif
|
|
return pDeviceInfo->pNext;
|
|
}
|
|
|
|
/*
|
|
* If a PnP thread is waiting in RequestDeviceChange for some action to be
|
|
* performed on this device, just mark it for freeing and signal that PnP
|
|
* thread with the pkeHidChangeCompleted so that it will free it
|
|
*/
|
|
#ifdef GENERIC_INPUT
|
|
/*
|
|
* Now that pDeviceInfo is handle based, if we don't own the user critical section.
|
|
* we mark it to be freed later on and have to bail out,
|
|
*/
|
|
if ((pDeviceInfo->usActions & GDIAF_PNPWAITING) || !ExIsResourceAcquiredExclusiveLite(gpresUser))
|
|
#else
|
|
if (pDeviceInfo->usActions & GDIAF_PNPWAITING)
|
|
#endif
|
|
{
|
|
#if DIAGNOSE_IO
|
|
pDeviceInfo->bFlags |= GDIF_PNPMUSTFREE;
|
|
#endif
|
|
TAGMSG1(DBGTAG_PNP, "** FreeDeviceInfo(%#p) DEFERRED : PnP must free", pDeviceInfo);
|
|
pDeviceInfo->usActions |= GDIAF_FREEME;
|
|
KeSetEvent(pDeviceInfo->pkeHidChangeCompleted, EVENT_INCREMENT, FALSE);
|
|
return pDeviceInfo->pNext;
|
|
}
|
|
|
|
#ifdef TRACK_PNP_NOTIFICATION
|
|
if (gfRecordPnpNotification) {
|
|
RecordPnpNotification(PNP_NTF_FREEDEVICEINFO, pDeviceInfo, pDeviceInfo->usActions);
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef GENERIC_INPUT
|
|
CheckCritIn();
|
|
#endif
|
|
|
|
ppDeviceInfo = &gpDeviceInfoList;
|
|
|
|
while (*ppDeviceInfo) {
|
|
if (*ppDeviceInfo == pDeviceInfo
|
|
#ifdef GENERIC_INPUT
|
|
&& HMMarkObjectDestroy(pDeviceInfo)
|
|
#endif
|
|
) {
|
|
/*
|
|
* Found the DEVICEINFO struct, so free it and its members.
|
|
*/
|
|
if (pDeviceInfo->pkeHidChangeCompleted != NULL) {
|
|
// N.b. the timing could be pretty critical around this
|
|
FreeKernelEvent(&pDeviceInfo->pkeHidChangeCompleted);
|
|
}
|
|
if (pDeviceInfo->ustrName.Buffer != NULL) {
|
|
UserFreePool(pDeviceInfo->ustrName.Buffer);
|
|
}
|
|
#ifdef GENERIC_INPUT
|
|
if (pDeviceInfo->type == DEVICE_TYPE_HID) {
|
|
CheckCritIn();
|
|
/*
|
|
* Unlock the device request list
|
|
*/
|
|
UserAssert(pDeviceInfo->hid.pTLCInfo);
|
|
if (--pDeviceInfo->hid.pTLCInfo->cDevices == 0) {
|
|
if (!HidTLCActive(pDeviceInfo->hid.pTLCInfo)) {
|
|
// Nobody is interested in this device anymore
|
|
FreeHidTLCInfo(pDeviceInfo->hid.pTLCInfo);
|
|
}
|
|
}
|
|
/*
|
|
* Unlock the HID descriptor
|
|
*/
|
|
UserAssert(pDeviceInfo->hid.pHidDesc);
|
|
FreeHidDesc(pDeviceInfo->hid.pHidDesc);
|
|
}
|
|
#endif
|
|
|
|
*ppDeviceInfo = pDeviceInfo->pNext;
|
|
|
|
#ifdef GENERIC_INPUT
|
|
TAGMSG1(DBGTAG_PNP, "FreeDeviceInfo: freeing deviceinfo=%#p", pDeviceInfo);
|
|
HMFreeObject(pDeviceInfo);
|
|
#else
|
|
UserFreePool(pDeviceInfo);
|
|
#endif
|
|
|
|
return *ppDeviceInfo;
|
|
}
|
|
ppDeviceInfo = &(*ppDeviceInfo)->pNext;
|
|
}
|
|
|
|
RIPMSG1(RIP_ERROR, "pDeviceInfo %#p not found in gpDeviceInfoList", pDeviceInfo);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* UpdateMouseInfo
|
|
*
|
|
* This function updates mouse information for a remote session.
|
|
*
|
|
* History:
|
|
* 05-22-98 clupu Created.
|
|
\***************************************************************************/
|
|
VOID UpdateMouseInfo(
|
|
VOID)
|
|
{
|
|
DEVICEINFO *pDeviceInfo;
|
|
CheckCritIn(); // expect no surprises
|
|
|
|
UserAssert(IsRemoteConnection());
|
|
|
|
if (ghRemoteMouseChannel == NULL) {
|
|
return;
|
|
}
|
|
|
|
UserAssert(gnMice == 1);
|
|
|
|
/*
|
|
* Mark the mice and signal the RIT to do the work asynchronously
|
|
*/
|
|
EnterDeviceInfoListCrit();
|
|
for (pDeviceInfo = gpDeviceInfoList; pDeviceInfo; pDeviceInfo = pDeviceInfo->pNext) {
|
|
if (pDeviceInfo->type == DEVICE_TYPE_MOUSE) {
|
|
TAGMSG1(DBGTAG_PNP, "UpdateMouseInfo(): pDeviceInfo %#p ARRIVED", pDeviceInfo);
|
|
RequestDeviceChange(pDeviceInfo, GDIAF_ARRIVED | GDIAF_RECONNECT, TRUE);
|
|
}
|
|
}
|
|
LeaveDeviceInfoListCrit();
|
|
}
|
|
|
|
|
|
NTSTATUS DeviceNotify(IN PPLUGPLAY_NOTIFY_HDR, IN PDEVICEINFO);
|
|
|
|
|
|
/*
|
|
* The below two routines are transplanted from i8042prt
|
|
* to get the BIOS NumLock status.
|
|
*/
|
|
typedef struct _LED_INFO {
|
|
USHORT usLedFlags;
|
|
BOOLEAN fFound;
|
|
} LED_INFO, *PLED_INFO;
|
|
|
|
|
|
/****************************************************************************
|
|
*
|
|
* Routine Description:
|
|
*
|
|
* This is the callout routine sent as a parameter to
|
|
* IoQueryDeviceDescription. It grabs the keyboard peripheral configuration
|
|
* information.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* Context - Context parameter that was passed in by the routine
|
|
* that called IoQueryDeviceDescription.
|
|
*
|
|
* PathName - The full pathname for the registry key.
|
|
*
|
|
* BusType - Bus interface type (Isa, Eisa, Mca, etc.).
|
|
*
|
|
* BusNumber - The bus sub-key (0, 1, etc.).
|
|
*
|
|
* BusInformation - Pointer to the array of pointers to the full value
|
|
* information for the bus.
|
|
*
|
|
* ControllerType - The controller type (should be KeyboardController).
|
|
*
|
|
* ControllerNumber - The controller sub-key (0, 1, etc.).
|
|
*
|
|
* ControllerInformation - Pointer to the array of pointers to the full
|
|
* value information for the controller key.
|
|
*
|
|
* PeripheralType - The peripheral type (should be KeyboardPeripheral).
|
|
*
|
|
* PeripheralNumber - The peripheral sub-key.
|
|
*
|
|
* PeripheralInformation - Pointer to the array of pointers to the full
|
|
* value information for the peripheral key.
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
* None. If successful, will have the following side-effects:
|
|
*
|
|
* - Sets DeviceObject->DeviceExtension->HardwarePresent.
|
|
* - Sets configuration fields in
|
|
* DeviceObject->DeviceExtension->Configuration.
|
|
*
|
|
****************************************************************************/
|
|
NTSTATUS
|
|
KeyboardDeviceSpecificCallout(
|
|
IN PVOID Context,
|
|
IN PUNICODE_STRING PathName,
|
|
IN INTERFACE_TYPE BusType,
|
|
IN ULONG BusNumber,
|
|
IN PKEY_VALUE_FULL_INFORMATION *BusInformation,
|
|
IN CONFIGURATION_TYPE ControllerType,
|
|
IN ULONG ControllerNumber,
|
|
IN PKEY_VALUE_FULL_INFORMATION *ControllerInformation,
|
|
IN CONFIGURATION_TYPE PeripheralType,
|
|
IN ULONG PeripheralNumber,
|
|
IN PKEY_VALUE_FULL_INFORMATION *PeripheralInformation)
|
|
{
|
|
PUCHAR pPeripheralData;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR pResDesc;
|
|
PCM_KEYBOARD_DEVICE_DATA pKbdDeviceData;
|
|
PLED_INFO pInfo;
|
|
ULONG i, listCount;
|
|
|
|
TAGMSG0(DBGTAG_KBD, "KeyboardDeviceSpecificCallout: called.");
|
|
|
|
UNREFERENCED_PARAMETER(PathName);
|
|
UNREFERENCED_PARAMETER(BusType);
|
|
UNREFERENCED_PARAMETER(BusNumber);
|
|
UNREFERENCED_PARAMETER(BusInformation);
|
|
UNREFERENCED_PARAMETER(ControllerType);
|
|
UNREFERENCED_PARAMETER(ControllerNumber);
|
|
UNREFERENCED_PARAMETER(ControllerInformation);
|
|
UNREFERENCED_PARAMETER(PeripheralType);
|
|
UNREFERENCED_PARAMETER(PeripheralNumber);
|
|
|
|
pInfo = (PLED_INFO)Context;
|
|
|
|
if (pInfo->fFound) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Look through the peripheral's resource list for device-specific
|
|
// information.
|
|
//
|
|
if (PeripheralInformation[IoQueryDeviceConfigurationData]->DataLength != 0) {
|
|
pPeripheralData =
|
|
((PUCHAR)(PeripheralInformation[IoQueryDeviceConfigurationData])) +
|
|
PeripheralInformation[IoQueryDeviceConfigurationData]->DataOffset;
|
|
|
|
pPeripheralData += FIELD_OFFSET(CM_FULL_RESOURCE_DESCRIPTOR, PartialResourceList);
|
|
|
|
listCount = ((PCM_PARTIAL_RESOURCE_LIST)pPeripheralData)->Count;
|
|
|
|
pResDesc = ((PCM_PARTIAL_RESOURCE_LIST)pPeripheralData)->PartialDescriptors;
|
|
|
|
for (i = 0; i < listCount; i++, pResDesc++) {
|
|
if (pResDesc->Type == CmResourceTypeDeviceSpecific) {
|
|
//
|
|
// Get the keyboard type, subtype, and the initial
|
|
// settings for the LEDs.
|
|
//
|
|
pKbdDeviceData = (PCM_KEYBOARD_DEVICE_DATA)
|
|
(((PUCHAR) pResDesc) + sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR));
|
|
|
|
TAGMSG1(DBGTAG_KBD, "KeyboardDeviceSpecificCallout: specific data is %p\n", pKbdDeviceData);
|
|
|
|
#ifdef LATER
|
|
if (pKbdDeviceData->Type <= NUM_KNOWN_KEYBOARD_TYPES) {
|
|
pInfo->KeyboardExtension->KeyboardAttributes.KeyboardIdentifier.Type =
|
|
pKbdDeviceData->Type;
|
|
}
|
|
|
|
pInfo->KeyboardExtension->KeyboardAttributes.KeyboardIdentifier.Subtype =
|
|
pKbdDeviceData->Subtype;
|
|
#endif
|
|
|
|
pInfo->usLedFlags = (pKbdDeviceData->KeyboardFlags >> 4) &
|
|
(KEYBOARD_SCROLL_LOCK_ON | KEYBOARD_NUM_LOCK_ON | KEYBOARD_CAPS_LOCK_ON);
|
|
|
|
TAGMSG1(DBGTAG_KBD, "KeyboardDeviceSpecificCallout: LED %04x", pInfo->usLedFlags);
|
|
|
|
pInfo->fFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID GetBiosNumLockStatus(
|
|
VOID)
|
|
{
|
|
LED_INFO info;
|
|
INTERFACE_TYPE interfaceType;
|
|
CONFIGURATION_TYPE controllerType = KeyboardController;
|
|
CONFIGURATION_TYPE peripheralType = KeyboardPeripheral;
|
|
ULONG i;
|
|
|
|
info.usLedFlags = 0;
|
|
info.fFound = FALSE;
|
|
|
|
for (i = 0; i < MaximumInterfaceType; i++) {
|
|
//
|
|
// Get the registry information for this device.
|
|
//
|
|
interfaceType = i;
|
|
IoQueryDeviceDescription(&interfaceType,
|
|
NULL,
|
|
&controllerType,
|
|
NULL,
|
|
&peripheralType,
|
|
NULL,
|
|
KeyboardDeviceSpecificCallout,
|
|
(PVOID)&info);
|
|
if (info.fFound) {
|
|
gklpBootTime.LedFlags = info.usLedFlags;
|
|
return;
|
|
}
|
|
}
|
|
|
|
RIPMSG0(RIP_WARNING, "GetBiosNumLockStatus: could not find the BIOS LED info!!!");
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* InitKeyboardState
|
|
*
|
|
* This function clears the keyboard down state. It will be required
|
|
* when the system resumes from hybernation.
|
|
* states.
|
|
*
|
|
* History:
|
|
* 12-12-00 Hiroyama
|
|
\***************************************************************************/
|
|
VOID InitKeyboardState(
|
|
VOID)
|
|
{
|
|
TAGMSG0(DBGTAG_KBD, "InitKeyboardState >>>>>");
|
|
|
|
/*
|
|
* Clear the cached modifier state for the hotkey.
|
|
* (WindowsBug #252051)
|
|
*/
|
|
ClearCachedHotkeyModifiers();
|
|
|
|
TAGMSG0(DBGTAG_KBD, "InitKeyboardState <<<<<<");
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* InitKeyboard
|
|
*
|
|
* This function gets information about the keyboard and initialize the internal
|
|
* states.
|
|
*
|
|
* History:
|
|
* 11-26-90 DavidPe Created.
|
|
* XX-XX-00 Hiroyama
|
|
\***************************************************************************/
|
|
|
|
VOID InitKeyboard(VOID)
|
|
{
|
|
if (!IsRemoteConnection()) {
|
|
/*
|
|
* Get the BIOS Numlock status.
|
|
*/
|
|
GetBiosNumLockStatus();
|
|
|
|
/*
|
|
* Initialize the keyboard state.
|
|
*/
|
|
InitKeyboardState();
|
|
}
|
|
|
|
UpdatePerUserKeyboardMappings(NULL);
|
|
}
|
|
|
|
VOID UpdatePerUserKeyboardMappings(PUNICODE_STRING pProfileUserName)
|
|
{
|
|
/*
|
|
* Get or clean the Scancode Mapping, if any.
|
|
*/
|
|
InitScancodeMap(pProfileUserName);
|
|
}
|
|
|
|
|
|
HKL GetActiveHKL()
|
|
{
|
|
CheckCritIn();
|
|
if (gpqForeground && gpqForeground->spwndActive) {
|
|
PTHREADINFO ptiForeground = GETPTI(gpqForeground->spwndActive);
|
|
if (ptiForeground && ptiForeground->spklActive) {
|
|
return ptiForeground->spklActive->hkl;
|
|
}
|
|
}
|
|
return _GetKeyboardLayout(0L);
|
|
}
|
|
|
|
VOID FinalizeKoreanImeCompStrOnMouseClick(PWND pwnd)
|
|
{
|
|
PTHREADINFO ptiWnd = GETPTI(pwnd);
|
|
|
|
/*
|
|
* 274007: MFC flushes mouse related messages if keyup is posted
|
|
* while it's in context help mode.
|
|
*/
|
|
if (gpqForeground->spwndCapture == NULL &&
|
|
/*
|
|
* Hack for OnScreen Keyboard: no finalization on button event.
|
|
*/
|
|
(GetAppImeCompatFlags(ptiWnd) & IMECOMPAT_NOFINALIZECOMPSTR) == 0) {
|
|
|
|
if (LOWORD(ptiWnd->dwExpWinVer) > VER40) {
|
|
PWND pwndIme = ptiWnd->spwndDefaultIme;
|
|
|
|
if (pwndIme && !TestWF(pwndIme, WFINDESTROY)) {
|
|
/*
|
|
* For new applications, we no longer post hacky WM_KEYUP.
|
|
* Instead, we use private IME_SYSTEM message.
|
|
*/
|
|
_PostMessage(pwndIme, WM_IME_SYSTEM, IMS_FINALIZE_COMPSTR, 0);
|
|
}
|
|
} else {
|
|
/*
|
|
* For the backward compatibility w/NT4, we post WM_KEYUP to finalize
|
|
* the composition string.
|
|
*/
|
|
PostInputMessage(gpqForeground, NULL, WM_KEYUP, VK_PROCESSKEY, 0, 0, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef GENERIC_INPUT
|
|
#ifdef GI_SINK
|
|
|
|
__inline VOID FillRawMouseInput(
|
|
PHIDDATA pHidData,
|
|
PMOUSE_INPUT_DATA pmei)
|
|
{
|
|
/*
|
|
* Set the data.
|
|
*/
|
|
pHidData->rid.data.mouse.usFlags = pmei->Flags;
|
|
pHidData->rid.data.mouse.ulButtons = pmei->Buttons;
|
|
pHidData->rid.data.mouse.ulRawButtons = pmei->RawButtons;
|
|
pHidData->rid.data.mouse.lLastX = pmei->LastX;
|
|
pHidData->rid.data.mouse.lLastY = pmei->LastY;
|
|
pHidData->rid.data.mouse.ulExtraInformation = pmei->ExtraInformation;
|
|
}
|
|
|
|
|
|
BOOL PostRawMouseInput(
|
|
PQ pq,
|
|
DWORD dwTime,
|
|
HANDLE hDevice,
|
|
PMOUSE_INPUT_DATA pmei)
|
|
{
|
|
PHIDDATA pHidData;
|
|
PWND pwnd;
|
|
PPROCESS_HID_TABLE pHidTable;
|
|
|
|
if (pmei->UnitId == INVALID_UNIT_ID) {
|
|
TAGMSG1(DBGTAG_PNP, "PostRawMouseInput: MOUSE_INPUT_DATA %p is already handled.", pmei);
|
|
return TRUE;
|
|
}
|
|
|
|
if (pq) {
|
|
pHidTable = PtiMouseFromQ(pq)->ppi->pHidTable;
|
|
} else {
|
|
pHidTable = NULL;
|
|
}
|
|
|
|
if (pHidTable && pHidTable->fRawMouse) {
|
|
UserAssert(PtiMouseFromQ(pq)->ppi->pHidTable);
|
|
pwnd = PtiMouseFromQ(pq)->ppi->pHidTable->spwndTargetMouse;
|
|
if (pwnd) {
|
|
pq = GETPTI(pwnd)->pq;
|
|
}
|
|
|
|
pHidData = AllocateHidData(hDevice, RIM_TYPEMOUSE, sizeof(RAWMOUSE), RIM_INPUT, pwnd);
|
|
|
|
UserAssert(pq);
|
|
|
|
if (pHidData == NULL) {
|
|
// failed to allocate
|
|
RIPMSG0(RIP_WARNING, "PostRawMouseInput: filed to allocate HIDDATA.");
|
|
return FALSE;
|
|
}
|
|
|
|
UserAssert(pmei);
|
|
|
|
FillRawMouseInput(pHidData, pmei);
|
|
|
|
PostInputMessage(pq, pwnd, WM_INPUT, RIM_INPUT, (LPARAM)PtoH(pHidData), dwTime, pmei->ExtraInformation);
|
|
}
|
|
|
|
#if DBG
|
|
pHidData = NULL;
|
|
#endif
|
|
|
|
if (IsMouseSinkPresent()) {
|
|
/*
|
|
* Walk through the global sink list.
|
|
*/
|
|
PLIST_ENTRY pList = gHidRequestTable.ProcessRequestList.Flink;
|
|
|
|
for (; pList != &gHidRequestTable.ProcessRequestList; pList = pList->Flink) {
|
|
PPROCESS_HID_TABLE pProcessHidTable = CONTAINING_RECORD(pList, PROCESS_HID_TABLE, link);
|
|
PPROCESSINFO ppiForeground;
|
|
|
|
if (pq) {
|
|
ppiForeground = PtiMouseFromQ(pq)->ppi;
|
|
} else {
|
|
ppiForeground = NULL;
|
|
}
|
|
|
|
UserAssert(pProcessHidTable);
|
|
if (pProcessHidTable->fRawMouseSink) {
|
|
/*
|
|
* Sink is specified. Let's check out if it's the legid receiver.
|
|
*/
|
|
|
|
UserAssert(pProcessHidTable->spwndTargetMouse); // shouldn't be NULL.
|
|
|
|
if (pProcessHidTable->spwndTargetMouse == NULL ||
|
|
TestWF(pProcessHidTable->spwndTargetMouse, WFINDESTROY) ||
|
|
TestWF(pProcessHidTable->spwndTargetMouse, WFDESTROYED)) {
|
|
/*
|
|
* This guy doesn't have a legit spwndTarget or the window is
|
|
* halfly destroyed.
|
|
*/
|
|
#ifdef LATER
|
|
pProcessHidTable->fRawMouse = pProcessHidTable->fRawMouseSink =
|
|
pProcessHidTable->fNoLegacyMouse = FALSE;
|
|
#endif
|
|
continue;
|
|
}
|
|
|
|
if (pProcessHidTable->spwndTargetMouse->head.rpdesk != grpdeskRitInput) {
|
|
/*
|
|
* This guy belongs to the other desktop, let's skip it.
|
|
*/
|
|
continue;
|
|
}
|
|
|
|
if (GETPTI(pProcessHidTable->spwndTargetMouse)->ppi == ppiForeground) {
|
|
/*
|
|
* Should be already handled, let's skip it.
|
|
*/
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Let's post the message to this guy.
|
|
*/
|
|
pHidData = AllocateHidData(hDevice, RIM_TYPEMOUSE, sizeof(RAWMOUSE), RIM_INPUTSINK, pProcessHidTable->spwndTargetMouse);
|
|
|
|
if (pHidData == NULL) {
|
|
RIPMSG1(RIP_WARNING, "PostInputMessage: failed to allocate HIDDATA for sink: %p", pProcessHidTable);
|
|
return FALSE;
|
|
}
|
|
|
|
FillRawMouseInput(pHidData, pmei);
|
|
pwnd = pProcessHidTable->spwndTargetMouse;
|
|
PostInputMessage(GETPTI(pwnd)->pq,
|
|
pwnd,
|
|
WM_INPUT,
|
|
RIM_INPUTSINK,
|
|
(LPARAM)PtoH(pHidData),
|
|
dwTime,
|
|
pmei->ExtraInformation);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Mark this raw input as processed.
|
|
*/
|
|
pmei->UnitId = INVALID_UNIT_ID;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#else // GI_SINK
|
|
|
|
// original code
|
|
|
|
BOOL PostRawMouseInput(
|
|
PQ pq,
|
|
DWORD dwTime,
|
|
HANDLE hDevice,
|
|
PMOUSE_INPUT_DATA pmei)
|
|
{
|
|
PHIDDATA pHidData;
|
|
PWND pwnd;
|
|
|
|
UserAssert(PtiMouseFromQ(pq)->ppi->pHidTable);
|
|
if (pmei->UnitId == INVALID_UNIT_ID) {
|
|
TAGMSG1(DBGTAG_PNP, "PostRawMouseInput: MOUSE_INPUT_DATA %p is already handled.", pmei);
|
|
return TRUE;
|
|
}
|
|
pwnd = PtiMouseFromQ(pq)->ppi->pHidTable->spwndTargetMouse;
|
|
if (pwnd) {
|
|
pq = GETPTI(pwnd)->pq;
|
|
}
|
|
|
|
pHidData = AllocateHidData(hDevice, RIM_TYPEMOUSE, sizeof(RAWMOUSE), RIM_INPUT, pwnd);
|
|
|
|
UserAssert(pq);
|
|
|
|
if (pHidData == NULL) {
|
|
// failed to allocate
|
|
RIPMSG0(RIP_WARNING, "PostRawMouseInput: filed to allocate HIDDATA.");
|
|
return FALSE;
|
|
}
|
|
|
|
UserAssert(hDevice);
|
|
UserAssert(pmei);
|
|
pHidData->rid.data.mouse.usFlags = pmei->Flags;
|
|
pHidData->rid.data.mouse.ulButtons = pmei->Buttons;
|
|
pHidData->rid.data.mouse.ulRawButtons = pmei->RawButtons;
|
|
pHidData->rid.data.mouse.lLastX = pmei->LastX;
|
|
pHidData->rid.data.mouse.lLastY = pmei->LastY;
|
|
pHidData->rid.data.mouse.ulExtraInformation = pmei->ExtraInformation;
|
|
|
|
/*
|
|
* Mark this raw input as processed.
|
|
*/
|
|
pmei->UnitId = INVALID_UNIT_ID;
|
|
|
|
PostInputMessage(pq, pwnd, WM_INPUT, RIM_INPUT, (LPARAM)PtoH(pHidData), dwTime, pmei->ExtraInformation);
|
|
|
|
return TRUE;
|
|
}
|
|
#endif // GI_SINK
|
|
|
|
BOOL RawInputRequestedForMouse(PTHREADINFO pti)
|
|
{
|
|
#ifdef GI_SINK
|
|
return gHidCounters.cMouseSinks > 0 || TestRawInputMode(pti, RawMouse);
|
|
#else
|
|
return TestRawInputMode(pti, RawKeyboard);
|
|
#endif
|
|
}
|
|
|
|
#endif // GENERIC_INPUT
|
|
|
|
/***************************************************************************\
|
|
* xxxButtonEvent (RIT)
|
|
*
|
|
* Button events from the mouse driver go here. Based on the location of
|
|
* the cursor the event is directed to specific window. When a button down
|
|
* occurs, a mouse owner window is established. All mouse events up to and
|
|
* including the corresponding button up go to the mouse owner window. This
|
|
* is done to best simulate what applications want when doing mouse capturing.
|
|
* Since we're processing these events asynchronously, but the application
|
|
* calls SetCapture() in response to it's synchronized processing of input
|
|
* we have no other way to get this functionality.
|
|
*
|
|
* The async keystate table for VK_*BUTTON is updated here.
|
|
*
|
|
* History:
|
|
* 10-18-90 DavidPe Created.
|
|
* 01-25-91 IanJa xxxWindowHitTest change
|
|
* 03-12-92 JonPa Make caller enter crit instead of this function
|
|
\***************************************************************************/
|
|
|
|
VOID xxxButtonEvent(
|
|
DWORD ButtonNumber,
|
|
POINT ptPointer,
|
|
BOOL fBreak,
|
|
DWORD time,
|
|
ULONG_PTR ExtraInfo,
|
|
#ifdef GENERIC_INPUT
|
|
HANDLE hDevice,
|
|
PMOUSE_INPUT_DATA pmei,
|
|
#endif
|
|
BOOL bInjected,
|
|
BOOL fDblClk)
|
|
{
|
|
UINT message, usVK, usOtherVK, wHardwareButton;
|
|
PWND pwnd;
|
|
LPARAM lParam;
|
|
WPARAM wParam;
|
|
int xbutton;
|
|
TL tlpwnd;
|
|
PHOOK pHook;
|
|
#ifdef GENERIC_INPUT
|
|
BOOL fMouseExclusive = FALSE;
|
|
#endif
|
|
|
|
#ifdef REDIRECTION
|
|
PWND pwndStart;
|
|
#endif // REDIRECTION
|
|
|
|
CheckCritIn();
|
|
|
|
|
|
/*
|
|
* Cancel Alt-Tab if the user presses a mouse button
|
|
*/
|
|
if (gspwndAltTab != NULL) {
|
|
xxxCancelCoolSwitch();
|
|
}
|
|
|
|
/*
|
|
* Grab the mouse button before we process any button swapping.
|
|
* This is so we won't get confused if someone calls
|
|
* SwapMouseButtons() inside a down-click/up-click.
|
|
*/
|
|
wHardwareButton = (UINT)ButtonNumber;
|
|
|
|
/*
|
|
* If this is the left or right mouse button, we have to handle mouse
|
|
* button swapping.
|
|
*/
|
|
if (ButtonNumber & (MOUSE_BUTTON_LEFT | MOUSE_BUTTON_RIGHT)) {
|
|
/*
|
|
* If button swapping is on, swap the mouse buttons
|
|
*/
|
|
if (SYSMET(SWAPBUTTON)) {
|
|
ButtonNumber ^= (MOUSE_BUTTON_LEFT | MOUSE_BUTTON_RIGHT);
|
|
}
|
|
|
|
/*
|
|
* Figure out VK
|
|
*/
|
|
if (ButtonNumber == MOUSE_BUTTON_RIGHT) {
|
|
usVK = VK_RBUTTON;
|
|
usOtherVK = VK_LBUTTON;
|
|
} else if (ButtonNumber == MOUSE_BUTTON_LEFT) {
|
|
usVK = VK_LBUTTON;
|
|
usOtherVK = VK_RBUTTON;
|
|
} else {
|
|
RIPMSG1(RIP_ERROR, "Unexpected Button number %d", ButtonNumber);
|
|
}
|
|
|
|
/*
|
|
* If the mouse buttons have recently been swapped AND the button
|
|
* transition doesn't match what we have in our keystate, then swap the
|
|
* button to match.
|
|
* This is to fix the ruler (tabs and margins) in Word 97 SR1, which
|
|
* calls SwapMouseButtons(0) to determine if button swapping is on, and
|
|
* if so then calls SwapMouseButtons(1) to restore it: if we receive a
|
|
* button event between these two calls, we may swap incorrectly, and
|
|
* be left with a mouse button stuck down or see the wrong button going
|
|
* down. This really messed up single/double button tab/margin setting!
|
|
* The same bug shows up under Windows '95, although very infrequently:
|
|
* Word 9 will use GetSystemMetrics(SM_SWAPBUTTON) instead according to
|
|
* to Mark Walker (MarkWal). (IanJa) #165157
|
|
*/
|
|
if (gbMouseButtonsRecentlySwapped) {
|
|
if ((!fBreak == !!TestAsyncKeyStateDown(usVK)) &&
|
|
(fBreak == !!TestAsyncKeyStateDown(usOtherVK))) {
|
|
RIPMSG4(RIP_WARNING, "Correct %s %s to %s %s",
|
|
ButtonNumber == MOUSE_BUTTON_LEFT ? "Left" : "Right",
|
|
fBreak ? "Up" : "Down",
|
|
ButtonNumber == MOUSE_BUTTON_LEFT ? "Right" : "Left",
|
|
fBreak ? "Up" : "Down");
|
|
ButtonNumber ^= (MOUSE_BUTTON_LEFT | MOUSE_BUTTON_RIGHT);
|
|
usVK = usOtherVK;
|
|
}
|
|
gbMouseButtonsRecentlySwapped = FALSE;
|
|
}
|
|
}
|
|
|
|
xbutton = 0;
|
|
switch (ButtonNumber) {
|
|
case MOUSE_BUTTON_RIGHT:
|
|
if (fBreak) {
|
|
message = WM_RBUTTONUP;
|
|
} else {
|
|
if (ISTS() && fDblClk)
|
|
message = WM_RBUTTONDBLCLK;
|
|
else
|
|
message = WM_RBUTTONDOWN;
|
|
}
|
|
break;
|
|
|
|
case MOUSE_BUTTON_LEFT:
|
|
if (fBreak) {
|
|
message = WM_LBUTTONUP;
|
|
} else {
|
|
if (ISTS() && fDblClk)
|
|
message = WM_LBUTTONDBLCLK;
|
|
else
|
|
message = WM_LBUTTONDOWN;
|
|
}
|
|
break;
|
|
|
|
case MOUSE_BUTTON_MIDDLE:
|
|
if (fBreak) {
|
|
message = WM_MBUTTONUP;
|
|
} else {
|
|
if (ISTS() && fDblClk)
|
|
message = WM_MBUTTONDBLCLK;
|
|
else
|
|
message = WM_MBUTTONDOWN;
|
|
}
|
|
usVK = VK_MBUTTON;
|
|
break;
|
|
|
|
case MOUSE_BUTTON_X1:
|
|
case MOUSE_BUTTON_X2:
|
|
if (fBreak) {
|
|
message = WM_XBUTTONUP;
|
|
} else {
|
|
if (ISTS() && fDblClk)
|
|
message = WM_XBUTTONDBLCLK;
|
|
else
|
|
message = WM_XBUTTONDOWN;
|
|
}
|
|
|
|
if (ButtonNumber == MOUSE_BUTTON_X1) {
|
|
usVK = VK_XBUTTON1;
|
|
xbutton = XBUTTON1;
|
|
} else {
|
|
usVK = VK_XBUTTON2;
|
|
xbutton = XBUTTON2;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
/*
|
|
* Unknown button. Since we don't
|
|
* have messages for these buttons, ignore them.
|
|
*/
|
|
return;
|
|
}
|
|
UserAssert(usVK != 0);
|
|
|
|
/*
|
|
* Check for click-lock
|
|
*/
|
|
if (TestEffectUP(MOUSECLICKLOCK)) {
|
|
if (message == WM_LBUTTONDOWN) {
|
|
if (gfStartClickLock) {
|
|
/*
|
|
* Already inside click-lock, so just throw this message away
|
|
* and turn click-lock off.
|
|
*/
|
|
gfStartClickLock = FALSE;
|
|
return;
|
|
} else {
|
|
/*
|
|
* Start click-lock and record the time.
|
|
*/
|
|
gfStartClickLock = TRUE;
|
|
gdwStartClickLockTick = time;
|
|
}
|
|
} else if (message == WM_LBUTTONUP) {
|
|
if (gfStartClickLock) {
|
|
DWORD dwDeltaTick = time - gdwStartClickLockTick;
|
|
if (dwDeltaTick > UPDWORDValue(SPI_GETMOUSECLICKLOCKTIME)) {
|
|
/*
|
|
* Inside a potential click-lock, so throw this message away
|
|
* if waited beyond the click-lock period.
|
|
*/
|
|
return;
|
|
} else {
|
|
/*
|
|
* The mouse up occurred before the click-lock period completed,
|
|
* so cancel the click-lock.
|
|
*/
|
|
|
|
gfStartClickLock = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
wParam = MAKEWPARAM(0, xbutton);
|
|
|
|
/*
|
|
* Call low level mouse hooks to see if they allow this message
|
|
* to pass through USER
|
|
*/
|
|
if ((pHook = PhkFirstValid(PtiCurrent(), WH_MOUSE_LL)) != NULL) {
|
|
MSLLHOOKSTRUCT mslls;
|
|
BOOL bAnsiHook;
|
|
|
|
mslls.pt = ptPointer;
|
|
mslls.mouseData = (LONG)wParam;
|
|
mslls.flags = bInjected;
|
|
mslls.time = time;
|
|
mslls.dwExtraInfo = ExtraInfo;
|
|
|
|
if (xxxCallHook2(pHook, HC_ACTION, (DWORD)message, (LPARAM)&mslls, &bAnsiHook)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef GENERIC_INPUT
|
|
UserAssert(gpqForeground == NULL || PtiMouseFromQ(gpqForeground));
|
|
if (gpqForeground) {
|
|
if (hDevice && RawInputRequestedForMouse(PtiMouseFromQ(gpqForeground))) {
|
|
PostRawMouseInput(gpqForeground, time, hDevice, pmei);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
* This is from HYDRA
|
|
*/
|
|
UserAssert(grpdeskRitInput != NULL);
|
|
|
|
#ifdef GENERIC_INPUT
|
|
if (gpqForeground && TestRawInputMode(PtiMouseFromQ(gpqForeground), CaptureMouse)) {
|
|
fMouseExclusive = TRUE;
|
|
pwnd = PtiMouseFromQ(gpqForeground)->ppi->pHidTable->spwndTargetMouse;
|
|
UserAssert(pwnd);
|
|
if (pwnd) {
|
|
goto KeyStatusUpdate;
|
|
}
|
|
// Something bad happened to our HidTable, but
|
|
// not let it AV because of that.
|
|
}
|
|
#endif
|
|
#ifdef REDIRECTION
|
|
/*
|
|
* Call the speed hit test hook
|
|
*/
|
|
pwndStart = xxxCallSpeedHitTestHook(&ptPointer);
|
|
if (pwndStart == NULL) {
|
|
pwndStart = grpdeskRitInput->pDeskInfo->spwnd;
|
|
}
|
|
|
|
pwnd = SpeedHitTest(pwndStart, ptPointer);
|
|
#else
|
|
pwnd = SpeedHitTest(grpdeskRitInput->pDeskInfo->spwnd, ptPointer);
|
|
#endif // REDIRECTION
|
|
|
|
/*
|
|
* Only post the message if we actually hit a window.
|
|
*/
|
|
if (pwnd == NULL) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Assign the message to a window.
|
|
*/
|
|
lParam = MAKELONG((SHORT)ptPointer.x, (SHORT)ptPointer.y);
|
|
|
|
/*
|
|
* KOREAN:
|
|
* Send VK_PROCESSKEY to finalize current composition string (NT4 behavior)
|
|
* Post private message to let IMM finalize the composition string (NT5)
|
|
*/
|
|
if (IS_IME_ENABLED() &&
|
|
!fBreak &&
|
|
KOREAN_KBD_LAYOUT(GetActiveHKL()) &&
|
|
!TestCF(pwnd, CFIME) &&
|
|
gpqForeground != NULL) {
|
|
FinalizeKoreanImeCompStrOnMouseClick(pwnd);
|
|
}
|
|
|
|
/*
|
|
* If screen capture is active do it
|
|
*/
|
|
if (gspwndScreenCapture != NULL)
|
|
pwnd = gspwndScreenCapture;
|
|
|
|
/*
|
|
* If this is a button down event and there isn't already
|
|
* a mouse owner, setup the mouse ownership globals.
|
|
*/
|
|
if (gspwndMouseOwner == NULL) {
|
|
if (!fBreak) {
|
|
PWND pwndCapture;
|
|
|
|
/*
|
|
* BIG HACK: If the foreground window has the capture
|
|
* and the mouse is outside the foreground queue then
|
|
* send a buttondown/up pair to that queue so it'll
|
|
* cancel it's modal loop.
|
|
*/
|
|
if (pwndCapture = PwndForegroundCapture()) {
|
|
|
|
if (GETPTI(pwnd)->pq != GETPTI(pwndCapture)->pq) {
|
|
PQ pqCapture;
|
|
|
|
pqCapture = GETPTI(pwndCapture)->pq;
|
|
PostInputMessage(pqCapture, pwndCapture, message,
|
|
0, lParam, 0, 0);
|
|
PostInputMessage(pqCapture, pwndCapture, message + 1,
|
|
0, lParam, 0, 0);
|
|
|
|
/*
|
|
* EVEN BIGGER HACK: To maintain compatibility
|
|
* with how tracking deals with this, we don't
|
|
* pass this event along. This prevents mouse
|
|
* clicks in other windows from causing them to
|
|
* become foreground while tracking. The exception
|
|
* to this is when we have the sysmenu up on
|
|
* an iconic window.
|
|
*/
|
|
if ((GETPTI(pwndCapture)->pmsd != NULL) &&
|
|
!IsMenuStarted(GETPTI(pwndCapture))) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
Lock(&(gspwndMouseOwner), pwnd);
|
|
gwMouseOwnerButton |= wHardwareButton;
|
|
glinp.ptLastClick = gpsi->ptCursor;
|
|
} else {
|
|
|
|
/*
|
|
* The mouse owner must have been destroyed or unlocked
|
|
* by a fullscreen switch. Keep the button state in sync.
|
|
*/
|
|
gwMouseOwnerButton &= ~wHardwareButton;
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
* Give any other button events to the mouse-owner window
|
|
* to be consistent with old capture semantics.
|
|
*/
|
|
if (gspwndScreenCapture == NULL) {
|
|
/*
|
|
* NT5 Foreground and Drag Drop.
|
|
* If the mouse goes up on a different thread
|
|
* make the mouse up thread the owner of this click
|
|
*/
|
|
if (fBreak && (GETPTI(pwnd) != GETPTI(gspwndMouseOwner))) {
|
|
glinp.ptiLastWoken = GETPTI(pwnd);
|
|
TAGMSG1(DBGTAG_FOREGROUND, "xxxButtonEvent. ptiLastWoken %#p", glinp.ptiLastWoken);
|
|
}
|
|
pwnd = gspwndMouseOwner;
|
|
}
|
|
|
|
/*
|
|
* If this is the button-up event for the mouse-owner
|
|
* clear gspwndMouseOwner.
|
|
*/
|
|
if (fBreak) {
|
|
gwMouseOwnerButton &= ~wHardwareButton;
|
|
if (!gwMouseOwnerButton)
|
|
Unlock(&gspwndMouseOwner);
|
|
} else {
|
|
gwMouseOwnerButton |= wHardwareButton;
|
|
}
|
|
}
|
|
|
|
KeyStatusUpdate:
|
|
/*
|
|
* Only update the async keystate when we know which window this
|
|
* event goes to (or else we can't keep the thread specific key
|
|
* state in sync).
|
|
*/
|
|
UserAssert(usVK != 0);
|
|
UpdateAsyncKeyState(GETPTI(pwnd)->pq, usVK, fBreak);
|
|
|
|
#ifdef GENERIC_INPUT
|
|
if (fMouseExclusive) {
|
|
/*
|
|
* If the foreground application requests mouse exclusive
|
|
* raw input, let's not post the activate messages etc.
|
|
* The mouse exclusiveness requires no activation,
|
|
* even within the same app.
|
|
*/
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Put pwnd into the foreground if this is a button down event
|
|
* and it isn't already the foreground window.
|
|
*/
|
|
if (!fBreak && GETPTI(pwnd)->pq != gpqForeground) {
|
|
/*
|
|
* If this is an WM_*BUTTONDOWN on a desktop window just do
|
|
* cancel-mode processing. Check to make sure that there
|
|
* wasn't already a mouse owner window. See comments below.
|
|
*/
|
|
if ((gpqForeground != NULL) && (pwnd == grpdeskRitInput->pDeskInfo->spwnd) &&
|
|
((gwMouseOwnerButton & wHardwareButton) ||
|
|
(gwMouseOwnerButton == 0))) {
|
|
PostEventMessage(gpqForeground->ptiMouse,
|
|
gpqForeground, QEVENT_CANCELMODE, NULL, 0, 0, 0);
|
|
|
|
} else if ((gwMouseOwnerButton & wHardwareButton) ||
|
|
(gwMouseOwnerButton == 0)) {
|
|
|
|
/*
|
|
* Don't bother setting the foreground window if there's
|
|
* already mouse owner window from a button-down different
|
|
* than this event. This prevents weird things from happening
|
|
* when the user starts a tracking operation with the left
|
|
* button and clicks the right button during the tracking
|
|
* operation.
|
|
*/
|
|
/*
|
|
* If pwnd is a descendent of a WS_EX_NOACTIVATE window, then we
|
|
* won't set it to the foreground
|
|
*/
|
|
PWND pwndTopLevel = GetTopLevelWindow(pwnd);
|
|
if (!TestWF(pwndTopLevel, WEFNOACTIVATE)) {
|
|
ThreadLockAlways(pwnd, &tlpwnd);
|
|
xxxSetForegroundWindow2(pwnd, NULL, 0);
|
|
/*
|
|
* Ok to unlock right away: the above didn't really leave the crit sec.
|
|
* We lock here for consistency so the debug macros work ok.
|
|
*/
|
|
ThreadUnlock(&tlpwnd);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef GENERIC_INPUT
|
|
if (TestRawInputMode(PtiMouseFromQ(GETPTI(pwnd)->pq), NoLegacyMouse)) {
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (GETPTI(pwnd)->pq->QF_flags & QF_MOUSEMOVED) {
|
|
PostMove(GETPTI(pwnd)->pq);
|
|
}
|
|
|
|
PostInputMessage(GETPTI(pwnd)->pq, pwnd, message, wParam, lParam, time, ExtraInfo);
|
|
|
|
/*
|
|
* If this is a mouse up event and stickykeys is enabled all latched
|
|
* keys will be released.
|
|
*/
|
|
if (fBreak && (TEST_ACCESSFLAG(StickyKeys, SKF_STICKYKEYSON) ||
|
|
TEST_ACCESSFLAG(MouseKeys, MKF_MOUSEKEYSON))) {
|
|
xxxHardwareMouseKeyUp(ButtonNumber);
|
|
}
|
|
|
|
if (message == WM_LBUTTONDOWN) {
|
|
PDESKTOP pdesk = GETPTI(pwnd)->rpdesk;
|
|
if (pdesk != NULL && pdesk->rpwinstaParent != NULL) {
|
|
|
|
UserAssert(!(pdesk->rpwinstaParent->dwWSF_Flags & WSF_NOIO));
|
|
|
|
#ifdef HUNGAPP_GHOSTING
|
|
if (FHungApp(GETPTI(pwnd), CMSHUNGAPPTIMEOUT)) {
|
|
SignalGhost(pwnd);
|
|
}
|
|
#endif // HUNGAPP_GHOSTING
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* The Button-Click Queue is protected by the semaphore gcsMouseEventQueue
|
|
*
|
|
\***************************************************************************/
|
|
#ifdef LOCK_MOUSE_CODE
|
|
#pragma alloc_text(MOUSE, QueueMouseEvent)
|
|
#endif
|
|
|
|
/***************************************************************************\
|
|
* QueueMouseEvent
|
|
*
|
|
* Params:
|
|
* ButtonFlags - button flags from the driver in MOUSE_INPUT_DATA.ButtonFlags
|
|
*
|
|
* ButtonData - data from the driver in MOUSE_INPUT_DATA.ButtonData
|
|
* Stores the wheel delta
|
|
*
|
|
* ExtraInfo - extra information from the driver in MOUSE_INPUT_DATA.ExtraInfo
|
|
* ptMouse - mouse delta
|
|
* time - tick count at time of event
|
|
* bInjected - injected by SendInput?
|
|
* bWakeRIT - wake the RIT?
|
|
*
|
|
\***************************************************************************/
|
|
|
|
VOID QueueMouseEvent(
|
|
USHORT ButtonFlags,
|
|
USHORT ButtonData,
|
|
ULONG_PTR ExtraInfo,
|
|
POINT ptMouse,
|
|
LONG time,
|
|
#ifdef GENERIC_INPUT
|
|
HANDLE hDevice,
|
|
PMOUSE_INPUT_DATA pmei,
|
|
#endif
|
|
BOOL bInjected,
|
|
BOOL bWakeRIT
|
|
)
|
|
{
|
|
CheckCritOut();
|
|
|
|
EnterMouseCrit();
|
|
|
|
LOGTIME(gMouseQueueMouseEventTime);
|
|
|
|
/*
|
|
* Button data must always be accompanied by a flag to interpret it.
|
|
*/
|
|
UserAssert(ButtonData == 0 || ButtonFlags != 0);
|
|
|
|
/*
|
|
* We can coalesce this mouse event with the previous event if there is a
|
|
* previous event, and if the previous event and this event involve no
|
|
* key transitions.
|
|
*/
|
|
if ((gdwMouseEvents == 0) ||
|
|
(ButtonFlags != 0) ||
|
|
(gMouseEventQueue[gdwMouseQueueHead].ButtonFlags != 0)) {
|
|
/*
|
|
* Can't coalesce: must add a new mouse event
|
|
*/
|
|
if (gdwMouseEvents >= NELEM_BUTTONQUEUE) {
|
|
/*
|
|
* But no more room!
|
|
*/
|
|
LeaveMouseCrit();
|
|
UserBeep(440, 125);
|
|
return;
|
|
}
|
|
|
|
gdwMouseQueueHead = (gdwMouseQueueHead + 1) % NELEM_BUTTONQUEUE;
|
|
gMouseEventQueue[gdwMouseQueueHead].ButtonFlags = ButtonFlags;
|
|
gMouseEventQueue[gdwMouseQueueHead].ButtonData = ButtonData;
|
|
gdwMouseEvents++;
|
|
}
|
|
|
|
gMouseEventQueue[gdwMouseQueueHead].ExtraInfo = ExtraInfo;
|
|
gMouseEventQueue[gdwMouseQueueHead].ptPointer = ptMouse;
|
|
gMouseEventQueue[gdwMouseQueueHead].time = time;
|
|
gMouseEventQueue[gdwMouseQueueHead].bInjected = bInjected;
|
|
#ifdef GENERIC_INPUT
|
|
gMouseEventQueue[gdwMouseQueueHead].hDevice = hDevice;
|
|
if (pmei) {
|
|
gMouseEventQueue[gdwMouseQueueHead].rawData = *pmei;
|
|
} else {
|
|
/*
|
|
* To indicate the rawData is invalid, set INVALID_UNIT_ID.
|
|
*/
|
|
gMouseEventQueue[gdwMouseQueueHead].rawData.UnitId = INVALID_UNIT_ID;
|
|
}
|
|
#endif
|
|
|
|
LeaveMouseCrit();
|
|
|
|
if (bWakeRIT) {
|
|
/*
|
|
* Signal RIT to complete the mouse input processing
|
|
*/
|
|
KeSetEvent(gpkeMouseData, EVENT_INCREMENT, FALSE);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************\
|
|
*
|
|
* Gets mouse events out of the queue
|
|
*
|
|
* Returns:
|
|
* TRUE - a mouse event is obtained in *pme
|
|
* FALSE - no mouse event available
|
|
*
|
|
\*****************************************************************************/
|
|
|
|
BOOL UnqueueMouseEvent(
|
|
PMOUSEEVENT pme
|
|
)
|
|
{
|
|
DWORD dwTail;
|
|
|
|
EnterMouseCrit();
|
|
|
|
LOGTIME(gMouseUnqueueMouseEventTime);
|
|
|
|
if (gdwMouseEvents == 0) {
|
|
LeaveMouseCrit();
|
|
return FALSE;
|
|
} else {
|
|
dwTail = (gdwMouseQueueHead - gdwMouseEvents + 1) % NELEM_BUTTONQUEUE;
|
|
*pme = gMouseEventQueue[dwTail];
|
|
gdwMouseEvents--;
|
|
}
|
|
|
|
LeaveMouseCrit();
|
|
return TRUE;
|
|
}
|
|
|
|
VOID xxxDoButtonEvent(PMOUSEEVENT pme)
|
|
{
|
|
ULONG dwButtonMask;
|
|
ULONG dwButtonState;
|
|
LPARAM lParam;
|
|
BOOL fWheel;
|
|
PHOOK pHook;
|
|
ULONG dwButtonData = (ULONG) pme->ButtonData;
|
|
|
|
CheckCritIn();
|
|
|
|
dwButtonState = (ULONG) pme->ButtonFlags;
|
|
fWheel = dwButtonState & MOUSE_WHEEL;
|
|
dwButtonState &= ~MOUSE_WHEEL;
|
|
|
|
for( dwButtonMask = 1;
|
|
dwButtonState != 0;
|
|
dwButtonData >>= 2, dwButtonState >>= 2, dwButtonMask <<= 1) {
|
|
|
|
if (dwButtonState & 1) {
|
|
xxxButtonEvent(dwButtonMask, pme->ptPointer, FALSE,
|
|
pme->time, pme->ExtraInfo,
|
|
#ifdef GENERIC_INPUT
|
|
pme->hDevice,
|
|
&pme->rawData,
|
|
#endif
|
|
pme->bInjected,
|
|
gbClientDoubleClickSupport && (dwButtonData & 1));
|
|
}
|
|
|
|
if (dwButtonState & 2) {
|
|
xxxButtonEvent(dwButtonMask, pme->ptPointer, TRUE,
|
|
pme->time, pme->ExtraInfo,
|
|
#ifdef GENERIC_INPUT
|
|
pme->hDevice,
|
|
&pme->rawData,
|
|
#endif
|
|
pme->bInjected ,FALSE);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Handle the wheel msg.
|
|
*/
|
|
if (fWheel && pme->ButtonData != 0 && gpqForeground) {
|
|
|
|
lParam = MAKELONG((SHORT)pme->ptPointer.x, (SHORT)pme->ptPointer.y);
|
|
|
|
/*
|
|
* Call low level mouse hooks to see if they allow this message
|
|
* to pass through USER
|
|
*/
|
|
if ((pHook = PhkFirstValid(PtiCurrent(), WH_MOUSE_LL)) != NULL) {
|
|
MSLLHOOKSTRUCT mslls;
|
|
BOOL bAnsiHook;
|
|
|
|
mslls.pt = pme->ptPointer;
|
|
mslls.mouseData = MAKELONG(0, pme->ButtonData);
|
|
mslls.flags = pme->bInjected;
|
|
mslls.time = pme->time;
|
|
mslls.dwExtraInfo = pme->ExtraInfo;
|
|
|
|
if (xxxCallHook2(pHook, HC_ACTION, (DWORD)WM_MOUSEWHEEL,
|
|
(LPARAM)&mslls, &bAnsiHook)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
#ifdef GENERIC_INPUT
|
|
UserAssert(gpqForeground == NULL || PtiMouseFromQ(gpqForeground));
|
|
if (gpqForeground && RawInputRequestedForMouse(PtiMouseFromQ(gpqForeground))) {
|
|
PostRawMouseInput(gpqForeground, pme->time, pme->hDevice, &pme->rawData);
|
|
}
|
|
|
|
if (gpqForeground && !TestRawInputMode(PtiMouseFromQ(gpqForeground), NoLegacyMouse)) {
|
|
#endif
|
|
PostInputMessage(
|
|
gpqForeground,
|
|
NULL,
|
|
WM_MOUSEWHEEL,
|
|
MAKELONG(0, pme->ButtonData),
|
|
lParam, pme->time,
|
|
pme->ExtraInfo);
|
|
#ifdef GENERIC_INPUT
|
|
}
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
VOID NTAPI InputApc(
|
|
IN PVOID ApcContext,
|
|
IN PIO_STATUS_BLOCK IoStatusBlock,
|
|
IN ULONG Reserved
|
|
)
|
|
{
|
|
PDEVICEINFO pDeviceInfo = (PDEVICEINFO)ApcContext;
|
|
UNREFERENCED_PARAMETER(Reserved);
|
|
|
|
/*
|
|
* Check if the RIT is being terminated.
|
|
* If we hit this assertion, the RIT was killed by someone inadvertently.
|
|
* Not much can be done if it once happens.
|
|
*/
|
|
UserAssert(gptiRit);
|
|
UserAssert((gptiRit->TIF_flags & TIF_INCLEANUP) == 0);
|
|
|
|
|
|
#ifdef DIAGNOSE_IO
|
|
pDeviceInfo->nReadsOutstanding--;
|
|
#endif
|
|
|
|
/*
|
|
* If this device needs freeing, abandon reading now and request the free.
|
|
* (Don't even process the input that we received in this APC)
|
|
*/
|
|
if (pDeviceInfo->usActions & GDIAF_FREEME) {
|
|
#ifdef GENERIC_INPUT
|
|
CheckCritOut();
|
|
EnterCrit();
|
|
#endif
|
|
EnterDeviceInfoListCrit();
|
|
pDeviceInfo->bFlags &= ~GDIF_READING;
|
|
FreeDeviceInfo(pDeviceInfo);
|
|
LeaveDeviceInfoListCrit();
|
|
#ifdef GENERIC_INPUT
|
|
LeaveCrit();
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
if (NT_SUCCESS(IoStatusBlock->Status) && pDeviceInfo->handle) {
|
|
PDEVICE_TEMPLATE pDevTpl = &aDeviceTemplate[pDeviceInfo->type];
|
|
pDevTpl->DeviceRead(pDeviceInfo);
|
|
}
|
|
|
|
if (IsRemoteConnection()) {
|
|
|
|
PoSetSystemState(ES_SYSTEM_REQUIRED);
|
|
|
|
}
|
|
|
|
StartDeviceRead(pDeviceInfo);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* ProcessMouseInput
|
|
*
|
|
* This function is called whenever a mouse event occurs. Once the event
|
|
* has been processed by USER, StartDeviceRead() is called again to request
|
|
* the next mouse event.
|
|
*
|
|
* When this routin returns, InputApc will start another read.
|
|
*
|
|
* History:
|
|
* 11-26-90 DavidPe Created.
|
|
* 07-23-92 Mikehar Moved most of the processing to _InternalMouseEvent()
|
|
* 11-08-92 JonPa Rewrote button code to work with new mouse drivers
|
|
* 11-18-97 IanJa Renamed from MouseApcProcedure etc, for multiple mice
|
|
\***************************************************************************/
|
|
VOID ProcessMouseInput(
|
|
PDEVICEINFO pMouseInfo)
|
|
{
|
|
PMOUSE_INPUT_DATA pmei, pmeiNext;
|
|
LONG time;
|
|
POINT ptLastMove;
|
|
|
|
/*
|
|
* This is an APC, so we don't need the DeviceInfoList Critical Section
|
|
* In fact, we don't want it either. We will not remove the device until
|
|
* ProcessMouseInput has signalled that it is OK to do so. (TBD)
|
|
*/
|
|
CheckCritOut();
|
|
CheckDeviceInfoListCritOut();
|
|
|
|
UserAssert(pMouseInfo);
|
|
UserAssert((PtiCurrentShared() == gTermIO.ptiDesktop) ||
|
|
(PtiCurrentShared() == gTermNOIO.ptiDesktop));
|
|
|
|
LOGTIME(gMouseProcessMiceInputTime);
|
|
|
|
if (gptiBlockInput != NULL) {
|
|
return;
|
|
}
|
|
|
|
if (TEST_ACCF(ACCF_ACCESSENABLED)) {
|
|
/*
|
|
* Any mouse movement resets the count of consecutive shift key
|
|
* presses. The shift key is used to enable & disable the
|
|
* stickykeys accessibility functionality.
|
|
*/
|
|
gStickyKeysLeftShiftCount = 0;
|
|
gStickyKeysRightShiftCount = 0;
|
|
|
|
/*
|
|
* Any mouse movement also cancels the FilterKeys activation timer.
|
|
* Entering critsect here breaks non-jerky mouse movement
|
|
*/
|
|
if (gtmridFKActivation != 0) {
|
|
EnterCrit();
|
|
KILLRITTIMER(NULL, gtmridFKActivation);
|
|
gtmridFKActivation = 0;
|
|
gFilterKeysState = FKMOUSEMOVE;
|
|
LeaveCrit();
|
|
}
|
|
}
|
|
|
|
#ifdef MOUSE_IP
|
|
/*
|
|
* Any mouse movement stops the sonar.
|
|
*/
|
|
if (IS_SONAR_ACTIVE()) {
|
|
EnterCrit();
|
|
if (IS_SONAR_ACTIVE()) {
|
|
StopSonar();
|
|
CLEAR_SONAR_LASTVK();
|
|
}
|
|
LeaveCrit();
|
|
}
|
|
#endif
|
|
|
|
if (!NT_SUCCESS(pMouseInfo->iosb.Status)) {
|
|
/*
|
|
* If we get a bad status, we abandon reading this mouse.
|
|
*/
|
|
|
|
if (!IsRemoteConnection())
|
|
if (pMouseInfo->iosb.Status != STATUS_DELETE_PENDING) {
|
|
RIPMSG3(RIP_ERROR, "iosb.Status %lx for mouse %#p (id %x) tell IanJa x63321",
|
|
pMouseInfo->iosb.Status,
|
|
pMouseInfo, pMouseInfo->mouse.Attr.MouseIdentifier);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* get the last move point from ptCursorAsync
|
|
*/
|
|
ptLastMove = gptCursorAsync;
|
|
|
|
pmei = pMouseInfo->mouse.Data;
|
|
while (pmei != NULL) {
|
|
|
|
time = NtGetTickCount();
|
|
|
|
/*
|
|
* Figure out where the next event is.
|
|
*/
|
|
pmeiNext = pmei + 1;
|
|
if ((PUCHAR)pmeiNext >=
|
|
(PUCHAR)(((PUCHAR)pMouseInfo->mouse.Data) + pMouseInfo->iosb.Information)) {
|
|
|
|
/*
|
|
* If there isn't another event set pmeiNext to
|
|
* NULL so we exit the loop and don't get confused.
|
|
*/
|
|
pmeiNext = NULL;
|
|
}
|
|
|
|
/*
|
|
* If a PS/2 mouse was plugged in, evaluate the (new) mouse and
|
|
* the skip the input record.
|
|
*/
|
|
if (pmei->Flags & MOUSE_ATTRIBUTES_CHANGED) {
|
|
RequestDeviceChange(pMouseInfo, GDIAF_REFRESH_MOUSE, FALSE);
|
|
goto NextMouseInputRecord;
|
|
}
|
|
|
|
/*
|
|
* First process any mouse movement that occured.
|
|
* It is important to process movement before button events, otherwise
|
|
* absolute coordinate pointing devices like touch-screens and tablets
|
|
* will produce button clicks at old coordinates.
|
|
*/
|
|
if (pmei->LastX || pmei->LastY) {
|
|
|
|
/*
|
|
* Get the actual point that will be injected.
|
|
*/
|
|
GetMouseCoord(pmei->LastX,
|
|
pmei->LastY,
|
|
pmei->Flags,
|
|
time,
|
|
pmei->ExtraInformation,
|
|
&ptLastMove);
|
|
|
|
/*
|
|
* If this is a move-only event, and the next one is also a
|
|
* move-only event, skip/coalesce it.
|
|
*/
|
|
if ( (pmeiNext != NULL) &&
|
|
(pmei->ButtonFlags == 0) &&
|
|
(pmeiNext->ButtonFlags == 0) &&
|
|
(fAbsoluteMouse(pmei) == fAbsoluteMouse(pmeiNext))) {
|
|
|
|
pmei = pmeiNext;
|
|
|
|
continue;
|
|
}
|
|
|
|
#ifdef GENERIC_INPUT
|
|
UserAssert(sizeof(HANDLE) == sizeof(pMouseInfo));
|
|
#endif
|
|
/*
|
|
* Moves the cursor on the screen and updates gptCursorAsync
|
|
* Call directly xxxMoveEventAbsolute because we already did the
|
|
* acceleration sensitivity and clipping.
|
|
*/
|
|
xxxMoveEventAbsolute(
|
|
ptLastMove.x,
|
|
ptLastMove.y,
|
|
pmei->ExtraInformation,
|
|
#ifdef GENERIC_INPUT
|
|
PtoHq(pMouseInfo),
|
|
pmei,
|
|
#endif
|
|
time,
|
|
FALSE
|
|
);
|
|
|
|
/*
|
|
* Now update ptLastMove with ptCursorAsync because ptLastMove
|
|
* doesn't reflect the clipping.
|
|
*/
|
|
ptLastMove = gptCursorAsync;
|
|
}
|
|
|
|
/*
|
|
* Queue mouse event for the other thread to pick up when it finishes
|
|
* with the USER critical section.
|
|
* If pmeiNext == NULL, there is no more mouse input yet, so wake RIT.
|
|
*/
|
|
QueueMouseEvent(
|
|
pmei->ButtonFlags,
|
|
pmei->ButtonData,
|
|
pmei->ExtraInformation,
|
|
gptCursorAsync,
|
|
time,
|
|
#ifdef GENERIC_INPUT
|
|
PtoH(pMouseInfo),
|
|
pmei,
|
|
#endif
|
|
FALSE,
|
|
(pmeiNext == NULL));
|
|
|
|
NextMouseInputRecord:
|
|
pmei = pmeiNext;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* IsHexNumpadKeys (RIT) inline
|
|
*
|
|
* If you change this code, you may need to change
|
|
* xxxInternalToUnicode() as well.
|
|
\***************************************************************************/
|
|
__inline BOOL IsHexNumpadKeys(
|
|
BYTE Vk,
|
|
WORD wScanCode)
|
|
{
|
|
return (wScanCode >= SCANCODE_NUMPAD_FIRST && wScanCode <= SCANCODE_NUMPAD_LAST && aVkNumpad[wScanCode - SCANCODE_NUMPAD_FIRST] != 0xff) ||
|
|
(Vk >= L'A' && Vk <= L'F') ||
|
|
(Vk >= L'0' && Vk <= L'9');
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* LowLevelHexNumpad (RIT) inline
|
|
*
|
|
* If you change this code, you may need to change
|
|
* xxxInternalToUnicode() as well.
|
|
\***************************************************************************/
|
|
VOID LowLevelHexNumpad(
|
|
WORD wScanCode,
|
|
BYTE Vk,
|
|
BOOL fBreak,
|
|
USHORT usExtraStuff)
|
|
{
|
|
if (!TestAsyncKeyStateDown(VK_MENU)) {
|
|
if (gfInNumpadHexInput & NUMPAD_HEXMODE_LL) {
|
|
gfInNumpadHexInput &= ~NUMPAD_HEXMODE_LL;
|
|
}
|
|
} else {
|
|
if (!fBreak) { // if it's key down
|
|
if ((gfInNumpadHexInput & NUMPAD_HEXMODE_LL) ||
|
|
wScanCode == SCANCODE_NUMPAD_PLUS || wScanCode == SCANCODE_NUMPAD_DOT) {
|
|
if ((usExtraStuff & KBDEXT) == 0) {
|
|
/*
|
|
* We need to check whether the input is escape character
|
|
* of hex input mode.
|
|
* This should be equivalent code as in xxxInternalToUnicode().
|
|
* If you change this code, you may need to change
|
|
* xxxInternalToUnicode() as well.
|
|
*/
|
|
WORD wModBits = 0;
|
|
|
|
wModBits |= TestAsyncKeyStateDown(VK_MENU) ? KBDALT : 0;
|
|
wModBits |= TestAsyncKeyStateDown(VK_SHIFT) ? KBDSHIFT : 0;
|
|
wModBits |= TestAsyncKeyStateDown(VK_KANA) ? KBDKANA : 0;
|
|
|
|
if (MODIFIER_FOR_ALT_NUMPAD(wModBits)) {
|
|
if ((gfInNumpadHexInput & NUMPAD_HEXMODE_LL) == 0) {
|
|
/*
|
|
* Only if it's not a hotkey, we enter hex Alt+Numpad mode.
|
|
*/
|
|
UINT wHotKeyMod = 0;
|
|
|
|
wHotKeyMod |= (wModBits & KBDSHIFT) ? MOD_SHIFT : 0;
|
|
wHotKeyMod |= TestAsyncKeyStateDown(VK_CONTROL) ? MOD_CONTROL : 0;
|
|
UserAssert(wModBits & KBDALT);
|
|
wHotKeyMod |= MOD_ALT;
|
|
wHotKeyMod |= TestAsyncKeyStateDown(VK_LWIN) || TestAsyncKeyStateDown(VK_RWIN) ?
|
|
MOD_WIN : 0;
|
|
|
|
if (IsHotKey(wHotKeyMod, Vk) == NULL) {
|
|
UserAssert(wScanCode == SCANCODE_NUMPAD_PLUS || wScanCode == SCANCODE_NUMPAD_DOT);
|
|
gfInNumpadHexInput |= NUMPAD_HEXMODE_LL;
|
|
}
|
|
} else if (!IsHexNumpadKeys(Vk, wScanCode)) {
|
|
gfInNumpadHexInput &= ~NUMPAD_HEXMODE_LL;
|
|
}
|
|
} else {
|
|
gfInNumpadHexInput &= ~NUMPAD_HEXMODE_LL;
|
|
}
|
|
} else {
|
|
gfInNumpadHexInput &= ~NUMPAD_HEXMODE_LL;
|
|
}
|
|
} else {
|
|
UserAssert((gfInNumpadHexInput & NUMPAD_HEXMODE_LL) == 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef GENERIC_INPUT
|
|
#if defined(GI_SINK)
|
|
|
|
__inline VOID FillRawKeyboardInput(
|
|
PHIDDATA pHidData,
|
|
PKEYBOARD_INPUT_DATA pkei,
|
|
UINT message,
|
|
USHORT vkey)
|
|
{
|
|
/*
|
|
* Set the data.
|
|
*/
|
|
pHidData->rid.data.keyboard.MakeCode = pkei->MakeCode;
|
|
pHidData->rid.data.keyboard.Flags = pkei->Flags;
|
|
pHidData->rid.data.keyboard.Reserved = pkei->Reserved;
|
|
pHidData->rid.data.keyboard.Message = message;
|
|
pHidData->rid.data.keyboard.VKey = vkey;
|
|
pHidData->rid.data.keyboard.ExtraInformation = pkei->ExtraInformation;
|
|
}
|
|
|
|
BOOL PostRawKeyboardInput(
|
|
PQ pq,
|
|
DWORD dwTime,
|
|
HANDLE hDevice,
|
|
PKEYBOARD_INPUT_DATA pkei,
|
|
UINT message,
|
|
USHORT vkey)
|
|
{
|
|
PPROCESS_HID_TABLE pHidTable = PtiKbdFromQ(pq)->ppi->pHidTable;
|
|
PHIDDATA pHidData;
|
|
PWND pwnd;
|
|
WPARAM wParam = RIM_INPUT;
|
|
|
|
if (pHidTable && pHidTable->fRawKeyboard) {
|
|
PTHREADINFO pti;
|
|
|
|
UserAssert(PtiKbdFromQ(pq)->ppi->pHidTable);
|
|
pti = PtiKbdFromQ(pq);
|
|
pwnd = pti->ppi->pHidTable->spwndTargetKbd;
|
|
|
|
if (pwnd == NULL) {
|
|
pwnd = pq->spwndFocus;
|
|
} else {
|
|
pq = GETPTI(pwnd)->pq;
|
|
}
|
|
|
|
if (TestRawInputModeNoCheck(pti, RawKeyboard)) {
|
|
wParam = RIM_INPUT;
|
|
}
|
|
|
|
pHidData = AllocateHidData(hDevice, RIM_TYPEKEYBOARD, sizeof(RAWKEYBOARD), wParam, pwnd);
|
|
|
|
UserAssert(pq);
|
|
|
|
if (pHidData == NULL) {
|
|
// failed to allocate
|
|
RIPMSG0(RIP_WARNING, "PostRawKeyboardInput: failed to allocate HIDDATA.");
|
|
return FALSE;
|
|
}
|
|
|
|
UserAssert(pkei);
|
|
|
|
FillRawKeyboardInput(pHidData, pkei, message, vkey);
|
|
|
|
if (!PostInputMessage(pq, pwnd, WM_INPUT, RIM_INPUT, (LPARAM)PtoHq(pHidData), dwTime, pkei->ExtraInformation)) {
|
|
FreeHidData(pHidData);
|
|
}
|
|
}
|
|
|
|
#if DBG
|
|
pHidData = NULL;
|
|
#endif
|
|
|
|
if (IsKeyboardSinkPresent()) {
|
|
/*
|
|
* Walk through the global sink list.
|
|
*/
|
|
PLIST_ENTRY pList = gHidRequestTable.ProcessRequestList.Flink;
|
|
PPROCESSINFO ppiForeground = PtiKbdFromQ(pq)->ppi;
|
|
|
|
for (; pList != &gHidRequestTable.ProcessRequestList; pList = pList->Flink) {
|
|
PPROCESS_HID_TABLE pProcessHidTable = CONTAINING_RECORD(pList, PROCESS_HID_TABLE, link);
|
|
|
|
UserAssert(pProcessHidTable);
|
|
if (pProcessHidTable->fRawKeyboardSink) {
|
|
/*
|
|
* Sink is specified. Let's check out if it's the legid receiver.
|
|
*/
|
|
|
|
UserAssert(pProcessHidTable->spwndTargetKbd); // shouldn't be NULL.
|
|
|
|
if (pProcessHidTable->spwndTargetKbd == NULL ||
|
|
TestWF(pProcessHidTable->spwndTargetKbd, WFINDESTROY) ||
|
|
TestWF(pProcessHidTable->spwndTargetKbd, WFDESTROYED)) {
|
|
/*
|
|
* This guy doesn't have a legit spwndTarget or the window is
|
|
* halfly destroyed.
|
|
*/
|
|
#ifdef LATER
|
|
pProcessHidTable->fRawKeyboard = pProcessHidTable->fRawKeyboardSink =
|
|
pProcessHidTable->fNoLegacyKeyboard = FALSE;
|
|
#endif
|
|
continue;
|
|
}
|
|
|
|
if (pProcessHidTable->spwndTargetKbd->head.rpdesk != grpdeskRitInput) {
|
|
/*
|
|
* This guy belongs to the other desktop, let's skip it.
|
|
*/
|
|
continue;
|
|
}
|
|
|
|
if (GETPTI(pProcessHidTable->spwndTargetKbd)->ppi == ppiForeground) {
|
|
/*
|
|
* Should be already handled, let's skip it.
|
|
*/
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Let's post the message to this guy.
|
|
*/
|
|
pHidData = AllocateHidData(hDevice, RIM_TYPEKEYBOARD, sizeof(RAWKEYBOARD), RIM_INPUTSINK, pProcessHidTable->spwndTargetKbd);
|
|
|
|
if (pHidData == NULL) {
|
|
RIPMSG1(RIP_WARNING, "PostInputMessage: failed to allocate HIDDATA for sink: %p", pProcessHidTable);
|
|
return FALSE;
|
|
}
|
|
|
|
FillRawKeyboardInput(pHidData, pkei, message, vkey);
|
|
pwnd = pProcessHidTable->spwndTargetKbd;
|
|
pq = GETPTI(pwnd)->pq;
|
|
PostInputMessage(pq, pwnd, WM_INPUT, RIM_INPUTSINK, (LPARAM)PtoHq(pHidData), dwTime, pkei->ExtraInformation);
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#else // GI_SINK
|
|
|
|
BOOL PostRawKeyboardInput(
|
|
PQ pq,
|
|
DWORD dwTime,
|
|
HANDLE hDevice,
|
|
PKEYBOARD_INPUT_DATA pkei,
|
|
UINT message,
|
|
USHORT vkey)
|
|
{
|
|
PHIDDATA pHidData;
|
|
PWND pwnd;
|
|
|
|
UserAssert(PtiKbdFromQ(pq)->ppi->pHidTable);
|
|
pwnd = PtiKbdFromQ(pq)->ppi->pHidTable->spwndTargetKbd;
|
|
|
|
if (pwnd == NULL) {
|
|
pwnd = pq->spwndFocus;
|
|
} else {
|
|
pq = GETPTI(pwnd)->pq;
|
|
}
|
|
|
|
pHidData = AllocateHidData(hDevice, RIM_TYPEKEYBOARD, sizeof(RAWKEYBOARD), RIM_INPUT, pwnd);
|
|
|
|
UserAssert(pq);
|
|
|
|
if (pHidData == NULL) {
|
|
// failed to allocate
|
|
RIPMSG0(RIP_WARNING, "PostRawKeyboardInput: failed to allocate HIDDATA.");
|
|
return FALSE;
|
|
}
|
|
|
|
UserAssert(hDevice);
|
|
UserAssert(pkei);
|
|
|
|
/*
|
|
* Set the data.
|
|
*/
|
|
pHidData->rid.data.keyboard.MakeCode = pkei->MakeCode;
|
|
pHidData->rid.data.keyboard.Flags = pkei->Flags;
|
|
pHidData->rid.data.keyboard.Reserved = pkei->Reserved;
|
|
pHidData->rid.data.keyboard.Message = message;
|
|
pHidData->rid.data.keyboard.VKey = vkey;
|
|
pHidData->rid.data.keyboard.ExtraInformation = pkei->ExtraInformation;
|
|
|
|
PostInputMessage(pq, pwnd, WM_INPUT, RIM_INPUT, (LPARAM)PtoHq(pHidData), dwTime, pkei->ExtraInformation);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#endif // GI_SINK
|
|
|
|
BOOL RawInputRequestedForKeyboard(PTHREADINFO pti)
|
|
{
|
|
#ifdef GI_SINK
|
|
return IsKeyboardSinkPresent() || TestRawInputMode(pti, RawKeyboard);
|
|
#else
|
|
return TestRawInputMode(pti, RawKeyboard);
|
|
#endif
|
|
}
|
|
|
|
#endif // GENERIC_INPUT
|
|
|
|
/***************************************************************************\
|
|
* xxxKeyEvent (RIT)
|
|
*
|
|
* All events from the keyboard driver go here. We receive a scan code
|
|
* from the driver and convert it to a virtual scan code and virtual
|
|
* key.
|
|
*
|
|
* The async keystate table and keylights are also updated here. Based
|
|
* on the 'focus' window we direct the input to a specific window. If
|
|
* the ALT key is down we send the events as WM_SYSKEY* messages.
|
|
*
|
|
* History:
|
|
* 10-18-90 DavidPe Created.
|
|
* 11-13-90 DavidPe WM_SYSKEY* support.
|
|
* 11-30-90 DavidPe Added keylight updating support.
|
|
* 12-05-90 DavidPe Added hotkey support.
|
|
* 03-14-91 DavidPe Moved most lParam flag support to xxxCookMessage().
|
|
* 06-07-91 DavidPe Changed to use gpqForeground rather than pwndFocus.
|
|
\***************************************************************************/
|
|
|
|
VOID xxxKeyEvent(
|
|
USHORT usFlaggedVk,
|
|
WORD wScanCode,
|
|
DWORD time,
|
|
ULONG_PTR ExtraInfo,
|
|
#ifdef GENERIC_INPUT
|
|
HANDLE hDevice,
|
|
PKEYBOARD_INPUT_DATA pkei,
|
|
#endif
|
|
BOOL bInjected)
|
|
{
|
|
USHORT message, usExtraStuff;
|
|
BOOL fBreak;
|
|
BYTE VkHanded;
|
|
BYTE Vk;
|
|
TL tlpwndActivate;
|
|
DWORD fsReserveKeys;
|
|
static BOOL fMakeAltUpASysKey;
|
|
PHOOK pHook;
|
|
PTHREADINFO ptiCurrent = PtiCurrent();
|
|
#ifdef GENERIC_INPUT
|
|
PTHREADINFO ptiKbd; // N.b. needs revalidation every time
|
|
// it leaves the critsec.
|
|
BOOL fSASHandled = FALSE;
|
|
#endif
|
|
|
|
CheckCritIn();
|
|
|
|
fBreak = usFlaggedVk & KBDBREAK;
|
|
SET_SRVIF(SRVIF_LASTRITWASKEYBOARD);
|
|
|
|
/*
|
|
* Is this a keyup or keydown event?
|
|
*/
|
|
message = fBreak ? WM_KEYUP : WM_KEYDOWN;
|
|
|
|
VkHanded = (BYTE)usFlaggedVk; // get rid of state bits - no longer needed
|
|
usExtraStuff = usFlaggedVk & KBDEXT;
|
|
|
|
/*
|
|
* Convert Left/Right Ctrl/Shift/Alt key to "unhanded" key.
|
|
* ie: if VK_LCONTROL or VK_RCONTROL, convert to VK_CONTROL etc.
|
|
* Update this "unhanded" key's state if necessary.
|
|
*/
|
|
if ((VkHanded >= VK_LSHIFT) && (VkHanded <= VK_RMENU)) {
|
|
BYTE VkOtherHand = VkHanded ^ 1;
|
|
|
|
Vk = (BYTE)((VkHanded - VK_LSHIFT) / 2 + VK_SHIFT);
|
|
if (!fBreak || !TestAsyncKeyStateDown(VkOtherHand)) {
|
|
if ((gptiBlockInput == NULL) || (gptiBlockInput != ptiCurrent)) {
|
|
UpdateAsyncKeyState(gpqForeground, Vk, fBreak);
|
|
}
|
|
}
|
|
} else {
|
|
Vk = VkHanded;
|
|
}
|
|
|
|
/*
|
|
* Maintain gfsSASModifiersDown to indicate which of Ctrl/Shift/Alt
|
|
* are really truly physically down
|
|
*/
|
|
if (!bInjected && ((wScanCode & SCANCODE_SIMULATED) == 0)) {
|
|
if (fBreak) {
|
|
gfsSASModifiersDown &= ~VKTOMODIFIERS(Vk);
|
|
} else {
|
|
gfsSASModifiersDown |= VKTOMODIFIERS(Vk);
|
|
}
|
|
}
|
|
|
|
#ifdef GENERIC_INPUT
|
|
ptiKbd = ValidatePtiKbd(gpqForeground);
|
|
#endif
|
|
|
|
/*
|
|
* Call low level keyboard hook to see if it allows this
|
|
* message to pass
|
|
*/
|
|
if ((pHook = PhkFirstValid(ptiCurrent, WH_KEYBOARD_LL)) != NULL) {
|
|
KBDLLHOOKSTRUCT kbds;
|
|
BOOL bAnsiHook;
|
|
USHORT msg = message;
|
|
USHORT usExtraLL = usExtraStuff;
|
|
|
|
#ifdef GENERIC_INPUT
|
|
UserAssert(GETPTI(pHook));
|
|
if (ptiKbd && ptiKbd->ppi == GETPTI(pHook)->ppi) {
|
|
// Skip LL hook call if the foreground application has
|
|
// a LL keyboard hook and the raw input enabled
|
|
// at the same time.
|
|
if (TestRawInputMode(ptiKbd, RawKeyboard)) {
|
|
goto skip_llhook;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Check if this is a WM_SYS* message
|
|
*/
|
|
if (TestRawKeyDown(VK_MENU) &&
|
|
!TestRawKeyDown(VK_CONTROL)) {
|
|
|
|
msg += (WM_SYSKEYDOWN - WM_KEYDOWN);
|
|
usExtraLL |= 0x2000; // ALT key down
|
|
}
|
|
|
|
kbds.vkCode = (DWORD)VkHanded;
|
|
kbds.scanCode = (DWORD)wScanCode;
|
|
kbds.flags = HIBYTE(usExtraLL | (bInjected ? (LLKHF_INJECTED << 8) : 0));
|
|
kbds.flags |= (fBreak ? (KBDBREAK >> 8) : 0);
|
|
kbds.time = time;
|
|
kbds.dwExtraInfo = ExtraInfo;
|
|
|
|
if (xxxCallHook2(pHook, HC_ACTION, (DWORD)msg, (LPARAM)&kbds, &bAnsiHook)) {
|
|
|
|
UINT fsModifiers;
|
|
|
|
/*
|
|
* We can't let low level hooks or BlockInput() eat SAS
|
|
* or someone could write a trojan winlogon look alike.
|
|
*/
|
|
if (IsSAS(VkHanded, &fsModifiers)) {
|
|
RIPMSG0(RIP_WARNING, "xxxKeyEvent: SAS ignore bad response from low level hook");
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef GENERIC_INPUT
|
|
skip_llhook:
|
|
#endif
|
|
|
|
/*
|
|
* If someone is blocking input and it's not us, don't allow this input
|
|
*/
|
|
if (gptiBlockInput && (gptiBlockInput != ptiCurrent)) {
|
|
UINT fsModifiers;
|
|
if (IsSAS(VkHanded, &fsModifiers)) {
|
|
RIPMSG0(RIP_WARNING, "xxxKeyEvent: SAS unblocks BlockInput");
|
|
gptiBlockInput = NULL;
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
UpdateAsyncKeyState(gpqForeground, VkHanded, fBreak);
|
|
|
|
/*
|
|
* Clear gfInNumpadHexInput if Menu key is up.
|
|
*/
|
|
if (gfEnableHexNumpad && gpqForeground
|
|
#ifdef GENERIC_INPUT
|
|
&& !TestRawInputMode(PtiKbdFromQ(gpqForeground), NoLegacyKeyboard)
|
|
#endif
|
|
) {
|
|
LowLevelHexNumpad(wScanCode, Vk, fBreak, usExtraStuff);
|
|
}
|
|
|
|
/*
|
|
* If this is a make and the key is one linked to the keyboard LEDs,
|
|
* update their state.
|
|
*/
|
|
|
|
if (!fBreak &&
|
|
((Vk == VK_CAPITAL) || (Vk == VK_NUMLOCK) || (Vk == VK_SCROLL) ||
|
|
(Vk == VK_KANA && JAPANESE_KBD_LAYOUT(GetActiveHKL())))) {
|
|
/*
|
|
* Only Japanese keyboard layout could generate VK_KANA.
|
|
*
|
|
* [Comments for before]
|
|
* Since NT 3.x, UpdatesKeyLisghts() had been called for VK_KANA
|
|
* at both of 'make' and 'break' to support NEC PC-9800 Series
|
|
* keyboard hardware, but for NT 4.0, thier keyboard driver emurate
|
|
* PC/AT keyboard hardware, then this is changed to
|
|
* "Call UpdateKeyLights() only at 'make' for VK_KANA"
|
|
*/
|
|
UpdateKeyLights(bInjected);
|
|
}
|
|
|
|
/*
|
|
* check for reserved keys
|
|
*/
|
|
fsReserveKeys = 0;
|
|
if (gptiForeground != NULL)
|
|
fsReserveKeys = gptiForeground->fsReserveKeys;
|
|
|
|
/*
|
|
* Check the RIT's queue to see if it's doing the cool switch thing.
|
|
* Cancel if the user presses any other key.
|
|
*/
|
|
if (gspwndAltTab != NULL && (!fBreak) &&
|
|
Vk != VK_TAB && Vk != VK_SHIFT && Vk != VK_MENU) {
|
|
|
|
/*
|
|
* Remove the Alt-tab window
|
|
*/
|
|
xxxCancelCoolSwitch();
|
|
|
|
/*
|
|
* eat VK_ESCAPE if the app doesn't want it
|
|
*/
|
|
if ((Vk == VK_ESCAPE) && !(fsReserveKeys & CONSOLE_ALTESC)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check for hotkeys.
|
|
*/
|
|
if (xxxDoHotKeyStuff(Vk, fBreak, fsReserveKeys)) {
|
|
|
|
#ifdef GENERIC_INPUT
|
|
UINT fsModifiers;
|
|
|
|
/*
|
|
* Windows Bug 268903: DI folks want the DEL key reported
|
|
* even though it's already handled --- for the compatibility
|
|
* with the LL hook.
|
|
*/
|
|
if (IsSAS(VkHanded, &fsModifiers)) {
|
|
fSASHandled = TRUE;
|
|
} else {
|
|
#endif
|
|
/*
|
|
* The hotkey was processed so don't pass on the event.
|
|
*/
|
|
return;
|
|
#ifdef GENERIC_INPUT
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef GENERIC_INPUT
|
|
/*
|
|
* If the foreground thread wants RawInput, post it here.
|
|
*/
|
|
|
|
ptiKbd = ValidatePtiKbd(gpqForeground);
|
|
|
|
if (pkei && ptiKbd && RawInputRequestedForKeyboard(ptiKbd)) {
|
|
DWORD msg = message;
|
|
#if POST_EXTRALL
|
|
DWORD usExtraLL = usExtraStuff;
|
|
#endif
|
|
|
|
/*
|
|
* Check if this is a WM_SYS* message
|
|
*/
|
|
if (TestRawKeyDown(VK_MENU) &&
|
|
!TestRawKeyDown(VK_CONTROL)) {
|
|
|
|
msg += (WM_SYSKEYDOWN - WM_KEYDOWN);
|
|
#if POST_EXTRA_LL
|
|
usExtraLL |= 0x2000; // ALT key down
|
|
#endif
|
|
}
|
|
|
|
TAGMSG3(DBGTAG_PNP, "xxxKeyEvent: posting to pwnd=%#p, vk=%02x, flag=%04x", gpqForeground->spwndFocus, Vk, pkei->Flags);
|
|
PostRawKeyboardInput(gpqForeground, time, hDevice, pkei, msg, (USHORT)Vk);
|
|
}
|
|
|
|
/*
|
|
* If SAS key is handled, this is a special case, just bail out.
|
|
*/
|
|
if (fSASHandled) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* If the foreground thread does not want the legacy input, bail out.
|
|
*/
|
|
if (ptiKbd) {
|
|
if (VkHanded == 0) {
|
|
TAGMSG0(DBGTAG_PNP, "xxxKeyEvent: vkHanded is zero, bail out.");
|
|
return;
|
|
}
|
|
|
|
if (TestRawInputMode(ptiKbd, NoLegacyKeyboard)) {
|
|
if (Vk == VK_MENU || Vk == VK_TAB || gspwndAltTab != NULL) {
|
|
/*
|
|
* Special case for fast switching. We should always
|
|
* handle these hotkeys.
|
|
*/
|
|
TAGMSG0(DBGTAG_PNP, "xxxKeyEvent: we'll do Alt+Tab even if the FG thread requests NoLegacy");
|
|
} else if ((TestRawInputMode(ptiKbd, AppKeys)) &&
|
|
(Vk >= VK_APPCOMMAND_FIRST && Vk <= VK_APPCOMMAND_LAST)) {
|
|
TAGMSG0(DBGTAG_PNP, "xxxKeyEvent: we'll do app commands if the FG thread requests NoLegacy and AppKeys");
|
|
} else {
|
|
TAGMSG0(DBGTAG_PNP, "xxxKeyEvent: FG thread doen't want legacy kbd. bail out");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // GENERIC_INPUT
|
|
|
|
/*
|
|
* If the ALT key is down and the CTRL key
|
|
* isn't, this is a WM_SYS* message.
|
|
*/
|
|
if (TestAsyncKeyStateDown(VK_MENU) && !TestAsyncKeyStateDown(VK_CONTROL) && Vk != VK_JUNJA) {
|
|
// VK_JUNJA is ALT+'+'. Since all KOR VKs are not converted to IME hotkey IDs and
|
|
// should be passed directly to IME, KOR related VKs are not treated as SYSKEYDOWN.
|
|
message += (WM_SYSKEYDOWN - WM_KEYDOWN);
|
|
usExtraStuff |= 0x2000;
|
|
|
|
/*
|
|
* If this is the ALT-down set this flag, otherwise
|
|
* clear it since we got a key inbetween the ALT-down
|
|
* and ALT-up. (see comment below)
|
|
*/
|
|
if (Vk == VK_MENU) {
|
|
fMakeAltUpASysKey = TRUE;
|
|
/*
|
|
* Unlock SetForegroundWindow (if locked) when the ALT key went down.
|
|
*/
|
|
if (!fBreak) {
|
|
gppiLockSFW = NULL;
|
|
}
|
|
} else {
|
|
fMakeAltUpASysKey = FALSE;
|
|
}
|
|
|
|
} else if (Vk == VK_MENU) {
|
|
if (fBreak) {
|
|
/*
|
|
* End our switch if we are in the middle of one.
|
|
*/
|
|
if (fMakeAltUpASysKey) {
|
|
|
|
/*
|
|
* We don't make the keyup of the ALT key a WM_SYSKEYUP if any
|
|
* other key is typed while the ALT key was down. I don't know
|
|
* why we do this, but it's been here since version 1 and any
|
|
* app that uses SDM relies on it (eg - opus).
|
|
*
|
|
* The Alt bit is not set for the KEYUP message either.
|
|
*/
|
|
message += (WM_SYSKEYDOWN - WM_KEYDOWN);
|
|
}
|
|
|
|
if (gspwndAltTab != NULL) {
|
|
|
|
/*
|
|
* Send the alt up message before we change queues
|
|
*/
|
|
if (gpqForeground != NULL) {
|
|
#ifdef GENERIC_INPUT
|
|
if (!TestRawInputMode(PtiKbdFromQ(gpqForeground), NoLegacyKeyboard)) {
|
|
#endif
|
|
/*
|
|
* Set this flag so that we know we're doing a tab-switch.
|
|
* This makes sure that both cases where the ALT-KEY is released
|
|
* before or after the TAB-KEY is handled. It is checked in
|
|
* xxxDefWindowProc().
|
|
*/
|
|
gpqForeground->QF_flags |= QF_TABSWITCHING;
|
|
|
|
PostInputMessage(gpqForeground, NULL, message, (DWORD)Vk,
|
|
MAKELONG(1, (wScanCode | usExtraStuff)),
|
|
time, ExtraInfo);
|
|
#ifdef GENERIC_INPUT
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Remove the Alt-tab window
|
|
*/
|
|
xxxCancelCoolSwitch();
|
|
|
|
if (gspwndActivate != NULL) {
|
|
/*
|
|
* Make our selected window active and destroy our
|
|
* switch window. If the new window is minmized,
|
|
* restore it. If we are switching in the same
|
|
* queue, we clear out gpqForeground to make
|
|
* xxxSetForegroundWindow2 to change the pwnd
|
|
* and make the switch. This case will happen
|
|
* with WOW and Console apps.
|
|
*/
|
|
if (gpqForeground == GETPTI(gspwndActivate)->pq) {
|
|
gpqForeground = NULL;
|
|
}
|
|
|
|
/*
|
|
* Make the selected window thread the owner of the last input;
|
|
* since the user has selected him, he owns the ALT-TAB.
|
|
*/
|
|
glinp.ptiLastWoken = GETPTI(gspwndActivate);
|
|
|
|
|
|
ThreadLockAlways(gspwndActivate, &tlpwndActivate);
|
|
xxxSetForegroundWindow2(gspwndActivate, NULL,
|
|
SFW_SWITCH | SFW_ACTIVATERESTORE);
|
|
/*
|
|
* Win3.1 calls SetWindowPos() with activate, which z-orders
|
|
* first regardless, then activates. Our code relies on
|
|
* xxxActivateThisWindow() to z-order, and it'll only do
|
|
* it if the window does not have the child bit set (regardless
|
|
* that the window is a child of the desktop).
|
|
*
|
|
* To be compatible, we'll just force z-order here if the
|
|
* window has the child bit set. This z-order is asynchronous,
|
|
* so this'll z-order after the activate event is processed.
|
|
* That'll allow it to come on top because it'll be foreground
|
|
* then. (Grammatik has a top level window with the child
|
|
* bit set that wants to be come the active window).
|
|
*/
|
|
if (TestWF(gspwndActivate, WFCHILD)) {
|
|
xxxSetWindowPos(gspwndActivate, (PWND)HWND_TOP, 0, 0, 0, 0,
|
|
SWP_NOSIZE | SWP_NOMOVE | SWP_ASYNCWINDOWPOS);
|
|
}
|
|
ThreadUnlock(&tlpwndActivate);
|
|
|
|
Unlock(&gspwndActivate);
|
|
}
|
|
return;
|
|
}
|
|
} else {
|
|
/*
|
|
* The ALT key is down, unlock SetForegroundWindow (if locked)
|
|
*/
|
|
gppiLockSFW = NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Handle switching. Eat the Key if we are doing switching.
|
|
*/
|
|
if (!FJOURNALPLAYBACK() && !FJOURNALRECORD() && (!fBreak) &&
|
|
(TestAsyncKeyStateDown(VK_MENU)) &&
|
|
(!TestAsyncKeyStateDown(VK_CONTROL)) && //gpqForeground &&
|
|
(((Vk == VK_TAB) && !(fsReserveKeys & CONSOLE_ALTTAB)) ||
|
|
((Vk == VK_ESCAPE) && !(fsReserveKeys & CONSOLE_ALTESC)))) {
|
|
|
|
xxxNextWindow(gpqForeground ? gpqForeground : gptiRit->pq, Vk);
|
|
|
|
} else if (gpqForeground != NULL) {
|
|
PQMSG pqmsgPrev = gpqForeground->mlInput.pqmsgWriteLast;
|
|
DWORD wParam = (DWORD)Vk;
|
|
LONG lParam;
|
|
|
|
#ifdef GENERIC_INPUT
|
|
if (TestRawInputMode(PtiKbdFromQ(gpqForeground), NoLegacyKeyboard)) {
|
|
if (!TestRawInputMode(PtiKbdFromQ(gpqForeground), AppKeys) ||
|
|
!(Vk >= VK_APPCOMMAND_FIRST && Vk <= VK_APPCOMMAND_LAST)) {
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* We have a packet containing a Unicode character
|
|
* This is injected by Pen via SendInput
|
|
*/
|
|
if ((Vk == VK_PACKET) && (usFlaggedVk & KBDUNICODE)) {
|
|
wParam |= (wScanCode << 16);
|
|
wScanCode = 0;
|
|
}
|
|
lParam = MAKELONG(1, (wScanCode | usExtraStuff));
|
|
|
|
/*
|
|
* WM_*KEYDOWN messages are left unchanged on the queue except the
|
|
* repeat count field (LOWORD(lParam)) is incremented.
|
|
*/
|
|
if (pqmsgPrev != NULL &&
|
|
pqmsgPrev->msg.message == message &&
|
|
(message == WM_KEYDOWN || message == WM_SYSKEYDOWN) &&
|
|
pqmsgPrev->msg.wParam == wParam &&
|
|
HIWORD(pqmsgPrev->msg.lParam) == HIWORD(lParam)) {
|
|
#ifdef GENERIC_INPUT
|
|
/*
|
|
* We shouldn't be here for a generic input keyboard that
|
|
* doesn't want legacy support.
|
|
*/
|
|
UserAssert(!TestRawInputMode(PtiKbdFromQ(gpqForeground), NoLegacyKeyboard));
|
|
#endif
|
|
/*
|
|
* Increment the queued message's repeat count. This could
|
|
* conceivably overflow but Win 3.0 doesn't deal with it
|
|
* and anyone who buffers up 65536 keystrokes is a chimp
|
|
* and deserves to have it wrap anyway.
|
|
*/
|
|
pqmsgPrev->msg.lParam = MAKELONG(LOWORD(pqmsgPrev->msg.lParam) + 1,
|
|
HIWORD(lParam));
|
|
|
|
WakeSomeone(gpqForeground, message, pqmsgPrev);
|
|
|
|
} else {
|
|
/*
|
|
* check if these are speedracer keys - bug 339877
|
|
* for the speedracer keys we want to post an event message and generate the
|
|
* wm_appcommand in xxxprocesseventmessage
|
|
* Since SpeedRacer software looks for the hotkeys we want to let those through
|
|
* It is going in here since we don't want the ability to eat up tons of pool memory
|
|
* so we post the event message here and then post the input message for the wm_keydown
|
|
* below - that way if the key is repeated then there is coalescing done above and no more
|
|
* qevent_appcommands are posted to the input queue.
|
|
*/
|
|
if (VK_APPCOMMAND_FIRST <= Vk && Vk <= VK_APPCOMMAND_LAST) {
|
|
/*
|
|
* Only send wm_appcommands for wm_keydown (& wm_syskeydown) messages -
|
|
* essentially we ignore wm_keyup for those vk's defined for wm_appcommand messages
|
|
*/
|
|
if (!fBreak && gpqForeground) {
|
|
/*
|
|
* post an event message so we can syncronize with normal types of input
|
|
* send through the vk - we will construct the message in xxxProcessEventMessage
|
|
*/
|
|
PostEventMessage(gpqForeground->ptiKeyboard, gpqForeground, QEVENT_APPCOMMAND,
|
|
NULL, 0, (WPARAM)0, Vk);
|
|
}
|
|
#ifdef GENERIC_INPUT
|
|
if (TestRawInputMode(PtiKbdFromQ(gpqForeground), NoLegacyKeyboard)) {
|
|
return;
|
|
}
|
|
#endif
|
|
}
|
|
/*
|
|
* We let the key go through since we want wm_keydowns/ups to get generated for these
|
|
* SpeedRacer keys
|
|
*/
|
|
|
|
if (gpqForeground->QF_flags & QF_MOUSEMOVED) {
|
|
PostMove(gpqForeground);
|
|
}
|
|
|
|
PostInputMessage(gpqForeground, NULL, message, wParam,
|
|
lParam, time, ExtraInfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* GetMouseCoord
|
|
*
|
|
* Calculates the coordinates of the point that will be injected.
|
|
*
|
|
* History:
|
|
* 11-01-96 CLupu Created.
|
|
* 12-18-97 MCostea MOUSE_VIRTUAL_DESKTOP support
|
|
\**************************************************************************/
|
|
VOID GetMouseCoord(
|
|
LONG dx,
|
|
LONG dy,
|
|
DWORD dwFlags,
|
|
LONG time,
|
|
ULONG_PTR ExtraInfo,
|
|
PPOINT ppt)
|
|
{
|
|
if (dwFlags & MOUSE_MOVE_ABSOLUTE) {
|
|
|
|
LONG cxMetric, cyMetric;
|
|
|
|
/*
|
|
* If MOUSE_VIRTUAL_DESKTOP was specified, map to entire virtual screen
|
|
*/
|
|
if (dwFlags & MOUSE_VIRTUAL_DESKTOP) {
|
|
cxMetric = SYSMET(CXVIRTUALSCREEN);
|
|
cyMetric = SYSMET(CYVIRTUALSCREEN);
|
|
} else {
|
|
cxMetric = SYSMET(CXSCREEN);
|
|
cyMetric = SYSMET(CYSCREEN);
|
|
}
|
|
|
|
/*
|
|
* Absolute pointing device used: deltas are actually the current
|
|
* position. Update the global mouse position.
|
|
*
|
|
* Note that the position is always reported in units of
|
|
* (0,0)-(0xFFFF,0xFFFF) which corresponds to
|
|
* (0,0)-(SYSMET(CXSCREEN), SYSMET(CYSCREEN)) in pixels.
|
|
* We must first scale it to fit on the screen using the formula:
|
|
* ptScreen = ptMouse * resPrimaryMonitor / 64K
|
|
*
|
|
* The straightforward algorithm coding of this algorithm is:
|
|
*
|
|
* ppt->x = (dx * SYSMET(CXSCREEN)) / (long)0x0000FFFF;
|
|
* ppt->y = (dy * SYSMET(CYSCREEN)) / (long)0x0000FFFF;
|
|
*
|
|
* On x86, with 14 more bytes we can avoid the division function with
|
|
* the following code.
|
|
*/
|
|
|
|
ppt->x = dx * cxMetric;
|
|
if (ppt->x >= 0) {
|
|
ppt->x = HIWORD(ppt->x);
|
|
} else {
|
|
ppt->x = - (long) HIWORD(-ppt->x);
|
|
}
|
|
|
|
ppt->y = dy * cyMetric;
|
|
if (ppt->y >= 0) {
|
|
ppt->y = HIWORD(ppt->y);
|
|
} else {
|
|
ppt->y = - (long) HIWORD(-ppt->y);
|
|
}
|
|
|
|
/*
|
|
* (0, 0) must map to the leftmost point on the desktop
|
|
*/
|
|
if (dwFlags & MOUSE_VIRTUAL_DESKTOP) {
|
|
ppt->x += SYSMET(XVIRTUALSCREEN);
|
|
ppt->y += SYSMET(YVIRTUALSCREEN);
|
|
}
|
|
|
|
/*
|
|
* Reset the mouse sensitivity remainder.
|
|
*/
|
|
idxRemainder = idyRemainder = 0;
|
|
|
|
/*
|
|
* Save the absolute coordinates in the global array
|
|
* for GetMouseMovePointsEx.
|
|
*/
|
|
SAVEPOINT(dx, dy, 0xFFFF, 0xFFFF, time, ExtraInfo);
|
|
} else {
|
|
/*
|
|
* Is there any mouse acceleration to do?
|
|
*/
|
|
if (gMouseSpeed != 0) {
|
|
#ifdef SUBPIXEL_MOUSE
|
|
DoNewMouseAccel(&dx, &dy);
|
|
#else
|
|
dx = DoMouseAccel(dx);
|
|
dy = DoMouseAccel(dy);
|
|
#endif
|
|
} else if (gMouseSensitivity != MOUSE_SENSITIVITY_DEFAULT) {
|
|
int iNumerator;
|
|
|
|
/*
|
|
* Does the mouse sensitivity need to be adjusted?
|
|
*/
|
|
|
|
if (dx != 0) {
|
|
iNumerator = dx * gMouseSensitivityFactor + idxRemainder;
|
|
dx = iNumerator / 256;
|
|
idxRemainder = iNumerator % 256;
|
|
if ((iNumerator < 0) && (idxRemainder > 0)) {
|
|
dx++;
|
|
idxRemainder -= 256;
|
|
}
|
|
}
|
|
|
|
if (dy != 0) {
|
|
iNumerator = dy * gMouseSensitivityFactor + idyRemainder;
|
|
dy = iNumerator / 256;
|
|
idyRemainder = iNumerator % 256;
|
|
if ((iNumerator < 0) && (idyRemainder > 0)) {
|
|
dy++;
|
|
idyRemainder -= 256;
|
|
}
|
|
}
|
|
}
|
|
|
|
ppt->x += dx;
|
|
ppt->y += dy;
|
|
|
|
/*
|
|
* Save the absolute coordinates in the global array
|
|
* for GetMouseMovePointsEx.
|
|
*/
|
|
SAVEPOINT(ppt->x, ppt->y,
|
|
SYSMET(CXVIRTUALSCREEN) - 1, SYSMET(CYVIRTUALSCREEN) - 1,
|
|
time, ExtraInfo);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxMoveEventAbsolute (RIT)
|
|
*
|
|
* Mouse move events from the mouse driver are processed here. If there is a
|
|
* mouse owner window setup from xxxButtonEvent() the event is automatically
|
|
* sent there, otherwise it's sent to the window the mouse is over.
|
|
*
|
|
* Mouse acceleration happens here as well as cursor clipping (as a result of
|
|
* the ClipCursor() API).
|
|
*
|
|
* History:
|
|
* 10-18-90 DavidPe Created.
|
|
* 11-29-90 DavidPe Added mouse acceleration support.
|
|
* 01-25-91 IanJa xxxWindowHitTest change
|
|
* IanJa non-jerky mouse moves
|
|
\***************************************************************************/
|
|
#ifdef LOCK_MOUSE_CODE
|
|
#pragma alloc_text(MOUSE, xxxMoveEventAbsolute)
|
|
#endif
|
|
|
|
VOID xxxMoveEventAbsolute(
|
|
LONG x,
|
|
LONG y,
|
|
ULONG_PTR dwExtraInfo,
|
|
#ifdef GENERIC_INPUT
|
|
HANDLE hDevice,
|
|
PMOUSE_INPUT_DATA pmei,
|
|
#endif
|
|
DWORD time,
|
|
BOOL bInjected
|
|
)
|
|
{
|
|
LONG ulMoveFlags = MP_NORMAL;
|
|
CheckCritOut();
|
|
|
|
if (IsHooked(gptiRit, WHF_FROM_WH(WH_MOUSE_LL))) {
|
|
MSLLHOOKSTRUCT mslls;
|
|
BOOL bEatEvent = FALSE;
|
|
BOOL bAnsiHook;
|
|
PHOOK pHook;
|
|
|
|
mslls.pt.x = x;
|
|
mslls.pt.y = y;
|
|
mslls.mouseData = 0;
|
|
mslls.flags = bInjected;
|
|
mslls.time = time;
|
|
mslls.dwExtraInfo = dwExtraInfo;
|
|
|
|
/*
|
|
* Call low level mouse hooks to see if they allow this message
|
|
* to pass through USER
|
|
*/
|
|
|
|
EnterCrit();
|
|
|
|
/*
|
|
* Check again to see if we still have the hook installed. Fix for 80477.
|
|
*/
|
|
if ((pHook = PhkFirstValid(gptiRit, WH_MOUSE_LL)) != NULL) {
|
|
PTHREADINFO ptiCurrent;
|
|
|
|
bEatEvent = (xxxCallHook2(pHook, HC_ACTION, WM_MOUSEMOVE, (LPARAM)&mslls, &bAnsiHook) != 0);
|
|
ptiCurrent = PtiCurrent();
|
|
if (ptiCurrent->pcti->fsChangeBits & ptiCurrent->pcti->fsWakeMask & ~QS_SMSREPLY) {
|
|
RIPMSG1(RIP_WARNING, "xxxMoveEventAbsolute: applying changed wake bits (0x%x) during the LL hook callback",
|
|
ptiCurrent->pcti->fsChangeBits & ~QS_SMSREPLY);
|
|
SetWakeBit(ptiCurrent, ptiCurrent->pcti->fsChangeBits & ~QS_SMSREPLY);
|
|
}
|
|
|
|
}
|
|
|
|
LeaveCrit();
|
|
|
|
if (bEatEvent) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
#ifdef GENERIC_INPUT
|
|
if (pmei && gpqForeground && RawInputRequestedForMouse(PtiMouseFromQ(gpqForeground))) {
|
|
EnterCrit();
|
|
|
|
PostRawMouseInput(gpqForeground, time, hDevice, pmei);
|
|
LeaveCrit();
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Blow off the event if WH_JOURNALPLAYBACK is installed. Do not
|
|
* use FJOURNALPLAYBACK() because this routine may be called from
|
|
* multiple desktop threads and the hook check must be done
|
|
* for the rit thread, not the calling thread.
|
|
*/
|
|
if (IsGlobalHooked(gptiRit, WHF_FROM_WH(WH_JOURNALPLAYBACK))) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* For the atomicness of monitor. Let's bail out while the monitor
|
|
* is being updated by the other thread.
|
|
*/
|
|
if (InterlockedCompareExchange(&gdwMonitorBusy, TRUE, FALSE) != FALSE) {
|
|
RIPMSGF0(RIP_VERBOSE, "the monitor info is being updated. We have to bail out here");
|
|
return;
|
|
}
|
|
|
|
gptCursorAsync.x = x;
|
|
gptCursorAsync.y = y;
|
|
|
|
BoundCursor(&gptCursorAsync);
|
|
|
|
/*
|
|
* Move the screen pointer.
|
|
* Pass an event source parameter as the flags so that TS
|
|
* can correctly send a mouse update to the client if the mouse
|
|
* move is originating from a shadow client or if the move is injected.
|
|
*/
|
|
#ifdef GENERIC_INPUT
|
|
if (pmei && (pmei->Flags & MOUSE_TERMSRV_SRC_SHADOW)) {
|
|
ulMoveFlags = MP_TERMSRV_SHADOW;
|
|
}
|
|
else if (bInjected) {
|
|
ulMoveFlags = MP_PROCEDURAL;
|
|
}
|
|
#endif
|
|
GreMovePointer(gpDispInfo->hDev, gptCursorAsync.x, gptCursorAsync.y,
|
|
ulMoveFlags);
|
|
|
|
|
|
/*
|
|
* Save the time stamp in a global so we can use it in PostMove
|
|
*/
|
|
gdwMouseMoveTimeStamp = time;
|
|
|
|
/*
|
|
* Reset the locking, so that the pMonitor update can continue
|
|
*/
|
|
UserAssert(gdwMonitorBusy == TRUE);
|
|
InterlockedExchange(&gdwMonitorBusy, FALSE);
|
|
|
|
/*
|
|
* Set the number of trails to hide to gMouseTrails + 1 to avoid calling
|
|
* GreMovePointer while the mouse is moving, look at HideMouseTrails().
|
|
*/
|
|
if (GETMOUSETRAILS()) {
|
|
InterlockedExchange(&gMouseTrailsToHide, gMouseTrails + 1);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxMoveEvent (RIT)
|
|
*
|
|
* The dwFlags can be
|
|
* 0 relative move
|
|
* MOUSEEVENTF_ABSOLUTE absolute move
|
|
* MOUSEEVENTF_VIRTUALDESK the absolute coordinates will be maped
|
|
* to the entire virtual desktop. This flag makes sense only with MOUSEEVENTF_ABSOLUTE
|
|
*
|
|
\***************************************************************************/
|
|
#ifdef LOCK_MOUSE_CODE
|
|
#pragma alloc_text(MOUSE, xxxMoveEvent)
|
|
#endif
|
|
|
|
VOID xxxMoveEvent(
|
|
LONG dx,
|
|
LONG dy,
|
|
DWORD dwFlags,
|
|
ULONG_PTR dwExtraInfo,
|
|
#ifdef GENERIC_INPUT
|
|
HANDLE hDevice,
|
|
PMOUSE_INPUT_DATA pmei,
|
|
#endif
|
|
DWORD time,
|
|
BOOL bInjected)
|
|
{
|
|
POINT ptLastMove = gptCursorAsync;
|
|
|
|
CheckCritOut();
|
|
|
|
/*
|
|
* Get the actual point that will be injected.
|
|
*/
|
|
GetMouseCoord(dx, dy, ConvertToMouseDriverFlags(dwFlags),
|
|
time, dwExtraInfo, &ptLastMove);
|
|
|
|
/*
|
|
* move the mouse
|
|
*/
|
|
xxxMoveEventAbsolute(
|
|
ptLastMove.x,
|
|
ptLastMove.y,
|
|
dwExtraInfo,
|
|
#ifdef GENERIC_INPUT
|
|
hDevice,
|
|
pmei,
|
|
#endif
|
|
time,
|
|
bInjected);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* UpdateRawKeyState
|
|
*
|
|
* A helper routine for ProcessKeyboardInput.
|
|
* Based on a VK and a make/break flag, this function will update the physical
|
|
* keystate table.
|
|
*
|
|
* History:
|
|
* 10-13-91 IanJa Created.
|
|
\***************************************************************************/
|
|
VOID UpdateRawKeyState(
|
|
BYTE Vk,
|
|
BOOL fBreak)
|
|
{
|
|
CheckCritIn();
|
|
|
|
if (fBreak) {
|
|
ClearRawKeyDown(Vk);
|
|
} else {
|
|
|
|
/*
|
|
* This is a key make. If the key was not already down, update the
|
|
* physical toggle bit.
|
|
*/
|
|
if (!TestRawKeyDown(Vk)) {
|
|
ToggleRawKeyToggle(Vk);
|
|
}
|
|
|
|
/*
|
|
* This is a make, so turn on the physical key down bit.
|
|
*/
|
|
SetRawKeyDown(Vk);
|
|
}
|
|
}
|
|
|
|
|
|
VOID CleanupResources(
|
|
VOID)
|
|
{
|
|
PPCLS ppcls;
|
|
PTHREADINFO pti;
|
|
|
|
UserAssert(!gbCleanedUpResources);
|
|
|
|
gbCleanedUpResources = TRUE;
|
|
|
|
HYDRA_HINT(HH_CLEANUPRESOURCES);
|
|
|
|
/*
|
|
* Prevent power callouts.
|
|
*/
|
|
CleanupPowerRequestList();
|
|
|
|
/*
|
|
* Destroy the system classes also
|
|
*/
|
|
ppcls = &gpclsList;
|
|
while (*ppcls != NULL) {
|
|
DestroyClass(ppcls);
|
|
}
|
|
|
|
/*
|
|
* Unlock the cursor from all the CSRSS's threads.
|
|
* We do this here because RIT might not be the only
|
|
* CSRSS process running at this time and we want
|
|
* to prevent the change of thread ownership
|
|
* after RIT is gone.
|
|
*/
|
|
pti = PpiCurrent()->ptiList;
|
|
|
|
while (pti != NULL) {
|
|
|
|
if (pti->pq != NULL) {
|
|
LockQCursor(pti->pq, NULL);
|
|
}
|
|
pti = pti->ptiSibling;
|
|
}
|
|
|
|
UnloadCursorsAndIcons();
|
|
|
|
/*
|
|
* Cleanup the GDI globals in USERK
|
|
*/
|
|
CleanupGDI();
|
|
}
|
|
|
|
#if 0 // Temporariry
|
|
|
|
typedef struct _EX_RUNDOWN_WAIT_BLOCK {
|
|
ULONG Count;
|
|
KEVENT WakeEvent;
|
|
} EX_RUNDOWN_WAIT_BLOCK, *PEX_RUNDOWN_WAIT_BLOCK;
|
|
|
|
|
|
//NTKERNELAPI
|
|
VOID
|
|
FASTCALL
|
|
__ExWaitForRundownProtectionRelease (
|
|
IN PEX_RUNDOWN_REF RunRef
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Wait till all outstanding rundown protection calls have exited
|
|
|
|
Arguments:
|
|
|
|
RunRef - Pointer to a rundown structure
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
EX_RUNDOWN_WAIT_BLOCK WaitBlock;
|
|
PKEVENT Event;
|
|
ULONG_PTR Value, NewValue;
|
|
ULONG WaitCount;
|
|
#if 1
|
|
LARGE_INTEGER liTimeout;
|
|
NTSTATUS Status;
|
|
ULONG counter;
|
|
#endif
|
|
|
|
PAGED_CODE ();
|
|
|
|
//
|
|
// Fast path. this should be the normal case. If Value is zero then there are no current accessors and we have
|
|
// marked the rundown structure as rundown. If the value is EX_RUNDOWN_ACTIVE then the structure has already
|
|
// been rundown and ExRundownCompleted. This second case allows for callers that might initiate rundown
|
|
// multiple times (like handle table rundown) to have subsequent rundowns become noops.
|
|
//
|
|
|
|
Value = (ULONG_PTR) InterlockedCompareExchangePointer (&RunRef->Ptr,
|
|
(PVOID) EX_RUNDOWN_ACTIVE,
|
|
(PVOID) 0);
|
|
if (Value == 0 || Value == EX_RUNDOWN_ACTIVE) {
|
|
#if 1
|
|
RIPMSG1(RIP_WARNING, "__ExWaitForRundownProtectionRelease: rundown finished in session %d", gSessionId);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Slow path
|
|
//
|
|
Event = NULL;
|
|
#if 1
|
|
counter = 0;
|
|
#endif
|
|
do {
|
|
|
|
//
|
|
// Extract total number of waiters. Its biased by 2 so we can hanve the rundown active bit.
|
|
//
|
|
WaitCount = (ULONG) (Value >> EX_RUNDOWN_COUNT_SHIFT);
|
|
|
|
//
|
|
// If there are some accessors present then initialize and event (once only).
|
|
//
|
|
if (WaitCount > 0 && Event == NULL) {
|
|
Event = &WaitBlock.WakeEvent;
|
|
KeInitializeEvent (Event, SynchronizationEvent, FALSE);
|
|
}
|
|
//
|
|
// Store the wait count in the wait block. Waiting threads will start to decrement this as they exit
|
|
// if our exchange succeeds. Its possible for accessors to come and go between our initial fetch and
|
|
// the interlocked swap. This doesn't matter so long as there is the same number of outstanding accessors
|
|
// to wait for.
|
|
//
|
|
WaitBlock.Count = WaitCount;
|
|
|
|
NewValue = ((ULONG_PTR) &WaitBlock) | EX_RUNDOWN_ACTIVE;
|
|
|
|
NewValue = (ULONG_PTR) InterlockedCompareExchangePointer (&RunRef->Ptr,
|
|
(PVOID) NewValue,
|
|
(PVOID) Value);
|
|
if (NewValue == Value) {
|
|
if (WaitCount > 0) {
|
|
#if 1
|
|
/*
|
|
* NT Base calls take time values in 100 nanosecond units.
|
|
* Make it relative (negative)...
|
|
* Timeout in 20 minutes.
|
|
*/
|
|
liTimeout.QuadPart = Int32x32To64(-10000, 300000 * 4);
|
|
Status = KeWaitForSingleObject (Event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
&liTimeout);
|
|
|
|
if (Status == STATUS_TIMEOUT) {
|
|
FRE_RIPMSG1(RIP_ERROR, "__ExWaitForRundownProtectionRelease: Rundown wait time out in session %d", gSessionId);
|
|
}
|
|
#endif
|
|
|
|
ASSERT (WaitBlock.Count == 0);
|
|
|
|
}
|
|
return;
|
|
}
|
|
Value = NewValue;
|
|
|
|
ASSERT ((Value&EX_RUNDOWN_ACTIVE) == 0);
|
|
|
|
#if 1
|
|
#define THRESHOLD (50000)
|
|
if (++counter > THRESHOLD) {
|
|
FRE_RIPMSG2(RIP_ERROR, "__ExWaitForRundownProtectionRelease: Rundown wait loop over %d in session %d", THRESHOLD, gSessionId);
|
|
counter = 0;
|
|
}
|
|
#endif
|
|
} while (TRUE);
|
|
}
|
|
#endif
|
|
|
|
VOID WaitForWinstaRundown(
|
|
PKEVENT pRundownEvent)
|
|
{
|
|
if (pRundownEvent) {
|
|
KeSetEvent(pRundownEvent, EVENT_INCREMENT, FALSE);
|
|
}
|
|
|
|
/*
|
|
* Wait for any WindowStation objects to get freed.
|
|
*/
|
|
|
|
#if 0
|
|
/*
|
|
* HACK ALERT!
|
|
* Tentatively, we call our own copy of WaitForRundown
|
|
* to let it timeout in the target session.
|
|
*/
|
|
__ExWaitForRundownProtectionRelease(&gWinstaRunRef);
|
|
#endif
|
|
|
|
ExWaitForRundownProtectionRelease(&gWinstaRunRef);
|
|
ExRundownCompleted (&gWinstaRunRef);
|
|
}
|
|
|
|
VOID SetWaitForWinstaRundown(
|
|
VOID)
|
|
{
|
|
OBJECT_ATTRIBUTES obja;
|
|
NTSTATUS Status;
|
|
HANDLE hProcess = NULL;
|
|
HANDLE hThreadWinstaRundown = NULL;
|
|
PKEVENT pRundownEvent = NULL;
|
|
|
|
pRundownEvent = CreateKernelEvent(SynchronizationEvent, FALSE);
|
|
|
|
InitializeObjectAttributes(&obja,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL);
|
|
|
|
UserAssert(gpepCSRSS != NULL);
|
|
|
|
Status = ObOpenObjectByPointer(
|
|
gpepCSRSS,
|
|
0,
|
|
NULL,
|
|
PROCESS_CREATE_THREAD,
|
|
NULL,
|
|
KernelMode,
|
|
&hProcess);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto ExitClean;
|
|
}
|
|
|
|
UserAssert(hProcess != NULL);
|
|
|
|
|
|
Status = PsCreateSystemThread(
|
|
&hThreadWinstaRundown,
|
|
THREAD_ALL_ACCESS,
|
|
&obja,
|
|
hProcess,
|
|
NULL,
|
|
(PKSTART_ROUTINE)WaitForWinstaRundown,
|
|
pRundownEvent);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto ExitClean;
|
|
}
|
|
|
|
if (pRundownEvent) {
|
|
KeWaitForSingleObject(pRundownEvent, WrUserRequest,
|
|
KernelMode, FALSE, NULL);
|
|
} else {
|
|
UserSleep(100);
|
|
}
|
|
|
|
ExitClean:
|
|
if (pRundownEvent) {
|
|
FreeKernelEvent(&pRundownEvent);
|
|
}
|
|
|
|
if (hProcess) {
|
|
ZwClose(hProcess);
|
|
}
|
|
|
|
if (hThreadWinstaRundown) {
|
|
ZwClose(hThreadWinstaRundown);
|
|
}
|
|
}
|
|
|
|
/***************************************************************
|
|
* NumHandles
|
|
*
|
|
* This function returns the number of handles of an Ob Object.
|
|
*
|
|
* History:
|
|
* 03/29/2001 MohamB Created.
|
|
****************************************************************/
|
|
ULONG NumHandles(
|
|
HANDLE hObjectHandle)
|
|
{
|
|
NTSTATUS Status;
|
|
OBJECT_BASIC_INFORMATION Obi;
|
|
|
|
if (hObjectHandle != NULL) {
|
|
Status = ZwQueryObject(hObjectHandle,
|
|
ObjectBasicInformation,
|
|
&Obi,
|
|
sizeof (OBJECT_BASIC_INFORMATION),
|
|
NULL);
|
|
if (Status == STATUS_SUCCESS) {
|
|
if (Obi.HandleCount > 1) {
|
|
HYDRA_HINT(HH_DTWAITONHANDLES);
|
|
}
|
|
return Obi.HandleCount;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* InitiateWin32kCleanup (RIT)
|
|
*
|
|
* This function starts the cleanup of a win32k
|
|
*
|
|
* History:
|
|
* 04-Dec-97 clupu Created.
|
|
\***************************************************************************/
|
|
BOOL InitiateWin32kCleanup(
|
|
VOID)
|
|
{
|
|
PTHREADINFO ptiCurrent;
|
|
PWINDOWSTATION pwinsta;
|
|
BOOLEAN fWait = TRUE;
|
|
PDESKTOP pdesk;
|
|
UNICODE_STRING ustrName;
|
|
WCHAR szName[MAX_SESSION_PATH];
|
|
HANDLE hevtRitExited;
|
|
OBJECT_ATTRIBUTES obja;
|
|
NTSTATUS Status;
|
|
LARGE_INTEGER timeout;
|
|
NTSTATUS Reason;
|
|
BOOL fFirstTimeout = TRUE;
|
|
|
|
TRACE_HYDAPI(("InitiateWin32kCleanup\n"));
|
|
|
|
TAGMSG0(DBGTAG_RIT, "Exiting Win32k ...");
|
|
|
|
SetWaitForWinstaRundown();
|
|
|
|
/*
|
|
* Prevent power callouts.
|
|
*/
|
|
CleanupPowerRequestList();
|
|
|
|
/*
|
|
* Unregister Device notifications for sessions attached to Physical Console
|
|
* We already do this during Session disconnection -- but if disconnect fails, we leak notifications which is not good
|
|
*/
|
|
if (!IsRemoteConnection()) {
|
|
/*
|
|
* Cleanup device class notifications
|
|
*/
|
|
xxxUnregisterDeviceClassNotifications();
|
|
}
|
|
|
|
EnterCrit();
|
|
|
|
gbCleanupInitiated = TRUE;
|
|
|
|
HYDRA_HINT(HH_INITIATEWIN32KCLEANUP);
|
|
|
|
ptiCurrent = PtiCurrent();
|
|
|
|
UserAssert(ptiCurrent != NULL);
|
|
|
|
pwinsta = ptiCurrent->pwinsta;
|
|
|
|
/*
|
|
* Give DTs 5 minutes to go away
|
|
*/
|
|
timeout.QuadPart = Int32x32To64(-10000, 600000);
|
|
|
|
/*
|
|
* Wait for all desktops to exit other than the disconnected desktop.
|
|
*/
|
|
while (fWait) {
|
|
|
|
/*
|
|
* If things are left on the destroy list or the disconnected desktop is
|
|
* not the current desktop (at the end we should always switch to the
|
|
* disconnected desktop), then wait.
|
|
*/
|
|
if (pwinsta == NULL) {
|
|
break;
|
|
}
|
|
|
|
pdesk = pwinsta->rpdeskList;
|
|
|
|
if (pdesk == NULL) {
|
|
break;
|
|
}
|
|
|
|
fWait = pdesk != gspdeskDisconnect
|
|
|| pdesk->rpdeskNext != NULL
|
|
|| pwinsta->pTerm->rpdeskDestroy != NULL
|
|
|| NumHandles(ghDisconnectDesk) > 1;
|
|
|
|
if (fWait) {
|
|
|
|
LeaveCrit();
|
|
|
|
Reason = KeWaitForSingleObject(gpevtDesktopDestroyed, WrUserRequest,
|
|
KernelMode, FALSE, &timeout);
|
|
|
|
if (Reason == STATUS_TIMEOUT) {
|
|
#if 0
|
|
|
|
/*
|
|
* The first time we timeout might be because winlogon died
|
|
* before calling ExitWindowsEx. In that case there may be processes
|
|
* w/ GUI threads running and those threads will have an hdesk
|
|
* in the THREADINFO structure. Thus the desktop threads will not exit.
|
|
* In this situation we signal the event 'EventRitStuck' so that
|
|
* csrss can tell termsrv to start killing the remaining processes
|
|
* calling NtTerminateProcess on them. csrss signals that to termsrv
|
|
* by closing the LPC port in ntuser\server\api.c (W32WinStationTerminate)
|
|
*/
|
|
|
|
if (fFirstTimeout) {
|
|
|
|
HANDLE hevtRitStuck;
|
|
|
|
FRE_RIPMSG0(RIP_ERROR,
|
|
"Timeout in RIT waiting for gpevtDesktopDestroyed. Signal EventRitStuck...");
|
|
|
|
swprintf(szName, L"\\Sessions\\%ld\\BaseNamedObjects\\EventRitStuck",
|
|
gSessionId);
|
|
|
|
RtlInitUnicodeString(&ustrName, szName);
|
|
|
|
InitializeObjectAttributes(&obja,
|
|
&ustrName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = ZwCreateEvent(&hevtRitStuck,
|
|
EVENT_ALL_ACCESS,
|
|
&obja,
|
|
SynchronizationEvent,
|
|
FALSE);
|
|
|
|
UserAssert((! gbRemoteSession) || NT_SUCCESS(Status));
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
ZwSetEvent(hevtRitStuck, NULL);
|
|
ZwClose(hevtRitStuck);
|
|
|
|
fFirstTimeout = FALSE;
|
|
}
|
|
|
|
} else {
|
|
FRE_RIPMSG0(RIP_WARNING,
|
|
"Timeout in RIT waiting for gpevtDesktopDestroyed.\n"
|
|
"There are still GUI threads (assigned to a desktop) running !");
|
|
}
|
|
|
|
RIPMSG0(RIP_WARNING,
|
|
"Timeout in RIT waiting for gpevtDesktopDestroyed. Signal EventRitStuck...");
|
|
{
|
|
SYSTEM_KERNEL_DEBUGGER_INFORMATION KernelDebuggerInfo;
|
|
NTSTATUS Status;
|
|
|
|
Status = ZwQuerySystemInformation(SystemKernelDebuggerInformation,
|
|
&KernelDebuggerInfo, sizeof(KernelDebuggerInfo), NULL);
|
|
if (NT_SUCCESS(Status) && KernelDebuggerInfo.KernelDebuggerEnabled)
|
|
DbgBreakPoint();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
EnterCrit();
|
|
}
|
|
}
|
|
TAGMSG0(DBGTAG_RIT, "All other desktops exited...");
|
|
|
|
Unlock(&gspwndLogonNotify);
|
|
|
|
/*
|
|
* Set ExitInProgress -- this will prevent us from posting any
|
|
* device reads in the future.
|
|
*/
|
|
gbExitInProgress = TRUE;
|
|
|
|
TAGMSG2(DBGTAG_RIT, "Shutting down ptiCurrent %lx cWindows %d",
|
|
ptiCurrent, ptiCurrent->cWindows);
|
|
|
|
/*
|
|
* Clear out some values so some operations won't be possible.
|
|
*/
|
|
gpqCursor = NULL;
|
|
UserAssert(gspwndScreenCapture == NULL);
|
|
Unlock(&gspwndMouseOwner);
|
|
UserAssert(gspwndMouseOwner == NULL);
|
|
UserAssert(gspwndInternalCapture == NULL);
|
|
|
|
/*
|
|
* Free any SPBs.
|
|
*/
|
|
if (gpDispInfo) {
|
|
FreeAllSpbs();
|
|
}
|
|
|
|
/*
|
|
* Close the disconnected desktop.
|
|
*/
|
|
if (ghDisconnectWinSta) {
|
|
UserVerify(NT_SUCCESS(ZwClose(ghDisconnectWinSta)));
|
|
ghDisconnectWinSta = NULL;
|
|
}
|
|
|
|
if (ghDisconnectDesk) {
|
|
CloseProtectedHandle(ghDisconnectDesk);
|
|
ghDisconnectDesk = NULL;
|
|
}
|
|
|
|
if (pwinsta) {
|
|
UserAssert(pwinsta->rpdeskList == NULL);
|
|
}
|
|
|
|
/*
|
|
* Unlock the logon desktop from the global variable
|
|
*/
|
|
UnlockDesktop(&grpdeskLogon, LDU_DESKLOGON, 0);
|
|
|
|
/*
|
|
* Unlock the disconnect logon
|
|
*
|
|
* This was referenced when we created it, so free it now.
|
|
* This is also a flag since the disconnect code checks to see if
|
|
* the disconnected desktop is still around.
|
|
*/
|
|
UnlockDesktop(&gspdeskDisconnect, LDU_DESKDISCONNECT, 0);
|
|
|
|
/*
|
|
* Unlock any windows still locked in the SMS list. We need to do
|
|
* this here because if we don't, we end up with zombie windows in the
|
|
* desktop thread that we'll try to assign to RIT but RIT will be gone.
|
|
*/
|
|
{
|
|
PSMS psms = gpsmsList;
|
|
|
|
while (psms != NULL) {
|
|
if (psms->spwnd != NULL) {
|
|
UserAssert(psms->message == WM_CLIENTSHUTDOWN);
|
|
|
|
RIPMSG1(RIP_WARNING, "Window %#p locked in the SMS list",
|
|
psms->spwnd);
|
|
|
|
Unlock(&psms->spwnd);
|
|
}
|
|
psms = psms->psmsNext;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Free outstanding timers.
|
|
*/
|
|
while (gptmrFirst != NULL) {
|
|
FreeTimer(gptmrFirst);
|
|
}
|
|
|
|
/*
|
|
* Free the task switch window if there.
|
|
*/
|
|
if (gspwndAltTab != NULL) {
|
|
Unlock(&gspwndAltTab);
|
|
}
|
|
|
|
TAGMSG0(DBGTAG_RIT, "posting WM_QUIT to the IO DT");
|
|
|
|
if (pwinsta) {
|
|
UserAssert(pwinsta->pTerm->ptiDesktop != NULL);
|
|
UserAssert(pwinsta->pTerm == &gTermIO);
|
|
}
|
|
|
|
{
|
|
/*
|
|
* Wait for desktop thread(s) to exit.
|
|
* This thread (RIT) is used to assign
|
|
* objects if the orginal thread leaves. So it should be
|
|
* the last one to go. Hopefully, if the desktop thread
|
|
* exits, there shouldn't be any objects in use.
|
|
*/
|
|
PVOID aDT[2];
|
|
ULONG cObjects = 0;
|
|
|
|
if (gTermIO.ptiDesktop != NULL) {
|
|
aDT[0] = gTermIO.ptiDesktop->pEThread;
|
|
ObReferenceObject(aDT[0]);
|
|
cObjects++;
|
|
|
|
if (!_PostThreadMessage(gTermIO.ptiDesktop, WM_QUIT, 0, 0)) {
|
|
FRE_RIPMSG1(RIP_ERROR, "InitiateWin32kCleanup: failed to post WM_QUIT message to IO desktop thread %p",
|
|
gTermIO.ptiDesktop);
|
|
}
|
|
|
|
HYDRA_HINT(HH_DTQUITPOSTED);
|
|
}
|
|
|
|
if (gTermNOIO.ptiDesktop != NULL) {
|
|
aDT[1] = gTermNOIO.ptiDesktop->pEThread;
|
|
ObReferenceObject(aDT[1]);
|
|
cObjects++;
|
|
|
|
if (!_PostThreadMessage(gTermNOIO.ptiDesktop, WM_QUIT, 0, 0)) {
|
|
FRE_RIPMSG1(RIP_ERROR, "InitiateWin32kCleanup: failed to post WM_QUIT message to NOIO desktop thread %p",
|
|
gTermNOIO.ptiDesktop);
|
|
}
|
|
}
|
|
|
|
if (cObjects > 0) {
|
|
LeaveCrit();
|
|
|
|
TAGMSG0(DBGTAG_RIT, "waiting on desktop thread(s) destruction ...");
|
|
|
|
/*
|
|
* Give DTs 5 minutes to go away
|
|
*/
|
|
timeout.QuadPart = Int32x32To64(-10000, 300000);
|
|
WaitAgain:
|
|
|
|
Reason =
|
|
|
|
KeWaitForMultipleObjects(cObjects,
|
|
aDT,
|
|
WaitAll,
|
|
WrUserRequest,
|
|
KernelMode,
|
|
TRUE,
|
|
&timeout,
|
|
NULL);
|
|
|
|
if (Reason == STATUS_TIMEOUT) {
|
|
FRE_RIPMSG0(RIP_ERROR,
|
|
"InitiateWin32kCleanup: Timeout in RIT waiting for desktop threads to go away.");
|
|
goto WaitAgain;
|
|
}
|
|
|
|
TAGMSG0(DBGTAG_RIT, "Desktop thread(s) destroyed");
|
|
|
|
ObDereferenceObject(aDT[0]);
|
|
|
|
if (cObjects > 1) {
|
|
ObDereferenceObject(aDT[1]);
|
|
}
|
|
|
|
EnterCrit();
|
|
}
|
|
}
|
|
|
|
HYDRA_HINT(HH_ALLDTGONE);
|
|
|
|
/*
|
|
* If still connected, tell the miniport driver to disconnect
|
|
*/
|
|
if (gbConnected) {
|
|
if (!gfRemotingConsole) {
|
|
|
|
bDrvDisconnect(gpDispInfo->hDev, ghRemoteThinwireChannel,
|
|
gThinwireFileObject);
|
|
} else{
|
|
|
|
ASSERT(!IsRemoteConnection());
|
|
ASSERT(gConsoleShadowhDev != NULL);
|
|
bDrvDisconnect(gConsoleShadowhDev, ghConsoleShadowThinwireChannel,
|
|
gConsoleShadowThinwireFileObject);
|
|
}
|
|
}
|
|
|
|
UnlockDesktop(&grpdeskRitInput, LDU_DESKRITINPUT, 0);
|
|
UnlockDesktop(&gspdeskShouldBeForeground, LDU_DESKSHOULDBEFOREGROUND, 0);
|
|
|
|
/*
|
|
* Kill the csr port so no hard errors are services after this point
|
|
*/
|
|
if (CsrApiPort != NULL) {
|
|
ObDereferenceObject(CsrApiPort);
|
|
CsrApiPort = NULL;
|
|
}
|
|
|
|
Unlock(&gspwndCursor);
|
|
|
|
/*
|
|
* set this to NULL
|
|
*/
|
|
gptiRit = NULL;
|
|
|
|
TAGMSG0(DBGTAG_RIT, "TERMINATING !!!");
|
|
|
|
#if DBG
|
|
{
|
|
PPROCESSINFO ppi = gppiList;
|
|
|
|
KdPrint(("Processes still running:\n"));
|
|
KdPrint(("-------------------------\n"));
|
|
|
|
while (ppi) {
|
|
|
|
PTHREADINFO pti;
|
|
|
|
KdPrint(("ppi '%s' %#p threads: %d\n",
|
|
PsGetProcessImageFileName(ppi->Process),
|
|
ppi,
|
|
ppi->cThreads));
|
|
|
|
KdPrint(("\tGUI threads\n"));
|
|
|
|
pti = ppi->ptiList;
|
|
|
|
while (pti) {
|
|
KdPrint(("\t%#p\n", pti));
|
|
pti = pti->ptiSibling;
|
|
}
|
|
|
|
ppi = ppi->ppiNextRunning;
|
|
}
|
|
KdPrint(("-------------------------\n"));
|
|
}
|
|
#endif // DBG
|
|
|
|
LeaveCrit();
|
|
|
|
if (gbRemoteSession) {
|
|
swprintf(szName, L"\\Sessions\\%ld\\BaseNamedObjects\\EventRitExited",
|
|
gSessionId);
|
|
|
|
RtlInitUnicodeString(&ustrName, szName);
|
|
|
|
InitializeObjectAttributes(&obja,
|
|
&ustrName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = ZwCreateEvent(&hevtRitExited,
|
|
EVENT_ALL_ACCESS,
|
|
&obja,
|
|
SynchronizationEvent,
|
|
FALSE);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
ZwSetEvent(hevtRitExited, NULL);
|
|
ZwClose(hevtRitExited);
|
|
} else {
|
|
RIPMSG1(RIP_ERROR, "RIT unable to create EventRitExited: 0x%x\n", Status);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Clear TIF_PALETTEAWARE or else we will AV in xxxDestroyThreadInfo
|
|
* MCostea #412136
|
|
*/
|
|
ptiCurrent->TIF_flags &= ~TIF_PALETTEAWARE;
|
|
|
|
HYDRA_HINT(HH_RITGONE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* RemoteSyncToggleKeys (RIT)
|
|
*
|
|
* This function is called whenever a remote client needs to synchronize the
|
|
* current toggle key state of the server. If the keys are out of sync, it
|
|
* injects the correct toggle key sequences.
|
|
*
|
|
* History:
|
|
* 11-12-98 JParsons Created.
|
|
\***************************************************************************/
|
|
VOID RemoteSyncToggleKeys(
|
|
ULONG toggleKeys)
|
|
{
|
|
KE ke;
|
|
BOOL bInjected;
|
|
|
|
CheckCritIn();
|
|
gSetLedReceived = toggleKeys | KEYBOARD_LED_INJECTED;
|
|
|
|
#ifdef GENERIC_INPUT
|
|
ke.hDevice = NULL;
|
|
#endif
|
|
|
|
// Key injection only works if there is a ready application queue.
|
|
if (gpqForeground != NULL) {
|
|
|
|
bInjected = gSetLedReceived & KEYBOARD_SHADOW ? TRUE : FALSE;
|
|
|
|
if (!(gSetLedReceived & KEYBOARD_CAPS_LOCK_ON) != !TestRawKeyToggle(VK_CAPITAL)) {
|
|
ke.bScanCode = (BYTE)(0x3a);
|
|
ke.usFlaggedVk = VK_CAPITAL;
|
|
xxxProcessKeyEvent(&ke, 0, bInjected);
|
|
|
|
ke.bScanCode = (BYTE)(0xba & 0x7f);
|
|
ke.usFlaggedVk = VK_CAPITAL | KBDBREAK;
|
|
xxxProcessKeyEvent(&ke, 0, bInjected);
|
|
}
|
|
|
|
if (!(gSetLedReceived & KEYBOARD_NUM_LOCK_ON) != !TestRawKeyToggle(VK_NUMLOCK)) {
|
|
ke.bScanCode = (BYTE)(0x45);
|
|
ke.usFlaggedVk = VK_NUMLOCK;
|
|
xxxProcessKeyEvent(&ke, 0, bInjected);
|
|
|
|
ke.bScanCode = (BYTE)(0xc5 & 0x7f);
|
|
ke.usFlaggedVk = VK_NUMLOCK | KBDBREAK;
|
|
xxxProcessKeyEvent(&ke, 0, bInjected);
|
|
}
|
|
|
|
if (!(gSetLedReceived & KEYBOARD_SCROLL_LOCK_ON) != !TestRawKeyToggle(VK_SCROLL)) {
|
|
ke.bScanCode = (BYTE)(0x46);
|
|
ke.usFlaggedVk = VK_SCROLL;
|
|
xxxProcessKeyEvent(&ke, 0, bInjected);
|
|
|
|
ke.bScanCode = (BYTE)(0xc6 & 0x7f);
|
|
ke.usFlaggedVk = VK_SCROLL | KBDBREAK;
|
|
xxxProcessKeyEvent(&ke, 0, bInjected);
|
|
}
|
|
|
|
if (JAPANESE_KBD_LAYOUT(GetActiveHKL())) {
|
|
if (!(gSetLedReceived & KEYBOARD_KANA_LOCK_ON) != !TestRawKeyToggle(VK_KANA)) {
|
|
ke.bScanCode = (BYTE)(0x70);
|
|
ke.usFlaggedVk = VK_KANA;
|
|
xxxProcessKeyEvent(&ke, 0, bInjected);
|
|
|
|
ke.bScanCode = (BYTE)(0xf0 & 0x7f);
|
|
ke.usFlaggedVk = VK_KANA | KBDBREAK;
|
|
xxxProcessKeyEvent(&ke, 0, bInjected);
|
|
}
|
|
}
|
|
|
|
gSetLedReceived = 0;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* ProcessKeyboardInput (RIT)
|
|
*
|
|
* This function is called whenever a keyboard input is ready to be consumed.
|
|
* It calls xxxProcessKeyEvent() for every input event, and once all the events
|
|
* have been consumed, calls StartDeviceRead() to request more keyboard events.
|
|
*
|
|
* Return value: "OK to continue walking gpDeviceInfoList"
|
|
* TRUE - processed input without leaving gpresDeviceInfoList critical section
|
|
* FALSE - had to leave the gpresDeviceInfoList critical section
|
|
*
|
|
* History:
|
|
* 11-26-90 DavidPe Created.
|
|
\***************************************************************************/
|
|
VOID ProcessKeyboardInputWorker(
|
|
PKEYBOARD_INPUT_DATA pkei,
|
|
#ifdef GENERIC_INPUT
|
|
PDEVICEINFO pDeviceInfo,
|
|
#endif
|
|
BOOL fProcessRemap)
|
|
{
|
|
BYTE Vk;
|
|
BYTE bPrefix;
|
|
KE ke;
|
|
|
|
#ifdef GENERIC_INPUT
|
|
/*
|
|
* Set the device handle and raw data
|
|
*/
|
|
ke.hDevice = PtoH(pDeviceInfo);
|
|
UserAssert(pkei);
|
|
ke.data = *pkei;
|
|
#endif
|
|
|
|
/*
|
|
* Remote terminal server clients occationally need to be able to set
|
|
* the server's toggle key state to match the client. All other
|
|
* standard keyboard inputs are processed below since this is the most
|
|
* frequent code path.
|
|
*/
|
|
if ((pkei->Flags & (KEY_TERMSRV_SET_LED | KEY_TERMSRV_VKPACKET)) == 0) {
|
|
|
|
// Process any deferred remote key sync requests
|
|
if (!(gSetLedReceived & KEYBOARD_LED_INJECTED)) {
|
|
goto ProcessKeys;
|
|
} else {
|
|
RemoteSyncToggleKeys(gSetLedReceived);
|
|
}
|
|
|
|
ProcessKeys:
|
|
if (pkei->Flags & KEY_E0) {
|
|
bPrefix = 0xE0;
|
|
} else if (pkei->Flags & KEY_E1) {
|
|
bPrefix = 0xE1;
|
|
} else {
|
|
bPrefix = 0;
|
|
}
|
|
|
|
if (pkei->MakeCode == 0xFF) {
|
|
/*
|
|
* Kbd overrun (kbd hardware and/or keyboard driver) : Beep!
|
|
* (some DELL keyboards send 0xFF if keys are hit hard enough,
|
|
* presumably due to keybounce)
|
|
*/
|
|
LeaveCrit();
|
|
UserBeep(440, 125);
|
|
EnterCrit();
|
|
return;
|
|
}
|
|
|
|
ke.bScanCode = (BYTE)(pkei->MakeCode & 0x7F);
|
|
if (fProcessRemap && (gpScancodeMap || gpFlexMap)) {
|
|
ke.usFlaggedVk = 0;
|
|
if (pkei->Flags & KEY_BREAK) {
|
|
ke.usFlaggedVk |= KBDBREAK;
|
|
}
|
|
if (!MapScancode(&ke, &bPrefix
|
|
#ifdef GENERIC_INPUT
|
|
, pDeviceInfo
|
|
#endif
|
|
)) {
|
|
/*
|
|
* If the input is all processed within MapScancode, go to the
|
|
* next one.
|
|
*/
|
|
return;
|
|
}
|
|
}
|
|
|
|
gbVKLastDown = Vk = VKFromVSC(&ke, bPrefix, gafRawKeyState);
|
|
|
|
|
|
|
|
if (Vk == 0
|
|
#ifdef GENERIC_INPUT
|
|
&& gpqForeground && !RawInputRequestedForKeyboard(PtiKbdFromQ(gpqForeground))
|
|
#endif
|
|
) {
|
|
return;
|
|
}
|
|
|
|
if (pkei->Flags & KEY_BREAK) {
|
|
ke.usFlaggedVk |= KBDBREAK;
|
|
}
|
|
|
|
|
|
/*
|
|
* We don't know if the client system or the host should get the
|
|
* windows key, so the choice is to not support it on the host.
|
|
* (The windows key is a local key.)
|
|
*
|
|
* The other practical problem is that the local shell intercepts
|
|
* the "break" of the windows key and switches to the start menu.
|
|
* The client never sees the "break" so the host thinks the
|
|
* windows key is always depressed.
|
|
*
|
|
* Newer clients may indicate they support the windows key.
|
|
* If the client has indicated this through the gfEnableWindowsKey,
|
|
* then we allow it to be processed here on the host.
|
|
*/
|
|
if (IsRemoteConnection()) {
|
|
BYTE CheckVk = (BYTE)ke.usFlaggedVk;
|
|
|
|
if (CheckVk == VK_LWIN || CheckVk == VK_RWIN) {
|
|
if (!gfEnableWindowsKey) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Keep track of real modifier key state. Conveniently, the values for
|
|
// VK_LSHIFT, VK_RSHIFT, VK_LCONTROL, VK_RCONTROL, VK_LMENU and
|
|
// VK_RMENU are contiguous. We'll construct a bit field to keep track
|
|
// of the current modifier key state. If a bit is set, the corresponding
|
|
// modifier key is down. The bit field has the following format:
|
|
//
|
|
// +---------------------------------------------------+
|
|
// | Right | Left | Right | Left | Right | Left |
|
|
// | Alt | Alt | Control | Control | Shift | Shift |
|
|
// +---------------------------------------------------+
|
|
// 5 4 3 2 1 0 Bit
|
|
//
|
|
// Add bit 7 -- VK_RWIN
|
|
// bit 6 -- VK_LWIN
|
|
|
|
switch (Vk) {
|
|
case VK_LSHIFT:
|
|
case VK_RSHIFT:
|
|
case VK_LCONTROL:
|
|
case VK_RCONTROL:
|
|
case VK_LMENU:
|
|
case VK_RMENU:
|
|
gCurrentModifierBit = 1 << (Vk & 0xf);
|
|
break;
|
|
case VK_LWIN:
|
|
gCurrentModifierBit = 0x40;
|
|
break;
|
|
case VK_RWIN:
|
|
gCurrentModifierBit = 0x80;
|
|
break;
|
|
default:
|
|
gCurrentModifierBit = 0;
|
|
}
|
|
if (gCurrentModifierBit) {
|
|
/*
|
|
* If this is a break of a modifier key then clear the bit value.
|
|
* Otherwise, set it.
|
|
*/
|
|
if (pkei->Flags & KEY_BREAK) {
|
|
gPhysModifierState &= ~gCurrentModifierBit;
|
|
} else {
|
|
gPhysModifierState |= gCurrentModifierBit;
|
|
}
|
|
}
|
|
|
|
if (!TEST_ACCF(ACCF_ACCESSENABLED)) {
|
|
xxxProcessKeyEvent(&ke, (ULONG_PTR)pkei->ExtraInformation,
|
|
pkei->Flags & KEY_TERMSRV_SHADOW ? TRUE : FALSE);
|
|
} else {
|
|
if ((gtmridAccessTimeOut != 0) && TEST_ACCESSFLAG(AccessTimeOut, ATF_TIMEOUTON)) {
|
|
gtmridAccessTimeOut = InternalSetTimer(
|
|
NULL,
|
|
gtmridAccessTimeOut,
|
|
(UINT)gAccessTimeOut.iTimeOutMSec,
|
|
xxxAccessTimeOutTimer,
|
|
TMRF_RIT | TMRF_ONESHOT
|
|
);
|
|
}
|
|
if (AccessProceduresStream(&ke, pkei->ExtraInformation, 0)) {
|
|
xxxProcessKeyEvent(&ke, (ULONG_PTR)pkei->ExtraInformation,
|
|
pkei->Flags & KEY_TERMSRV_SHADOW ? TRUE : FALSE);
|
|
}
|
|
}
|
|
} else {
|
|
|
|
// Special toggle key synchronization for Terminal Server
|
|
if (pkei->Flags & KEY_TERMSRV_SET_LED) {
|
|
if (pkei->Flags & KEY_TERMSRV_SHADOW) {
|
|
pkei->ExtraInformation |= KEYBOARD_SHADOW;
|
|
}
|
|
RemoteSyncToggleKeys(pkei->ExtraInformation);
|
|
}
|
|
|
|
if (pkei->Flags & KEY_TERMSRV_VKPACKET) {
|
|
ke.wchInjected = (WCHAR)pkei->MakeCode;
|
|
ke.usFlaggedVk = VK_PACKET | KBDUNICODE |
|
|
((pkei->Flags & KEY_BREAK) ? KBDBREAK : 0);
|
|
xxxProcessKeyEvent(
|
|
&ke, 0,
|
|
pkei->Flags & KEY_TERMSRV_SHADOW ? TRUE : FALSE
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID SearchAndSetKbdTbl(
|
|
PDEVICEINFO pDeviceInfo,
|
|
DWORD dwType,
|
|
DWORD dwSubType)
|
|
{
|
|
PKBDFILE pkf = gpKL->spkfPrimary;
|
|
UINT i;
|
|
|
|
if (pkf->pKbdTbl->dwType == dwType && pkf->pKbdTbl->dwSubType == dwSubType) {
|
|
goto primary_match;
|
|
}
|
|
|
|
if ((pDeviceInfo->bFlags & GDIF_NOTPNP) == 0) {
|
|
TAGMSG2(DBGTAG_KBD, "SearchAndSetKbdTbl: new type 0x%x:0x%x", dwType, dwSubType);
|
|
|
|
/*
|
|
* Search for matching keyboard layout in the current KL
|
|
*/
|
|
for (i = 0; i < gpKL->uNumTbl; ++i) {
|
|
TAGMSG2(DBGTAG_KBD, "SearchAndSetKbdTbl: searching 0x%x:0x%x",
|
|
gpKL->pspkfExtra[i]->pKbdTbl->dwType,
|
|
gpKL->pspkfExtra[i]->pKbdTbl->dwSubType);
|
|
if (gpKL->pspkfExtra[i]->pKbdTbl->dwType == dwType &&
|
|
gpKL->pspkfExtra[i]->pKbdTbl->dwSubType == dwSubType) {
|
|
TAGMSG2(DBGTAG_KBD, "SearchAndSetKbdTbl: new layout for 0x%x:0x%x",
|
|
gpKL->pspkfExtra[i]->pKbdTbl->dwType,
|
|
gpKL->pspkfExtra[i]->pKbdTbl->dwSubType);
|
|
pkf = gpKL->pspkfExtra[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i >= gpKL->uNumTbl) {
|
|
/*
|
|
* Unknown type to this KL.
|
|
*/
|
|
TAGMSG0(DBGTAG_KBD, "ProcessKeyboardInput: cannot find the matching KL. Reactivating primary.");
|
|
}
|
|
|
|
} else {
|
|
TAGMSG0(DBGTAG_KBD, "ProcessKeyboardInput: The new keyboard is not PnP. Use primary.");
|
|
}
|
|
|
|
primary_match:
|
|
if (gpKL->spkf != pkf) {
|
|
Lock(&gpKL->spkf, pkf);
|
|
SetGlobalKeyboardTableInfo(gpKL);
|
|
}
|
|
}
|
|
|
|
VOID ProcessKeyboardInput(PDEVICEINFO pDeviceInfo)
|
|
{
|
|
PKEYBOARD_INPUT_DATA pkei;
|
|
PKEYBOARD_INPUT_DATA pkeiStart, pkeiEnd;
|
|
|
|
EnterCrit();
|
|
UserAssert(pDeviceInfo->type == DEVICE_TYPE_KEYBOARD);
|
|
UserAssert(pDeviceInfo->iosb.Information);
|
|
UserAssert(NT_SUCCESS(pDeviceInfo->iosb.Status));
|
|
|
|
/*
|
|
* Switch the keyboard layout table, if the current KL has multiple
|
|
* tables.
|
|
*/
|
|
if (gpKL && gpKL->uNumTbl > 0 &&
|
|
(gpKL->dwLastKbdType != GET_KEYBOARD_DEVINFO_TYPE(pDeviceInfo) ||
|
|
gpKL->dwLastKbdSubType != GET_KEYBOARD_DEVINFO_SUBTYPE(pDeviceInfo))) {
|
|
SearchAndSetKbdTbl(pDeviceInfo,
|
|
GET_KEYBOARD_DEVINFO_TYPE(pDeviceInfo),
|
|
GET_KEYBOARD_DEVINFO_SUBTYPE(pDeviceInfo));
|
|
/*
|
|
* Whether or not we found the match, cache the type/subtype so that
|
|
* we will not try to find the same type/subtype for a while.
|
|
*/
|
|
gpKL->dwLastKbdType = GET_KEYBOARD_DEVINFO_TYPE(pDeviceInfo);
|
|
gpKL->dwLastKbdSubType = GET_KEYBOARD_DEVINFO_SUBTYPE(pDeviceInfo);
|
|
}
|
|
|
|
pkeiStart = pDeviceInfo->keyboard.Data;
|
|
pkeiEnd = (PKEYBOARD_INPUT_DATA)((PBYTE)pkeiStart + pDeviceInfo->iosb.Information);
|
|
for (pkei = pkeiStart; pkei < pkeiEnd; pkei++) {
|
|
ProcessKeyboardInputWorker(pkei,
|
|
#ifdef GENERIC_INPUT
|
|
pDeviceInfo,
|
|
#endif
|
|
TRUE);
|
|
}
|
|
|
|
LeaveCrit();
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxProcessKeyEvent (RIT)
|
|
*
|
|
* This function is called to process an individual keystroke (up or down).
|
|
* It performs some OEM, language and layout specific processing which
|
|
* discards or modifies the keystroke or introduces additional keystrokes.
|
|
* The RawKeyState is updated here, also termination of screen saver and video
|
|
* power down is initiated here.
|
|
* xxxKeyEvent() is called for each resulting keystroke.
|
|
*
|
|
* History:
|
|
* 11-26-90 DavidPe Created.
|
|
\***************************************************************************/
|
|
|
|
VOID xxxProcessKeyEvent(
|
|
PKE pke,
|
|
ULONG_PTR ExtraInformation,
|
|
BOOL bInjected)
|
|
{
|
|
BYTE Vk;
|
|
|
|
CheckCritIn();
|
|
|
|
Vk = (BYTE)pke->usFlaggedVk;
|
|
|
|
/*
|
|
* KOREAN:
|
|
* Check this is Korean keyboard layout, or not..
|
|
*
|
|
* NOTE:
|
|
* It would be better check this by "keyboard hardware" or
|
|
* "keyboard layout" ???
|
|
*
|
|
* 1. Check by hardware :
|
|
*
|
|
* if (KOREAN_KEYBOARD(gKeyboardInfo.KeyboardIdentifier)) {
|
|
*
|
|
* 2. Check by layout :
|
|
*
|
|
* if (KOREAN_KBD_LAYOUT(_GetKeyboardLayout(0L))) {
|
|
*/
|
|
if (KOREAN_KBD_LAYOUT(GetActiveHKL())) {
|
|
if ((pke->usFlaggedVk & KBDBREAK) &&
|
|
!(pke->usFlaggedVk & KBDUNICODE) &&
|
|
(pke->bScanCode == 0xF1 || pke->bScanCode == 0xF2) &&
|
|
!TestRawKeyDown(Vk)) {
|
|
/*
|
|
* This is actually a keydown with a scancode of 0xF1 or 0xF2 from a
|
|
* Korean keyboard. Korean IMEs and apps want a WM_KEYDOWN with a
|
|
* scancode of 0xF1 or 0xF2. They don't mind not getting the WM_KEYUP.
|
|
* Don't update physical keystate to allow a real 0x71/0x72 keydown.
|
|
*/
|
|
pke->usFlaggedVk &= ~KBDBREAK;
|
|
} else {
|
|
UpdateRawKeyState(Vk, pke->usFlaggedVk & KBDBREAK);
|
|
}
|
|
} else {
|
|
UpdateRawKeyState(Vk, pke->usFlaggedVk & KBDBREAK);
|
|
}
|
|
|
|
/*
|
|
* Convert Left/Right Ctrl/Shift/Alt key to "unhanded" key.
|
|
* ie: if VK_LCONTROL or VK_RCONTROL, convert to VK_CONTROL etc.
|
|
*/
|
|
if ((Vk >= VK_LSHIFT) && (Vk <= VK_RMENU)) {
|
|
Vk = (BYTE)((Vk - VK_LSHIFT) / 2 + VK_SHIFT);
|
|
UpdateRawKeyState(Vk, pke->usFlaggedVk & KBDBREAK);
|
|
}
|
|
|
|
/*
|
|
* Setup to shutdown screen saver and exit video power down mode.
|
|
*/
|
|
if (glinp.dwFlags & LINP_POWERTIMEOUTS) {
|
|
/*
|
|
* Call video driver here to exit power down mode.
|
|
*/
|
|
TAGMSG0(DBGTAG_Power, "Exit video power down mode");
|
|
DrvSetMonitorPowerState(gpDispInfo->pmdev, PowerDeviceD0);
|
|
}
|
|
glinp.dwFlags = (glinp.dwFlags & ~(LINP_INPUTTIMEOUTS | LINP_INPUTSOURCES)) | LINP_KEYBOARD;
|
|
|
|
gpsi->dwLastRITEventTickCount = NtGetTickCount();
|
|
if (!gbBlockSendInputResets || !bInjected) {
|
|
glinp.timeLastInputMessage = gpsi->dwLastRITEventTickCount;
|
|
}
|
|
|
|
if (gpsi->dwLastRITEventTickCount - gpsi->dwLastSystemRITEventTickCountUpdate > SYSTEM_RIT_EVENT_UPDATE_PERIOD) {
|
|
SharedUserData->LastSystemRITEventTickCount = gpsi->dwLastRITEventTickCount;
|
|
gpsi->dwLastSystemRITEventTickCountUpdate = gpsi->dwLastRITEventTickCount;
|
|
}
|
|
|
|
if (!bInjected || (pke->dwTime == 0)) {
|
|
pke->dwTime = glinp.timeLastInputMessage;
|
|
}
|
|
|
|
#ifdef MOUSE_IP
|
|
/*
|
|
* Sonar
|
|
*/
|
|
CheckCritIn();
|
|
#ifdef KBDMAPPEDVK
|
|
if ((pke->usFlaggedVk & KBDMAPPEDVK) == 0) {
|
|
#endif
|
|
/*
|
|
* Sonar is not activated for simulated modifier keys
|
|
*/
|
|
if ((pke->usFlaggedVk & KBDBREAK) == 0) {
|
|
/*
|
|
* Key down:
|
|
* When the key is down, sonar needs to be stopped.
|
|
*/
|
|
if (IS_SONAR_ACTIVE()) {
|
|
StopSonar();
|
|
}
|
|
/*
|
|
* Do not process the repeated keys...
|
|
* If this key is not pressed before, remember it for the key up event.
|
|
*/
|
|
if (gbLastVkForSonar != Vk) {
|
|
gbLastVkForSonar = Vk;
|
|
}
|
|
} else {
|
|
/*
|
|
* Key up:
|
|
*/
|
|
if ((BYTE)Vk == gbVkForSonarKick && (BYTE)Vk == gbLastVkForSonar && TestUP(MOUSESONAR)) {
|
|
/*
|
|
* If this is keyup and it is the Sonar key, and it's the last key downed,
|
|
* kick the sonar now.
|
|
*/
|
|
StartSonar();
|
|
}
|
|
/*
|
|
* Clear the last VK for the next key event.
|
|
*/
|
|
CLEAR_SONAR_LASTVK();
|
|
}
|
|
#ifdef KBDMAPPEDVK
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
/*
|
|
* Now call all the OEM- and Locale- specific KEProcs.
|
|
* If KEProcs return FALSE, the keystroke has been discarded, in
|
|
* which case don't pass the key event on to xxxKeyEvent().
|
|
*/
|
|
if (pke->usFlaggedVk & KBDUNICODE) {
|
|
xxxKeyEvent(pke->usFlaggedVk, pke->wchInjected,
|
|
pke->dwTime, ExtraInformation,
|
|
#ifdef GENERIC_INPUT
|
|
NULL,
|
|
NULL,
|
|
#endif
|
|
bInjected);
|
|
} else {
|
|
if (KEOEMProcs(pke) && xxxKELocaleProcs(pke) && xxxKENLSProcs(pke,ExtraInformation)) {
|
|
xxxKeyEvent(pke->usFlaggedVk, pke->bScanCode,
|
|
pke->dwTime, ExtraInformation,
|
|
#ifdef GENERIC_INPUT
|
|
pke->hDevice,
|
|
&pke->data,
|
|
#endif
|
|
bInjected);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifndef SUBPIXEL_MOUSE
|
|
/***************************************************************************\
|
|
* DoMouseAccel (RIT)
|
|
*
|
|
* History:
|
|
* 11-29-90 DavidPe Created.
|
|
\***************************************************************************/
|
|
#ifdef LOCK_MOUSE_CODE
|
|
#pragma alloc_text(MOUSE, DoMouseAccel)
|
|
#endif
|
|
|
|
LONG DoMouseAccel(
|
|
LONG Delta)
|
|
{
|
|
LONG newDelta = Delta;
|
|
|
|
if (abs(Delta) > gMouseThresh1) {
|
|
newDelta *= 2;
|
|
|
|
if ((abs(Delta) > gMouseThresh2) && (gMouseSpeed == 2)) {
|
|
newDelta *= 2;
|
|
}
|
|
}
|
|
|
|
return newDelta;
|
|
}
|
|
#endif
|
|
|
|
|
|
/***************************************************************************\
|
|
* PwndForegroundCapture
|
|
*
|
|
* History:
|
|
* 10-23-91 DavidPe Created.
|
|
\***************************************************************************/
|
|
|
|
PWND PwndForegroundCapture(VOID)
|
|
{
|
|
if (gpqForeground != NULL) {
|
|
return gpqForeground->spwndCapture;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* SetKeyboardRate
|
|
*
|
|
* This function calls the keyboard driver to set a new keyboard repeat
|
|
* rate and delay. It limits the values to the min and max given by
|
|
* the driver so it won't return an error when we call it.
|
|
*
|
|
* History:
|
|
* 11-29-90 DavidPe Created.
|
|
\***************************************************************************/
|
|
VOID SetKeyboardRate(
|
|
UINT nKeySpeedAndDelay
|
|
)
|
|
{
|
|
UINT nKeyDelay;
|
|
UINT nKeySpeed;
|
|
|
|
nKeyDelay = (nKeySpeedAndDelay & KDELAY_MASK) >> KDELAY_SHIFT;
|
|
|
|
nKeySpeed = KSPEED_MASK & nKeySpeedAndDelay;
|
|
|
|
gktp.Rate = (USHORT)( ( gKeyboardInfo.KeyRepeatMaximum.Rate -
|
|
gKeyboardInfo.KeyRepeatMinimum.Rate
|
|
) * nKeySpeed / KSPEED_MASK
|
|
) +
|
|
gKeyboardInfo.KeyRepeatMinimum.Rate;
|
|
|
|
gktp.Delay = (USHORT)( ( gKeyboardInfo.KeyRepeatMaximum.Delay -
|
|
gKeyboardInfo.KeyRepeatMinimum.Delay
|
|
) * nKeyDelay / (KDELAY_MASK >> KDELAY_SHIFT)
|
|
) +
|
|
gKeyboardInfo.KeyRepeatMinimum.Delay;
|
|
|
|
/*
|
|
* Hand off the IOCTL to the RIT, since only the system process can
|
|
* access keyboard handles
|
|
*/
|
|
gdwUpdateKeyboard |= UPDATE_KBD_TYPEMATIC;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* UpdateKeyLights
|
|
*
|
|
* This function calls the keyboard driver to set the keylights into the
|
|
* current state specified by the async keystate table.
|
|
*
|
|
* bInjected: (explanation from John Parsons via email)
|
|
* Set this TRUE if you do something on the server to asynchronously change the
|
|
* indicators behind the TS client's back, to get this reflected back to the
|
|
* client. Examples are toggling num lock or caps lock programatically, or our
|
|
* favorite example is the automatic spelling correction on Word: if you type
|
|
* "tHE mouse went up the clock", Word will fix it by automagically pressing
|
|
* CAPS LOCK, then retyping the T -- if the client is not informed, the keys
|
|
* get out of sync.
|
|
* Set this to FALSE for indicator changes initiated by the client (let's say by
|
|
* pressing CAPS LOCK) in which case we don't loop back the indicator change
|
|
* since the client has already changed state locally.
|
|
*
|
|
* History:
|
|
* 11-29-90 DavidPe Created.
|
|
\***************************************************************************/
|
|
|
|
VOID UpdateKeyLights(BOOL bInjected)
|
|
{
|
|
/*
|
|
* Looking at async keystate. Must be in critical section.
|
|
*/
|
|
CheckCritIn();
|
|
|
|
/*
|
|
* Based on the toggle bits in the async keystate table,
|
|
* set the key lights.
|
|
*/
|
|
gklp.LedFlags = 0;
|
|
if (TestAsyncKeyStateToggle(VK_CAPITAL)) {
|
|
gklp.LedFlags |= KEYBOARD_CAPS_LOCK_ON;
|
|
SetRawKeyToggle(VK_CAPITAL);
|
|
} else {
|
|
ClearRawKeyToggle(VK_CAPITAL);
|
|
}
|
|
|
|
if (TestAsyncKeyStateToggle(VK_NUMLOCK)) {
|
|
gklp.LedFlags |= KEYBOARD_NUM_LOCK_ON;
|
|
SetRawKeyToggle(VK_NUMLOCK);
|
|
} else {
|
|
ClearRawKeyToggle(VK_NUMLOCK);
|
|
}
|
|
|
|
if (TestAsyncKeyStateToggle(VK_SCROLL)) {
|
|
gklp.LedFlags |= KEYBOARD_SCROLL_LOCK_ON;
|
|
SetRawKeyToggle(VK_SCROLL);
|
|
} else {
|
|
ClearRawKeyToggle(VK_SCROLL);
|
|
}
|
|
|
|
/*
|
|
* Only "Japanese keyboard hardware" has "KANA" LEDs, and switch to
|
|
* "KANA" state.
|
|
*/
|
|
if (JAPANESE_KEYBOARD(gKeyboardInfo.KeyboardIdentifier)) {
|
|
if (TestAsyncKeyStateToggle(VK_KANA)) {
|
|
gklp.LedFlags |= KEYBOARD_KANA_LOCK_ON;
|
|
SetRawKeyToggle(VK_KANA);
|
|
} else {
|
|
ClearRawKeyToggle(VK_KANA);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* On terminal server, we need to tell the WD about application injected
|
|
* toggle keys so it can update the client accordingly.
|
|
*/
|
|
|
|
if (IsRemoteConnection()) {
|
|
if (bInjected)
|
|
gklp.LedFlags |= KEYBOARD_LED_INJECTED;
|
|
else
|
|
gklp.LedFlags &= ~KEYBOARD_LED_INJECTED;
|
|
}
|
|
|
|
|
|
if (PtiCurrent() != gptiRit) {
|
|
/*
|
|
* Hand off the IOCTL to the RIT, since only the system process can
|
|
* access the keyboard handles. Happens when applying user's profile.
|
|
* IanJa: Should we check PpiCurrent() == gptiRit->ppi instead?
|
|
*/
|
|
gdwUpdateKeyboard |= UPDATE_KBD_LEDS;
|
|
} else {
|
|
/*
|
|
* Do it immediately (avoids a small delay between keydown and LED
|
|
* on when typing)
|
|
*/
|
|
PDEVICEINFO pDeviceInfo;
|
|
|
|
EnterDeviceInfoListCrit();
|
|
for (pDeviceInfo = gpDeviceInfoList; pDeviceInfo; pDeviceInfo = pDeviceInfo->pNext) {
|
|
if ((pDeviceInfo->type == DEVICE_TYPE_KEYBOARD) && (pDeviceInfo->handle)) {
|
|
ZwDeviceIoControlFile(pDeviceInfo->handle, NULL, NULL, NULL,
|
|
&giosbKbdControl, IOCTL_KEYBOARD_SET_INDICATORS,
|
|
(PVOID)&gklp, sizeof(gklp), NULL, 0);
|
|
}
|
|
}
|
|
LeaveDeviceInfoListCrit();
|
|
|
|
if (gfRemotingConsole) {
|
|
ZwDeviceIoControlFile(ghConsoleShadowKeyboardChannel, NULL, NULL, NULL,
|
|
&giosbKbdControl, IOCTL_KEYBOARD_SET_INDICATORS,
|
|
(PVOID)&gklp, sizeof(gklp), NULL, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* _GetKeyboardType is obsolete API. The API cannot
|
|
* deal with the multiple keyboards attached.
|
|
* This API returns the best guess that older apps
|
|
* would expect.
|
|
*/
|
|
int _GetKeyboardType(int nTypeFlag)
|
|
{
|
|
|
|
switch (nTypeFlag) {
|
|
case 0:
|
|
if (gpKL) {
|
|
DWORD dwType;
|
|
|
|
//
|
|
// If there's gpKL, use its primary
|
|
// type info rather than the one used
|
|
// last time.
|
|
//
|
|
UserAssert(gpKL->spkfPrimary);
|
|
UserAssert(gpKL->spkfPrimary->pKbdTbl);
|
|
dwType = gpKL->spkfPrimary->pKbdTbl->dwType;
|
|
if (dwType != 0 && dwType != KEYBOARD_TYPE_UNKNOWN) {
|
|
return dwType;
|
|
}
|
|
}
|
|
return gKeyboardInfo.KeyboardIdentifier.Type;
|
|
|
|
case 1:
|
|
// FE_SB
|
|
{
|
|
int OEMId = 0;
|
|
DWORD dwSubType;
|
|
PKBDNLSTABLES pKbdNlsTbl = gpKbdNlsTbl;
|
|
|
|
//
|
|
// If there's gpKL, use its primary value
|
|
// rather than the one used last time.
|
|
//
|
|
if (gpKL) {
|
|
UserAssert(gpKL->spkfPrimary);
|
|
if (gpKL->spkfPrimary->pKbdNlsTbl) {
|
|
pKbdNlsTbl =gpKL->spkfPrimary->pKbdNlsTbl;
|
|
}
|
|
UserAssert(gpKL->spkfPrimary->pKbdTbl);
|
|
dwSubType = gpKL->spkfPrimary->pKbdTbl->dwSubType;
|
|
} else {
|
|
dwSubType = gKeyboardInfo.KeyboardIdentifier.Subtype;
|
|
}
|
|
|
|
//
|
|
// If this keyboard layout is compatible with 101 or 106
|
|
// Japanese keyboard, we just return 101 or 106's keyboard
|
|
// id, not this keyboard's one to let application handle
|
|
// this keyboard as 101 or 106 Japanese keyboard.
|
|
//
|
|
if (pKbdNlsTbl) {
|
|
if (pKbdNlsTbl->LayoutInformation & NLSKBD_INFO_EMURATE_101_KEYBOARD) {
|
|
return MICROSOFT_KBD_101_TYPE;
|
|
}
|
|
if (pKbdNlsTbl->LayoutInformation & NLSKBD_INFO_EMURATE_106_KEYBOARD) {
|
|
return MICROSOFT_KBD_106_TYPE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// PSS ID Number: Q130054
|
|
// Article last modified on 05-16-1995
|
|
//
|
|
// 3.10 1.20 | 3.50 1.20
|
|
// WINDOWS | WINDOWS NT
|
|
//
|
|
// ---------------------------------------------------------------------
|
|
// The information in this article applies to:
|
|
// - Microsoft Windows Software Development Kit (SDK) for Windows
|
|
// version 3.1
|
|
// - Microsoft Win32 Software Development Kit (SDK) version 3.5
|
|
// - Microsoft Win32s version 1.2
|
|
// ---------------------------------------------------------------------
|
|
// SUMMARY
|
|
// =======
|
|
// Because of the variety of computer manufacturers (NEC, Fujitsu, IBMJ, and
|
|
// so on) in Japan, sometimes Windows-based applications need to know which
|
|
// OEM (original equipment manufacturer) manufactured the computer that is
|
|
// running the application. This article explains how.
|
|
//
|
|
// MORE INFORMATION
|
|
// ================
|
|
// There is no documented way to detect the manufacturer of the computer that
|
|
// is currently running an application. However, a Windows-based application
|
|
// can detect the type of OEM Windows by using the return value of the
|
|
// GetKeyboardType() function.
|
|
//
|
|
// If an application uses the GetKeyboardType API, it can get OEM ID by
|
|
// specifying "1" (keyboard subtype) as argument of the function. Each OEM ID
|
|
// is listed here:
|
|
//
|
|
// OEM Windows OEM ID
|
|
// ------------------------------
|
|
// Microsoft 00H (DOS/V)
|
|
// all AX 01H
|
|
// EPSON 04H
|
|
// Fujitsu 05H
|
|
// IBMJ 07H
|
|
// Matsushita 0AH
|
|
// NEC 0DH
|
|
// Toshiba 12H
|
|
//
|
|
// Application programs can use these OEM IDs to distinguish the type of OEM
|
|
// Windows. Note, however, that this method is not documented, so Microsoft
|
|
// may not support it in the future version of Windows.
|
|
//
|
|
// As a rule, application developers should write hardware-independent code,
|
|
// especially when making Windows-based applications. If they need to make a
|
|
// hardware-dependent application, they must prepare the separated program
|
|
// file for each different hardware architecture.
|
|
//
|
|
// Additional reference words: 3.10 1.20 3.50 1.20 kbinf
|
|
// KBCategory: kbhw
|
|
// KBSubcategory: wintldev
|
|
// =============================================================================
|
|
// Copyright Microsoft Corporation 1995.
|
|
|
|
if (pKbdNlsTbl) {
|
|
//
|
|
// Get OEM (Windows) ID.
|
|
//
|
|
OEMId = ((int)pKbdNlsTbl->OEMIdentifier) << 8;
|
|
}
|
|
//
|
|
// The format of KeyboardIdentifier.Subtype :
|
|
//
|
|
// 0 - 3 bits = keyboard subtype
|
|
// 4 - 7 bits = kernel mode kerboard driver provider id.
|
|
//
|
|
// Kernel mode keyboard dirver provier | ID
|
|
// ------------------------------------+-----
|
|
// Microsoft | 00H
|
|
// all AX | 01H
|
|
// Toshiba | 02H
|
|
// EPSON | 04H
|
|
// Fujitsu | 05H
|
|
// IBMJ | 07H
|
|
// Matsushita | 0AH
|
|
// NEC | 0DH
|
|
//
|
|
|
|
//
|
|
// And here is the format of return value.
|
|
//
|
|
// 0 - 7 bits = Keyboard Subtype.
|
|
// 8 - 15 bits = OEM (Windows) Id.
|
|
// 16 - 31 bits = not used.
|
|
//
|
|
return (int)(OEMId | (dwSubType & 0x0f));
|
|
}
|
|
|
|
case 2:
|
|
return gKeyboardInfo.NumberOfFunctionKeys;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* xxxMouseEventDirect
|
|
*
|
|
* Mouse event inserts a mouse event into the input stream.
|
|
*
|
|
* The parameters are the same as the fields of the MOUSEINPUT structure
|
|
* used in SendInput.
|
|
*
|
|
* dx Delta x
|
|
* dy Delta y
|
|
* mouseData Mouse wheel movement or xbuttons
|
|
* dwMEFlags Mouse event flags
|
|
* dwExtraInfo Extra info from driver.
|
|
*
|
|
* History:
|
|
* 07-23-92 Mikehar Created.
|
|
* 01-08-93 JonPa Made it work with new mouse drivers
|
|
\**************************************************************************/
|
|
|
|
BOOL xxxMouseEventDirect(
|
|
DWORD dx,
|
|
DWORD dy,
|
|
DWORD mouseData,
|
|
DWORD dwMEFlags,
|
|
DWORD dwTime,
|
|
ULONG_PTR dwExtraInfo)
|
|
{
|
|
DWORD dwDriverMouseFlags;
|
|
DWORD dwDriverMouseData;
|
|
#ifdef GENERIC_INPUT
|
|
MOUSE_INPUT_DATA mei;
|
|
#endif
|
|
|
|
PTHREADINFO pti = PtiCurrent();
|
|
if (dwTime == 0) {
|
|
dwTime = NtGetTickCount();
|
|
}
|
|
|
|
/*
|
|
* The calling thread must be on the active desktop
|
|
* and have journal playback access to that desktop.
|
|
*/
|
|
if (pti->rpdesk == grpdeskRitInput) {
|
|
UserAssert(!(pti->rpdesk->rpwinstaParent->dwWSF_Flags & WSF_NOIO));
|
|
if (!CheckGrantedAccess(pti->amdesk, DESKTOP_JOURNALPLAYBACK)) {
|
|
|
|
RIPNTERR0(STATUS_ACCESS_DENIED, RIP_WARNING,
|
|
"mouse_event(): No DESKTOP_JOURNALPLAYBACK access to input desktop.");
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
/*
|
|
* 3/22/95 BradG - Only allow below HACK for pre 4.0 applications
|
|
*/
|
|
if (LOWORD(pti->dwExpWinVer) >= VER40) {
|
|
RIPMSG0(RIP_VERBOSE,"mouse_event(): Calls not forwarded for 4.0 or greater apps.");
|
|
return FALSE;
|
|
} else {
|
|
BOOL fAccessToDesktop;
|
|
|
|
/*
|
|
* 3/22/95 BradG - Bug #9314: Screensavers are not deactivated by mouse_event()
|
|
* The main problem is the check above, since screensavers run on their own
|
|
* desktop. This causes the above check to fail because the process using
|
|
* mouse_event() is running on another desktop. The solution is to determine
|
|
* if we have access to the input desktop by calling _OpenDesktop for the
|
|
* current input desktop, grpdeskRitInput, with a request for DESKTOP_JOURNALPLAYBACK
|
|
* access. If this succeeds, we can allow this mouse_event() request to pass
|
|
* through, otherwise return.
|
|
*/
|
|
UserAssert(grpdeskRitInput != NULL);
|
|
|
|
UserAssert(!(grpdeskRitInput->rpwinstaParent->dwWSF_Flags & WSF_NOIO));
|
|
fAccessToDesktop = AccessCheckObject(grpdeskRitInput,
|
|
DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS | DESKTOP_JOURNALPLAYBACK,
|
|
KernelMode,
|
|
&DesktopMapping);
|
|
if (!fAccessToDesktop) {
|
|
RIPMSG0(RIP_VERBOSE, "mouse_event(): Call NOT forwarded to input desktop" );
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* We do have access to the desktop, so
|
|
* let this mouse_event() call go through.
|
|
*/
|
|
RIPMSG0( RIP_VERBOSE, "mouse_event(): Call forwarded to input desktop" );
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This process is providing input so it gets the right to
|
|
* call SetForegroundWindow
|
|
*/
|
|
gppiInputProvider = pti->ppi;
|
|
|
|
/*
|
|
* The following code assumes that MOUSEEVENTF_MOVE == 1,
|
|
* that MOUSEEVENTF_ABSOLUTE > all button flags, and that the
|
|
* mouse_event button flags are defined in the same order as the
|
|
* MOUSE_INPUT_DATA button bits.
|
|
*/
|
|
#if MOUSEEVENTF_MOVE != 1
|
|
# error("MOUSEEVENTF_MOVE != 1")
|
|
#endif
|
|
#if MOUSEEVENTF_LEFTDOWN != MOUSE_LEFT_BUTTON_DOWN * 2
|
|
# error("MOUSEEVENTF_LEFTDOWN != MOUSE_LEFT_BUTTON_DOWN * 2")
|
|
#endif
|
|
#if MOUSEEVENTF_LEFTUP != MOUSE_LEFT_BUTTON_UP * 2
|
|
# error("MOUSEEVENTF_LEFTUP != MOUSE_LEFT_BUTTON_UP * 2")
|
|
#endif
|
|
#if MOUSEEVENTF_RIGHTDOWN != MOUSE_RIGHT_BUTTON_DOWN * 2
|
|
# error("MOUSEEVENTF_RIGHTDOWN != MOUSE_RIGHT_BUTTON_DOWN * 2")
|
|
#endif
|
|
#if MOUSEEVENTF_RIGHTUP != MOUSE_RIGHT_BUTTON_UP * 2
|
|
# error("MOUSEEVENTF_RIGHTUP != MOUSE_RIGHT_BUTTON_UP * 2")
|
|
#endif
|
|
#if MOUSEEVENTF_MIDDLEDOWN != MOUSE_MIDDLE_BUTTON_DOWN * 2
|
|
# error("MOUSEEVENTF_MIDDLEDOWN != MOUSE_MIDDLE_BUTTON_DOWN * 2")
|
|
#endif
|
|
#if MOUSEEVENTF_MIDDLEUP != MOUSE_MIDDLE_BUTTON_UP * 2
|
|
# error("MOUSEEVENTF_MIDDLEUP != MOUSE_MIDDLE_BUTTON_UP * 2")
|
|
#endif
|
|
#if MOUSEEVENTF_WHEEL != MOUSE_WHEEL * 2
|
|
# error("MOUSEEVENTF_WHEEL != MOUSE_WHEEL * 2")
|
|
#endif
|
|
|
|
/* set legal values */
|
|
dwDriverMouseFlags = dwMEFlags & MOUSEEVENTF_BUTTONMASK;
|
|
|
|
/* remove MOUSEEVENTF_XDOWN/UP because we are going to add
|
|
MOUSEEVENTF_DRIVER_X1/2DOWN/UP later */
|
|
dwDriverMouseFlags &= ~(MOUSEEVENTF_XDOWN | MOUSEEVENTF_XUP);
|
|
|
|
dwDriverMouseData = 0;
|
|
|
|
/*
|
|
* Handle mouse wheel and xbutton inputs.
|
|
*
|
|
* Note that MOUSEEVENTF_XDOWN/UP and MOUSEEVENTF_MOUSEWHEEL cannot both
|
|
* be specified since they share the mouseData field
|
|
*/
|
|
if ( ((dwMEFlags & (MOUSEEVENTF_XDOWN | MOUSEEVENTF_WHEEL)) == (MOUSEEVENTF_XDOWN | MOUSEEVENTF_WHEEL)) ||
|
|
((dwMEFlags & (MOUSEEVENTF_XUP | MOUSEEVENTF_WHEEL)) == (MOUSEEVENTF_XUP | MOUSEEVENTF_WHEEL))) {
|
|
|
|
RIPMSG1(RIP_WARNING, "Can't specify both MOUSEEVENTF_XDOWN/UP and MOUSEEVENTF_WHEEL in call to SendInput, dwFlags=0x%.8X", dwMEFlags);
|
|
dwDriverMouseFlags &= ~(MOUSEEVENTF_XDOWN | MOUSEEVENTF_XUP | MOUSEEVENTF_WHEEL);
|
|
} else if (dwMEFlags & MOUSEEVENTF_WHEEL) {
|
|
/*
|
|
* Force the value to a short. We cannot fail if it is out of range
|
|
* because we accepted a 32 bit value in NT 4.
|
|
*/
|
|
dwDriverMouseData = min(max(SHRT_MIN, (LONG)mouseData), SHRT_MAX);
|
|
} else {
|
|
|
|
/* don't process xbuttons if mousedata has invalid buttons */
|
|
if (~XBUTTON_MASK & mouseData) {
|
|
RIPMSG1(RIP_WARNING, "Invalid xbutton specified in SendInput, mouseData=0x%.8X", mouseData);
|
|
} else {
|
|
if (dwMEFlags & MOUSEEVENTF_XDOWN) {
|
|
if (mouseData & XBUTTON1) {
|
|
dwDriverMouseFlags |= MOUSEEVENTF_DRIVER_X1DOWN;
|
|
}
|
|
if (mouseData & XBUTTON2) {
|
|
dwDriverMouseFlags |= MOUSEEVENTF_DRIVER_X2DOWN;
|
|
}
|
|
}
|
|
if (dwMEFlags & MOUSEEVENTF_XUP) {
|
|
if (mouseData & XBUTTON1) {
|
|
dwDriverMouseFlags |= MOUSEEVENTF_DRIVER_X1UP;
|
|
}
|
|
if (mouseData & XBUTTON2) {
|
|
dwDriverMouseFlags |= MOUSEEVENTF_DRIVER_X2UP;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Convert the MOUSEEVENTF_ flags to MOUSE_BUTTON flags sent by the driver */
|
|
dwDriverMouseFlags >>= 1;
|
|
|
|
#ifdef GENERIC_INPUT
|
|
mei.UnitId = INJECTED_UNIT_ID;
|
|
if (dwMEFlags & MOUSEEVENTF_ABSOLUTE) {
|
|
mei.Flags = MOUSE_MOVE_ABSOLUTE;
|
|
} else {
|
|
mei.Flags = MOUSE_MOVE_RELATIVE;
|
|
}
|
|
if (dwMEFlags & MOUSEEVENTF_VIRTUALDESK) {
|
|
mei.Flags |= MOUSE_VIRTUAL_DESKTOP;
|
|
}
|
|
mei.Buttons = dwDriverMouseFlags;
|
|
if (dwDriverMouseData) {
|
|
mei.ButtonData = (USHORT)dwDriverMouseData;
|
|
}
|
|
mei.RawButtons = 0; // LATER...
|
|
mei.LastX = dx;
|
|
mei.LastY = dy;
|
|
mei.ExtraInformation = (ULONG)dwExtraInfo;
|
|
#endif
|
|
|
|
LeaveCrit();
|
|
|
|
/*
|
|
* Process coordinates first. This is especially useful for absolute
|
|
* pointing devices like touch-screens and tablets.
|
|
*/
|
|
if (dwMEFlags & MOUSEEVENTF_MOVE) {
|
|
TAGMSG2(DBGTAG_PNP, "xxxMouseEventDirect: posting mouse move msg: Flag=%04x MouseData=%04x",
|
|
mei.Flags, mei.Buttons);
|
|
xxxMoveEvent(dx, dy, dwMEFlags, dwExtraInfo,
|
|
#ifdef GENERIC_INPUT
|
|
/*
|
|
* This is a simulated input from SendInput API.
|
|
* There is no real mouse device associated with this input,
|
|
* so we can only pass NULL as a hDevice.
|
|
*/
|
|
NULL,
|
|
&mei,
|
|
#endif
|
|
dwTime, TRUE);
|
|
}
|
|
|
|
TAGMSG2(DBGTAG_PNP, "xxxMoveEvent: queueing mouse msg: Flag=%04x MouseData=%04x",
|
|
mei.Flags, mei.Buttons);
|
|
QueueMouseEvent(
|
|
(USHORT) dwDriverMouseFlags,
|
|
(USHORT) dwDriverMouseData,
|
|
dwExtraInfo,
|
|
gptCursorAsync,
|
|
dwTime,
|
|
#ifdef GENERIC_INPUT
|
|
NULL,
|
|
&mei,
|
|
#endif
|
|
TRUE,
|
|
FALSE
|
|
);
|
|
|
|
ProcessQueuedMouseEvents();
|
|
|
|
EnterCrit();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* xxxInternalKeyEventDirect
|
|
*
|
|
* key event inserts a key event into the input stream.
|
|
*
|
|
* History:
|
|
* 07-23-92 Mikehar Created.
|
|
\**************************************************************************/
|
|
BOOL xxxInternalKeyEventDirect(
|
|
BYTE bVk,
|
|
WORD wScan,
|
|
DWORD dwFlags,
|
|
DWORD dwTime,
|
|
ULONG_PTR dwExtraInfo)
|
|
{
|
|
PTHREADINFO pti = PtiCurrent();
|
|
KE KeyEvent;
|
|
|
|
/*
|
|
* The calling thread must be on the active desktop
|
|
* and have journal playback access to that desktop.
|
|
*/
|
|
if (pti->rpdesk != grpdeskRitInput ||
|
|
!(ISCSRSS() ||
|
|
RtlAreAllAccessesGranted(pti->amdesk, DESKTOP_JOURNALPLAYBACK))) {
|
|
|
|
RIPNTERR0(STATUS_ACCESS_DENIED, RIP_WARNING,
|
|
"Injecting key failed: Non active desktop or access denied");
|
|
|
|
return FALSE;
|
|
}
|
|
UserAssert(!(pti->rpdesk->rpwinstaParent->dwWSF_Flags & WSF_NOIO));
|
|
|
|
KeyEvent.bScanCode = (BYTE)wScan;
|
|
#ifdef GENERIC_INPUT
|
|
/*
|
|
* This is a injected key, no real device is associated with this...
|
|
*/
|
|
KeyEvent.hDevice = NULL;
|
|
#endif
|
|
|
|
if (dwFlags & KEYEVENTF_SCANCODE) {
|
|
bVk = VKFromVSC(&KeyEvent,
|
|
(BYTE)(dwFlags & KEYEVENTF_EXTENDEDKEY ? 0xE0 : 0),
|
|
gafRawKeyState);
|
|
KeyEvent.usFlaggedVk = (USHORT)bVk;
|
|
} else {
|
|
KeyEvent.usFlaggedVk = bVk | KBDINJECTEDVK;
|
|
}
|
|
|
|
if (dwFlags & KEYEVENTF_KEYUP)
|
|
KeyEvent.usFlaggedVk |= KBDBREAK;
|
|
|
|
if (dwFlags & KEYEVENTF_UNICODE) {
|
|
KeyEvent.usFlaggedVk |= KBDUNICODE;
|
|
KeyEvent.wchInjected = wScan;
|
|
} else if (dwFlags & KEYEVENTF_EXTENDEDKEY) {
|
|
KeyEvent.usFlaggedVk |= KBDEXT;
|
|
} else {
|
|
// Is it from the numeric keypad?
|
|
if (((bVk >= VK_NUMPAD0) && (bVk <= VK_NUMPAD9)) || (bVk == VK_DECIMAL)) {
|
|
KeyEvent.usFlaggedVk |= KBDNUMPAD;
|
|
} else {
|
|
int i;
|
|
for (i = 0; ausNumPadCvt[i] != 0; i++) {
|
|
if (bVk == LOBYTE(ausNumPadCvt[i])) {
|
|
KeyEvent.usFlaggedVk |= KBDNUMPAD;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef GENERIC_INPUT
|
|
/*
|
|
* Let's simulate the input as far as we can.
|
|
*/
|
|
KeyEvent.data.MakeCode = (BYTE)wScan;
|
|
if (dwFlags & KEYEVENTF_KEYUP) {
|
|
KeyEvent.data.Flags = KEY_BREAK;
|
|
} else {
|
|
KeyEvent.data.Flags = KEY_MAKE;
|
|
}
|
|
if (dwFlags & KEYEVENTF_EXTENDEDKEY) {
|
|
KeyEvent.data.Flags |= KEY_E0;
|
|
}
|
|
KeyEvent.data.Reserved = 0;
|
|
KeyEvent.data.UnitId = INJECTED_UNIT_ID;
|
|
KeyEvent.data.ExtraInformation = (ULONG)dwExtraInfo;
|
|
#endif
|
|
|
|
/*
|
|
* This process is providing input so it gets the right to
|
|
* call SetForegroundWindow
|
|
*/
|
|
gppiInputProvider = pti->ppi;
|
|
|
|
KeyEvent.dwTime = dwTime;
|
|
xxxProcessKeyEvent(&KeyEvent, dwExtraInfo, TRUE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*****************************************************************************\
|
|
*
|
|
* _BlockInput()
|
|
*
|
|
* This disables/enables input into USER via keyboard or mouse
|
|
* If input is enabled and the caller
|
|
* is disabling it, the caller gets the 'input cookie.' This means two
|
|
* things:
|
|
* (a) Only the caller's thread can reenable input
|
|
* (b) Only the caller's thread can fake input messages by calling
|
|
* SendInput().
|
|
*
|
|
* This guarantees a sequential uninterrupted input stream.
|
|
*
|
|
* It can be used in conjunction with a journal playback hook however,
|
|
* since USER still does some processing in *_event functions before
|
|
* noticing a journal playback hook is around.
|
|
*
|
|
* Note that the disabled state can be suspended, and will be, when the
|
|
* fault dialog comes up. ForceInputState() will save away the enabled
|
|
* status, so input is cleared, then whack back the old stuff when done.
|
|
* We do the same thing for capture, modality, blah blah. This makes sure
|
|
* that if somebody is hung, the end user can still type Ctrl+Alt+Del and
|
|
* interact with the dialog.
|
|
*
|
|
\*****************************************************************************/
|
|
BOOL
|
|
_BlockInput(BOOL fBlockIt)
|
|
{
|
|
PTHREADINFO ptiCurrent;
|
|
|
|
ptiCurrent = PtiCurrent();
|
|
|
|
/*
|
|
* The calling thread must be on the active desktop and have journal
|
|
* playback access to that desktop if it wants to block input.
|
|
* (Unblocking is less restricted)
|
|
*/
|
|
if (fBlockIt &&
|
|
(ptiCurrent->rpdesk != grpdeskRitInput ||
|
|
!RtlAreAllAccessesGranted(ptiCurrent->amdesk, DESKTOP_JOURNALPLAYBACK))) {
|
|
|
|
RIPNTERR0(STATUS_ACCESS_DENIED, RIP_WARNING,
|
|
"BlockInput failed: Non active desktop or access denied");
|
|
return FALSE;
|
|
}
|
|
UserAssert(!(ptiCurrent->rpdesk->rpwinstaParent->dwWSF_Flags & WSF_NOIO));
|
|
|
|
/*
|
|
* If we are enabling input
|
|
* * Is it disabled? No, then fail the call
|
|
* * Is it disabled but we aren't the dude in control? Yes, then
|
|
* fail the call.
|
|
* If we are disabling input
|
|
* * Is it enabled? No, then fail the call
|
|
* * Set us up as the dude in control
|
|
*/
|
|
|
|
if (fBlockIt) {
|
|
/*
|
|
* Is input blocked right now?
|
|
*/
|
|
if (gptiBlockInput != NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Is this thread exiting? If so, fail the call now. User's
|
|
* cleanup code won't get a chance to whack this back if so.
|
|
*/
|
|
if (ptiCurrent->TIF_flags & TIF_INCLEANUP) {
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Set blocking on.
|
|
*/
|
|
gptiBlockInput = ptiCurrent;
|
|
} else {
|
|
/*
|
|
* Fail if input is not blocked, or blocked by another thread
|
|
*/
|
|
if (gptiBlockInput != ptiCurrent) {
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* This thread was blocking input, so now clear the block.
|
|
*/
|
|
gptiBlockInput = NULL;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/**************************************************************************\
|
|
* xxxSendInput
|
|
*
|
|
* input injection
|
|
*
|
|
* History:
|
|
* 11-01-96 CLupu Created.
|
|
\**************************************************************************/
|
|
UINT xxxSendInput(
|
|
UINT nInputs,
|
|
LPINPUT pInputs)
|
|
{
|
|
UINT nEv;
|
|
LPINPUT pEvent;
|
|
BOOLEAN fCanDiscontinue = Is510Compat(PtiCurrent()->dwExpWinVer);
|
|
|
|
for (nEv = 0, pEvent = pInputs; nEv < nInputs; nEv++) {
|
|
|
|
switch (pEvent->type) {
|
|
case INPUT_MOUSE:
|
|
if (!xxxMouseEventDirect(
|
|
pEvent->mi.dx,
|
|
pEvent->mi.dy,
|
|
pEvent->mi.mouseData,
|
|
pEvent->mi.dwFlags,
|
|
pEvent->mi.time,
|
|
pEvent->mi.dwExtraInfo) &&
|
|
fCanDiscontinue) {
|
|
/*
|
|
* Note: the error code should have been assigned in
|
|
* xxx.*EventDirect routines, so we should just
|
|
* bail out.
|
|
*/
|
|
RIPMSG0(RIP_WARNING, "xxxMouseEventDirect: failed");
|
|
goto discontinue;
|
|
}
|
|
break;
|
|
|
|
case INPUT_KEYBOARD:
|
|
if ((pEvent->ki.dwFlags & KEYEVENTF_UNICODE) &&
|
|
(pEvent->ki.wVk == 0) &&
|
|
((pEvent->ki.dwFlags & ~(KEYEVENTF_KEYUP | KEYEVENTF_UNICODE)) == 0)) {
|
|
if (!xxxInternalKeyEventDirect(
|
|
VK_PACKET,
|
|
pEvent->ki.wScan, // actually a Unicode character
|
|
pEvent->ki.dwFlags,
|
|
pEvent->ki.time,
|
|
pEvent->ki.dwExtraInfo) &&
|
|
fCanDiscontinue) {
|
|
goto discontinue;
|
|
}
|
|
} else {
|
|
if (!xxxInternalKeyEventDirect(
|
|
LOBYTE(pEvent->ki.wVk),
|
|
LOBYTE(pEvent->ki.wScan),
|
|
pEvent->ki.dwFlags,
|
|
pEvent->ki.time,
|
|
pEvent->ki.dwExtraInfo) &&
|
|
fCanDiscontinue) {
|
|
goto discontinue;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case INPUT_HARDWARE:
|
|
if (fCanDiscontinue) {
|
|
/*
|
|
* Not supported on NT.
|
|
*/
|
|
RIPERR0(ERROR_CALL_NOT_IMPLEMENTED, RIP_WARNING, "xxxSendInput: INPUT_HARDWARE is for 9x only.");
|
|
goto discontinue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
pEvent++;
|
|
}
|
|
|
|
discontinue:
|
|
return nEv;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* _SetConsoleReserveKeys
|
|
*
|
|
* Sets the reserved keys field in the console's pti.
|
|
*
|
|
* History:
|
|
* 02-17-93 JimA Created.
|
|
\**************************************************************************/
|
|
|
|
BOOL _SetConsoleReserveKeys(
|
|
PWND pwnd,
|
|
DWORD fsReserveKeys)
|
|
{
|
|
GETPTI(pwnd)->fsReserveKeys = fsReserveKeys;
|
|
return TRUE;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* _GetMouseMovePointsEx
|
|
*
|
|
* Gets the last nPoints mouse moves from the global buffer starting with
|
|
* ppt. Returns -1 if it doesn't find it. It uses the timestamp if it was
|
|
* provided to differentiate between mouse points with the same coordinates.
|
|
*
|
|
* History:
|
|
* 03-17-97 CLupu Created.
|
|
\**************************************************************************/
|
|
int _GetMouseMovePointsEx(
|
|
CONST MOUSEMOVEPOINT* ppt,
|
|
MOUSEMOVEPOINT* ccxpptBuf,
|
|
UINT nPoints,
|
|
DWORD resolution)
|
|
{
|
|
UINT uInd, uStart, nPointsRetrieved, i;
|
|
BOOL bFound = FALSE;
|
|
int x, y;
|
|
DWORD resX, resY;
|
|
|
|
/*
|
|
* Search the point in the global buffer and get the first occurance.
|
|
*/
|
|
uInd = uStart = PREVPOINT(gptInd);
|
|
|
|
|
|
do {
|
|
/*
|
|
* The resolutions can be zero only if the buffer is still not full
|
|
*/
|
|
if (HIWORD(gaptMouse[uInd].x) == 0 || HIWORD(gaptMouse[uInd].y) == 0) {
|
|
break;
|
|
}
|
|
|
|
resX = (DWORD)HIWORD(gaptMouse[uInd].x) + 1;
|
|
resY = (DWORD)HIWORD(gaptMouse[uInd].y) + 1;
|
|
|
|
if ((int)resX != SYSMET(CXVIRTUALSCREEN)) {
|
|
UserAssert(resX == 0x10000);
|
|
x = LOWORD(gaptMouse[uInd].x) * SYSMET(CXVIRTUALSCREEN) / resX;
|
|
} else {
|
|
x = LOWORD(gaptMouse[uInd].x);
|
|
}
|
|
|
|
if ((int)resY != SYSMET(CYVIRTUALSCREEN)) {
|
|
UserAssert(resY == 0x10000);
|
|
y = LOWORD(gaptMouse[uInd].y) * SYSMET(CYVIRTUALSCREEN) / resY;
|
|
} else {
|
|
y = LOWORD(gaptMouse[uInd].y);
|
|
}
|
|
|
|
if (x == ppt->x && y == ppt->y) {
|
|
/*
|
|
* If the timestamp was provided check to see if it's the right
|
|
* timestamp.
|
|
*/
|
|
if (ppt->time != 0 && ppt->time != gaptMouse[uInd].time) {
|
|
uInd = PREVPOINT(uInd);
|
|
RIPMSG4(RIP_VERBOSE,
|
|
"GetMouseMovePointsEx: Found point (%x, %x) but timestamp %x diff from %x",
|
|
x, y, ppt->time, gaptMouse[uInd].time);
|
|
continue;
|
|
}
|
|
|
|
bFound = TRUE;
|
|
break;
|
|
}
|
|
uInd = PREVPOINT(uInd);
|
|
} while (uInd != uStart);
|
|
|
|
/*
|
|
* The point might not be in the buffer anymore.
|
|
*/
|
|
if (!bFound) {
|
|
RIPERR2(ERROR_POINT_NOT_FOUND, RIP_VERBOSE,
|
|
"GetMouseMovePointsEx: point not found (%x, %x)", ppt->x, ppt->y);
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* See how many points we can retrieve.
|
|
*/
|
|
nPointsRetrieved = (uInd <= uStart ? uInd + MAX_MOUSEPOINTS - uStart : uInd - uStart);
|
|
|
|
nPointsRetrieved = min(nPointsRetrieved, nPoints);
|
|
|
|
/*
|
|
* Copy the points to the app buffer.
|
|
*/
|
|
try {
|
|
for (i = 0; i < nPointsRetrieved; i++) {
|
|
resX = (DWORD)HIWORD(gaptMouse[uInd].x) + 1;
|
|
resY = (DWORD)HIWORD(gaptMouse[uInd].y) + 1;
|
|
|
|
/*
|
|
* If one of the resolutions is 0 then we're done.
|
|
*/
|
|
if (HIWORD(gaptMouse[uInd].x) == 0 || HIWORD(gaptMouse[uInd].y) == 0) {
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* LOWORD(gaptMouse[uInd].x) contains the x point on the scale
|
|
* specified by HIWORD(gaptMouse[uInd].x).
|
|
*/
|
|
if (resolution == GMMP_USE_HIGH_RESOLUTION_POINTS) {
|
|
ccxpptBuf[i].x = (DWORD)LOWORD(gaptMouse[uInd].x) * 0xFFFF / (resX - 1);
|
|
ccxpptBuf[i].y = (DWORD)LOWORD(gaptMouse[uInd].y) * 0xFFFF / (resY - 1);
|
|
|
|
} else {
|
|
UserAssert(resolution == GMMP_USE_DISPLAY_POINTS);
|
|
|
|
ccxpptBuf[i].x = LOWORD(gaptMouse[uInd].x) * SYSMET(CXVIRTUALSCREEN) / resX;
|
|
ccxpptBuf[i].y = LOWORD(gaptMouse[uInd].y) * SYSMET(CYVIRTUALSCREEN) / resY;
|
|
}
|
|
ccxpptBuf[i].time = gaptMouse[uInd].time;
|
|
ccxpptBuf[i].dwExtraInfo = gaptMouse[uInd].dwExtraInfo;
|
|
|
|
uInd = PREVPOINT(uInd);
|
|
}
|
|
} except(W32ExceptionHandler(FALSE, RIP_WARNING)) {
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
|
|
/**************************************************************************\
|
|
* ProcessQueuedMouseEvents
|
|
*
|
|
* Process mouse events.
|
|
*
|
|
* History:
|
|
* 11-01-96 CLupu Created.
|
|
\**************************************************************************/
|
|
VOID ProcessQueuedMouseEvents(
|
|
VOID)
|
|
{
|
|
MOUSEEVENT MouseEvent;
|
|
static POINT ptCursorLast = {0,0};
|
|
|
|
while (UnqueueMouseEvent(&MouseEvent)) {
|
|
|
|
EnterCrit();
|
|
|
|
// Setup to shutdown screen saver and exit video power down mode.
|
|
if (glinp.dwFlags & LINP_POWERTIMEOUTS) {
|
|
// Call video driver here to exit power down mode.
|
|
TAGMSG0(DBGTAG_Power, "Exit video power down mode");
|
|
DrvSetMonitorPowerState(gpDispInfo->pmdev, PowerDeviceD0);
|
|
}
|
|
glinp.dwFlags &= ~(LINP_INPUTTIMEOUTS | LINP_INPUTSOURCES);
|
|
|
|
gpsi->dwLastRITEventTickCount = MouseEvent.time;
|
|
if (!gbBlockSendInputResets || !MouseEvent.bInjected) {
|
|
glinp.timeLastInputMessage = MouseEvent.time;
|
|
}
|
|
|
|
if (gpsi->dwLastRITEventTickCount - gpsi->dwLastSystemRITEventTickCountUpdate > SYSTEM_RIT_EVENT_UPDATE_PERIOD) {
|
|
SharedUserData->LastSystemRITEventTickCount = gpsi->dwLastRITEventTickCount;
|
|
gpsi->dwLastSystemRITEventTickCountUpdate = gpsi->dwLastRITEventTickCount;
|
|
}
|
|
|
|
CLEAR_SRVIF(SRVIF_LASTRITWASKEYBOARD);
|
|
|
|
gpsi->ptCursor = MouseEvent.ptPointer;
|
|
|
|
#ifdef GENERIC_INPUT
|
|
if ((gpqForeground && TestRawInputMode(PtiMouseFromQ(gpqForeground), RawMouse))
|
|
#ifdef GI_SINK
|
|
|| IsMouseSinkPresent()
|
|
#endif
|
|
) {
|
|
PostRawMouseInput(gpqForeground, MouseEvent.time, MouseEvent.hDevice, &MouseEvent.rawData);
|
|
}
|
|
#endif
|
|
|
|
if ((ptCursorLast.x != gpsi->ptCursor.x) ||
|
|
(ptCursorLast.y != gpsi->ptCursor.y)) {
|
|
|
|
/*
|
|
* This mouse move ExtraInfo is global (as ptCursor
|
|
* was) and is associated with the current ptCursor
|
|
* position. ExtraInfo is sent from the driver - pen
|
|
* win people use it.
|
|
*/
|
|
gdwMouseMoveExtraInfo = MouseEvent.ExtraInfo;
|
|
|
|
ptCursorLast = gpsi->ptCursor;
|
|
|
|
/*
|
|
* Wake up someone. xxxSetFMouseMoved() clears
|
|
* dwMouseMoveExtraInfo, so we must then restore it.
|
|
*/
|
|
#ifdef GENERIC_INPUT
|
|
#ifdef GI_SINK
|
|
if (IsMouseSinkPresent()) {
|
|
PostRawMouseInput(gpqForeground, MouseEvent.time, MouseEvent.hDevice, &MouseEvent.rawData);
|
|
}
|
|
#endif
|
|
if (gpqForeground == NULL || !TestRawInputMode(PtiMouseFromQ(gpqForeground), NoLegacyMouse)) {
|
|
zzzSetFMouseMoved();
|
|
}
|
|
#else
|
|
zzzSetFMouseMoved();
|
|
#endif
|
|
|
|
gdwMouseMoveExtraInfo = MouseEvent.ExtraInfo;
|
|
}
|
|
|
|
if (MouseEvent.ButtonFlags != 0) {
|
|
xxxDoButtonEvent(&MouseEvent);
|
|
}
|
|
|
|
LeaveCrit();
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* RawInputThread (RIT)
|
|
*
|
|
* This is the RIT. It gets low-level/raw input from the device drivers
|
|
* and posts messages the appropriate queue. It gets the input via APC
|
|
* calls requested by calling NtReadFile() for the keyboard and mouse
|
|
* drivers. Basically it makes the first calls to Start*Read() and then
|
|
* sits in an NtWaitForSingleObject() loop which allows the APC calls to
|
|
* occur.
|
|
*
|
|
* All functions called exclusively on the RIT will have (RIT) next to
|
|
* the name in the header.
|
|
*
|
|
* History:
|
|
* 10-18-90 DavidPe Created.
|
|
* 11-26-90 DavidPe Rewrote to stop using POS layer.
|
|
\***************************************************************************/
|
|
#if DBG
|
|
DWORD gBlockDelay = 0;
|
|
DWORD gBlockSleep = 0;
|
|
#endif
|
|
|
|
VOID RawInputThread(
|
|
PRIT_INIT pInitData)
|
|
{
|
|
KPRIORITY Priority;
|
|
NTSTATUS Status;
|
|
UNICODE_STRING strRIT;
|
|
UINT NumberOfHandles = ID_NUMBER_HYDRA_REMOTE_HANDLES;
|
|
PTERMINAL pTerm;
|
|
PMONITOR pMonitorPrimary;
|
|
HANDLE hevtShutDown;
|
|
PKEVENT pEvents[2];
|
|
USHORT cEvents = 1;
|
|
static DWORD nLastRetryReadInput = 0;
|
|
|
|
/*
|
|
* Session 0 Console session does not need the shutdown event
|
|
*/
|
|
|
|
|
|
pTerm = pInitData->pTerm;
|
|
|
|
/*
|
|
* Initialize GDI accelerators. Identify this thread as a server thread.
|
|
*/
|
|
apObjects = UserAllocPoolNonPaged(NumberOfHandles * sizeof(PVOID), TAG_SYSTEM);
|
|
|
|
gWaitBlockArray = UserAllocPoolNonPagedNS(NumberOfHandles * sizeof(KWAIT_BLOCK),
|
|
TAG_SYSTEM);
|
|
|
|
if (apObjects == NULL || gWaitBlockArray == NULL) {
|
|
RIPMSG0(RIP_WARNING, "RIT failed to allocate memory");
|
|
goto Exit;
|
|
}
|
|
|
|
RtlZeroMemory(apObjects, NumberOfHandles * sizeof(PVOID));
|
|
|
|
/*
|
|
* Set the priority of the RIT to maximum allowed.
|
|
* LOW_REALTIME_PRIORITY - 1 is chosen so that the RIT
|
|
* does not block the Mm Working set trimmer thread
|
|
* in the memory starvation situation.
|
|
*/
|
|
#ifdef W2K_COMPAT_PRIORITY
|
|
Priority = LOW_REALTIME_PRIORITY + 3;
|
|
#else
|
|
Priority = LOW_REALTIME_PRIORITY - 1;
|
|
#endif
|
|
|
|
ZwSetInformationThread(NtCurrentThread(),
|
|
ThreadPriority,
|
|
&Priority,
|
|
sizeof(KPRIORITY));
|
|
|
|
RtlInitUnicodeString(&strRIT, L"WinSta0_RIT");
|
|
|
|
/*
|
|
* Create an event for signalling mouse/kbd attach/detach and device-change
|
|
* notifications such as QueryRemove, RemoveCancelled etc.
|
|
*/
|
|
aDeviceTemplate[DEVICE_TYPE_KEYBOARD].pkeHidChange =
|
|
apObjects[ID_HIDCHANGE] =
|
|
CreateKernelEvent(SynchronizationEvent, FALSE);
|
|
aDeviceTemplate[DEVICE_TYPE_MOUSE].pkeHidChange =
|
|
CreateKernelEvent(SynchronizationEvent, FALSE);
|
|
|
|
#ifdef GENERIC_INPUT
|
|
gpkeHidChange =
|
|
apObjects[ID_TRUEHIDCHANGE] =
|
|
aDeviceTemplate[DEVICE_TYPE_HID].pkeHidChange = CreateKernelEvent(SynchronizationEvent, FALSE);
|
|
#endif
|
|
|
|
/*
|
|
* Create an event for desktop threads to pass mouse input to RIT
|
|
*/
|
|
apObjects[ID_MOUSE] = CreateKernelEvent(SynchronizationEvent, FALSE);
|
|
gpkeMouseData = apObjects[ID_MOUSE];
|
|
|
|
if (aDeviceTemplate[DEVICE_TYPE_MOUSE].pkeHidChange == NULL ||
|
|
apObjects[ID_HIDCHANGE] == NULL ||
|
|
gpkeMouseData == NULL
|
|
#ifdef GENERIC_INPUT
|
|
|| gpkeHidChange == NULL
|
|
#endif
|
|
) {
|
|
RIPMSG0(RIP_WARNING, "RIT failed to create a required input event");
|
|
goto Exit;
|
|
}
|
|
|
|
/*
|
|
* Initialize keyboard device driver.
|
|
*/
|
|
EnterCrit();
|
|
InitKeyboard();
|
|
InitMice();
|
|
LeaveCrit();
|
|
|
|
Status = InitSystemThread(&strRIT);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG0(RIP_WARNING, "RIT failed InitSystemThread");
|
|
goto Exit;
|
|
}
|
|
|
|
UserAssert(gpepCSRSS != NULL);
|
|
|
|
/*
|
|
* Allow the system to read the screen
|
|
*/
|
|
((PW32PROCESS)PsGetProcessWin32Process(gpepCSRSS))->W32PF_Flags |= (W32PF_READSCREENACCESSGRANTED|W32PF_IOWINSTA);
|
|
|
|
/*
|
|
* Initialize the cursor clipping rectangle to the screen rectangle.
|
|
*/
|
|
UserAssert(gpDispInfo != NULL);
|
|
grcCursorClip = gpDispInfo->rcScreen;
|
|
|
|
/*
|
|
* Initialize gpsi->ptCursor and gptCursorAsync
|
|
*/
|
|
pMonitorPrimary = GetPrimaryMonitor();
|
|
|
|
UserAssert(gpsi != NULL);
|
|
|
|
gpsi->ptCursor.x = pMonitorPrimary->rcMonitor.right / 2;
|
|
gpsi->ptCursor.y = pMonitorPrimary->rcMonitor.bottom / 2;
|
|
gptCursorAsync = gpsi->ptCursor;
|
|
|
|
/*
|
|
* The hung redraw list should already be set to NULL by the compiler,
|
|
* linker & loader since it is an uninitialized global variable. Memory will
|
|
* be allocated the first time a pwnd is added to this list (hungapp.c)
|
|
*/
|
|
UserAssert(gpvwplHungRedraw == NULL);
|
|
|
|
/*
|
|
* Initialize the pre-defined hotkeys
|
|
*/
|
|
EnterCrit();
|
|
_RegisterHotKey(PWND_INPUTOWNER, IDHOT_WINDOWS, MOD_WIN, VK_NONE);
|
|
SetDebugHotKeys();
|
|
LeaveCrit();
|
|
|
|
/*
|
|
* Create a timer for timers.
|
|
*/
|
|
gptmrMaster = UserAllocPoolNonPagedNS(sizeof(KTIMER),
|
|
TAG_SYSTEM);
|
|
if (gptmrMaster == NULL) {
|
|
RIPMSG0(RIP_WARNING, "RIT failed to create gptmrMaster");
|
|
goto Exit;
|
|
}
|
|
|
|
KeInitializeTimer(gptmrMaster);
|
|
apObjects[ID_TIMER] = gptmrMaster;
|
|
|
|
/*
|
|
* Create an event for mouse device reads to signal the desktop thread to
|
|
* move the pointer and QueueMouseEvent().
|
|
* We should do this *before* we have any devices.
|
|
*/
|
|
UserAssert(gpDeviceInfoList == NULL);
|
|
|
|
|
|
if (!gbRemoteSession) {
|
|
gptmrWD = UserAllocPoolNonPagedNS(sizeof(KTIMER), TAG_SYSTEM);
|
|
|
|
if (gptmrWD == NULL) {
|
|
Status = STATUS_NO_MEMORY;
|
|
RIPMSG0(RIP_WARNING, "RemoteConnect failed to create gptmrWD");
|
|
goto Exit;
|
|
}
|
|
KeInitializeTimerEx(gptmrWD, SynchronizationTimer);
|
|
}
|
|
|
|
|
|
/*
|
|
* At this point, the WD timer must already have been initialized by RemoteConnect
|
|
*/
|
|
|
|
|
|
|
|
UserAssert(gptmrWD != NULL);
|
|
apObjects[ID_WDTIMER] = gptmrWD;
|
|
|
|
if (IsRemoteConnection() ) {
|
|
BOOL fSuccess=TRUE;
|
|
fSuccess &= !!HDXDrvEscape(gpDispInfo->hDev,
|
|
ESC_SET_WD_TIMEROBJ,
|
|
(PVOID)gptmrWD,
|
|
sizeof(gptmrWD));
|
|
|
|
if (!fSuccess) {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
RIPMSG0(RIP_WARNING, "RemoteConnect failed to pass gptmrWD to display driver");
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (IsRemoteConnection()) {
|
|
|
|
UNICODE_STRING ustrName;
|
|
BOOL fSuccess = TRUE;
|
|
|
|
|
|
|
|
RtlInitUnicodeString(&ustrName, NULL);
|
|
|
|
/*
|
|
* Pass a pointer to the timer to the WD via the display driver
|
|
*/
|
|
EnterCrit();
|
|
|
|
fSuccess &= !!CreateDeviceInfo(DEVICE_TYPE_MOUSE, &ustrName, 0);
|
|
fSuccess &= !!CreateDeviceInfo(DEVICE_TYPE_KEYBOARD, &ustrName, 0);
|
|
|
|
LeaveCrit();
|
|
|
|
if (!fSuccess) {
|
|
RIPMSG0(RIP_WARNING,
|
|
"RIT failed HDXDrvEscape or the creation of input devices");
|
|
goto Exit;
|
|
}
|
|
} else {
|
|
EnterCrit();
|
|
|
|
/*
|
|
* Register for Plug and Play devices.
|
|
* If any PnP devices are already attached, these will be opened and
|
|
* we will start reading them at this time.
|
|
*/
|
|
xxxRegisterForDeviceClassNotifications();
|
|
|
|
LeaveCrit();
|
|
}
|
|
|
|
if (gbRemoteSession) {
|
|
WCHAR szName[MAX_SESSION_PATH];
|
|
UNICODE_STRING ustrName;
|
|
OBJECT_ATTRIBUTES obja;
|
|
/*
|
|
* Create the shutdown event. This event will be signaled
|
|
* from W32WinStationTerminate.
|
|
* This is a named event opend by CSR to signal that win32k should
|
|
* go away. It's used in ntuser\server\api.c
|
|
*/
|
|
swprintf(szName, L"\\Sessions\\%ld\\BaseNamedObjects\\EventShutDownCSRSS",
|
|
gSessionId);
|
|
RtlInitUnicodeString(&ustrName, szName);
|
|
|
|
InitializeObjectAttributes(&obja,
|
|
&ustrName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = ZwCreateEvent(&hevtShutDown,
|
|
EVENT_ALL_ACCESS,
|
|
&obja,
|
|
SynchronizationEvent,
|
|
FALSE);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG0(RIP_WARNING, "RIT failed to create EventShutDownCSRSS");
|
|
goto Exit;
|
|
}
|
|
|
|
ObReferenceObjectByHandle(hevtShutDown,
|
|
EVENT_ALL_ACCESS,
|
|
*ExEventObjectType,
|
|
KernelMode,
|
|
&apObjects[ID_SHUTDOWN],
|
|
NULL);
|
|
pEvents[1] = apObjects[ID_SHUTDOWN];
|
|
cEvents++;
|
|
} else {
|
|
|
|
hevtShutDown = NULL;
|
|
|
|
Status = PoRequestShutdownEvent(&apObjects[ID_SHUTDOWN]);
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG0(RIP_WARNING, "RIT failed to get shutdown event");
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Get the rit-thread.
|
|
*/
|
|
gptiRit = PtiCurrentShared();
|
|
|
|
HYDRA_HINT(HH_RITCREATED);
|
|
|
|
/*
|
|
* Don't allow this thread to get involved with journal synchronization.
|
|
*/
|
|
gptiRit->TIF_flags |= TIF_DONTJOURNALATTACH;
|
|
|
|
/*
|
|
* Also wait on our input event so the cool switch window can
|
|
* receive messages.
|
|
*/
|
|
apObjects[ID_INPUT] = gptiRit->pEventQueueServer;
|
|
|
|
/*
|
|
* Signal that the rit has been initialized
|
|
*/
|
|
KeSetEvent(pInitData->pRitReadyEvent, EVENT_INCREMENT, FALSE);
|
|
|
|
pEvents[0] = pTerm->pEventInputReady;
|
|
|
|
/*
|
|
* Wait until the first desktop is created.
|
|
*/
|
|
ObReferenceObjectByPointer(pEvents[0],
|
|
EVENT_ALL_ACCESS,
|
|
*ExEventObjectType,
|
|
KernelMode);
|
|
|
|
Status = KeWaitForMultipleObjects(cEvents,
|
|
pEvents,
|
|
WaitAny,
|
|
WrUserRequest,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL,
|
|
NULL);
|
|
ObDereferenceObject(pEvents[0]);
|
|
|
|
if (Status == WAIT_OBJECT_0 + 1) {
|
|
KeSetEvent(pEvents[1], EVENT_INCREMENT, FALSE);
|
|
InitiateWin32kCleanup();
|
|
|
|
ObDereferenceObject(pEvents[1]);
|
|
if (hevtShutDown) {
|
|
ZwClose(hevtShutDown);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Switch to the first desktop if no switch has been
|
|
* performed.
|
|
*/
|
|
EnterCrit();
|
|
|
|
if (gptiRit->rpdesk == NULL) {
|
|
UserVerify(xxxSwitchDesktop(gptiRit->pwinsta, gptiRit->pwinsta->rpdeskList, 0));
|
|
}
|
|
|
|
/*
|
|
* The io desktop thread is supposed to be created at this point.
|
|
* The xxxSwitchDesktop call is expected to set the io desktop thread to run in grpdeskritinput
|
|
*/
|
|
if ((pTerm->ptiDesktop == NULL) || (pTerm->ptiDesktop->rpdesk != grpdeskRitInput)) {
|
|
FRE_RIPMSG0(RIP_ERROR, "RawInputThread: Desktop thread not running on grpdeskRitInput");
|
|
}
|
|
|
|
/*
|
|
* Create a timer for hung app detection/redrawing.
|
|
*/
|
|
StartTimers();
|
|
|
|
LeaveCrit();
|
|
|
|
/*
|
|
* Go into a wait loop so we can process input events and APCs as
|
|
* they occur.
|
|
*/
|
|
while (TRUE) {
|
|
|
|
CheckCritOut();
|
|
|
|
Status = KeWaitForMultipleObjects(NumberOfHandles,
|
|
apObjects,
|
|
WaitAny,
|
|
WrUserRequest,
|
|
KernelMode,
|
|
TRUE,
|
|
NULL,
|
|
gWaitBlockArray);
|
|
|
|
UserAssert(NT_SUCCESS(Status));
|
|
|
|
if (gdwUpdateKeyboard != 0) {
|
|
/*
|
|
* Here's our opportunity to process pending IOCTLs for the kbds:
|
|
* These are asynchronous IOCTLS, so be sure any buffers passed
|
|
* in to ZwDeviceIoControlFile are not in the stack!
|
|
* Using gdwUpdateKeyboard to tell the RIT to issue these IOCTLS
|
|
* renders the action asynchronous (delayed until next apObjects
|
|
* event), but the IOCTL was asynch anyway
|
|
*/
|
|
PDEVICEINFO pDeviceInfo;
|
|
EnterDeviceInfoListCrit();
|
|
for (pDeviceInfo = gpDeviceInfoList; pDeviceInfo; pDeviceInfo = pDeviceInfo->pNext) {
|
|
if ((pDeviceInfo->type == DEVICE_TYPE_KEYBOARD) && (pDeviceInfo->handle)) {
|
|
if (gdwUpdateKeyboard & UPDATE_KBD_TYPEMATIC) {
|
|
ZwDeviceIoControlFile(pDeviceInfo->handle, NULL, NULL, NULL,
|
|
&giosbKbdControl, IOCTL_KEYBOARD_SET_TYPEMATIC,
|
|
(PVOID)&gktp, sizeof(gktp), NULL, 0);
|
|
}
|
|
if (gdwUpdateKeyboard & UPDATE_KBD_LEDS) {
|
|
ZwDeviceIoControlFile(pDeviceInfo->handle, NULL, NULL, NULL,
|
|
&giosbKbdControl, IOCTL_KEYBOARD_SET_INDICATORS,
|
|
(PVOID)&gklp, sizeof(gklp), NULL, 0);
|
|
}
|
|
}
|
|
}
|
|
LeaveDeviceInfoListCrit();
|
|
if ((gdwUpdateKeyboard & UPDATE_KBD_LEDS) && gfRemotingConsole) {
|
|
ZwDeviceIoControlFile(ghConsoleShadowKeyboardChannel, NULL, NULL, NULL,
|
|
&giosbKbdControl, IOCTL_KEYBOARD_SET_INDICATORS,
|
|
(PVOID)&gklp, sizeof(gklp), NULL, 0);
|
|
}
|
|
gdwUpdateKeyboard &= ~(UPDATE_KBD_TYPEMATIC | UPDATE_KBD_LEDS);
|
|
}
|
|
|
|
if (Status == ID_MOUSE) {
|
|
/*
|
|
* A desktop thread got some Mouse input for us. Process it.
|
|
*/
|
|
ProcessQueuedMouseEvents();
|
|
|
|
} else if (Status == ID_HIDCHANGE) {
|
|
TAGMSG0(DBGTAG_PNP | RIP_THERESMORE, "RIT wakes for HID Change");
|
|
EnterCrit();
|
|
ProcessDeviceChanges(DEVICE_TYPE_KEYBOARD);
|
|
LeaveCrit();
|
|
}
|
|
#ifdef GENERIC_INPUT
|
|
else if (Status == ID_TRUEHIDCHANGE) {
|
|
TAGMSG0(DBGTAG_PNP | RIP_THERESMORE, "RIT wakes for True HID Change");
|
|
EnterCrit();
|
|
ProcessDeviceChanges(DEVICE_TYPE_HID);
|
|
LeaveCrit();
|
|
}
|
|
#endif
|
|
else if (Status == ID_SHUTDOWN) {
|
|
|
|
InitiateWin32kCleanup();
|
|
|
|
if(gbRemoteSession) {
|
|
ObDereferenceObject(apObjects[ID_SHUTDOWN]);
|
|
}
|
|
if (hevtShutDown) {
|
|
ZwClose(hevtShutDown);
|
|
}
|
|
|
|
break;
|
|
|
|
} else if (Status == ID_WDTIMER) {
|
|
//LARGE_INTEGER liTemp;
|
|
|
|
EnterCrit();
|
|
|
|
|
|
/*
|
|
* Call the TShare display driver to flush the frame buffer
|
|
*/
|
|
|
|
if (IsRemoteConnection()) {
|
|
if (!HDXDrvEscape(gpDispInfo->hDev, ESC_TIMEROBJ_SIGNALED, NULL, 0)) {
|
|
UserAssert(FALSE);
|
|
}
|
|
} else {
|
|
if (gfRemotingConsole && gConsoleShadowhDev != NULL) {
|
|
ASSERT(gConsoleShadowhDev != NULL);
|
|
if (!HDXDrvEscape(gConsoleShadowhDev, ESC_TIMEROBJ_SIGNALED, NULL, 0)) {
|
|
UserAssert(FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
LeaveCrit();
|
|
|
|
} else {
|
|
/*
|
|
* If the master timer has expired, then process the timer
|
|
* list. Otherwise, an APC caused the raw input thread to be
|
|
* awakened.
|
|
*/
|
|
if (Status == ID_TIMER) {
|
|
TimersProc();
|
|
/*
|
|
* If an input degvice read failed due to insufficient resources,
|
|
* we retry by signalling the proper thread: ProcessDeviceChanges
|
|
* will call RetryReadInput().
|
|
*/
|
|
if (gnRetryReadInput != nLastRetryReadInput) {
|
|
nLastRetryReadInput = gnRetryReadInput;
|
|
KeSetEvent(aDeviceTemplate[DEVICE_TYPE_MOUSE].pkeHidChange, EVENT_INCREMENT, FALSE);
|
|
KeSetEvent(aDeviceTemplate[DEVICE_TYPE_KEYBOARD].pkeHidChange, EVENT_INCREMENT, FALSE);
|
|
}
|
|
}
|
|
|
|
#if DBG
|
|
/*
|
|
* In the debugger set gBlockSleep to n:
|
|
* The RIT will sleep n millicseconds, then n timer ticks later
|
|
* will sleep n milliseconds again.
|
|
*/
|
|
if (gBlockDelay) {
|
|
gBlockDelay--;
|
|
} else if ((gBlockDelay == 0) && (gBlockSleep != 0)) {
|
|
UserSleep(gBlockSleep);
|
|
gBlockDelay = 100 * gBlockSleep;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* if in cool task switcher window, dispose of the messages
|
|
* on the queue
|
|
*/
|
|
if (gspwndAltTab != NULL) {
|
|
EnterCrit();
|
|
xxxReceiveMessages(gptiRit);
|
|
LeaveCrit();
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
|
|
Exit:
|
|
|
|
UserAssert(gptiRit == NULL);
|
|
|
|
/*
|
|
* Signal that the rit has been initialized
|
|
*/
|
|
KeSetEvent(pInitData->pRitReadyEvent, EVENT_INCREMENT, FALSE);
|
|
|
|
RIPMSG0(RIP_WARNING, "RIT initialization failure");
|
|
}
|