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