Leaked source code of windows server 2003
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

/****************************** 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");
}