You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1561 lines
66 KiB
1561 lines
66 KiB
/****************************************************************************/
|
|
/* aimapi.cpp */
|
|
/* */
|
|
/* RDP Input Manager API functions */
|
|
/* */
|
|
/* Copyright(c) Microsoft, PictureTel 1993-1997 */
|
|
/* Copyright (c) 1997-1999 Microsoft Corporation */
|
|
/****************************************************************************/
|
|
|
|
#include <precomp.h>
|
|
#pragma hdrstop
|
|
|
|
#define TRC_FILE "aimapi"
|
|
#include <adcg.h>
|
|
|
|
#include <as_conf.hpp>
|
|
|
|
#include <nwdwint.h>
|
|
|
|
|
|
/****************************************************************************/
|
|
/* API FUNCTION: IM_Init */
|
|
/* */
|
|
/* Called to initialize the IM */
|
|
/****************************************************************************/
|
|
void RDPCALL SHCLASS IM_Init(void)
|
|
{
|
|
TS_INPUT_CAPABILITYSET Caps;
|
|
|
|
DC_BEGIN_FN("IM_Init");
|
|
|
|
#define DC_INIT_DATA
|
|
#include <aimdata.c>
|
|
#undef DC_INIT_DATA
|
|
|
|
// Set up the input capabilities.
|
|
Caps.capabilitySetType = TS_CAPSETTYPE_INPUT;
|
|
Caps.lengthCapability = sizeof(Caps);
|
|
Caps.inputFlags = TS_INPUT_FLAG_SCANCODES | TS_INPUT_FLAG_MOUSEX |
|
|
TS_INPUT_FLAG_FASTPATH_INPUT2 | TS_INPUT_FLAG_VKPACKET;
|
|
CPC_RegisterCapabilities((PTS_CAPABILITYHEADER)&Caps,
|
|
sizeof(TS_INPUT_CAPABILITYSET));
|
|
|
|
TRC_NRM((TB, "IM initialized"));
|
|
|
|
DC_END_FN();
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// IM_PlaybackEvents
|
|
//
|
|
// Called when an IM input PDU arrives. Unpacks and injects events.
|
|
/****************************************************************************/
|
|
|
|
// Maximum number of batched mouse/keyboard events. We batch because sending
|
|
// the events to the next higher driver (IcaChannelInput()) is more expensive
|
|
// than the loop overhead and mispredicted branches incurred to create the
|
|
// array of events. This constant is set to the same number found as
|
|
// MAXIMUM_ITEMS_READ in ntos\w32\ntuser\kernel\ntinput.c.
|
|
#define EventBatchLen 10
|
|
|
|
void __fastcall SHCLASS IM_PlaybackEvents(
|
|
PTS_INPUT_PDU pInputPDU,
|
|
unsigned DataLength)
|
|
{
|
|
PTS_INPUT_EVENT pInputEvent;
|
|
unsigned i, j, MsgLimit;
|
|
NTSTATUS Status;
|
|
|
|
DC_BEGIN_FN("IM_PlaybackEvents");
|
|
|
|
/************************************************************************/
|
|
/* We do not handle NULL packets. */
|
|
/************************************************************************/
|
|
TRC_ASSERT((NULL != pInputPDU), (TB,"NULL input PDU"));
|
|
|
|
/************************************************************************/
|
|
// Make sure we have at least enough bytes to read the header. Not having
|
|
// any inputs in the packet is also considered an error.
|
|
/************************************************************************/
|
|
if (DataLength >= sizeof(TS_INPUT_PDU)) {
|
|
/********************************************************************/
|
|
// Convert the TS_INPUT_PDU from wire format.
|
|
/********************************************************************/
|
|
TRC_NRM((TB, "Received packet of %u events", pInputPDU->numberEvents));
|
|
|
|
// Make sure we have the full packet length available.
|
|
if (DataLength >= (sizeof(TS_INPUT_PDU) +
|
|
(pInputPDU->numberEvents - 1) * sizeof(TS_INPUT_EVENT))) {
|
|
|
|
// For each packet in the piggybacked packets array...
|
|
for (i = 0; i < pInputPDU->numberEvents; i++) {
|
|
// Get a pointer to the packet within the array of events.
|
|
pInputEvent = &pInputPDU->eventList[i];
|
|
|
|
switch (pInputEvent->messageType) {
|
|
case TS_INPUT_EVENT_SCANCODE: //intentional fallthru
|
|
case TS_INPUT_EVENT_VKPACKET: //intentional fallthru
|
|
{
|
|
BYTE FastPathEmulate[4];
|
|
unsigned CurKbdData;
|
|
KEYBOARD_INPUT_DATA KbdData[EventBatchLen];
|
|
|
|
MsgLimit = min((pInputPDU->numberEvents - i),
|
|
EventBatchLen);
|
|
CurKbdData = 0;
|
|
for (j = 0; j < MsgLimit; j++) {
|
|
if (pInputPDU->eventList[i + j].messageType ==
|
|
TS_INPUT_EVENT_SCANCODE) {
|
|
// To coalesce code, we convert this kbd format
|
|
// to fast-path and call the fast-path
|
|
// event converter. Since fast-path is now
|
|
// the default, extra work falls to this
|
|
// path.
|
|
FastPathEmulate[0] = (BYTE)
|
|
((pInputPDU->eventList[i + j].u.key.
|
|
keyboardFlags &
|
|
(TS_KBDFLAGS_EXTENDED |
|
|
TS_KBDFLAGS_EXTENDED1)) >> 7);
|
|
if (pInputPDU->eventList[i + j].u.key.
|
|
keyboardFlags & TS_KBDFLAGS_RELEASE)
|
|
FastPathEmulate[0] |=
|
|
TS_INPUT_FASTPATH_KBD_RELEASE;
|
|
FastPathEmulate[1] = (BYTE)
|
|
pInputPDU->eventList[i + j].u.key.
|
|
keyCode;
|
|
|
|
// Convert the wire packet to a kernel mode
|
|
// keyboard event. We pack into an array of
|
|
// events because an IcaChannelInput is
|
|
// expensive.
|
|
if (IMConvertFastPathKeyboardToEvent(
|
|
FastPathEmulate,
|
|
&KbdData[CurKbdData])) {
|
|
TRC_NRM((TB, "Add kbd evt to batch index "
|
|
"%d: MakeCode(%u) flags(%#x)",
|
|
CurKbdData,
|
|
KbdData[CurKbdData].MakeCode,
|
|
KbdData[CurKbdData].Flags));
|
|
|
|
CurKbdData++;
|
|
}
|
|
}
|
|
else if (pInputPDU->eventList[i+j].messageType ==
|
|
TS_INPUT_EVENT_VKPACKET)
|
|
{
|
|
FastPathEmulate[0] = (BYTE)
|
|
((pInputPDU->eventList[i + j].u.key.
|
|
keyboardFlags &
|
|
(TS_KBDFLAGS_EXTENDED |
|
|
TS_KBDFLAGS_EXTENDED1)) >> 7);
|
|
FastPathEmulate[0] |=
|
|
TS_INPUT_FASTPATH_EVENT_VKPACKET;
|
|
if (pInputPDU->eventList[i + j].u.key.
|
|
keyboardFlags & TS_KBDFLAGS_RELEASE)
|
|
FastPathEmulate[0] |=
|
|
TS_INPUT_FASTPATH_KBD_RELEASE;
|
|
memcpy(&FastPathEmulate[1],
|
|
&pInputPDU->eventList[i + j].u.key.keyCode,
|
|
2);
|
|
|
|
// Convert the wire packet to a kernel mode
|
|
// keyboard event. We pack into an array of
|
|
// events because an IcaChannelInput is
|
|
// expensive.
|
|
if (IMConvertFastPathKeyboardToEvent(
|
|
FastPathEmulate,
|
|
&KbdData[CurKbdData])) {
|
|
TRC_NRM((TB, "Add kbd evt to batch index "
|
|
"%d: MakeCode(%u) flags(%#x)",
|
|
CurKbdData,
|
|
KbdData[CurKbdData].MakeCode,
|
|
KbdData[CurKbdData].Flags));
|
|
|
|
CurKbdData++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Advance past the used messages, taking into account
|
|
// the outer loop increment.
|
|
i += j - 1;
|
|
|
|
// Now do the input.
|
|
if (m_pTSWd->shadowState != SHADOW_CLIENT) {
|
|
Status = IcaChannelInput(m_pTSWd->pContext,
|
|
Channel_Keyboard, 0, NULL,
|
|
(PUCHAR) KbdData,
|
|
sizeof(KEYBOARD_INPUT_DATA) * CurKbdData);
|
|
TRC_DBG((TB,"Return from keyboard input injection %lu",
|
|
Status));
|
|
}
|
|
|
|
// Else we must be shadowing, so blow the shadow if we
|
|
// see the hotkey in this set of input.
|
|
else {
|
|
Status = IMCheckForShadowHotkey(KbdData,
|
|
CurKbdData);
|
|
}
|
|
}
|
|
break;
|
|
|
|
|
|
case TS_INPUT_EVENT_MOUSE:
|
|
case TS_INPUT_EVENT_MOUSEX:
|
|
{
|
|
unsigned CurMouseData;
|
|
MOUSE_INPUT_DATA MouseData[EventBatchLen];
|
|
|
|
MsgLimit = min((pInputPDU->numberEvents - i),
|
|
EventBatchLen);
|
|
CurMouseData = 0;
|
|
for (j = 0; j < MsgLimit; j++) {
|
|
if ((pInputPDU->eventList[i + j].messageType ==
|
|
TS_INPUT_EVENT_MOUSE) ||
|
|
(pInputPDU->eventList[i + j].messageType ==
|
|
TS_INPUT_EVENT_MOUSEX)) {
|
|
// Convert the wire packet to a kernel mode
|
|
// mouse event. We pack into an array of
|
|
// events because an IcaChannelInput is
|
|
// expensive.
|
|
if (IMConvertMousePacketToEvent(
|
|
&pInputPDU->eventList[i + j].u.mouse,
|
|
&MouseData[CurMouseData],
|
|
(pInputPDU->eventList[i + j].messageType ==
|
|
TS_INPUT_EVENT_MOUSEX)))
|
|
{
|
|
TRC_NRM((TB, "Add mouse evt to batch "
|
|
"index %u: x(%ld) y(%ld) flags(%#hx) buttonflags(%#hx)",
|
|
CurMouseData,
|
|
MouseData[CurMouseData].LastX,
|
|
MouseData[CurMouseData].LastY,
|
|
MouseData[CurMouseData].Flags,
|
|
MouseData[CurMouseData].ButtonFlags));
|
|
|
|
CurMouseData++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Advance past the used messages, taking into account
|
|
// the outer loop increment.
|
|
i += j - 1;
|
|
|
|
// Now do the input.
|
|
Status = IcaChannelInput(m_pTSWd->pContext,
|
|
Channel_Mouse, 0, NULL,
|
|
(unsigned char *)MouseData,
|
|
sizeof(MOUSE_INPUT_DATA) * CurMouseData);
|
|
TRC_DBG((TB,"Return from mouse input injection %lu",
|
|
Status));
|
|
}
|
|
break;
|
|
|
|
|
|
case TS_INPUT_EVENT_SYNC:
|
|
Status = IMDoSync(pInputEvent->u.sync.toggleFlags);
|
|
break;
|
|
|
|
|
|
default:
|
|
{
|
|
// Unknown event type - log an event and disconnect
|
|
// the offending Client.
|
|
TRC_ERR((TB, "Unrecognized imPacket (%d)",
|
|
pInputEvent->messageType));
|
|
WDW_LogAndDisconnect(m_pTSWd, TRUE,
|
|
Log_RDP_InvalidInputPDUType,
|
|
(PBYTE)&(pInputEvent->messageType),
|
|
sizeof(pInputEvent->messageType));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Go into TURBO scheduling on user input to flush screen deltas
|
|
// faster.
|
|
SCH_ContinueScheduling(SCH_MODE_TURBO);
|
|
}
|
|
else {
|
|
goto InsufficientData;
|
|
}
|
|
}
|
|
else {
|
|
goto InsufficientData;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return;
|
|
|
|
// Error handling.
|
|
InsufficientData:
|
|
TRC_ERR((TB,"Input PDU received, len=%u, but data is not long enough",
|
|
DataLength));
|
|
WDW_LogAndDisconnect(m_pTSWd, TRUE, Log_RDP_InputPDUBadLength,
|
|
(PBYTE)pInputPDU, DataLength);
|
|
|
|
DC_END_FN();
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// IMCheckForShadowHotkey
|
|
//
|
|
// Looks for the shadow hotkey among client keyboard input.
|
|
/****************************************************************************/
|
|
NTSTATUS RDPCALL SHCLASS IMCheckForShadowHotkey(
|
|
KEYBOARD_INPUT_DATA *pKbdData,
|
|
unsigned NumData)
|
|
{
|
|
unsigned i;
|
|
NTSTATUS Status;
|
|
ICA_CHANNEL_COMMAND Data;
|
|
BOOLEAN bHotKeyDetected = FALSE;
|
|
|
|
DC_BEGIN_FN("IMCheckForShadowHotkey");
|
|
|
|
// Blow the shadow if we see the hotkey in this set of input.
|
|
for (i = 0; i < NumData; i++) {
|
|
bHotKeyDetected |= KeyboardHotKeyProcedure(
|
|
m_pTSWd->HotkeyVk,
|
|
m_pTSWd->HotkeyModifiers,
|
|
&pKbdData[i],
|
|
m_pTSWd->gpScancodeMap,
|
|
m_pTSWd->pKbdTbl,
|
|
m_pTSWd->KeyboardType101,
|
|
m_pTSWd->pgafPhysKeyState);
|
|
}
|
|
|
|
if (!bHotKeyDetected) {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
m_pTSWd->HotkeyVk = 0; // cut off all piped data
|
|
Data.Header.Command = ICA_COMMAND_SHADOW_HOTKEY;
|
|
Status = IcaChannelInput(m_pTSWd->pContext, Channel_Command, 0, NULL,
|
|
(PUCHAR)&Data, sizeof(Data));
|
|
TRC_ALT((TB,"Injected shadow HOTKEY command! status=%08X", Status));
|
|
}
|
|
|
|
DC_END_FN();
|
|
return Status;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// IM_DecodeFastPathInput
|
|
//
|
|
// On a primary stack, decodes optimized input bytestream and injects into
|
|
// the input stream. NumEvents is passed from MCS, decoded from the header
|
|
// -- if zero, the first byte of the data to decode contains the number of
|
|
// events.
|
|
/****************************************************************************/
|
|
void RDPCALL SHCLASS IM_DecodeFastPathInput(
|
|
BYTE *pData,
|
|
unsigned DataLength,
|
|
unsigned NumEvents)
|
|
{
|
|
unsigned i, j, MsgLimit;
|
|
NTSTATUS Status;
|
|
BYTE *pCurDecode = pData;
|
|
|
|
DC_BEGIN_FN("IM_DecodeFastPathInput");
|
|
|
|
// Make sure we've been given enough data.
|
|
if (NumEvents == 0) {
|
|
if (DataLength >= 1) {
|
|
NumEvents = *pData;
|
|
pData++;
|
|
DataLength--;
|
|
}
|
|
else {
|
|
TRC_ERR((TB,"Len %u too short for DataLength", DataLength));
|
|
goto ShortData;
|
|
}
|
|
}
|
|
|
|
// For each event...
|
|
for (i = 0; i < NumEvents; i++) {
|
|
if (DataLength >= 1) {
|
|
switch (*pData & TS_INPUT_FASTPATH_EVENT_MASK) {
|
|
case TS_INPUT_FASTPATH_EVENT_KEYBOARD:
|
|
{
|
|
unsigned CurKbdData;
|
|
KEYBOARD_INPUT_DATA KbdData[EventBatchLen];
|
|
|
|
MsgLimit = min((NumEvents - i), EventBatchLen);
|
|
|
|
CurKbdData = 0;
|
|
for (j = 0; j < MsgLimit; j++) {
|
|
if (DataLength >= 1) {
|
|
if ((*pData & TS_INPUT_FASTPATH_EVENT_MASK) ==
|
|
TS_INPUT_FASTPATH_EVENT_KEYBOARD) {
|
|
if (DataLength >= 2) {
|
|
if (IMConvertFastPathKeyboardToEvent(
|
|
pData, &KbdData[CurKbdData]))
|
|
CurKbdData++;
|
|
|
|
pData += 2;
|
|
DataLength -= 2;
|
|
}
|
|
else {
|
|
TRC_ERR((TB,"Ran out of space reading "
|
|
"keyboard events"));
|
|
goto ShortData;
|
|
}
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
TRC_ERR((TB,"Ran out of space reading keyboard "
|
|
"events"));
|
|
goto ShortData;
|
|
}
|
|
}
|
|
|
|
// Advance past the used messages, taking into account
|
|
// the outer loop increment.
|
|
i += j - 1;
|
|
|
|
// Now do the input.
|
|
if (m_pTSWd->shadowState != SHADOW_CLIENT) {
|
|
Status = IcaChannelInput(m_pTSWd->pContext,
|
|
Channel_Keyboard, 0, NULL,
|
|
(PUCHAR)KbdData,
|
|
sizeof(KEYBOARD_INPUT_DATA) * CurKbdData);
|
|
TRC_DBG((TB,"Return from keyboard input injection %lu",
|
|
Status));
|
|
}
|
|
|
|
// Else we must be shadowing, so blow the shadow if we
|
|
// see the hotkey in this set of input.
|
|
else {
|
|
Status = IMCheckForShadowHotkey(KbdData,
|
|
CurKbdData);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case TS_INPUT_FASTPATH_EVENT_VKPACKET:
|
|
{
|
|
unsigned CurKbdData;
|
|
KEYBOARD_INPUT_DATA KbdData[EventBatchLen];
|
|
|
|
MsgLimit = min((NumEvents - i), EventBatchLen);
|
|
|
|
CurKbdData = 0;
|
|
for (j = 0; j < MsgLimit; j++) {
|
|
if (DataLength >= 1) {
|
|
if ((*pData & TS_INPUT_FASTPATH_EVENT_MASK) ==
|
|
TS_INPUT_FASTPATH_EVENT_VKPACKET) {
|
|
if (DataLength >= 3) {
|
|
if (IMConvertFastPathKeyboardToEvent(
|
|
pData, &KbdData[CurKbdData]))
|
|
CurKbdData++;
|
|
|
|
pData += 3;
|
|
DataLength -= 3;
|
|
}
|
|
else {
|
|
TRC_ERR((TB,"Ran out of space reading "
|
|
"keyboard events"));
|
|
goto ShortData;
|
|
}
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
TRC_ERR((TB,"Ran out of space reading keyboard "
|
|
"events"));
|
|
goto ShortData;
|
|
}
|
|
}
|
|
|
|
// Advance past the used messages, taking into account
|
|
// the outer loop increment.
|
|
i += j - 1;
|
|
|
|
// Now do the input.
|
|
if (m_pTSWd->shadowState != SHADOW_CLIENT) {
|
|
Status = IcaChannelInput(m_pTSWd->pContext,
|
|
Channel_Keyboard, 0, NULL,
|
|
(PUCHAR)KbdData,
|
|
sizeof(KEYBOARD_INPUT_DATA) * CurKbdData);
|
|
TRC_DBG((TB,"Return from keyboard input injection %lu",
|
|
Status));
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
case TS_INPUT_FASTPATH_EVENT_MOUSE:
|
|
case TS_INPUT_FASTPATH_EVENT_MOUSEX:
|
|
{
|
|
unsigned CurMouseData;
|
|
MOUSE_INPUT_DATA MouseData[EventBatchLen];
|
|
|
|
// After the 1-byte header the following 6 bytes are
|
|
// the same format as a regular mouse input.
|
|
MsgLimit = min((NumEvents - i), EventBatchLen);
|
|
CurMouseData = 0;
|
|
for (j = 0; j < MsgLimit; j++) {
|
|
if (DataLength >= 1) {
|
|
if ((((*pData & TS_INPUT_FASTPATH_EVENT_MASK) ==
|
|
TS_INPUT_FASTPATH_EVENT_MOUSE) ||
|
|
(*pData & TS_INPUT_FASTPATH_EVENT_MASK) ==
|
|
TS_INPUT_FASTPATH_EVENT_MOUSEX)) {
|
|
if (DataLength >= 7) {
|
|
// Convert the wire packet to a kernel
|
|
// mode mouse event. We pack into an
|
|
// array of events because an
|
|
// IcaChannelInput is expensive.
|
|
if (IMConvertMousePacketToEvent(
|
|
(TS_POINTER_EVENT UNALIGNED *)
|
|
(pData + 1),
|
|
&MouseData[CurMouseData],
|
|
((*pData &
|
|
TS_INPUT_FASTPATH_EVENT_MASK) ==
|
|
TS_INPUT_FASTPATH_EVENT_MOUSEX)))
|
|
CurMouseData++;
|
|
|
|
pData += 7;
|
|
DataLength -= 7;
|
|
}
|
|
else {
|
|
TRC_ERR((TB,"Out of data decoding "
|
|
"mouse, i=%u, j=%u, NumEvents=%u, "
|
|
"DataLen=%u",
|
|
i, j, NumEvents, DataLength));
|
|
goto ShortData;
|
|
}
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
TRC_ERR((TB,"Out of data decoding "
|
|
"mouse, i=%u, j=%u, NumEvents=%u, "
|
|
"DataLen=%u",
|
|
i, j, NumEvents, DataLength));
|
|
goto ShortData;
|
|
}
|
|
}
|
|
|
|
// Advance past the used messages, taking into account
|
|
// the outer loop increment.
|
|
i += j - 1;
|
|
|
|
// Now do the input.
|
|
Status = IcaChannelInput(m_pTSWd->pContext,
|
|
Channel_Mouse, 0, NULL,
|
|
(unsigned char *)MouseData,
|
|
sizeof(MOUSE_INPUT_DATA) * CurMouseData);
|
|
TRC_DBG((TB,"Return from mouse input injection %lu",
|
|
Status));
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
case TS_INPUT_FASTPATH_EVENT_SYNC:
|
|
Status = IMDoSync(*pData & TS_INPUT_FASTPATH_FLAGS_MASK);
|
|
pData++;
|
|
DataLength--;
|
|
break;
|
|
|
|
|
|
default:
|
|
// Unknown event type - log an event and disconnect
|
|
// the offending Client.
|
|
TRC_ERR((TB, "Unrecognized imPacket (%d)",
|
|
*pData & TS_INPUT_FASTPATH_EVENT_MASK));
|
|
WDW_LogAndDisconnect(m_pTSWd, TRUE,
|
|
Log_RDP_InvalidInputPDUType, pData, 1);
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
else {
|
|
TRC_ERR((TB,"Out of data reading input events"));
|
|
goto ShortData;
|
|
}
|
|
} // end event loop
|
|
|
|
// Go into TURBO scheduling on user input to flush screen deltas
|
|
// faster.
|
|
SCH_ContinueScheduling(SCH_MODE_TURBO);
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return;
|
|
|
|
ShortData:
|
|
WDW_LogAndDisconnect(m_pTSWd, TRUE, Log_RDP_InputPDUBadLength,
|
|
(PBYTE)pData, DataLength);
|
|
DC_END_FN();
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// IM_ConvertFastPathToShadow
|
|
//
|
|
// Inverse of the client IHTranslateInputToFastPath() function -- takes
|
|
// a fast-path input stream and converts to the regular encoding. Used
|
|
// by a passthru stack to send the resulting regular encoding over the
|
|
// cross-server pipe via IcaRawInput().
|
|
/****************************************************************************/
|
|
#define MaxDefaultEvents 16
|
|
|
|
void RDPCALL SHCLASS IM_ConvertFastPathToShadow(
|
|
BYTE *pData,
|
|
unsigned DataLength,
|
|
unsigned NumEvents)
|
|
{
|
|
unsigned i, j, EventsThisPDU, PDUSize;
|
|
NTSTATUS Status;
|
|
PTS_INPUT_PDU pInput;
|
|
BYTE DefaultBuf[sizeof(TS_INPUT_PDU) + sizeof(TS_INPUT_EVENT) *
|
|
(MaxDefaultEvents - 1)];
|
|
|
|
DC_BEGIN_FN("IM_ConvertFastPathToShadow");
|
|
|
|
// Make sure we've been given enough data.
|
|
if (NumEvents == 0) {
|
|
if (DataLength >= 1) {
|
|
NumEvents = *pData;
|
|
pData++;
|
|
DataLength--;
|
|
}
|
|
else {
|
|
TRC_ERR((TB,"Len %u too short for DataLength", DataLength));
|
|
goto ShortData;
|
|
}
|
|
}
|
|
|
|
// We don't alloc memory, just send multiple input PDUs if we need to.
|
|
if (NumEvents > 0) {
|
|
pInput = (PTS_INPUT_PDU)DefaultBuf;
|
|
// set the input pdu array to 0.
|
|
memset(pInput, 0, sizeof(TS_INPUT_PDU) + sizeof(TS_INPUT_EVENT) *
|
|
(MaxDefaultEvents - 1));
|
|
|
|
}
|
|
else {
|
|
DC_QUIT;
|
|
}
|
|
|
|
// Set up the input PDU header info that won't be changing.
|
|
// Shadow handling does not care about the following, so we don't go to
|
|
// thr trouble of making up or grabbing values:
|
|
// shareDataHeader.shareControlHeader.pduSource
|
|
pInput->shareDataHeader.shareControlHeader.pduType = TS_PROTOCOL_VERSION |
|
|
TS_PDUTYPE_DATAPDU;
|
|
pInput->shareDataHeader.shareID = scShareID;
|
|
pInput->shareDataHeader.streamID = TS_STREAM_LOW;
|
|
pInput->shareDataHeader.pduType2 = TS_PDUTYPE2_INPUT;
|
|
|
|
// Loop while we need to send more PDUs.
|
|
for (j = 0; j < NumEvents;) {
|
|
// Reset the input PDU info.
|
|
EventsThisPDU = min(NumEvents - j, MaxDefaultEvents);
|
|
|
|
pInput->numberEvents = (TSUINT16)EventsThisPDU;
|
|
PDUSize = sizeof(TS_INPUT_PDU) + sizeof(TS_INPUT_EVENT) *
|
|
(EventsThisPDU - 1);
|
|
pInput->shareDataHeader.shareControlHeader.totalLength =
|
|
(TSUINT16)PDUSize;
|
|
pInput->shareDataHeader.uncompressedLength =
|
|
(TSUINT16)PDUSize;
|
|
|
|
// For each event...
|
|
for (i = 0; i < EventsThisPDU; i++) {
|
|
if (DataLength >= 1) {
|
|
switch (*pData & TS_INPUT_FASTPATH_EVENT_MASK) {
|
|
case TS_INPUT_FASTPATH_EVENT_KEYBOARD:
|
|
if (DataLength >= 2) {
|
|
// Use a mask, shift, and OR to avoid branches for the
|
|
// extended flags.
|
|
pInput->eventList[i].messageType =
|
|
TS_INPUT_EVENT_SCANCODE;
|
|
pInput->eventList[i].u.key.keyboardFlags =
|
|
(*pData & (BYTE)(
|
|
TS_INPUT_FASTPATH_KBD_EXTENDED |
|
|
TS_INPUT_FASTPATH_KBD_EXTENDED1)) << 7;
|
|
if (*pData & TS_INPUT_FASTPATH_KBD_RELEASE)
|
|
pInput->eventList[i].u.key.keyboardFlags |=
|
|
TS_KBDFLAGS_RELEASE;
|
|
|
|
pInput->eventList[i].u.key.keyCode = pData[1];
|
|
pData += 2;
|
|
DataLength -= 2;
|
|
}
|
|
else {
|
|
goto ShortData;
|
|
}
|
|
break;
|
|
|
|
case TS_INPUT_FASTPATH_EVENT_VKPACKET:
|
|
if (DataLength >= 3) {
|
|
// Use a mask, shift, and OR to avoid branches for the
|
|
// extended flags.
|
|
pInput->eventList[i].messageType =
|
|
TS_INPUT_EVENT_VKPACKET;
|
|
pInput->eventList[i].u.key.keyboardFlags =
|
|
(*pData & (BYTE)(
|
|
TS_INPUT_FASTPATH_KBD_EXTENDED |
|
|
TS_INPUT_FASTPATH_KBD_EXTENDED1)) << 7;
|
|
if (*pData & TS_INPUT_FASTPATH_KBD_RELEASE)
|
|
pInput->eventList[i].u.key.keyboardFlags |=
|
|
TS_KBDFLAGS_RELEASE;
|
|
memcpy(&pInput->eventList[i].u.key.keyCode,
|
|
&pData[1],
|
|
2);
|
|
|
|
TRC_NRM((TB,"Shadow pass: 0x%x flags:0x%x\n",
|
|
pInput->eventList[i].u.key.keyCode,
|
|
pInput->eventList[i].u.key.keyboardFlags));
|
|
|
|
pData += 3;
|
|
DataLength -= 3;
|
|
}
|
|
else {
|
|
goto ShortData;
|
|
}
|
|
break;
|
|
|
|
|
|
case TS_INPUT_FASTPATH_EVENT_MOUSE:
|
|
case TS_INPUT_FASTPATH_EVENT_MOUSEX:
|
|
if (DataLength >= 7) {
|
|
pInput->eventList[i].messageType =
|
|
((*pData & TS_INPUT_FASTPATH_EVENT_MASK) ==
|
|
TS_INPUT_FASTPATH_EVENT_MOUSE ?
|
|
TS_INPUT_EVENT_MOUSE :
|
|
TS_INPUT_EVENT_MOUSEX);
|
|
memcpy(&pInput->eventList[i].u.mouse, pData + 1,
|
|
sizeof(TS_POINTER_EVENT));
|
|
pData += 7;
|
|
DataLength -= 7;
|
|
}
|
|
else {
|
|
goto ShortData;
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case TS_INPUT_FASTPATH_EVENT_SYNC:
|
|
pInput->eventList[i].messageType = TS_INPUT_EVENT_SYNC;
|
|
pInput->eventList[i].u.sync.toggleFlags =
|
|
(*pData & (BYTE)TS_INPUT_FASTPATH_FLAGS_MASK);
|
|
pData++;
|
|
DataLength--;
|
|
break;
|
|
|
|
|
|
default:
|
|
TRC_ERR((TB, "Unrecognized imPacket (%d)",
|
|
*pData & TS_INPUT_FASTPATH_EVENT_MASK));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
else {
|
|
TRC_ERR((TB,"Out of data reading input events"));
|
|
goto ShortData;
|
|
}
|
|
|
|
} // end event loop
|
|
|
|
j += i;
|
|
|
|
// Launch the PDU.
|
|
TRC_NRM((TB, "Forwarding shadow data: %ld", DataLength));
|
|
Status = IcaRawInput(m_pTSWd->pContext, NULL, (BYTE *)pInput,
|
|
PDUSize);
|
|
if (!NT_SUCCESS(Status)) {
|
|
TRC_ERR((TB, "Failed shadow input data [%ld]: %x",
|
|
DataLength, Status));
|
|
}
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return;
|
|
|
|
ShortData:
|
|
TRC_ERR((TB,"Short PDU during passthru translation"));
|
|
DC_END_FN();
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// IM_CheckUpdateCursor
|
|
//
|
|
// Called during output processing to check to see if we need to send
|
|
// a mouse-moved packet to the client.
|
|
/****************************************************************************/
|
|
void RDPCALL SHCLASS IM_CheckUpdateCursor(
|
|
PPDU_PACKAGE_INFO pPkgInfo,
|
|
UINT32 currentTime)
|
|
{
|
|
PPOINTL pCursorPos;
|
|
UINT32 timeDelta;
|
|
|
|
DC_BEGIN_FN("IM_CheckUpdateCursor");
|
|
|
|
// Check to see if the cursor has moved since last time we came
|
|
// through - if not, then there's no point in doing any of the
|
|
// following tests!
|
|
if (!CM_CursorMoved()) {
|
|
TRC_DBG((TB, "No move since last time through"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
// Get the current cursor position - we always need this.
|
|
pCursorPos = CM_GetCursorPos();
|
|
|
|
// Check to see if the mouse has been moved at the display driver level
|
|
// yet - don't do anything until it has to avoid the mouse leaping to 0,0
|
|
// on connection
|
|
if (pCursorPos->x != 0xffffffff) {
|
|
// Check to see if the cursor is hidden - we should do nothing
|
|
// here if it is. In particular, the 'real' cursor is hidden
|
|
// during dragging of a single file and a 'fake' is drawn (by
|
|
// Explorer?). Ignoring the fact that it is hidden causes the
|
|
// 'faked' cursor to keep leaping back to where the drag started!
|
|
if (CM_IsCursorVisible()) {
|
|
|
|
timeDelta = currentTime - imLastLowLevelMouseEventTime;
|
|
TRC_NRM((TB, "SetCursorPos (%d:%d) lastEvent:%#lx "
|
|
"delta:%#lx", pCursorPos->x, pCursorPos->y,
|
|
imLastLowLevelMouseEventTime, timeDelta));
|
|
|
|
|
|
CM_SendCursorMovedPacket(pPkgInfo);
|
|
}
|
|
else {
|
|
TRC_NRM((TB, "Cursor hidden - skipping"));
|
|
}
|
|
}
|
|
else {
|
|
TRC_NRM((TB, "No mouse updates rec'd from client - not moving"));
|
|
}
|
|
|
|
// Clear the cursor moved flag.
|
|
CM_ClearCursorMoved();
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* API FUNCTION: IM_PartyJoiningShare */
|
|
/* */
|
|
/* Called by SC when a new party is joining the share */
|
|
/* */
|
|
/* PARAMETERS: */
|
|
/* personID - the local ID of the new party. */
|
|
/* oldShareSize - the number of the parties which were in the share (ie */
|
|
/* excludes the joining party). */
|
|
/* */
|
|
/* RETURNS: */
|
|
/* TRUE - the IM can accept the new party */
|
|
/* FALSE - the IM cannot accept the new party */
|
|
/****************************************************************************/
|
|
BOOL RDPCALL SHCLASS IM_PartyJoiningShare(
|
|
LOCALPERSONID personID,
|
|
unsigned oldShareSize)
|
|
{
|
|
BOOL rc = FALSE;
|
|
PTS_INPUT_CAPABILITYSET pIMCaps;
|
|
|
|
DC_BEGIN_FN("IM_PartyJoiningShare");
|
|
|
|
DC_IGNORE_PARAMETER(oldShareSize)
|
|
|
|
// One-time init for each new share.
|
|
if (oldShareSize == 0) {
|
|
KEYBOARD_INDICATOR_PARAMETERS kip = {0};
|
|
SD_IOCTL sdIoctl;
|
|
|
|
// The keys will initially all be up.
|
|
memset(imKeyStates, 0, sizeof(imKeyStates));
|
|
|
|
// Reset when we last saw a low level mouse event.
|
|
COM_GETTICKCOUNT(imLastLowLevelMouseEventTime);
|
|
|
|
// Get the toggle key states.
|
|
sdIoctl.IoControlCode = IOCTL_KEYBOARD_QUERY_INDICATORS;
|
|
sdIoctl.InputBuffer = NULL;
|
|
sdIoctl.InputBufferLength = 0;
|
|
sdIoctl.OutputBuffer = &kip;
|
|
sdIoctl.OutputBufferLength = sizeof(KEYBOARD_INDICATOR_PARAMETERS);
|
|
sdIoctl.BytesReturned = 0;
|
|
|
|
if (WDW_QueryKeyboardIndicators(m_pTSWd, &sdIoctl) ==
|
|
STATUS_SUCCESS) {
|
|
TRC_NRM((TB, "Got toggle key states ok"));
|
|
imKeyStates[IM_SC_CAPITAL] = kip.LedFlags & KEYBOARD_CAPS_LOCK_ON;
|
|
imKeyStates[IM_SC_NUMLOCK] = kip.LedFlags & KEYBOARD_NUM_LOCK_ON;
|
|
imKeyStates[IM_SC_SCROLL] = kip.LedFlags &
|
|
KEYBOARD_SCROLL_LOCK_ON;
|
|
}
|
|
|
|
TRC_NRM((TB, "Toggle key states: Caps:%s, Num:%s, Scroll:%s",
|
|
(imKeyStates[IM_SC_CAPITAL] & 0x01) ? "ON" : "OFF",
|
|
(imKeyStates[IM_SC_NUMLOCK] & 0x01) ? "ON" : "OFF",
|
|
(imKeyStates[IM_SC_SCROLL] & 0x01) ? "ON" : "OFF"));
|
|
}
|
|
|
|
// Make sure scancodes are supported by client.
|
|
pIMCaps = (PTS_INPUT_CAPABILITYSET)
|
|
CPC_GetCapabilitiesForPerson(personID, TS_CAPSETTYPE_INPUT);
|
|
if (pIMCaps != NULL && pIMCaps->inputFlags & TS_INPUT_FLAG_SCANCODES) {
|
|
rc = TRUE;
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Rejecting join from [%u]: has no scancode support",
|
|
personID));
|
|
}
|
|
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* API FUNCTION: IM_PartyLeftShare */
|
|
/* */
|
|
/* Called when a party has left the share. */
|
|
/* */
|
|
/* PARAMETERS: */
|
|
/* personID - the local ID of the new party. */
|
|
/* newShareSize - the number of the parties now in the share (ie excludes */
|
|
/* the leaving party). */
|
|
/****************************************************************************/
|
|
void RDPCALL SHCLASS IM_PartyLeftShare(
|
|
LOCALPERSONID personID,
|
|
unsigned newShareSize)
|
|
{
|
|
DC_BEGIN_FN("IM_PartyLeftShare");
|
|
|
|
if (newShareSize == 0) {
|
|
// Need to make sure we set all keys up, just in case we were
|
|
// shadowing a console session.
|
|
if (m_pTSWd->StackClass == Stack_Shadow)
|
|
IMResetKeyStateArray();
|
|
}
|
|
|
|
DC_END_FN();
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* FUNCTION: IMConvertMousePacketToEvent */
|
|
/* */
|
|
/* Converts the TS_INPUT_EVENT format to a MOUSE_INPUT_DATA OS format */
|
|
/* */
|
|
/* PARAMETERS: */
|
|
/* pInputEvent - the TS_INPUT_EVENT to be converted */
|
|
/* pMouseData - the MOUSE_INPUT_DATA to modify */
|
|
/* */
|
|
/* RETURNS: */
|
|
/* TRUE if the packet has been recognised and converted */
|
|
/* FALSE if the packet was not recognised */
|
|
/****************************************************************************/
|
|
BOOL __fastcall SHCLASS IMConvertMousePacketToEvent(
|
|
TS_POINTER_EVENT UNALIGNED *pInputEvent,
|
|
MOUSE_INPUT_DATA *pMouseData,
|
|
BOOL bMouseX)
|
|
{
|
|
BOOL rc = TRUE;
|
|
|
|
DC_BEGIN_FN("IMConvertMousePacketToEvent");
|
|
|
|
/************************************************************************/
|
|
/* Set all the fields to zero */
|
|
/************************************************************************/
|
|
memset(pMouseData, 0, sizeof(MOUSE_INPUT_DATA));
|
|
|
|
// Check for a wheel rotate, since this is easy to process.
|
|
// (It cannot include any mouse movement as well).
|
|
// MouseX events are not used for wheel events.
|
|
if (!bMouseX && (pInputEvent->pointerFlags & TS_FLAG_MOUSE_WHEEL))
|
|
{
|
|
if (!(pInputEvent->pointerFlags &
|
|
(TS_FLAG_MOUSE_BUTTON1 |
|
|
TS_FLAG_MOUSE_BUTTON2 |
|
|
TS_FLAG_MOUSE_BUTTON3)))
|
|
{
|
|
/****************************************************************/
|
|
/* This is a wheel movement. */
|
|
/****************************************************************/
|
|
pMouseData->ButtonFlags = MOUSE_WHEEL;
|
|
pMouseData->ButtonData = pInputEvent->pointerFlags &
|
|
TS_FLAG_MOUSE_ROTATION_MASK;
|
|
|
|
/****************************************************************/
|
|
/* Sign extend the rotation amount up to the full 32 */
|
|
/* bits */
|
|
/****************************************************************/
|
|
if (pMouseData->ButtonData & TS_FLAG_MOUSE_DIRECTION)
|
|
{
|
|
pMouseData->ButtonData |= ~TS_FLAG_MOUSE_ROTATION_MASK;
|
|
}
|
|
}
|
|
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* We are left now with non wheel-rotate events. Note that we could be */
|
|
/* dealing with either a TS_INPUT_EVENT_MOUSE or a */
|
|
/* TS_INPUT_EVENT_MOUSEX. Either way we must store the mouse position. */
|
|
/************************************************************************/
|
|
pMouseData->LastX = min( (int)(m_desktopWidth - 1),
|
|
(int)(max(0, pInputEvent->x)) );
|
|
pMouseData->LastY = min( (int)(m_desktopHeight - 1),
|
|
(int)(max(0, pInputEvent->y)) );
|
|
|
|
/************************************************************************/
|
|
/* Add flags as appropriate. */
|
|
/************************************************************************/
|
|
/************************************************************************/
|
|
/* Make all submitted events absolute moves (both clicks and moves) */
|
|
/************************************************************************/
|
|
pMouseData->Flags = MOUSE_MOVE_ABSOLUTE | MOUSE_VIRTUAL_DESKTOP;
|
|
|
|
//
|
|
// Set the flags to indicate if this move is originating
|
|
// from a shadow client
|
|
//
|
|
if (m_pTSWd->StackClass == Stack_Shadow ) {
|
|
// this event is coming from a shadow client
|
|
pMouseData->Flags |= MOUSE_TERMSRV_SRC_SHADOW;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Set click flags for click events (i.e. non-move events) */
|
|
/************************************************************************/
|
|
if (!(!bMouseX && (pInputEvent->pointerFlags & TS_FLAG_MOUSE_MOVE)))
|
|
{
|
|
if (!bMouseX)
|
|
{
|
|
/****************************************************************/
|
|
/* A standard mouse event */
|
|
/****************************************************************/
|
|
switch (pInputEvent->pointerFlags &
|
|
(TS_FLAG_MOUSE_BUTTON1 | TS_FLAG_MOUSE_BUTTON2 |
|
|
TS_FLAG_MOUSE_BUTTON3 | TS_FLAG_MOUSE_DOWN))
|
|
{
|
|
case TS_FLAG_MOUSE_BUTTON1 | TS_FLAG_MOUSE_DOWN:
|
|
{
|
|
pMouseData->ButtonFlags = MOUSE_BUTTON_1_DOWN;
|
|
|
|
// Update the key state array.
|
|
IM_SET_KEY_DOWN(imKeyStates[IM_SC_LBUTTON]);
|
|
}
|
|
break;
|
|
|
|
case TS_FLAG_MOUSE_BUTTON1:
|
|
{
|
|
pMouseData->ButtonFlags = MOUSE_BUTTON_1_UP;
|
|
if (IM_KEY_STATE_IS_UP(imKeyStates[IM_SC_LBUTTON]))
|
|
{
|
|
/********************************************************/
|
|
/* Discard unmatched mouse button up event */
|
|
/********************************************************/
|
|
TRC_NRM((TB, "discard mouse up event"));
|
|
rc = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
// Update the key state array.
|
|
IM_SET_KEY_UP(imKeyStates[IM_SC_LBUTTON]);
|
|
}
|
|
break;
|
|
|
|
case TS_FLAG_MOUSE_BUTTON2 | TS_FLAG_MOUSE_DOWN:
|
|
{
|
|
pMouseData->ButtonFlags = MOUSE_BUTTON_2_DOWN;
|
|
|
|
// Update the key state array.
|
|
IM_SET_KEY_DOWN(imKeyStates[IM_SC_RBUTTON]);
|
|
}
|
|
break;
|
|
|
|
case TS_FLAG_MOUSE_BUTTON2:
|
|
{
|
|
pMouseData->ButtonFlags = MOUSE_BUTTON_2_UP;
|
|
if (IM_KEY_STATE_IS_UP(imKeyStates[IM_SC_RBUTTON]))
|
|
{
|
|
/********************************************************/
|
|
/* Discard unmatched mouse button up event */
|
|
/********************************************************/
|
|
TRC_NRM((TB, "discard mouse up event"));
|
|
rc = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
// Update the key state array.
|
|
IM_SET_KEY_UP(imKeyStates[IM_SC_RBUTTON]);
|
|
}
|
|
break;
|
|
|
|
case TS_FLAG_MOUSE_BUTTON3 | TS_FLAG_MOUSE_DOWN:
|
|
{
|
|
pMouseData->ButtonFlags = MOUSE_BUTTON_3_DOWN;
|
|
|
|
IM_SET_KEY_DOWN(imKeyStates[IM_SC_MBUTTON]);
|
|
}
|
|
break;
|
|
|
|
case TS_FLAG_MOUSE_BUTTON3:
|
|
{
|
|
pMouseData->ButtonFlags = MOUSE_BUTTON_3_UP;
|
|
if (IM_KEY_STATE_IS_UP(imKeyStates[IM_SC_MBUTTON]))
|
|
{
|
|
/********************************************************/
|
|
/* Discard unmatched mouse button up event */
|
|
/********************************************************/
|
|
TRC_NRM((TB, "discard mouse up event"));
|
|
rc = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
IM_SET_KEY_UP(imKeyStates[IM_SC_MBUTTON]);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
/************************************************************/
|
|
/* If we don't recognise this then don't play it back. This */
|
|
/* should not be possible according to the T.128 spec, */
|
|
/* which restricts the allowed flag combinations to the */
|
|
/* above */
|
|
/************************************************************/
|
|
TRC_ERR((TB, "Unrecognized mouse flags (%04X)",
|
|
pInputEvent->pointerFlags));
|
|
WDW_LogAndDisconnect(m_pTSWd, TRUE,
|
|
Log_RDP_InvalidInputPDUMouse,
|
|
(PBYTE)pInputEvent,
|
|
sizeof(PTS_INPUT_EVENT));
|
|
rc = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/****************************************************************/
|
|
/* An extended mouse event */
|
|
/****************************************************************/
|
|
switch (pInputEvent->pointerFlags &
|
|
(TS_FLAG_MOUSEX_BUTTON1 | TS_FLAG_MOUSEX_BUTTON2 |
|
|
TS_FLAG_MOUSEX_DOWN))
|
|
{
|
|
case TS_FLAG_MOUSEX_BUTTON1 | TS_FLAG_MOUSEX_DOWN:
|
|
{
|
|
pMouseData->ButtonFlags = MOUSE_BUTTON_4_DOWN;
|
|
|
|
// Update the key state array.
|
|
IM_SET_KEY_DOWN(imKeyStates[IM_SC_XBUTTON1]);
|
|
}
|
|
break;
|
|
|
|
case TS_FLAG_MOUSEX_BUTTON1:
|
|
{
|
|
pMouseData->ButtonFlags = MOUSE_BUTTON_4_UP;
|
|
if (IM_KEY_STATE_IS_UP(imKeyStates[IM_SC_XBUTTON1]))
|
|
{
|
|
/********************************************************/
|
|
/* Discard unmatched mouse button up event */
|
|
/********************************************************/
|
|
TRC_NRM((TB, "discard mouse up event"));
|
|
rc = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
// Update the key state array.
|
|
IM_SET_KEY_UP(imKeyStates[IM_SC_XBUTTON1]);
|
|
}
|
|
break;
|
|
|
|
case TS_FLAG_MOUSEX_BUTTON2 | TS_FLAG_MOUSEX_DOWN:
|
|
{
|
|
pMouseData->ButtonFlags = MOUSE_BUTTON_5_DOWN;
|
|
|
|
// Update the key state array.
|
|
IM_SET_KEY_DOWN(imKeyStates[IM_SC_XBUTTON2]);
|
|
}
|
|
break;
|
|
|
|
case TS_FLAG_MOUSEX_BUTTON2:
|
|
{
|
|
pMouseData->ButtonFlags = MOUSE_BUTTON_5_UP;
|
|
if (IM_KEY_STATE_IS_UP(imKeyStates[IM_SC_XBUTTON2]))
|
|
{
|
|
/********************************************************/
|
|
/* Discard unmatched mouse button up event */
|
|
/********************************************************/
|
|
TRC_NRM((TB, "discard mouse up event"));
|
|
rc = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
// Update the key state array.
|
|
IM_SET_KEY_UP(imKeyStates[IM_SC_XBUTTON2]);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
/********************************************************/
|
|
/* As for standard button clicks, if we don't recognise */
|
|
/* this then don't play it back. Capabilities should */
|
|
/* protect us from getting here. */
|
|
/********************************************************/
|
|
TRC_ERR((TB, "Unrecognized mouseX flags (%04X)",
|
|
pInputEvent->pointerFlags));
|
|
WDW_LogAndDisconnect(m_pTSWd, TRUE,
|
|
Log_RDP_InvalidInputPDUMouse,
|
|
(PBYTE)pInputEvent,
|
|
sizeof(PTS_INPUT_EVENT));
|
|
rc = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Store the injection time for guessing at SetCursorPos calls. */
|
|
/************************************************************************/
|
|
COM_GETTICKCOUNT(imLastLowLevelMouseEventTime);
|
|
|
|
/************************************************************************/
|
|
/* Store the mouse position before conversion. */
|
|
/************************************************************************/
|
|
imLastKnownMousePos.x = pMouseData->LastX;
|
|
imLastKnownMousePos.y = pMouseData->LastY;
|
|
|
|
/************************************************************************/
|
|
/* Scale the logical screen co-ordinates to the full 16-bit */
|
|
/* range (0..65535). */
|
|
/************************************************************************/
|
|
TRC_DBG((TB, "Scale absolute mouse move"));
|
|
pMouseData->LastX = IM_MOUSEPOS_LOG_TO_OS_ABS(pMouseData->LastX,
|
|
m_desktopWidth);
|
|
pMouseData->LastY = IM_MOUSEPOS_LOG_TO_OS_ABS(pMouseData->LastY,
|
|
m_desktopHeight);
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
|
|
/****************************************************************************/
|
|
// IMConvertFastPathKeyboardToEvent
|
|
//
|
|
// Converts the 2 or 3 byte representation of a fast-path keyboard event into
|
|
// a kernel keyboard event, taking care to save the key states. Byte 0 is
|
|
// the event code and flags byte, byte 1 is the scancode.
|
|
// In the case where this is a VK_PACKET input bytes 1 and 2 are the unicode
|
|
// character (contained in the scancode).
|
|
//
|
|
// This handles input of the form
|
|
// TS_INPUT_FASTPATH_EVENT_KEYBOARD and
|
|
// TS_INPUT_FASTPATH_EVENT_VKPACKET
|
|
//
|
|
/****************************************************************************/
|
|
BOOL __fastcall SHCLASS IMConvertFastPathKeyboardToEvent(
|
|
BYTE *pData,
|
|
KEYBOARD_INPUT_DATA *pKbdData)
|
|
{
|
|
BOOL rc = TRUE;
|
|
unsigned code = 0;
|
|
BOOL fHandlingVKPacket = FALSE;
|
|
|
|
DC_BEGIN_FN("IMConvertFastPathKeyboardToEvent");
|
|
|
|
// Set up basic params.
|
|
// We define the fastpath keyboard flags to be the same as the KEY_BREAK,
|
|
// KEY_E0, and KEY_E1 to allow us to simply copy the low-order 3 bits of
|
|
// the first byte into the KbdData.Flags field.
|
|
pKbdData->Flags = *pData & 0x07;
|
|
pKbdData->UnitId = 0;
|
|
if (TS_INPUT_FASTPATH_EVENT_KEYBOARD ==
|
|
(*pData & TS_INPUT_FASTPATH_EVENT_MASK))
|
|
{
|
|
code = pKbdData->MakeCode = pData[1];
|
|
}
|
|
else if (TS_INPUT_FASTPATH_EVENT_VKPACKET ==
|
|
(*pData & TS_INPUT_FASTPATH_EVENT_MASK))
|
|
{
|
|
fHandlingVKPacket = TRUE;
|
|
// Scancode is a 2 byte unicode char in this case
|
|
memcpy(&code, &pData[1], 2);
|
|
pKbdData->MakeCode = (USHORT)code;
|
|
pKbdData->Flags |= KEY_TERMSRV_VKPACKET;
|
|
}
|
|
|
|
pKbdData->ExtraInformation = 0;
|
|
|
|
if (m_pTSWd->StackClass == Stack_Shadow ) {
|
|
// this event is coming from a shadow client: tell the target to sync
|
|
pKbdData->Flags |= KEY_TERMSRV_SHADOW;
|
|
}
|
|
|
|
if (fHandlingVKPacket)
|
|
{
|
|
TRC_NRM((TB,"IH VKpkt Unicode val: 0x%x flags:0x%x\n",
|
|
code, pKbdData->Flags));
|
|
//No further processing
|
|
DC_QUIT;
|
|
}
|
|
|
|
// Special case control/ALT keys: distinguish L & R keys in the keystate
|
|
// array.
|
|
if (pData[0] & TS_INPUT_FASTPATH_KBD_EXTENDED) {
|
|
if (pData[1] == IM_SC_LCONTROL)
|
|
code = IM_SC_RCONTROL;
|
|
else if (pData[1] == IM_SC_LALT)
|
|
code = IM_SC_RALT;
|
|
}
|
|
|
|
// Check for the RELEASE flag, which is TRUE for a key release, FALSE
|
|
// for keypresses and repeats.
|
|
if (pData[0] & TS_INPUT_FASTPATH_KBD_RELEASE) {
|
|
|
|
#ifdef DELETE_UNMATCHED_KEYUPS
|
|
// Check whether this is an unmatched key up event (rather than
|
|
// a key event that's not been matched up!)
|
|
if (IM_KEY_STATE_IS_UP(imKeyStates[pData[1]])) {
|
|
// Discard unmatched key up event
|
|
TRC_NRM((TB, "discard up event %04hX", pData[1]));
|
|
rc = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
#endif
|
|
|
|
// Update the key state array.
|
|
TRC_DBG((TB,"set sc %u state UP (%#x)", code, imKeyStates[code]));
|
|
IM_SET_KEY_UP(imKeyStates[code]);
|
|
}
|
|
else {
|
|
// Update the key state array.
|
|
TRC_DBG((TB,"set sc %u state DOWN (%#x)", code, imKeyStates[code]));
|
|
IM_SET_KEY_DOWN(imKeyStates[code]);
|
|
}
|
|
|
|
// Compile-time assertions to make sure the flags are okay.
|
|
#if (TS_INPUT_FASTPATH_KBD_RELEASE != KEY_BREAK)
|
|
#error TS RELEASE definition doesn't agree with driver flag
|
|
#endif
|
|
#if (TS_INPUT_FASTPATH_KBD_EXTENDED != KEY_E0)
|
|
#error TS EXTENDED definition doesn't agree with driver flag
|
|
#endif
|
|
#if (TS_INPUT_FASTPATH_KBD_EXTENDED1 != KEY_E1)
|
|
#error TS EXTENDED1 definition doesn't agree with driver flag
|
|
#endif
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// IMDoSync
|
|
//
|
|
// Encapsulates the actions for a sync for common use by regular and
|
|
// fastpath input.
|
|
/****************************************************************************/
|
|
NTSTATUS RDPCALL SHCLASS IMDoSync(unsigned ToggleFlags)
|
|
{
|
|
NTSTATUS Status;
|
|
KEYBOARD_INPUT_DATA KbdData;
|
|
|
|
DC_BEGIN_FN("IMDoSync");
|
|
|
|
// We just need to reset the key state.
|
|
IMResetKeyStateArray();
|
|
|
|
// Send special "reset keylight state" injection to win32k.
|
|
// The particular states to set are contained in ToggleFlags.
|
|
KbdData.MakeCode = 0xFF;
|
|
#ifdef _HYDRA_
|
|
KbdData.Flags = KEY_TERMSRV_SET_LED;
|
|
#else
|
|
KbdData.Flags = KEY_CITRIX_SET_LED;
|
|
#endif
|
|
|
|
if (m_pTSWd->StackClass == Stack_Shadow ) {
|
|
// this event is coming from a shadow client: tell the target to sync
|
|
KbdData.Flags |= KEY_TERMSRV_SHADOW;
|
|
}
|
|
|
|
KbdData.ExtraInformation = ToggleFlags;
|
|
|
|
TRC_NRM((TB, "Injecting toggle keys sync event %lx", ToggleFlags));
|
|
Status = IcaChannelInput(m_pTSWd->pContext, Channel_Keyboard, 0, NULL,
|
|
(unsigned char *)&KbdData, sizeof(KEYBOARD_INPUT_DATA));
|
|
TRC_DBG((TB, "Return from toggles input injection %lu",
|
|
Status));
|
|
|
|
DC_END_FN();
|
|
return Status;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* FUNCTION: IMResetKeyStateArray */
|
|
/* */
|
|
/* Called to reset the keystate array */
|
|
/****************************************************************************/
|
|
void RDPCALL SHCLASS IMResetKeyStateArray()
|
|
{
|
|
BOOL rc = TRUE;
|
|
unsigned i;
|
|
NTSTATUS Status;
|
|
|
|
DC_BEGIN_FN("IMResetKeyStateArray");
|
|
|
|
/************************************************************************/
|
|
/* This function is called to reset all keys to a known state */
|
|
/* (up) before resetting the keys with new states. */
|
|
/************************************************************************/
|
|
|
|
/************************************************************************/
|
|
/* Loop through all keys looking for any that are not in a neutral */
|
|
/* state. In this case any key that is in a KEY_DOWN state is not */
|
|
/* considered neutral. */
|
|
/************************************************************************/
|
|
for (i = 0; i < IM_KEY_STATE_SIZE; i++)
|
|
{
|
|
if (IM_KEY_STATE_IS_DOWN(imKeyStates[i])) {
|
|
TRC_NRM((TB, "Key is down %u", i));
|
|
|
|
/****************************************************************/
|
|
/* Handle the mouse buttons first */
|
|
/****************************************************************/
|
|
if ((i == IM_SC_LBUTTON) ||
|
|
(i == IM_SC_RBUTTON) ||
|
|
(i == IM_SC_MBUTTON) ||
|
|
(i == IM_SC_XBUTTON1) ||
|
|
(i == IM_SC_XBUTTON2))
|
|
{
|
|
MOUSE_INPUT_DATA MouseData;
|
|
|
|
/************************************************************/
|
|
/* Generate a mouse event with the particular button type */
|
|
/* and a relative mouse move of zero. */
|
|
/************************************************************/
|
|
memset(&MouseData, 0, sizeof(MOUSE_INPUT_DATA));
|
|
|
|
if (i == IM_SC_LBUTTON)
|
|
{
|
|
MouseData.ButtonFlags = MOUSE_LEFT_BUTTON_UP;
|
|
}
|
|
else if (i == IM_SC_RBUTTON)
|
|
{
|
|
MouseData.ButtonFlags = MOUSE_RIGHT_BUTTON_UP;
|
|
}
|
|
else if (i == IM_SC_MBUTTON)
|
|
{
|
|
MouseData.ButtonFlags = MOUSE_MIDDLE_BUTTON_UP;
|
|
}
|
|
else if (i == IM_SC_XBUTTON1)
|
|
{
|
|
MouseData.ButtonFlags = MOUSE_BUTTON_4_UP;
|
|
}
|
|
else /* IM_SC_XBUTTON2 */
|
|
{
|
|
MouseData.ButtonFlags = MOUSE_BUTTON_5_UP;
|
|
}
|
|
|
|
/************************************************************/
|
|
/* Store the injection time for guessing at SetCursorPos */
|
|
/* calls. */
|
|
/************************************************************/
|
|
COM_GETTICKCOUNT(imLastLowLevelMouseEventTime);
|
|
|
|
TRC_NRM((TB, "Inject mouse event: x(%ld) y(%ld) flags(%#hx)"
|
|
"buttonFlags(%#hx)",
|
|
MouseData.LastX,
|
|
MouseData.LastY,
|
|
MouseData.Flags,
|
|
MouseData.ButtonFlags));
|
|
Status = IcaChannelInput(m_pTSWd->pContext,
|
|
Channel_Mouse,
|
|
0,
|
|
NULL,
|
|
(unsigned char *)&MouseData,
|
|
sizeof(MOUSE_INPUT_DATA));
|
|
TRC_DBG((TB, "Return from mouse input injection %lu",
|
|
Status));
|
|
}
|
|
else {
|
|
KEYBOARD_INPUT_DATA KbdData;
|
|
|
|
/************************************************************/
|
|
/* Generate a keyboard key up event */
|
|
/************************************************************/
|
|
KbdData.UnitId = 0;
|
|
if (i == IM_SC_RCONTROL)
|
|
{
|
|
KbdData.Flags = KEY_BREAK | KEY_E0;
|
|
KbdData.MakeCode = IM_SC_LCONTROL;
|
|
}
|
|
else if (i == IM_SC_RALT)
|
|
{
|
|
KbdData.Flags = KEY_BREAK | KEY_E0;
|
|
KbdData.MakeCode = IM_SC_LALT;
|
|
}
|
|
else
|
|
{
|
|
KbdData.Flags = KEY_BREAK;
|
|
KbdData.MakeCode = (unsigned short)i;
|
|
}
|
|
|
|
KbdData.Reserved = 0;
|
|
KbdData.ExtraInformation = 0;
|
|
|
|
TRC_NRM((TB, "Inject keybd event: make code (%u) flags(%#x)",
|
|
KbdData.MakeCode, KbdData.Flags));
|
|
Status = IcaChannelInput(m_pTSWd->pContext,
|
|
Channel_Keyboard,
|
|
0,
|
|
NULL,
|
|
(unsigned char *)&KbdData,
|
|
sizeof(KEYBOARD_INPUT_DATA));
|
|
TRC_DBG((TB, "Return from keyboard input injection %lu",
|
|
Status));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set all keys up.
|
|
memset((PVOID)imKeyStates, 0, IM_KEY_STATE_SIZE);
|
|
|
|
DC_END_FN();
|
|
}
|
|
|