Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

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();
}