admin
base
com
developer
drivers
ds
enduser
inetcore
inetsrv
loc
mergedcomponents
multimedia
net
printscan
public
published
sdktools
shell
termsrv
admtools
apisub
cdmodem
clcreator
common
dload
drivers
httpproxy
icaapi
inc
license
perfts
publish
rdfilter
regapi
remdsk
client
common
msngr
rds
as
as16
cpi32
res
asmaster.cpp
awc.cpp
ba.cpp
bcd.cpp
ch.cpp
cm.cpp
cmg.cpp
control.cpp
cpc.cpp
cpi32dll.cpp
dcs.cpp
fh.cpp
gdc.cpp
globals.cpp
globals.h
host.cpp
im.cpp
makefile
mgc.cpp
nmas.def
nmas.rc
oa.cpp
od.cpp
od2.cpp
oe.cpp
oe2.cpp
pm.cpp
precomp.h
rbc.cpp
s20.cpp
sbc.cpp
sc.cpp
sch.cpp
sdg.cpp
sdp.cpp
shm.cpp
sources
ssi.cpp
swl.cpp
up.cpp
usr.cpp
ut.cpp
view.cpp
cpi32.9x
cpi32.nt
dd
h
thk
ascom.inc
common.inc
dirs
cert
dev
extern
h
nmutil
samples
t120
dirs
ntx.reg
project.mk
setup9x.bat
setupnt4.cmd
setupw2k.cmd
server
unittest
dirs
reskit
sessdir
setup
sld
syslib
tsappcmp
tscert
tsuserex
tsutil
wince
winsta
wmi
wtsapi
dirs
project.mk
tools
windows
dirs
makefil0
4511 lines
161 KiB
C++
4511 lines
161 KiB
C++
#include "precomp.h"
|
||
|
||
|
||
//
|
||
// IM.CPP
|
||
// Input Manager
|
||
//
|
||
// Copyright(c) Microsoft 1997-
|
||
//
|
||
#include <confreg.h>
|
||
|
||
#define MLZ_FILE_ZONE ZONE_INPUT
|
||
|
||
|
||
|
||
|
||
//
|
||
// IM_ShareStarting()
|
||
//
|
||
BOOL ASShare::IM_ShareStarting(void)
|
||
{
|
||
BOOL rc = FALSE;
|
||
HKEY hkeyBandwidth;
|
||
UINT i;
|
||
BYTE tmpVK;
|
||
|
||
DebugEntry(ASShare::IM_ShareStarting);
|
||
|
||
//
|
||
// Find out the scan codes for the left and right shift keys.
|
||
//
|
||
|
||
//
|
||
// SFR 2537: Get the scan codes for this keyboard for the left-right
|
||
// variants of SHIFT.
|
||
//
|
||
// We do not do this for the left-right variants of CONTROL and ALT (ie
|
||
// menu) because they are extended keys.
|
||
//
|
||
// The scan codes are used in the keyboard hook (when sending) and in
|
||
// the network translate to OS routine (when receiving), to
|
||
// distinguish between the left-right variants of VK_SHIFT, where
|
||
// Windows only reports a single value.
|
||
//
|
||
// This method is pretty long
|
||
//
|
||
m_imScanVKLShift = (BYTE) MapVirtualKey(VK_SHIFT, 0);
|
||
for (i = 0; i < 256; i++)
|
||
{
|
||
tmpVK = (BYTE)MapVirtualKey(i, 1);
|
||
if ( (tmpVK == VK_SHIFT) && (i != m_imScanVKLShift) )
|
||
{
|
||
m_imScanVKRShift = (BYTE)i;
|
||
break;
|
||
}
|
||
}
|
||
|
||
TRACE_OUT(( "Left/Right VK_SHIFT: scan codes = %02X, %02X",
|
||
m_imScanVKLShift, m_imScanVKRShift));
|
||
|
||
//
|
||
// Check the user-reported bandwidth to decide if we should optimize
|
||
// input for bandwidth or latency.
|
||
// BUGBUG will want to vary this via flow control instead in future
|
||
//
|
||
m_imInControlMouseWithhold = 0;
|
||
|
||
//
|
||
// Find out if this is a DBCS enabled system - if it is then we'll need
|
||
// to load IMM32.DLL.
|
||
//
|
||
ASSERT(m_imImmLib == NULL);
|
||
ASSERT(m_imImmGVK == NULL);
|
||
|
||
if (GetSystemMetrics(SM_DBCSENABLED))
|
||
{
|
||
//
|
||
// DBCS system, so load IMM32.DLL
|
||
//
|
||
m_imImmLib = LoadLibrary("imm32.dll");
|
||
if (!m_imImmLib)
|
||
{
|
||
ERROR_OUT(( "Failed to load imm32.dll"));
|
||
DC_QUIT;
|
||
}
|
||
|
||
//
|
||
// Now attempt to find the entry point in this DLL.
|
||
//
|
||
m_imImmGVK = (IMMGVK) GetProcAddress(m_imImmLib, "ImmGetVirtualKey");
|
||
if (!m_imImmGVK)
|
||
{
|
||
ERROR_OUT(( "Failed to fixup <ImmGetVirtualKey>"));
|
||
DC_QUIT;
|
||
}
|
||
}
|
||
|
||
rc = TRUE;
|
||
|
||
DC_EXIT_POINT:
|
||
DebugExitBOOL(ASShare::IM_ShareStarting, rc);
|
||
return(rc);
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// IM_ShareEnded()
|
||
//
|
||
void ASShare::IM_ShareEnded(void)
|
||
{
|
||
DebugEntry(ASShare::IM_ShareEnded);
|
||
|
||
// Free imm32 dll
|
||
m_imImmGVK = NULL;
|
||
|
||
if (m_imImmLib)
|
||
{
|
||
FreeLibrary(m_imImmLib);
|
||
m_imImmLib = NULL;
|
||
}
|
||
|
||
DebugExitVOID(ASShare::IM_ShareEnded);
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// IM_Controlled()
|
||
//
|
||
// Called when we start/stop being controlled.
|
||
//
|
||
BOOL ASShare::IM_Controlled(ASPerson * pasControlledBy)
|
||
{
|
||
BOOL rc;
|
||
|
||
DebugEntry(ASShare::IM_Controlled);
|
||
|
||
if (pasControlledBy)
|
||
{
|
||
// Incoming injected input queues should be empty
|
||
ASSERT(m_imControlledEventQ.numEvents == 0);
|
||
ASSERT(m_imControlledEventQ.head == 0);
|
||
ASSERT(m_imControlledOSQ.numEvents == 0);
|
||
ASSERT(m_imControlledOSQ.head == 0);
|
||
|
||
//
|
||
// Reset CONTROLLED vars
|
||
//
|
||
m_imfControlledMouseButtonsReversed = (GetSystemMetrics(SM_SWAPBUTTON) != 0);
|
||
m_imfControlledMouseClipped = FALSE;
|
||
m_imfControlledPaceInjection = FALSE;
|
||
m_imfControlledNewEvent = TRUE;
|
||
m_imControlledNumEventsPending = 0;
|
||
m_imControlledNumEventsReturned = 0;
|
||
|
||
m_imControlledLastLowLevelMouseEventTime = GetTickCount();
|
||
m_imControlledLastMouseRemoteTime = 0;
|
||
m_imControlledLastMouseLocalTime = 0;
|
||
m_imControlledLastIncompleteConversion = 0;
|
||
m_imControlledMouseBacklog = 0;
|
||
GetCursorPos(&m_imControlledLastMousePos);
|
||
|
||
// Get current keyboard state
|
||
GetKeyboardState(m_aimControlledKeyStates);
|
||
|
||
// Save it so we can put it back when done being controlled
|
||
ASSERT(sizeof(m_aimControlledSavedKeyStates) == sizeof(m_aimControlledKeyStates));
|
||
CopyMemory(m_aimControlledSavedKeyStates, m_aimControlledKeyStates, sizeof(m_aimControlledKeyStates));
|
||
|
||
// Clear original keyboard state
|
||
ZeroMemory(m_aimControlledKeyStates, sizeof(m_aimControlledKeyStates));
|
||
SetKeyboardState(m_aimControlledKeyStates);
|
||
|
||
//
|
||
// On the other side, the remote will start sending us events to
|
||
// bring our keyboard in sync with his. Then real input events.
|
||
//
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// We're no longer controlled. Clear the remote queues.
|
||
//
|
||
m_imControlledOSQ.head = 0;
|
||
m_imControlledOSQ.numEvents = 0;
|
||
|
||
m_imControlledEventQ.numEvents = 0;
|
||
|
||
//
|
||
// Put back our saved keyboard state
|
||
//
|
||
SetKeyboardState(m_aimControlledSavedKeyStates);
|
||
}
|
||
|
||
rc = OSI_InstallControlledHooks(pasControlledBy != NULL);
|
||
if (!rc)
|
||
{
|
||
ERROR_OUT(("IM_Controlled: Couldn't install controlled hooks"));
|
||
DC_QUIT;
|
||
}
|
||
g_lpimSharedData->imControlled = (pasControlledBy != NULL);
|
||
|
||
DC_EXIT_POINT:
|
||
DebugExitBOOL(ASShare:IM_Controlled, rc);
|
||
return(rc);
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// IM_InControl()
|
||
//
|
||
// Called when we start/stop being in control. We must observe high-level
|
||
// keyboard events.
|
||
//
|
||
void ASShare::IM_InControl(ASPerson * pasInControlOf)
|
||
{
|
||
DebugEntry(ASShare::IM_InControl);
|
||
|
||
if (pasInControlOf)
|
||
{
|
||
//
|
||
// Set up InControl vars.
|
||
//
|
||
|
||
// Get current key state
|
||
GetKeyboardState(m_aimInControlKeyStates);
|
||
|
||
m_imfInControlEventIsPending = FALSE;
|
||
m_imfInControlCtrlDown = FALSE;
|
||
m_imfInControlShiftDown = FALSE;
|
||
m_imfInControlMenuDown = FALSE;
|
||
m_imfInControlCapsLock = FALSE;
|
||
m_imfInControlNumLock = FALSE;
|
||
m_imfInControlScrollLock = FALSE;
|
||
m_imfInControlConsumeMenuUp = FALSE;
|
||
m_imfInControlConsumeEscapeUp = FALSE;
|
||
m_imfInControlNewEvent = TRUE;
|
||
m_imInControlMouseDownCount = 0;
|
||
m_imInControlMouseDownTime = 0;
|
||
m_imInControlMouseSpoilRate = 0;
|
||
m_imInControlNumEventsPending = 0;
|
||
m_imInControlNumEventsReturned = 0;
|
||
m_imInControlNextHotKeyEntry = 0;
|
||
|
||
//
|
||
// Send mouse move with our current position to the dude we're in
|
||
// control of.
|
||
//
|
||
ValidateView(pasInControlOf);
|
||
ASSERT(pasInControlOf->m_caControlledBy == m_pasLocal);
|
||
}
|
||
else
|
||
{
|
||
// Clear outgoing queues
|
||
m_imInControlEventQ.head = 0;
|
||
m_imInControlEventQ.numEvents = 0;
|
||
}
|
||
|
||
DebugExitVOID(ASShare::IM_InControl);
|
||
}
|
||
|
||
|
||
//
|
||
// IM_Periodic
|
||
//
|
||
void ASShare::IM_Periodic(void)
|
||
{
|
||
POINT cursorPos;
|
||
UINT timeDelta;
|
||
|
||
DebugEntry(ASShare::IM_Periodic);
|
||
|
||
if (m_pasLocal->m_caInControlOf)
|
||
{
|
||
//
|
||
// Send outgoing input to person we're in control of
|
||
//
|
||
IMFlushOutgoingEvents();
|
||
}
|
||
else if (m_pasLocal->m_caControlledBy)
|
||
{
|
||
ASSERT(m_pHost);
|
||
|
||
//
|
||
// Playback input from person in control of us
|
||
//
|
||
IMMaybeInjectEvents();
|
||
|
||
//
|
||
// Get the current cursor position - we always need this.
|
||
//
|
||
GetCursorPos(&cursorPos);
|
||
|
||
//
|
||
// First check if we think that a cursor clip will have affected the
|
||
// position when we replayed a remote event.
|
||
//
|
||
if (m_imfControlledMouseClipped)
|
||
{
|
||
RECT cursorClip;
|
||
|
||
//
|
||
// Get the current clip and the current cursor position.
|
||
//
|
||
GetClipCursor(&cursorClip);
|
||
|
||
if ((cursorPos.x == cursorClip.left) ||
|
||
(cursorPos.x == (cursorClip.right-1)) ||
|
||
(cursorPos.y == cursorClip.top) ||
|
||
(cursorPos.y == (cursorClip.bottom-1)))
|
||
{
|
||
WARNING_OUT(("CM_ApplicationMovedCursor {%04d, %04d}",
|
||
cursorPos.x, cursorPos.y));
|
||
|
||
//
|
||
// We thought the cursor was going to be clipped and now we
|
||
// find it is right at the edge of the clip so tell the CM to
|
||
// tell its peers about the cursor being moved.
|
||
//
|
||
m_pHost->CM_ApplicationMovedCursor();
|
||
m_imfControlledMouseClipped = FALSE;
|
||
}
|
||
}
|
||
|
||
// We are being controlled by somebody else.
|
||
// So now's the time to decide if a SetCursorPos has
|
||
// happened. For us to believe that a SetCursorPos has actually
|
||
// occurred, the elapsed time since the last low-level input event
|
||
// was injected must be greater than IM_EVENT_PERCOLATE_TIME
|
||
// and the cursor must be in a different position to that which we
|
||
// currently believe it to be.
|
||
//
|
||
if ((cursorPos.x != m_imControlledLastMousePos.x) ||
|
||
(cursorPos.y != m_imControlledLastMousePos.y))
|
||
{
|
||
TRACE_OUT(( "GCP gives (%d,%d), last mouse event is (%d,%d)",
|
||
cursorPos.x,
|
||
cursorPos.y,
|
||
m_imControlledLastMousePos.x,
|
||
m_imControlledLastMousePos.y));
|
||
|
||
//
|
||
// Get the current tick count.
|
||
//
|
||
timeDelta = GetTickCount() - m_imControlledLastLowLevelMouseEventTime;
|
||
|
||
if (timeDelta > IM_EVENT_PERCOLATE_TIME)
|
||
{
|
||
//
|
||
// Looks like a SetCursorPos has occured - tell CM.
|
||
//
|
||
WARNING_OUT(("CM_ApplicationMovedCursor {%04d, %04d}",
|
||
cursorPos.x, cursorPos.y));
|
||
m_pHost->CM_ApplicationMovedCursor();
|
||
|
||
//
|
||
// Update the last high level mouse position.
|
||
//
|
||
m_imControlledLastMousePos.x = cursorPos.x;
|
||
m_imControlledLastMousePos.y = cursorPos.y;
|
||
}
|
||
}
|
||
}
|
||
|
||
DebugExitVOID(ASShare::IM_Periodic);
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// IM_ReceivedPacket()
|
||
//
|
||
// A null packet pointer can be used to trigger the injection of another
|
||
// pending event
|
||
//
|
||
//
|
||
// DESCRIPTION:
|
||
//
|
||
// Called when an IM events packet arrives at the PR. The IM will accept
|
||
// the incoming packet. It may copy it to an internal queue rather than
|
||
// process it immediately. IM events packets contain a series of
|
||
// piggybacked IM events.
|
||
//
|
||
// PARAMETERS:
|
||
//
|
||
// personID - the source of the packet
|
||
//
|
||
// pPacket - a pointer to the packet
|
||
//
|
||
// RETURNS: NONE
|
||
//
|
||
void ASShare::IM_ReceivedPacket
|
||
(
|
||
ASPerson * pasFrom,
|
||
PS20DATAPACKET pPacket
|
||
)
|
||
{
|
||
LPIMPACKET pIMPacket;
|
||
UINT i;
|
||
|
||
DebugEntry(ASShare::IM_ReceivedPacket);
|
||
|
||
if (!pasFrom)
|
||
{
|
||
TRACE_OUT(("Simply inject any pending events in"));
|
||
DC_QUIT;
|
||
}
|
||
|
||
ValidatePerson(pasFrom);
|
||
|
||
pIMPacket = (PIMPACKET)pPacket;
|
||
|
||
// If this person isn't in control of us, blow this off
|
||
if (pasFrom->m_caInControlOf != m_pasLocal)
|
||
{
|
||
DC_QUIT;
|
||
}
|
||
|
||
//
|
||
// For each packet in the piggybacked packets array...
|
||
//
|
||
TRACE_OUT(("IM_ReceivedPacket: Processing packet with %d events",
|
||
pIMPacket->numEvents));
|
||
for (i = 0; i < pIMPacket->numEvents; i++)
|
||
{
|
||
switch (pIMPacket->aEvents[i].type)
|
||
{
|
||
case IM_TYPE_ASCII:
|
||
case IM_TYPE_VK1:
|
||
case IM_TYPE_VK2:
|
||
case IM_TYPE_3BUTTON:
|
||
{
|
||
IMAppendNetEvent(&(pIMPacket->aEvents[i]));
|
||
break;
|
||
}
|
||
|
||
default:
|
||
//
|
||
// Unexpected events are not error - we just ignore then
|
||
// for future compatibility
|
||
//
|
||
TRACE_OUT(("Person [%d] unrecognised IM type (%04X) - event discarded",
|
||
pasFrom->mcsID, pIMPacket->aEvents[i].type));
|
||
break;
|
||
}
|
||
}
|
||
|
||
DC_EXIT_POINT:
|
||
|
||
//
|
||
// Our final action is to feed one of the new events into USER.
|
||
// We do NOT feed them all in at once because we want to simulate
|
||
// typing them in, otherwise the amount of spoiling we see is
|
||
// totally dependent upon the network latency and piggybacking.
|
||
//
|
||
ValidatePerson(m_pasLocal);
|
||
if (m_pasLocal->m_caControlledBy)
|
||
{
|
||
//
|
||
// @@@JPB: Temporary - want to inject as many events as possible -
|
||
// this should be moved to a loop within IMMaybeInjectEvents...
|
||
//
|
||
// This greatly improves responsiveness when handling a large
|
||
// number of input events in a short space of time (e.g. pounding
|
||
// on the keyboard) - very little overrun.
|
||
//
|
||
for (i = 0; i < 10; i++)
|
||
{
|
||
IMMaybeInjectEvents();
|
||
}
|
||
|
||
//
|
||
// Go into TURBO scheduling if this is a real input packet.
|
||
//
|
||
if (pPacket != NULL)
|
||
{
|
||
SCH_ContinueScheduling(SCH_MODE_TURBO);
|
||
}
|
||
}
|
||
|
||
DebugExitVOID(ASShare::IM_ReceivedPacket);
|
||
}
|
||
|
||
|
||
|
||
|
||
//
|
||
// IMGetHighLevelKeyState
|
||
//
|
||
// DESCRIPTION:
|
||
//
|
||
// Called by the IEM when it is converting a local event to a network event
|
||
// to determine the state of the local keyboard when the event was
|
||
// generated.
|
||
//
|
||
// PARAMETERS:
|
||
//
|
||
// vk - the key
|
||
//
|
||
// RETURNS:
|
||
//
|
||
// Flags - bit 7 set/reset key down/up, bit 0 toggle
|
||
//
|
||
//
|
||
BYTE ASShare::IMGetHighLevelKeyState(UINT vk)
|
||
{
|
||
int keyState;
|
||
BYTE rc;
|
||
|
||
DebugEntry(ASShare::IMGetHighLevelKeyState);
|
||
|
||
keyState = GetKeyState(vk);
|
||
|
||
rc = (BYTE) (((keyState & 0x8000) >> 8) | keyState & 0x0001);
|
||
|
||
DebugExitDWORD(ASShare::IMGetHighLevelKeyState, rc);
|
||
return(rc);
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// FUNCTION: IMFlushOutgoingEvents
|
||
//
|
||
// DESCRIPTION:
|
||
//
|
||
// Called to send new IMEVENTs (as they are generated and periodically).
|
||
// This function will send as many IMEVENTs from the current backlog as
|
||
// possible.
|
||
//
|
||
// PARAMETERS: NONE
|
||
//
|
||
// RETURNS: NONE
|
||
//
|
||
//
|
||
void ASShare::IMFlushOutgoingEvents(void)
|
||
{
|
||
UINT i;
|
||
UINT sizeOfPacket;
|
||
PIMPACKET pIMPacket;
|
||
UINT lastEvent;
|
||
UINT secondLastEvent;
|
||
UINT elapsedTime;
|
||
UINT time;
|
||
UINT eventsToSend;
|
||
UINT curTime;
|
||
BOOL holdPacket;
|
||
#ifdef _DEBUG
|
||
UINT sentSize;
|
||
#endif // _DEBUG
|
||
|
||
DebugEntry(ASShare::IMFlushOutgoingEvents);
|
||
|
||
ValidateView(m_pasLocal->m_caInControlOf);
|
||
|
||
//
|
||
// Try to convert the input into a bunch of IMEVENTs
|
||
//
|
||
while (m_imfInControlEventIsPending && (m_imInControlEventQ.numEvents < IM_SIZE_EVENTQ))
|
||
{
|
||
//
|
||
// There is space to try and convert the pending packet.
|
||
//
|
||
m_imfInControlEventIsPending = (IMTranslateOutgoing(&m_imInControlPendingEvent,
|
||
&m_imInControlEventQ.events[CIRCULAR_INDEX(m_imInControlEventQ.head,
|
||
m_imInControlEventQ.numEvents, IM_SIZE_EVENTQ)]) != FALSE);
|
||
if (m_imfInControlEventIsPending)
|
||
{
|
||
//
|
||
// We have added a packet to the queue - update our queue
|
||
// tracking variables.
|
||
//
|
||
m_imInControlEventQ.numEvents++;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Mouse handling has been improved in the following ways
|
||
// - withhold generation of packets while we are purely handling
|
||
// mouse moves and we are within the LOCAL_MOUSE_WITHHOLD range
|
||
// While we are doing this spoil them to the highest frequency
|
||
// we are permitted to generate (SAMPLING_GAP_HIGH)
|
||
// - if we exceed the withholding threshhold but remain within queue
|
||
// size/2 spoil down to the intermediate range
|
||
// (SAMPLING_GAP_MEDIUM)
|
||
// - otherwise spoil down to the low range
|
||
//
|
||
// We spoil the events by hanging on to the last event for a while, if
|
||
// it was a mouse move, so that we can use it for subsequent spoiling.
|
||
// Whenever we get a non-mouse message then we spoil the lot to
|
||
// eliminate latency, on clicks, for example.
|
||
//
|
||
|
||
//
|
||
// Calculate the mouse spoil rate - do we need more than just the high
|
||
// rate spoiling?
|
||
//
|
||
if (m_imInControlEventQ.numEvents > m_imInControlMouseWithhold + 1)
|
||
{
|
||
//
|
||
// Are we into intermediate or low spoiling?
|
||
//
|
||
if (m_imInControlEventQ.numEvents < (IM_SIZE_EVENTQ +
|
||
m_imInControlMouseWithhold) / 2)
|
||
{
|
||
TRACE_OUT(( "Mouse spoil rate to MEDIUM"));
|
||
m_imInControlMouseSpoilRate = IM_LOCAL_MOUSE_SAMPLING_GAP_MEDIUM_MS;
|
||
}
|
||
else
|
||
{
|
||
TRACE_OUT(( "Mouse spoil rate to LOW"));
|
||
m_imInControlMouseSpoilRate = IM_LOCAL_MOUSE_SAMPLING_GAP_LOW_MS;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Spoil at the normal high rate
|
||
//
|
||
if (m_imInControlMouseSpoilRate != IM_LOCAL_MOUSE_SAMPLING_GAP_HIGH_MS)
|
||
{
|
||
TRACE_OUT(( "Mouse spoil rate to HIGH"));
|
||
m_imInControlMouseSpoilRate = IM_LOCAL_MOUSE_SAMPLING_GAP_HIGH_MS;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Firstly get a pointer to lastEvent for use here and in send arm
|
||
// below (We wont use it if m_imInControlEventQ.numEvents == 0)
|
||
//
|
||
lastEvent = CIRCULAR_INDEX(m_imInControlEventQ.head,
|
||
m_imInControlEventQ.numEvents - 1, IM_SIZE_EVENTQ);
|
||
|
||
//
|
||
// Now perform the spoiling, if necessary
|
||
//
|
||
if (m_imInControlEventQ.numEvents > 1)
|
||
{
|
||
if (lastEvent == 0)
|
||
{
|
||
secondLastEvent = IM_SIZE_EVENTQ - 1;
|
||
}
|
||
else
|
||
{
|
||
secondLastEvent = lastEvent - 1;
|
||
}
|
||
|
||
elapsedTime = m_imInControlEventQ.events[lastEvent].timeMS
|
||
- m_imInControlEventQ.events[secondLastEvent].timeMS;
|
||
TRACE_OUT(( "Inter packet time %d, sampling gap %ld",
|
||
elapsedTime,m_imInControlMouseSpoilRate));
|
||
|
||
if ((elapsedTime < m_imInControlMouseSpoilRate) &&
|
||
(m_imInControlEventQ.events[lastEvent].type == IM_TYPE_3BUTTON) &&
|
||
(m_imInControlEventQ.events[secondLastEvent].type == IM_TYPE_3BUTTON) &&
|
||
(m_imInControlEventQ.events[lastEvent].data.mouse.flags &
|
||
IM_FLAG_MOUSE_MOVE) &&
|
||
(m_imInControlEventQ.events[secondLastEvent].data.mouse.flags &
|
||
IM_FLAG_MOUSE_MOVE))
|
||
{
|
||
TRACE_OUT(( "spoil mouse move from pos %u", secondLastEvent));
|
||
time = m_imInControlEventQ.events[secondLastEvent].timeMS;
|
||
m_imInControlEventQ.events[secondLastEvent] =
|
||
m_imInControlEventQ.events[lastEvent];
|
||
m_imInControlEventQ.events[secondLastEvent].timeMS = time;
|
||
m_imInControlEventQ.numEvents--;
|
||
lastEvent = secondLastEvent;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If we have any events queued up and we are not waiting for a mouse
|
||
// button up event then try to send them. (Note we do not wait for a
|
||
// mouse up event if the queue is full because if we got a mouse up
|
||
// when the queue was full then we would have nowhere to put it!)
|
||
//
|
||
curTime = GetTickCount();
|
||
|
||
if ((m_imInControlEventQ.numEvents != 0) &&
|
||
((m_imfInControlEventIsPending ||
|
||
(m_imInControlMouseDownCount == 0) ||
|
||
(curTime - m_imInControlMouseDownTime > IM_MOUSE_UP_WAIT_TIME))))
|
||
{
|
||
//
|
||
// If there are mouse move messages on the queue and they are not
|
||
// so old that we should send them anyway then hold them to allow
|
||
// some spoiling to take place.
|
||
//
|
||
holdPacket = FALSE;
|
||
|
||
if (m_imInControlEventQ.numEvents <= m_imInControlMouseWithhold)
|
||
{
|
||
if ((m_imInControlEventQ.events[lastEvent].type == IM_TYPE_3BUTTON) &&
|
||
(m_imInControlEventQ.events[lastEvent].data.mouse.flags &
|
||
IM_FLAG_MOUSE_MOVE))
|
||
{
|
||
if (curTime < (m_imInControlEventQ.events[m_imInControlEventQ.head].timeMS +
|
||
IM_LOCAL_WITHHOLD_DELAY))
|
||
{
|
||
holdPacket = TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (m_imInControlEventQ.numEvents <= IM_LOCAL_KEYBOARD_WITHHOLD)
|
||
{
|
||
//
|
||
// If the message indicates the key is down then wait, either
|
||
// for the release we know is coming, or intil it has auto
|
||
// repeated for a while or until the buffer is full.
|
||
//
|
||
if (((m_imInControlEventQ.events[lastEvent].type == IM_TYPE_ASCII) ||
|
||
(m_imInControlEventQ.events[lastEvent].type == IM_TYPE_VK1) ||
|
||
(m_imInControlEventQ.events[lastEvent].type == IM_TYPE_VK2)) &&
|
||
(m_imInControlEventQ.events[lastEvent].data.keyboard.flags &
|
||
IM_FLAG_KEYBOARD_DOWN))
|
||
{
|
||
curTime = GetTickCount();
|
||
if (curTime < (m_imInControlEventQ.events[m_imInControlEventQ.head].timeMS +
|
||
IM_LOCAL_WITHHOLD_DELAY))
|
||
{
|
||
holdPacket = TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!holdPacket)
|
||
{
|
||
UINT destID;
|
||
|
||
TRACE_OUT(( "Sending all %d packets",m_imInControlEventQ.numEvents));
|
||
eventsToSend = m_imInControlEventQ.numEvents;
|
||
m_imInControlEventQ.numEvents = 0;
|
||
|
||
destID = m_pasLocal->m_caInControlOf->mcsID;
|
||
|
||
sizeOfPacket = sizeof(IMPACKET) + (eventsToSend-1)*sizeof(IMEVENT);
|
||
pIMPacket = (PIMPACKET)SC_AllocPkt(PROT_STR_INPUT, destID, sizeOfPacket);
|
||
if (!pIMPacket)
|
||
{
|
||
//
|
||
// Failed to send this packet - keep the data on the queue
|
||
// until the next time we are called. To prevent the loss
|
||
// of data, just make sure that the local packet list is
|
||
// not overwritten by restoring the current out packets
|
||
// count.
|
||
//
|
||
WARNING_OUT(("Failed to alloc IM packet, size %u", sizeOfPacket));
|
||
m_imInControlEventQ.numEvents = eventsToSend;
|
||
}
|
||
else
|
||
{
|
||
TRACE_OUT(( "NetAllocPkt successful for %d packets size %d",
|
||
eventsToSend, sizeOfPacket));
|
||
|
||
//
|
||
// Fill in the packet header.
|
||
//
|
||
pIMPacket->header.data.dataType = DT_IM;
|
||
|
||
//
|
||
// Construct the contents of the IM specific part of the
|
||
// packet.
|
||
//
|
||
pIMPacket->numEvents = (TSHR_UINT16)eventsToSend;
|
||
for (i = 0; i < eventsToSend; i++)
|
||
{
|
||
pIMPacket->aEvents[i] = m_imInControlEventQ.events[m_imInControlEventQ.head];
|
||
m_imInControlEventQ.head =
|
||
CIRCULAR_INDEX(m_imInControlEventQ.head, 1,
|
||
IM_SIZE_EVENTQ);
|
||
}
|
||
|
||
//
|
||
// Now send the packet.
|
||
//
|
||
#ifdef _DEBUG
|
||
sentSize =
|
||
#endif // _DEBUG
|
||
DCS_CompressAndSendPacket(PROT_STR_INPUT, destID,
|
||
&(pIMPacket->header), sizeOfPacket);
|
||
|
||
TRACE_OUT(("IM packet size: %08d, sent %08d", sizeOfPacket, sentSize));
|
||
}
|
||
}
|
||
}
|
||
|
||
DebugExitVOID(ASShare::IMFlushOutgoingEvents);
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// IMSpoilEvents()
|
||
//
|
||
// Called when outgoing IM packets get backlogged, we spoil every other
|
||
// mouse move to shrink the number of events and therefore the size of the
|
||
// IM packet(s).
|
||
//
|
||
void ASShare::IMSpoilEvents(void)
|
||
{
|
||
UINT lastEvent;
|
||
UINT i;
|
||
UINT j;
|
||
UINT k;
|
||
BOOL discard = TRUE;
|
||
|
||
DebugEntry(ASShare::IMSpoilEvents);
|
||
|
||
WARNING_OUT(( "Major spoiling due to IM packet queue backlog!"));
|
||
|
||
i = CIRCULAR_INDEX(m_imInControlEventQ.head,
|
||
m_imInControlEventQ.numEvents - 1, IM_SIZE_EVENTQ);
|
||
while (i != m_imInControlEventQ.head)
|
||
{
|
||
if ((m_imInControlEventQ.events[i].type == IM_TYPE_3BUTTON) &&
|
||
(m_imInControlEventQ.events[i].data.mouse.flags & IM_FLAG_MOUSE_MOVE))
|
||
{
|
||
if (discard)
|
||
{
|
||
TRACE_OUT(( "spoil mouse move from pos %u", i));
|
||
j = CIRCULAR_INDEX(i, 1, IM_SIZE_EVENTQ);
|
||
k = i;
|
||
lastEvent = CIRCULAR_INDEX(m_imInControlEventQ.head,
|
||
m_imInControlEventQ.numEvents - 1, IM_SIZE_EVENTQ);
|
||
while (k != lastEvent)
|
||
{
|
||
//
|
||
// Shuffle the entries along the queue.
|
||
//
|
||
m_imInControlEventQ.events[k] = m_imInControlEventQ.events[j];
|
||
|
||
k = CIRCULAR_INDEX(k, 1, IM_SIZE_EVENTQ);
|
||
j = CIRCULAR_INDEX(j, 1, IM_SIZE_EVENTQ);
|
||
}
|
||
|
||
m_imInControlEventQ.numEvents--;
|
||
discard = FALSE;
|
||
}
|
||
else
|
||
{
|
||
discard = TRUE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Move on to the next event infront of this one.
|
||
//
|
||
if (i > 0)
|
||
{
|
||
i = i - 1;
|
||
}
|
||
else
|
||
{
|
||
i = IM_SIZE_EVENTQ - 1;
|
||
}
|
||
}
|
||
|
||
DebugExitVOID(ASShare::IMSpoilEvents);
|
||
}
|
||
|
||
|
||
//
|
||
// IMAppendNetEvent()
|
||
//
|
||
// Add the incoming event to the remote network queue, doing basic
|
||
// translation like mouse button swapping. Ignore unrecognized events.
|
||
//
|
||
void ASShare::IMAppendNetEvent(PIMEVENT pIMEvent)
|
||
{
|
||
int i;
|
||
BOOL discard = TRUE;
|
||
|
||
DebugEntry(ASShare::IMAppendNetEvent);
|
||
|
||
switch (pIMEvent->type)
|
||
{
|
||
case IM_TYPE_3BUTTON:
|
||
if (!(pIMEvent->data.mouse.flags & IM_FLAG_MOUSE_MOVE))
|
||
{
|
||
//
|
||
// Swap the mouse buttons if necessary.
|
||
//
|
||
if (m_imfControlledMouseButtonsReversed &&
|
||
(pIMEvent->data.mouse.flags &
|
||
(TSHR_UINT16)(IM_FLAG_MOUSE_BUTTON1 |
|
||
IM_FLAG_MOUSE_BUTTON2)))
|
||
{
|
||
pIMEvent->data.mouse.flags ^=
|
||
(TSHR_UINT16)(IM_FLAG_MOUSE_BUTTON1 |
|
||
IM_FLAG_MOUSE_BUTTON2);
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
|
||
|
||
//
|
||
// Now put the IMEVENT into our queue.
|
||
// Before we try to add the current packet we will try to inject some
|
||
// more events (and therefore make space on the network event queue)
|
||
//
|
||
|
||
if (m_imControlledEventQ.numEvents >= IM_SIZE_EVENTQ)
|
||
{
|
||
//
|
||
// Our network event queue is full - discard every other mouse
|
||
// move event in the queue.
|
||
//
|
||
WARNING_OUT(( "Major spoiling due to network event queue backlog!"));
|
||
|
||
for (i = m_imControlledEventQ.numEvents - 1; i >= 0; i--)
|
||
{
|
||
if (IM_IS_MOUSE_MOVE(m_imControlledEventQ.events[i].data.mouse.flags))
|
||
{
|
||
if (discard)
|
||
{
|
||
//
|
||
// Remove this mouse move event by moving all events
|
||
// after it down one.
|
||
//
|
||
WARNING_OUT(("Discard mouse move to {%d, %d}",
|
||
(UINT)(m_imControlledEventQ.events[i].data.mouse.x),
|
||
(UINT)(m_imControlledEventQ.events[i].data.mouse.y)));
|
||
|
||
UT_MoveMemory(&(m_imControlledEventQ.events[i]),
|
||
&(m_imControlledEventQ.events[i+1]),
|
||
sizeof(IMEVENT) *
|
||
(m_imControlledEventQ.numEvents-1-i) );
|
||
|
||
m_imControlledEventQ.numEvents--;
|
||
discard = FALSE;
|
||
}
|
||
else
|
||
{
|
||
discard = TRUE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (m_imControlledEventQ.numEvents + 1 >= IM_SIZE_EVENTQ)
|
||
{
|
||
//
|
||
// We've done our best and can't find any space.
|
||
//
|
||
WARNING_OUT(( "IM packet dropped %04X", pIMEvent->type));
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Add this event to the queue
|
||
//
|
||
m_imControlledEventQ.events[m_imControlledEventQ.numEvents] = *pIMEvent;
|
||
m_imControlledEventQ.numEvents++;
|
||
}
|
||
|
||
DebugExitVOID(ASShare::IMAppendNetEvent);
|
||
}
|
||
|
||
|
||
|
||
|
||
//
|
||
// IM_OutgoingMouseInput()
|
||
//
|
||
// Called to send mouse moves and clicks to the remote host.
|
||
// Called from the view window code.
|
||
//
|
||
void ASShare::IM_OutgoingMouseInput
|
||
(
|
||
ASPerson * pasHost,
|
||
LPPOINT pMousePos,
|
||
UINT message,
|
||
UINT dwExtra
|
||
)
|
||
{
|
||
IMEVENT imEvent;
|
||
|
||
DebugEntry(ASShare::IM_OutgoingMouseInput);
|
||
|
||
ValidateView(pasHost);
|
||
ASSERT(pasHost->m_caControlledBy == m_pasLocal);
|
||
|
||
GetKeyboardState(m_aimInControlKeyStates);
|
||
|
||
//
|
||
// Create the event.
|
||
//
|
||
imEvent.type = IM_TYPE_3BUTTON;
|
||
|
||
//
|
||
// We should only get WM_MOUSE* messages.
|
||
//
|
||
ASSERT(message >= WM_MOUSEFIRST);
|
||
ASSERT(message <= WM_MOUSELAST);
|
||
|
||
//
|
||
// Convert to bit flags.
|
||
//
|
||
switch (message)
|
||
{
|
||
case WM_MOUSEMOVE:
|
||
imEvent.data.mouse.flags = IM_FLAG_MOUSE_MOVE;
|
||
break;
|
||
|
||
case WM_LBUTTONDOWN:
|
||
imEvent.data.mouse.flags = IM_FLAG_MOUSE_BUTTON1 |
|
||
IM_FLAG_MOUSE_DOWN;
|
||
break;
|
||
|
||
case WM_LBUTTONDBLCLK:
|
||
imEvent.data.mouse.flags = IM_FLAG_MOUSE_BUTTON1 |
|
||
IM_FLAG_MOUSE_DOUBLE |
|
||
IM_FLAG_MOUSE_DOWN;
|
||
break;
|
||
|
||
case WM_LBUTTONUP:
|
||
imEvent.data.mouse.flags = IM_FLAG_MOUSE_BUTTON1;
|
||
break;
|
||
|
||
case WM_RBUTTONDOWN:
|
||
imEvent.data.mouse.flags = IM_FLAG_MOUSE_BUTTON2 |
|
||
IM_FLAG_MOUSE_DOWN;
|
||
break;
|
||
|
||
case WM_RBUTTONDBLCLK:
|
||
imEvent.data.mouse.flags = IM_FLAG_MOUSE_BUTTON2 |
|
||
IM_FLAG_MOUSE_DOUBLE |
|
||
IM_FLAG_MOUSE_DOWN;
|
||
break;
|
||
|
||
case WM_RBUTTONUP:
|
||
imEvent.data.mouse.flags = IM_FLAG_MOUSE_BUTTON2;
|
||
break;
|
||
|
||
case WM_MBUTTONDOWN:
|
||
imEvent.data.mouse.flags = IM_FLAG_MOUSE_BUTTON3 |
|
||
IM_FLAG_MOUSE_DOWN;
|
||
break;
|
||
|
||
case WM_MBUTTONDBLCLK:
|
||
imEvent.data.mouse.flags = IM_FLAG_MOUSE_BUTTON3 |
|
||
IM_FLAG_MOUSE_DOUBLE |
|
||
IM_FLAG_MOUSE_DOWN;
|
||
break;
|
||
|
||
case WM_MBUTTONUP:
|
||
imEvent.data.mouse.flags = IM_FLAG_MOUSE_BUTTON3;
|
||
break;
|
||
|
||
case WM_MOUSEWHEEL:
|
||
//
|
||
// LAURABU BOGUSBOGUS
|
||
//
|
||
// The HIWORD of wParam represents the # of clicks the wheel
|
||
// has turned.
|
||
//
|
||
// But what about Win95? NT and Win95 Magellan mouse work
|
||
// differently.
|
||
//
|
||
imEvent.data.mouse.flags = IM_FLAG_MOUSE_WHEEL;
|
||
|
||
//
|
||
// Check for overflows. If the wheel delta is outside the
|
||
// values that can be sent by the protocol, send the maximum
|
||
// values.
|
||
//
|
||
if ((TSHR_INT16)HIWORD(dwExtra) >
|
||
(IM_FLAG_MOUSE_ROTATION_MASK - IM_FLAG_MOUSE_DIRECTION))
|
||
{
|
||
ERROR_OUT(( "Mouse wheel overflow %hd", HIWORD(dwExtra)));
|
||
imEvent.data.mouse.flags |=
|
||
(IM_FLAG_MOUSE_ROTATION_MASK - IM_FLAG_MOUSE_DIRECTION);
|
||
}
|
||
else if ((TSHR_INT16)HIWORD(dwExtra) < -IM_FLAG_MOUSE_DIRECTION)
|
||
{
|
||
ERROR_OUT(( "Mouse wheel underflow %hd", HIWORD(dwExtra)));
|
||
imEvent.data.mouse.flags |= IM_FLAG_MOUSE_DIRECTION;
|
||
}
|
||
else
|
||
{
|
||
imEvent.data.mouse.flags |=
|
||
(HIWORD(dwExtra) & IM_FLAG_MOUSE_ROTATION_MASK);
|
||
}
|
||
|
||
//
|
||
// Win95 boxes need to know whether the middle mouse button is
|
||
// up or down.
|
||
//
|
||
if (LOWORD(dwExtra) & MK_MBUTTON)
|
||
{
|
||
imEvent.data.mouse.flags |= IM_FLAG_MOUSE_DOWN;
|
||
}
|
||
break;
|
||
|
||
default:
|
||
imEvent.data.mouse.flags = IM_FLAG_MOUSE_MOVE;
|
||
ERROR_OUT(( "Unrecognised mouse event - %#x", message));
|
||
break;
|
||
}
|
||
|
||
TRACE_OUT(( "Mouse event flags %hx", imEvent.data.mouse.flags));
|
||
|
||
imEvent.data.mouse.x = (TSHR_INT16)(pMousePos->x);
|
||
imEvent.data.mouse.y = (TSHR_INT16)(pMousePos->y);
|
||
imEvent.timeMS = GetTickCount();
|
||
|
||
//
|
||
// If this is a mouse down event then we will wait a while before
|
||
// sending the packet for a mouse up event so that a single click
|
||
// can be sent in one packet to avoid timing problems on the remote
|
||
// side - with for example a scroll bar scrolling multiple lines
|
||
// instead of just one line.
|
||
//
|
||
|
||
if ((message == WM_LBUTTONDOWN) ||
|
||
(message == WM_RBUTTONDOWN) ||
|
||
(message == WM_MBUTTONDOWN) ||
|
||
(message == WM_LBUTTONDBLCLK) ||
|
||
(message == WM_RBUTTONDBLCLK) ||
|
||
(message == WM_MBUTTONDBLCLK))
|
||
{
|
||
m_imInControlMouseDownCount++;
|
||
m_imInControlMouseDownTime = GetTickCount();
|
||
}
|
||
else if ((message == WM_LBUTTONUP) ||
|
||
(message == WM_RBUTTONUP) ||
|
||
(message == WM_MBUTTONUP))
|
||
{
|
||
--m_imInControlMouseDownCount;
|
||
if (m_imInControlMouseDownCount < 0)
|
||
{
|
||
TRACE_OUT(("Unmatched button down for %d", message));
|
||
m_imInControlMouseDownCount = 0;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Try to send the packet.
|
||
//
|
||
if (!IMConvertAndSendEvent(pasHost, &imEvent))
|
||
{
|
||
WARNING_OUT(("Couldn't send mouse packet from local node"));
|
||
}
|
||
|
||
DebugExitVOID(ASShare::IM_OutgoingMouseInput);
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// IM_OutgoingKeyboardInput()
|
||
//
|
||
// Called to key downs, ups, and chars to the remote host.
|
||
// Called from the view window code.
|
||
//
|
||
void ASShare::IM_OutgoingKeyboardInput
|
||
(
|
||
ASPerson * pasHost,
|
||
UINT wParam,
|
||
UINT lParam
|
||
)
|
||
{
|
||
IMEVENT imEvent;
|
||
int rc;
|
||
int retFlags;
|
||
WORD result[2];
|
||
UINT i;
|
||
BOOL fSwallowDeadKey;
|
||
UINT mainVK;
|
||
|
||
DebugEntry(ASShare::IM_OutgoingKeyboardInput);
|
||
|
||
ValidateView(pasHost);
|
||
|
||
ASSERT(pasHost->m_caControlledBy = m_pasLocal);
|
||
|
||
GetKeyboardState(m_aimInControlKeyStates);
|
||
|
||
//
|
||
// Trace out the parameters once we've got this far.
|
||
//
|
||
TRACE_OUT(( "wParam - %04X, lParam - %08lX", wParam, lParam));
|
||
|
||
//
|
||
// Create the event.
|
||
//
|
||
imEvent.data.keyboard.flags = (TSHR_UINT16)
|
||
(HIWORD(lParam) & IM_MASK_KEYBOARD_SYSFLAGS);
|
||
imEvent.timeMS = GetTickCount();
|
||
imEvent.data.keyboard.keyCode = LOBYTE(wParam);
|
||
|
||
retFlags = CA_SEND_EVENT | CA_ALLOW_EVENT;
|
||
|
||
if ((wParam == VK_LWIN) || (wParam == VK_RWIN))
|
||
{
|
||
//
|
||
// The Windows keys give control to the local user interface.
|
||
//
|
||
// The keys are defined to do the following by the spec "New key
|
||
// support for Microsoft Windows Operating Systems and
|
||
// Applications"
|
||
//
|
||
// Left Windows key - set focus to Win95 user interface
|
||
// Right Windows key - as left
|
||
// Both Windows keys - Log-on key for Windows NT
|
||
// Windows key + any other - reserved for system hot keys
|
||
//
|
||
// Thus it does not make any sense to send these keys to the remote
|
||
// system at all.
|
||
//
|
||
retFlags &= ~CA_SEND_EVENT;
|
||
}
|
||
else if ((wParam == VK_PROCESSKEY) && (m_imImmGVK != NULL))
|
||
{
|
||
//
|
||
// An IME has processed this key - we want to find out what the
|
||
// original key was so call <ImmGetVirtualKey>.
|
||
//
|
||
ValidateView(pasHost);
|
||
wParam = m_imImmGVK(pasHost->m_pView->m_viewClient);
|
||
|
||
TRACE_OUT(( "Translated wP from VK_PROCESSKEY to %#lx", wParam));
|
||
}
|
||
|
||
if (retFlags & CA_SEND_EVENT)
|
||
{
|
||
//
|
||
// First check if this is a dead-key up stroke - if it is then
|
||
// don't call ToAscii as the shift state may have changed and we'll
|
||
// get the wrong accent or no accent at all. Assume that if the VK
|
||
// is a potential dead key VK (disregarding shift state) and
|
||
// m_imInControlNumDeadKeysDown is > 0 that this is a dead key - swallow
|
||
// it.
|
||
//
|
||
fSwallowDeadKey = FALSE;
|
||
|
||
if ((m_imInControlNumDeadKeysDown != 0) &&
|
||
(imEvent.data.keyboard.flags & IM_FLAG_KEYBOARD_RELEASE))
|
||
{
|
||
for (i = 0; i < m_imInControlNumDeadKeys; i++)
|
||
{
|
||
if (m_aimInControlDeadKeys[i] == (BYTE)imEvent.data.keyboard.keyCode)
|
||
{
|
||
//
|
||
// Assume this is a dead key up and therefore we don't
|
||
// want to pass it through ToAscii or generate any
|
||
// events based on it.
|
||
//
|
||
m_imInControlNumDeadKeysDown--;
|
||
TRACE_OUT(( "m_imInControlNumDeadKeysDown - %d",
|
||
m_imInControlNumDeadKeysDown));
|
||
fSwallowDeadKey = TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!fSwallowDeadKey)
|
||
{
|
||
//
|
||
// Find out if we can translate this virtual key into the
|
||
// Windows character set.
|
||
//
|
||
|
||
//
|
||
// Now try to convert this to an Ascii character.
|
||
//
|
||
rc = ToAscii(wParam,
|
||
LOBYTE(HIWORD(lParam)),
|
||
m_aimInControlKeyStates,
|
||
&result[0],
|
||
!(!(HIWORD(lParam) & KF_MENUMODE)));
|
||
|
||
if ((rc == 1) && (LOBYTE(result[0]) <= ' '))
|
||
{
|
||
//
|
||
// Don't use the results of ToAscii if its less than space
|
||
// (32) or space itself as Windows claims that the
|
||
// characters below this in the Windows character set are
|
||
// not supported and ToAscii will convert space plus
|
||
// modifiers to an ascii space and when we replay it
|
||
// VkKeyScan will tell us that ascii space shouldn't have
|
||
// any modifiers so we will undo any modifiers. This will
|
||
// clobber apps which interpret Ctrl-Space, Shift-Space.
|
||
//
|
||
rc = 0;
|
||
}
|
||
|
||
//
|
||
// Some Ascii characters can be generated from more than one
|
||
// key. (Eg '-' is on the main keyboard and the number pad).
|
||
// Convert this ASCII character back to a VK_ value. If it is
|
||
// different from the VK_ we started with, then do not send the
|
||
// key press as ASCII (Ie only send the 'main' way of entering
|
||
// an ASCII value as ASCII).
|
||
//
|
||
// Oprah1943: revert to the VK only if the ASCII code is less
|
||
// than 0x80. This avoids losing the diacritic in a dead-key
|
||
// sequence. VkKeyScan for the key down following the dead-key
|
||
// up returns the dead-key VK rather than that of the keystroke
|
||
// (wParam).
|
||
//
|
||
if (rc == 1)
|
||
{
|
||
mainVK = VkKeyScan(LOBYTE(result[0]));
|
||
|
||
if ( (LOBYTE(mainVK) != LOBYTE(wParam)) &&
|
||
(LOBYTE(result[0]) < 0x80) )
|
||
{
|
||
TRACE_OUT((
|
||
"Not MAIN VK pressed=0x%02hx main=0x%02hx ('%c'/%02hx)",
|
||
(TSHR_UINT16)LOBYTE(wParam),
|
||
(TSHR_UINT16)LOBYTE(mainVK),
|
||
(char)LOBYTE(result[0]),
|
||
(UINT)LOBYTE(result[0])));
|
||
rc = 0;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If ToAscii converts this to a dead key then don't send any
|
||
// packets at all.
|
||
//
|
||
if (rc != -1)
|
||
{
|
||
if (rc == 1)
|
||
{
|
||
TRACE_OUT(( "ToAscii rc=1, result - %02X",
|
||
LOBYTE(result[0])));
|
||
|
||
//
|
||
// Succesfully converted to an Ascii key.
|
||
//
|
||
imEvent.type = IM_TYPE_ASCII;
|
||
imEvent.data.keyboard.keyCode = LOBYTE(result[0]);
|
||
|
||
//
|
||
// Try to send the packet.
|
||
//
|
||
if (!IMConvertAndSendEvent(pasHost, &imEvent))
|
||
{
|
||
WARNING_OUT(( "dropped local key press %u",
|
||
(UINT)imEvent.data.keyboard.keyCode));
|
||
}
|
||
}
|
||
else if (rc == 2)
|
||
{
|
||
TRACE_OUT(( "ToAscii rc=2, result - %04X", result[0]));
|
||
|
||
//
|
||
// Succesfully converted to two Ascii keys. If this is
|
||
// a key down then we will return a key down and key up
|
||
// for the `dead' character first then the key down.
|
||
// If its a key up then just return the key up.
|
||
//
|
||
if (!(imEvent.data.keyboard.flags &
|
||
IM_FLAG_KEYBOARD_RELEASE))
|
||
{
|
||
//
|
||
// This is the key down - so generate a fake
|
||
// keyboard press for the dead key.
|
||
//
|
||
IMGenerateFakeKeyPress(IM_TYPE_ASCII,
|
||
LOBYTE(result[0]),
|
||
imEvent.data.keyboard.flags);
|
||
}
|
||
|
||
//
|
||
// Now return the current keystroke.
|
||
//
|
||
imEvent.type = IM_TYPE_ASCII;
|
||
imEvent.data.keyboard.keyCode = LOBYTE(result[1]);
|
||
|
||
//
|
||
// Try to send the packet.
|
||
//
|
||
if (!IMConvertAndSendEvent(pasHost, &imEvent))
|
||
{
|
||
WARNING_OUT(( "dropped local key press %u",
|
||
(UINT)imEvent.data.keyboard.keyCode));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Check for keys that we want to convert.
|
||
//
|
||
if (LOBYTE(wParam) == VK_KANJI)
|
||
{
|
||
//
|
||
// We only see a down press for VK_KANJI so we
|
||
// fake a complete key press so that the remote
|
||
// does not get confused.
|
||
//
|
||
IMGenerateFakeKeyPress(IM_TYPE_VK1,
|
||
VK_KANJI,
|
||
imEvent.data.keyboard.flags);
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// No conversion - use the VK itself.
|
||
//
|
||
imEvent.type = IM_TYPE_VK1;
|
||
imEvent.data.keyboard.keyCode = LOBYTE(wParam);
|
||
|
||
//
|
||
// SFR 2537: If this is a right shift VK (which we
|
||
// can detect via the scan code in lParam), set the
|
||
// right_variant keyboard flag. We do not do this
|
||
// for the right-variants of CONTROL and ALT (ie
|
||
// menu) because they are extended keys - already
|
||
// catered for by the extended flag.
|
||
//
|
||
if ( (m_imScanVKRShift != 0) &&
|
||
(m_imScanVKRShift == LOBYTE(HIWORD(lParam))) )
|
||
{
|
||
imEvent.data.keyboard.flags |=
|
||
IM_FLAG_KEYBOARD_RIGHT;
|
||
}
|
||
|
||
//
|
||
// Try to send the packet.
|
||
//
|
||
if (!IMConvertAndSendEvent(pasHost, &imEvent))
|
||
{
|
||
WARNING_OUT(( "dropped local key press %u",
|
||
(UINT)imEvent.data.keyboard.keyCode));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// This is a dead key - add it to our array of dead keys if
|
||
// we haven't already heard about it.
|
||
//
|
||
IMMaybeAddDeadKey(
|
||
(BYTE)imEvent.data.keyboard.keyCode);
|
||
m_imInControlNumDeadKeysDown++;
|
||
TRACE_OUT(( "m_imInControlNumDeadKeysDown - %d",
|
||
m_imInControlNumDeadKeysDown));
|
||
}
|
||
}
|
||
}
|
||
|
||
DebugExitVOID(ASShare::IM_OutgoingKeyboardInput);
|
||
}
|
||
|
||
|
||
//
|
||
// FUNCTION: IMGenerateFakeKeyPress(...)
|
||
//
|
||
// DESCRIPTION:
|
||
//
|
||
// Generates a fake keyboard press.
|
||
//
|
||
// PARAMETERS:
|
||
//
|
||
// type - packet type to generate.
|
||
// key - key to generate press for.
|
||
// flags - flags on keyboard press.
|
||
//
|
||
// RETURNS:
|
||
//
|
||
// Nothing.
|
||
//
|
||
//
|
||
void ASShare::IMGenerateFakeKeyPress
|
||
(
|
||
TSHR_UINT16 type,
|
||
TSHR_UINT16 key,
|
||
TSHR_UINT16 flags
|
||
)
|
||
{
|
||
IMEVENT imEventFake;
|
||
|
||
DebugEntry(ASShare::IMGenerateFakeKeyPress);
|
||
|
||
TRACE_OUT(( "Faking keyboard press:%#hx type:%#hx", key, type));
|
||
|
||
//
|
||
// Generate the key down first of all.
|
||
//
|
||
ZeroMemory(&imEventFake, sizeof(imEventFake));
|
||
|
||
imEventFake.type = type;
|
||
imEventFake.timeMS = GetTickCount();
|
||
imEventFake.data.keyboard.keyCode = key;
|
||
|
||
//
|
||
// Try to send the packet.
|
||
//
|
||
if (!IMConvertAndSendEvent(m_pasLocal->m_caInControlOf, &imEventFake))
|
||
{
|
||
WARNING_OUT(( "Dropped local key press %hu (flags: %#hx)",
|
||
imEventFake.data.keyboard.keyCode,
|
||
imEventFake.data.keyboard.flags));
|
||
}
|
||
|
||
//
|
||
// Set the release and down flags in order to fake the up.
|
||
//
|
||
imEventFake.data.keyboard.flags = IM_FLAG_KEYBOARD_DOWN | IM_FLAG_KEYBOARD_RELEASE;
|
||
|
||
//
|
||
// Try to send the packet.
|
||
//
|
||
if (!IMConvertAndSendEvent(m_pasLocal->m_caInControlOf, &imEventFake))
|
||
{
|
||
WARNING_OUT(( "Dropped local key press %hu (flags: %#hx)",
|
||
imEventFake.data.keyboard.keyCode,
|
||
imEventFake.data.keyboard.flags));
|
||
}
|
||
|
||
DebugExitVOID(ASShare::IMGenerateFakeKeyPress);
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
//
|
||
// FUNCTION: IMConvertAndSendEvent
|
||
//
|
||
// DESCRIPTION:
|
||
//
|
||
// Called with an IMEVENT this function will try to queue (and even send
|
||
// if possible) the packet. If it fails it will return FALSE - the caller
|
||
// should discard the packet. If it succeeds it will return TRUE.
|
||
//
|
||
// If pasFor is us, it means to send to everybody (and coords are relative
|
||
// to sender's screen).
|
||
//
|
||
// If pasFor is a remote, it means that the IM packet is meant for just
|
||
// that person and the coords are relative to pasFor's screen.
|
||
//
|
||
//
|
||
// PARAMETERS:
|
||
//
|
||
// pIMEvent - the IMEVENT to convert and send
|
||
//
|
||
// RETURNS: TRUE or FALSE - success or failure
|
||
//
|
||
//
|
||
BOOL ASShare::IMConvertAndSendEvent
|
||
(
|
||
ASPerson * pasFor,
|
||
PIMEVENT pIMEvent
|
||
)
|
||
{
|
||
BOOL rc = FALSE;
|
||
|
||
DebugEntry(ASShare::IMConvertAndSendEvent);
|
||
|
||
//
|
||
// If there is already a pending packet then see if we can flush some
|
||
// packets onto the network.
|
||
//
|
||
if (m_imfInControlEventIsPending)
|
||
{
|
||
IMFlushOutgoingEvents();
|
||
}
|
||
|
||
//
|
||
// If there is still a pending packet then see if we can spoil some
|
||
// events.
|
||
//
|
||
if (m_imfInControlEventIsPending)
|
||
{
|
||
TRACE_OUT(( "trying to drop mouse move events"));
|
||
IMSpoilEvents();
|
||
IMFlushOutgoingEvents();
|
||
}
|
||
|
||
//
|
||
// Now see if we are able to accept a new packet
|
||
//
|
||
if (m_imfInControlEventIsPending)
|
||
{
|
||
//
|
||
// If there is still a previous IMEVENT which we are in the
|
||
// process of converting then we are not ready to receive any more
|
||
// packets.
|
||
//
|
||
TRACE_OUT(( "can't queue packet"));
|
||
DC_QUIT;
|
||
}
|
||
|
||
//
|
||
// Now set up the new packet and try to flush the packets again.
|
||
//
|
||
m_imfInControlEventIsPending = TRUE;
|
||
m_imInControlPendingEvent = *pIMEvent;
|
||
IMFlushOutgoingEvents();
|
||
|
||
rc = TRUE;
|
||
|
||
DC_EXIT_POINT:
|
||
DebugExitBOOL(ASShare::IMConvertAndSendEvent, rc);
|
||
return(rc);
|
||
}
|
||
|
||
|
||
//
|
||
// FUNCTION: IMMaybeAddDeadKey
|
||
//
|
||
// DESCRIPTION:
|
||
//
|
||
// Called whenever ToAscii tells us about a dead key. If we haven't
|
||
// got it in our table already then we will add it. We create the table
|
||
// incrementally because we have found that some keyboard drivers don't
|
||
// cope very well with being queried with all possible VKs to find the
|
||
// dead keys. Note that this will not cope with someone switching their
|
||
// keyboard driver whilst DC-Share is running.
|
||
//
|
||
// PARAMETERS:
|
||
//
|
||
// vk - the VK in question
|
||
//
|
||
// RETURNS: NONE
|
||
//
|
||
//
|
||
void ASShare::IMMaybeAddDeadKey(BYTE vk)
|
||
{
|
||
UINT i;
|
||
|
||
DebugEntry(IMMaybeAddDeadKey);
|
||
|
||
//
|
||
// First see if we already know about this key.
|
||
//
|
||
for (i = 0; i < m_imInControlNumDeadKeys; i++)
|
||
{
|
||
if (m_aimInControlDeadKeys[i] == vk)
|
||
{
|
||
DC_QUIT;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Add this key if there's space in the array.
|
||
//
|
||
if (m_imInControlNumDeadKeys < IM_MAX_DEAD_KEYS)
|
||
{
|
||
TRACE_OUT(( "Add %02X", (TSHR_UINT16)vk));
|
||
m_aimInControlDeadKeys[m_imInControlNumDeadKeys++] = vk;
|
||
}
|
||
|
||
DC_EXIT_POINT:
|
||
DebugExitVOID(ASShare::IMMaybeAddDeadKey);
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// IMConvertIMEventToOSEvent()
|
||
// Converts incoming event to something we can playback.
|
||
//
|
||
// PARAMETERS:
|
||
//
|
||
// pIMEvent - the IMEVENT to be converted
|
||
//
|
||
// pOSEvent - the IMOSEVENT to be created
|
||
//
|
||
//
|
||
UINT ASShare::IMConvertIMEventToOSEvent
|
||
(
|
||
PIMEVENT pIMEvent,
|
||
LPIMOSEVENT pOSEvent
|
||
)
|
||
{
|
||
int mouseX;
|
||
int mouseY;
|
||
int realMouseX;
|
||
int realMouseY;
|
||
RECT cursorClip;
|
||
UINT rc = (IM_IMQUEUEREMOVE | IM_OSQUEUEINJECT);
|
||
|
||
DebugEntry(ASShare::IMConvertIMEventToOSEvent);
|
||
|
||
switch (pIMEvent->type)
|
||
{
|
||
case IM_TYPE_3BUTTON:
|
||
//
|
||
// Fill in common fields. Note that we claim to be a 3 button
|
||
// mouse so that we can replay events from remote three button
|
||
// mice and we always give absolute coordinates.
|
||
//
|
||
pOSEvent->type = IM_MOUSE_EVENT;
|
||
pOSEvent->flags = 0;
|
||
pOSEvent->time = pIMEvent->timeMS;
|
||
pOSEvent->event.mouse.cButtons = 3;
|
||
pOSEvent->event.mouse.mouseData = 0;
|
||
pOSEvent->event.mouse.dwExtraInfo = 0;
|
||
|
||
//
|
||
// First check for a wheel rotate, since this is easy to
|
||
// process. (It cannot include any mouse movement as well).
|
||
//
|
||
if (pIMEvent->data.mouse.flags & IM_FLAG_MOUSE_WHEEL)
|
||
{
|
||
if (pIMEvent->data.mouse.flags &
|
||
(IM_FLAG_MOUSE_BUTTON1 |
|
||
IM_FLAG_MOUSE_BUTTON2 |
|
||
IM_FLAG_MOUSE_BUTTON3))
|
||
{
|
||
//
|
||
// Using any of the button flags along with the wheel
|
||
// flag is currently undefined - for forward
|
||
// compatability we therefore ignore such an event by
|
||
// converting it into a NULL injected event.
|
||
//
|
||
// (We do not sg_lpimSharedData->imply discard it, since the logic to
|
||
// discard events does not seem to work).
|
||
//
|
||
pOSEvent->event.mouse.flags = 0;
|
||
pOSEvent->event.mouse.pt.x = 0;
|
||
pOSEvent->event.mouse.pt.y = 0;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// This is a wheel movement.
|
||
//
|
||
// Note that the protocol has sent whether the mouse's
|
||
// middle button is depressed or released, but we don't
|
||
// need that info for NT, so just ignore it.
|
||
//
|
||
pOSEvent->event.mouse.flags = MOUSEEVENTF_WHEEL;
|
||
|
||
pOSEvent->event.mouse.mouseData =
|
||
(pIMEvent->data.mouse.flags & IM_FLAG_MOUSE_ROTATION_MASK);
|
||
pOSEvent->event.mouse.pt.x = 0;
|
||
pOSEvent->event.mouse.pt.y = 0;
|
||
|
||
//
|
||
// Sign extend the rotation amount up to the full 32
|
||
// bits
|
||
//
|
||
if (pOSEvent->event.mouse.mouseData & IM_FLAG_MOUSE_DIRECTION)
|
||
{
|
||
pOSEvent->event.mouse.mouseData |=
|
||
~IM_FLAG_MOUSE_ROTATION_MASK;
|
||
}
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
//
|
||
// We are left now with non wheel-rotate events.
|
||
//
|
||
pOSEvent->event.mouse.flags = MOUSEEVENTF_ABSOLUTE;
|
||
|
||
//
|
||
// We must convert from virtual desktop coordinates to local
|
||
// screen coordinates here and we must also prevent the
|
||
// position wrapping if we try to replay a mouse move to an
|
||
// off-screen position.
|
||
//
|
||
|
||
realMouseX = pIMEvent->data.mouse.x;
|
||
realMouseY = pIMEvent->data.mouse.y;
|
||
|
||
//
|
||
// Now lg_lpimSharedData->imit to the size of the real screen.
|
||
//
|
||
mouseX = min((m_pasLocal->cpcCaps.screen.capsScreenWidth-1), max(0, realMouseX));
|
||
mouseY = min((m_pasLocal->cpcCaps.screen.capsScreenHeight-1), max(0, realMouseY));
|
||
|
||
//
|
||
// Work out if this event will be clipped by the clip cursor
|
||
//
|
||
GetClipCursor(&cursorClip);
|
||
|
||
if ((mouseX < cursorClip.left) ||
|
||
(mouseX >= cursorClip.right) ||
|
||
(mouseY < cursorClip.top) ||
|
||
(mouseY >= cursorClip.bottom))
|
||
{
|
||
//
|
||
// This event will actually be clipped because of the
|
||
// current clip cursor. Remember this.
|
||
//
|
||
m_imfControlledMouseClipped = TRUE;
|
||
}
|
||
else
|
||
{
|
||
m_imfControlledMouseClipped = FALSE;
|
||
|
||
//
|
||
// If we clamp the mouse position before replaying then we
|
||
// must remember the real packet and make the current
|
||
// packet into a move so that we don't click down/up at the
|
||
// wrong place.
|
||
//
|
||
if ((mouseX != realMouseX) || (mouseY != realMouseY))
|
||
{
|
||
//
|
||
// The mouse position we've recieved is off the
|
||
// local physical screen. Now that we no longer have
|
||
// desktop scrolling, we simply clamp it rather than
|
||
// inject it at the edge and wait for the scroll.
|
||
//
|
||
// We turn mouse down-clicks into moves and let
|
||
// up-clicks pass through (in case the mouse button
|
||
// has been pressed within the real screen).
|
||
//
|
||
// Note that the mouse position has already been
|
||
// adjusted so that it is within the real screen.
|
||
//
|
||
if (pIMEvent->data.mouse.flags & IM_FLAG_MOUSE_DOWN)
|
||
{
|
||
pIMEvent->data.mouse.flags = IM_FLAG_MOUSE_MOVE;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Store the mouse position.
|
||
//
|
||
pOSEvent->event.mouse.pt.x = mouseX;
|
||
pOSEvent->event.mouse.pt.y = mouseY;
|
||
|
||
//
|
||
// Add more flags as appropriate.
|
||
//
|
||
if (pIMEvent->data.mouse.flags & IM_FLAG_MOUSE_MOVE)
|
||
{
|
||
pOSEvent->event.mouse.flags |= MOUSEEVENTF_MOVE;
|
||
}
|
||
else
|
||
{
|
||
switch (pIMEvent->data.mouse.flags &
|
||
( IM_FLAG_MOUSE_BUTTON1 |
|
||
IM_FLAG_MOUSE_BUTTON2 |
|
||
IM_FLAG_MOUSE_BUTTON3 |
|
||
IM_FLAG_MOUSE_DOWN ))
|
||
{
|
||
case IM_FLAG_MOUSE_BUTTON1 | IM_FLAG_MOUSE_DOWN:
|
||
pOSEvent->event.mouse.flags |= MOUSEEVENTF_LEFTDOWN;
|
||
break;
|
||
|
||
case IM_FLAG_MOUSE_BUTTON1:
|
||
pOSEvent->event.mouse.flags |= MOUSEEVENTF_LEFTUP;
|
||
break;
|
||
|
||
case IM_FLAG_MOUSE_BUTTON2 | IM_FLAG_MOUSE_DOWN:
|
||
pOSEvent->event.mouse.flags |= MOUSEEVENTF_RIGHTDOWN;
|
||
break;
|
||
|
||
case IM_FLAG_MOUSE_BUTTON2:
|
||
pOSEvent->event.mouse.flags |= MOUSEEVENTF_RIGHTUP;
|
||
break;
|
||
|
||
case IM_FLAG_MOUSE_BUTTON3 | IM_FLAG_MOUSE_DOWN:
|
||
pOSEvent->event.mouse.flags |= MOUSEEVENTF_MIDDLEDOWN;
|
||
break;
|
||
|
||
case IM_FLAG_MOUSE_BUTTON3:
|
||
pOSEvent->event.mouse.flags |= MOUSEEVENTF_MIDDLEUP;
|
||
break;
|
||
|
||
default:
|
||
//
|
||
// If we don't recognise this then don't play it
|
||
// back
|
||
//
|
||
ERROR_OUT(("Unrecognised mouse flags (%04X)",
|
||
pIMEvent->data.mouse.flags));
|
||
rc = IM_IMQUEUEREMOVE;
|
||
break;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case IM_TYPE_VK1:
|
||
//
|
||
// Common fields.
|
||
//
|
||
pOSEvent->flags = 0;
|
||
if (pIMEvent->data.keyboard.flags & IM_FLAG_KEYBOARD_UPDATESTATE)
|
||
pOSEvent->flags |= IM_FLAG_UPDATESTATE;
|
||
|
||
pOSEvent->time = pIMEvent->timeMS;
|
||
|
||
//
|
||
// Now handle normal keyboard events.
|
||
//
|
||
pOSEvent->type = IM_KEYBOARD_EVENT;
|
||
|
||
//
|
||
// AX is the scancode in AL and 00h (press) or 80h (release) in
|
||
// AH. Map the DC protocol VK to the equivalent OS VK.
|
||
// AL = the scancode for the VK).
|
||
//
|
||
pOSEvent->event.keyboard.vkCode = LOBYTE(pIMEvent->data.keyboard.keyCode);
|
||
|
||
pOSEvent->event.keyboard.flags = 0;
|
||
if (IS_IM_KEY_RELEASE(pIMEvent->data.keyboard.flags))
|
||
{
|
||
pOSEvent->event.keyboard.flags |= KEYEVENTF_KEYUP;
|
||
}
|
||
|
||
//
|
||
// SFR 2537: If the flags indicate that the received VK is the
|
||
// right-variant, do not map the VK to a scan code, but rather
|
||
// directly use the already acquired right-variant scan code
|
||
// for the VK. (For the moment, the only case we support is
|
||
// for Windows, where this is an issue for SHIFT).
|
||
//
|
||
if ( IS_IM_KEY_RIGHT(pIMEvent->data.keyboard.flags) &&
|
||
(pIMEvent->data.keyboard.keyCode == VK_SHIFT) )
|
||
{
|
||
pOSEvent->event.keyboard.scanCode = m_imScanVKRShift;
|
||
}
|
||
else
|
||
{
|
||
pOSEvent->event.keyboard.scanCode =
|
||
(WORD)MapVirtualKey(pIMEvent->data.keyboard.keyCode, 0);
|
||
}
|
||
|
||
if (pIMEvent->data.keyboard.flags & IM_FLAG_KEYBOARD_EXTENDED)
|
||
{
|
||
pOSEvent->event.keyboard.flags |= KEYEVENTF_EXTENDEDKEY;
|
||
}
|
||
|
||
pOSEvent->event.keyboard.dwExtraInfo = 0;
|
||
break;
|
||
|
||
default:
|
||
ERROR_OUT(("Unrecognized imEvent (%d)", pIMEvent->type));
|
||
//
|
||
// Discard the event (remove from the IM queue and don't inject
|
||
// into the OS).
|
||
//
|
||
rc = IM_IMQUEUEREMOVE;
|
||
break;
|
||
}
|
||
|
||
|
||
DebugExitDWORD(ASShare::IMConvertIMEventToOSEvent, rc);
|
||
return(rc);
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// IMTranslateOutgoing()
|
||
//
|
||
// DESCRIPTION:
|
||
//
|
||
// Converts locally generated sequences of IMEVENTs into transmitted
|
||
// sequences of IMEVENTs. Does a 1 to (0-n) translation. Handles
|
||
// buffering modifier keys and translating DC-Share hot-key sequences.
|
||
//
|
||
// When the CA has decided an IMEVENT should be sent this function is
|
||
// called by the IM with a pointer to that packet in pIMEventIn.
|
||
// IMTranslateOutgoing can then return TRUE and fill in the packet at
|
||
// pIMEventOut or return FALSE. If IMTranslateOutgoing returns TRUE the IM
|
||
// will call it again with the same packet. The IMEVENTs returned are
|
||
// sent across the network by the IM.
|
||
//
|
||
// PARAMETERS:
|
||
//
|
||
// pIMEventIn - pointer to IMEVENT
|
||
//
|
||
// pIMEventOut - pointer to IMEVENT
|
||
//
|
||
// RETURNS:
|
||
//
|
||
// TRUE - packet returned (call function again)
|
||
//
|
||
// FALSE - no packet returned (don't call function again)
|
||
//
|
||
//
|
||
|
||
BOOL ASShare::IMTranslateOutgoing
|
||
(
|
||
LPIMEVENT pIMEventIn,
|
||
LPIMEVENT pIMEventOut
|
||
)
|
||
{
|
||
UINT hotKeyArrayIndex;
|
||
UINT hotKeyValue;
|
||
BOOL fHotKeyFound;
|
||
BOOL rc = FALSE;
|
||
|
||
DebugEntry(ASShare::IMTranslateOutgoing);
|
||
|
||
//
|
||
// Here we need to tell the remote system about certain keys which are
|
||
// consumed locally so that it can make good decisions about whether
|
||
// and how to replay them. We want to keep the remote system in step
|
||
// with the current modifier and toggle key state on our system (as it
|
||
// is possible that either a modifier/toggle key event occurred whilst
|
||
// a local app was active and was therefore never sent) We also want to
|
||
// recognise certain `hot key' sequences and send further packets as a
|
||
// result of these.
|
||
//
|
||
// The keys we comsume locally are:
|
||
//
|
||
// Esc down or up when Ctrl is down - operates task list locally
|
||
//
|
||
// Tab down or up when Alt is down - operates task switcher locally
|
||
//
|
||
// Esc down or up when Alt is pressed - switches to next window locally
|
||
//
|
||
// Esc up when corresponding Esc down occurred when Alt was down - as
|
||
// above
|
||
//
|
||
// The sequences we want to produce hot keys from are:
|
||
//
|
||
// Alt + 9?? on the numeric keypad
|
||
//
|
||
// To detect hotkeys we keep a record of the last four keypresses and
|
||
// when we detect an Alt up we check if they form a valid sequence.
|
||
//
|
||
// The keystrokes which form part of the hotkey are sent to the remote
|
||
// system so if they have some meaning on a remote system then that
|
||
// system must decide whether to buffer them to determine if they are
|
||
// part of a hotkey or play them back anyway - on Windows we play them
|
||
// back anyway as they are a legitimate key sequence when controlling a
|
||
// Windows app - the number typed on the numeric keypad has a % 256
|
||
// applied to it.
|
||
//
|
||
// This means that for each incoming event we may want to generate 0 or
|
||
// more outgoing events. To do this we have a structure which looks
|
||
// roughly like this:
|
||
//
|
||
// IF m_m_imfInControlNewEvent
|
||
// calculate an array of events which we want to return
|
||
// set m_m_imfInControlNewEvent to FALSE
|
||
// set number of events returned to 0
|
||
// ENDIF
|
||
//
|
||
// IF !m_m_imfInControlNewEvent
|
||
// IF this is the last event to return
|
||
// set m_m_imfInControlNewEvent to TRUE
|
||
// ENDIF
|
||
// return current event
|
||
// ENDIF
|
||
//
|
||
//
|
||
|
||
if (m_imfInControlNewEvent)
|
||
{
|
||
//
|
||
// This is the first time we have seen this event so accumulate
|
||
// our list of events to generate.
|
||
//
|
||
|
||
//
|
||
// Do tracing
|
||
//
|
||
if (pIMEventIn->type == IM_TYPE_ASCII)
|
||
{
|
||
TRACE_OUT(( "IN ASCII code 0x%04X, flags 0x%04X",
|
||
pIMEventIn->data.keyboard.keyCode, pIMEventIn->data.keyboard.flags));
|
||
}
|
||
else if (pIMEventIn->type == IM_TYPE_VK1)
|
||
{
|
||
TRACE_OUT(( "IN VKEY code %04X, flags %04X",
|
||
pIMEventIn->data.keyboard.keyCode, pIMEventIn->data.keyboard.flags));
|
||
}
|
||
else if ((pIMEventIn->type == IM_TYPE_3BUTTON) &&
|
||
!(pIMEventIn->data.mouse.flags & IM_FLAG_MOUSE_MOVE))
|
||
{
|
||
TRACE_OUT(( "IN 3BTTN flags %04X (%d,%d)",
|
||
pIMEventIn->data.mouse.flags, pIMEventIn->data.mouse.x,
|
||
pIMEventIn->data.mouse.y));
|
||
}
|
||
else if (pIMEventIn->type == IM_TYPE_3BUTTON)
|
||
{
|
||
TRACE_OUT(( "IN 3BTTN flags %04X (%d,%d)",
|
||
pIMEventIn->data.mouse.flags, pIMEventIn->data.mouse.x,
|
||
pIMEventIn->data.mouse.y));
|
||
}
|
||
else if (pIMEventIn->type == IM_TYPE_VK_ASCII)
|
||
{
|
||
TRACE_OUT(("IN VK_ASC code %04X, flags %04X",
|
||
pIMEventIn->data.keyboard.keyCode, pIMEventIn->data.keyboard.flags));
|
||
}
|
||
else
|
||
{
|
||
ERROR_OUT(("Invalid IM type %d", pIMEventIn->type));
|
||
}
|
||
|
||
//
|
||
// Start from the beginning of our returned events array.
|
||
//
|
||
m_imInControlNumEventsPending = 0;
|
||
m_imInControlNumEventsReturned = 0;
|
||
|
||
//
|
||
// First get our flags for the modifiers and locks we think we have
|
||
// sent to the remote side up to date allowing for this event.
|
||
//
|
||
if (pIMEventIn->type == IM_TYPE_VK1)
|
||
{
|
||
switch (pIMEventIn->data.keyboard.keyCode)
|
||
{
|
||
case VK_CONTROL:
|
||
if (IS_IM_KEY_RELEASE(pIMEventIn->data.keyboard.flags))
|
||
{
|
||
m_imfInControlCtrlDown = FALSE;
|
||
}
|
||
else
|
||
{
|
||
m_imfInControlCtrlDown = TRUE;
|
||
}
|
||
break;
|
||
|
||
case VK_SHIFT:
|
||
if (IS_IM_KEY_RELEASE(pIMEventIn->data.keyboard.flags))
|
||
{
|
||
m_imfInControlShiftDown = FALSE;
|
||
}
|
||
else
|
||
{
|
||
m_imfInControlShiftDown = TRUE;
|
||
}
|
||
break;
|
||
|
||
case VK_MENU:
|
||
if (IS_IM_KEY_RELEASE(pIMEventIn->data.keyboard.flags))
|
||
{
|
||
m_imfInControlMenuDown = FALSE;
|
||
}
|
||
else
|
||
{
|
||
m_imfInControlMenuDown = TRUE;
|
||
}
|
||
break;
|
||
|
||
case VK_CAPITAL:
|
||
if (IS_IM_KEY_PRESS(pIMEventIn->data.keyboard.flags))
|
||
{
|
||
m_imfInControlCapsLock = !m_imfInControlCapsLock;
|
||
}
|
||
break;
|
||
|
||
case VK_NUMLOCK:
|
||
if (IS_IM_KEY_PRESS(pIMEventIn->data.keyboard.flags))
|
||
{
|
||
m_imfInControlNumLock = !m_imfInControlNumLock;
|
||
}
|
||
break;
|
||
|
||
case VK_SCROLL:
|
||
if (IS_IM_KEY_PRESS(pIMEventIn->data.keyboard.flags))
|
||
{
|
||
m_imfInControlScrollLock = !m_imfInControlScrollLock;
|
||
}
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now check the current state versus our remembered state and
|
||
// prepare to insert events if necessary. Do this for any events
|
||
// (ie including mouse events) as mouse clicks can have different
|
||
// effects depending on the current modifer state.
|
||
//
|
||
|
||
//
|
||
// First the modifiers. IMGetHighLevelKeyState will return us the
|
||
// keyboard state including the event we are currently processing
|
||
// because it is adjusted before the keyboard hook. The top most
|
||
// bit is set of the key is down otherwise it is reset.
|
||
//
|
||
if (IMGetHighLevelKeyState(VK_CONTROL) & 0x80)
|
||
{
|
||
if (!m_imfInControlCtrlDown)
|
||
{
|
||
//
|
||
// The key is down locally but we last told the remote
|
||
// machine it was up.
|
||
//
|
||
m_aimInControlEventsToReturn[m_imInControlNumEventsPending++] =
|
||
IEM_EVENT_CTRL_DOWN;
|
||
m_imfInControlCtrlDown = TRUE;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (m_imfInControlCtrlDown)
|
||
{
|
||
//
|
||
// The key is up locally but we last told the remote
|
||
// machine it was down.
|
||
//
|
||
m_aimInControlEventsToReturn[m_imInControlNumEventsPending++] =
|
||
IEM_EVENT_CTRL_UP;
|
||
m_imfInControlCtrlDown = FALSE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Do the same for shift and menu (alt).
|
||
//
|
||
if (IMGetHighLevelKeyState(VK_SHIFT) & 0x80)
|
||
{
|
||
if (!m_imfInControlShiftDown)
|
||
{
|
||
m_aimInControlEventsToReturn[m_imInControlNumEventsPending++] =
|
||
IEM_EVENT_SHIFT_DOWN;
|
||
m_imfInControlShiftDown = TRUE;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (m_imfInControlShiftDown)
|
||
{
|
||
m_aimInControlEventsToReturn[m_imInControlNumEventsPending++] =
|
||
IEM_EVENT_SHIFT_UP;
|
||
m_imfInControlShiftDown = FALSE;
|
||
}
|
||
}
|
||
|
||
if (IMGetHighLevelKeyState(VK_MENU) & 0x80)
|
||
{
|
||
if (!m_imfInControlMenuDown)
|
||
{
|
||
m_aimInControlEventsToReturn[m_imInControlNumEventsPending++] =
|
||
IEM_EVENT_MENU_DOWN;
|
||
m_imfInControlMenuDown = TRUE;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (m_imfInControlMenuDown)
|
||
{
|
||
m_aimInControlEventsToReturn[m_imInControlNumEventsPending++] =
|
||
IEM_EVENT_MENU_UP;
|
||
m_imfInControlMenuDown = FALSE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now handle the toggles. The least significant bit is set when
|
||
// the toggle is on, reset otherwise.
|
||
//
|
||
if ((IMGetHighLevelKeyState(VK_CAPITAL) & IM_KEY_STATE_FLAG_TOGGLE) ?
|
||
!m_imfInControlCapsLock : m_imfInControlCapsLock)
|
||
{
|
||
//
|
||
// The current caps lock state and what we've sent to the
|
||
// remote system are out of synch - fix it.
|
||
//
|
||
m_aimInControlEventsToReturn[m_imInControlNumEventsPending++] =
|
||
IEM_EVENT_CAPS_LOCK_DOWN;
|
||
m_aimInControlEventsToReturn[m_imInControlNumEventsPending++] =
|
||
IEM_EVENT_CAPS_LOCK_UP;
|
||
m_imfInControlCapsLock = !m_imfInControlCapsLock;
|
||
}
|
||
|
||
//
|
||
// Do the same for Num lock and Scroll lock.
|
||
//
|
||
if ((IMGetHighLevelKeyState(VK_NUMLOCK) & 0x01) ?
|
||
!m_imfInControlNumLock : m_imfInControlNumLock)
|
||
{
|
||
m_aimInControlEventsToReturn[m_imInControlNumEventsPending++] =
|
||
IEM_EVENT_NUM_LOCK_DOWN;
|
||
m_aimInControlEventsToReturn[m_imInControlNumEventsPending++] =
|
||
IEM_EVENT_NUM_LOCK_UP;
|
||
m_imfInControlNumLock = !m_imfInControlNumLock;
|
||
}
|
||
|
||
if ((IMGetHighLevelKeyState(VK_SCROLL) & 0x01) ?
|
||
!m_imfInControlScrollLock : m_imfInControlScrollLock)
|
||
{
|
||
m_aimInControlEventsToReturn[m_imInControlNumEventsPending++] =
|
||
IEM_EVENT_SCROLL_LOCK_DOWN;
|
||
m_aimInControlEventsToReturn[m_imInControlNumEventsPending++] =
|
||
IEM_EVENT_SCROLL_LOCK_UP;
|
||
m_imfInControlScrollLock = !m_imfInControlScrollLock;
|
||
}
|
||
|
||
//
|
||
// Now we will do the appropriate processing for each type of
|
||
// packet we expect. We only expect to receive
|
||
//
|
||
// IM_TYPE_VK1
|
||
// IM_TYPE_ASCII
|
||
// IM_TYPE_3BUTTON
|
||
//
|
||
//
|
||
|
||
if (pIMEventIn->type == IM_TYPE_VK1)
|
||
{
|
||
//
|
||
// Now process a VK packet generated from the real keyboard.
|
||
// Check for Escape, Tab and Menu and decide whether to forward
|
||
// them or consume them first.
|
||
//
|
||
|
||
if (pIMEventIn->data.keyboard.keyCode == VK_ESCAPE)
|
||
{
|
||
//
|
||
// This is the escape key - check the current shift status
|
||
// to see whether we should flag this as consumed locally.
|
||
//
|
||
if (IMGetHighLevelKeyState(VK_MENU) & 0x80)
|
||
{
|
||
m_aimInControlEventsToReturn[m_imInControlNumEventsPending++] =
|
||
IEM_EVENT_CONSUMED;
|
||
|
||
//
|
||
// Also remember to consume the next Menu Up keystroke.
|
||
//
|
||
m_imfInControlConsumeMenuUp = TRUE;
|
||
|
||
if (!IS_IM_KEY_RELEASE(pIMEventIn->data.keyboard.flags))
|
||
{
|
||
//
|
||
// If this is an escape press then remember that we
|
||
// should consume the corresponding up stroke
|
||
// regardless of shift state.
|
||
//
|
||
m_imfInControlConsumeEscapeUp = TRUE;
|
||
}
|
||
}
|
||
else if (m_imfInControlConsumeEscapeUp &&
|
||
IS_IM_KEY_RELEASE(pIMEventIn->data.keyboard.flags))
|
||
{
|
||
//
|
||
// This is the up stroke corresponding to a down
|
||
// stroke we consumed so consume it too.
|
||
//
|
||
m_aimInControlEventsToReturn[m_imInControlNumEventsPending++] =
|
||
IEM_EVENT_CONSUMED;
|
||
m_imfInControlConsumeEscapeUp = FALSE;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// This Escape is not one of our special cases so
|
||
// forward it unchanged.
|
||
//
|
||
m_aimInControlEventsToReturn[m_imInControlNumEventsPending++] =
|
||
IEM_EVENT_FORWARD;
|
||
}
|
||
}
|
||
else if (pIMEventIn->data.keyboard.keyCode == VK_TAB)
|
||
{
|
||
//
|
||
// This is the Tab key - check for current shift status to
|
||
// see whether we should flag this as consumed locally.
|
||
//
|
||
if (IMGetHighLevelKeyState(VK_MENU) & 0x80)
|
||
{
|
||
m_aimInControlEventsToReturn[m_imInControlNumEventsPending++] =
|
||
IEM_EVENT_CONSUMED;
|
||
|
||
//
|
||
// Also remember to consume the next Menu Up keystroke.
|
||
//
|
||
m_imfInControlConsumeMenuUp = TRUE;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// This Tab is not our special case so forward it
|
||
// unchanged.
|
||
//
|
||
m_aimInControlEventsToReturn[m_imInControlNumEventsPending++] =
|
||
IEM_EVENT_FORWARD;
|
||
}
|
||
}
|
||
else if ((pIMEventIn->data.keyboard.keyCode == VK_MENU) &&
|
||
IS_IM_KEY_RELEASE(pIMEventIn->data.keyboard.flags))
|
||
{
|
||
//
|
||
// This is a menu up - check for one we should consume or
|
||
// for hotkeys.
|
||
//
|
||
if (m_imfInControlConsumeMenuUp)
|
||
{
|
||
//
|
||
// This is a menu up we want to consume - do so.
|
||
//
|
||
m_aimInControlEventsToReturn[m_imInControlNumEventsPending++] =
|
||
IEM_EVENT_CONSUMED;
|
||
m_imfInControlConsumeMenuUp = FALSE;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// This is a VK_MENU release
|
||
// hot key sequence in our array of last four key
|
||
// presses. Start looking at the next entry (the array
|
||
// is circular). A valid sequence is
|
||
//
|
||
// VK_MENU
|
||
// numeric pad 9
|
||
// numeric pad number
|
||
// numeric pad number
|
||
//
|
||
//
|
||
fHotKeyFound = FALSE;
|
||
hotKeyArrayIndex = m_imInControlNextHotKeyEntry;
|
||
if (m_aimInControlHotKeyArray[hotKeyArrayIndex] == VK_MENU)
|
||
{
|
||
hotKeyArrayIndex = (hotKeyArrayIndex+1)%4;
|
||
if (m_aimInControlHotKeyArray[hotKeyArrayIndex] == 9)
|
||
{
|
||
hotKeyArrayIndex = (hotKeyArrayIndex+1)%4;
|
||
if (m_aimInControlHotKeyArray[hotKeyArrayIndex] <= 9)
|
||
{
|
||
hotKeyValue =
|
||
10*m_aimInControlHotKeyArray[hotKeyArrayIndex];
|
||
hotKeyArrayIndex = (hotKeyArrayIndex+1)%4;
|
||
if (m_aimInControlHotKeyArray[hotKeyArrayIndex] <= 9)
|
||
{
|
||
//
|
||
// This is a valid hot key - add a
|
||
// consumed VK_MENU and then a hot key
|
||
// packet.
|
||
//
|
||
hotKeyValue +=
|
||
m_aimInControlHotKeyArray[hotKeyArrayIndex];
|
||
m_aimInControlEventsToReturn[
|
||
m_imInControlNumEventsPending++] =
|
||
IEM_EVENT_CONSUMED;
|
||
m_aimInControlEventsToReturn[
|
||
m_imInControlNumEventsPending++] =
|
||
IEM_EVENT_HOTKEY_BASE + hotKeyValue;
|
||
TRACE_OUT(("Hotkey found %d", hotKeyValue));
|
||
fHotKeyFound = TRUE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!fHotKeyFound)
|
||
{
|
||
//
|
||
// This was not a hotkey so send the menu up as
|
||
// normal.
|
||
//
|
||
m_aimInControlEventsToReturn[m_imInControlNumEventsPending++] =
|
||
IEM_EVENT_FORWARD;
|
||
}
|
||
}
|
||
}
|
||
else if (IS_IM_KEY_PRESS(pIMEventIn->data.keyboard.flags))
|
||
{
|
||
//
|
||
// Keep a record of the last four key presses (not
|
||
// including auto
|
||
// VK_MENU up event to determine if we have found a hotkey
|
||
// sequence.
|
||
//
|
||
|
||
//
|
||
// This is a key press and it is not a repeat. Throw out
|
||
// extended keys here so that we're not confused by the
|
||
// grey cursor keys.
|
||
//
|
||
if (pIMEventIn->data.keyboard.flags &
|
||
IM_FLAG_KEYBOARD_EXTENDED)
|
||
{
|
||
//
|
||
// An extended key breaks the sequence.
|
||
//
|
||
m_aimInControlHotKeyArray[m_imInControlNextHotKeyEntry] = 0xFF;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Add an entry to our array for this key. We add
|
||
// VK_MENUs and add and translate numeric keypad keys
|
||
// anything else breaks the sequencs.
|
||
//
|
||
switch (pIMEventIn->data.keyboard.keyCode)
|
||
{
|
||
case VK_MENU:
|
||
m_aimInControlHotKeyArray[m_imInControlNextHotKeyEntry] = VK_MENU;
|
||
break;
|
||
|
||
case VK_NUMPAD0:
|
||
case VK_INSERT:
|
||
m_aimInControlHotKeyArray[m_imInControlNextHotKeyEntry] = 0;
|
||
break;
|
||
|
||
case VK_NUMPAD1:
|
||
case VK_END:
|
||
m_aimInControlHotKeyArray[m_imInControlNextHotKeyEntry] = 1;
|
||
break;
|
||
|
||
case VK_NUMPAD2:
|
||
case VK_DOWN:
|
||
m_aimInControlHotKeyArray[m_imInControlNextHotKeyEntry] = 2;
|
||
break;
|
||
|
||
case VK_NUMPAD3:
|
||
case VK_NEXT:
|
||
m_aimInControlHotKeyArray[m_imInControlNextHotKeyEntry] = 3;
|
||
break;
|
||
|
||
case VK_NUMPAD4:
|
||
case VK_LEFT:
|
||
m_aimInControlHotKeyArray[m_imInControlNextHotKeyEntry] = 4;
|
||
break;
|
||
|
||
case VK_NUMPAD5:
|
||
case VK_CLEAR:
|
||
m_aimInControlHotKeyArray[m_imInControlNextHotKeyEntry] = 5;
|
||
break;
|
||
|
||
case VK_NUMPAD6:
|
||
case VK_RIGHT:
|
||
m_aimInControlHotKeyArray[m_imInControlNextHotKeyEntry] = 6;
|
||
break;
|
||
|
||
case VK_NUMPAD7:
|
||
case VK_HOME:
|
||
m_aimInControlHotKeyArray[m_imInControlNextHotKeyEntry] = 7;
|
||
break;
|
||
|
||
case VK_NUMPAD8:
|
||
case VK_UP:
|
||
m_aimInControlHotKeyArray[m_imInControlNextHotKeyEntry] = 8;
|
||
break;
|
||
|
||
case VK_NUMPAD9:
|
||
case VK_PRIOR:
|
||
m_aimInControlHotKeyArray[m_imInControlNextHotKeyEntry] = 9;
|
||
break;
|
||
|
||
default:
|
||
//
|
||
// Any unrecognised key breaks a sequence.
|
||
//
|
||
m_aimInControlHotKeyArray[m_imInControlNextHotKeyEntry] = 0xFF;
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Wrap the hot key array at 4 entries.
|
||
//
|
||
m_imInControlNextHotKeyEntry = (m_imInControlNextHotKeyEntry+1)%4;
|
||
|
||
//
|
||
// Forward the event.
|
||
//
|
||
m_aimInControlEventsToReturn[m_imInControlNumEventsPending++] =
|
||
IEM_EVENT_FORWARD;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Just forward the event as its not any of our special
|
||
// cases.
|
||
//
|
||
m_aimInControlEventsToReturn[m_imInControlNumEventsPending++] =
|
||
IEM_EVENT_FORWARD;
|
||
}
|
||
}
|
||
else if (pIMEventIn->type == IM_TYPE_VK_ASCII)
|
||
{
|
||
m_aimInControlEventsToReturn[m_imInControlNumEventsPending++] =
|
||
IEM_EVENT_FORWARD;
|
||
}
|
||
else if (pIMEventIn->type == IM_TYPE_ASCII)
|
||
{
|
||
//
|
||
// Any IM_TYPE_ASCII breaks the hot key sequence.
|
||
//
|
||
m_aimInControlHotKeyArray[m_imInControlNextHotKeyEntry] = 0xFF;
|
||
m_imInControlNextHotKeyEntry = (m_imInControlNextHotKeyEntry+1)%4;
|
||
|
||
//
|
||
// Then just forward the thing without doing anything clever.
|
||
//
|
||
m_aimInControlEventsToReturn[m_imInControlNumEventsPending++] =
|
||
IEM_EVENT_FORWARD;
|
||
}
|
||
else if (pIMEventIn->type == IM_TYPE_3BUTTON)
|
||
{
|
||
//
|
||
// To be nice and clean we would ideally have a completely new
|
||
// event for the wheeled Microsoft mouse. However to maintain
|
||
// backwards compatibility, we send the event out in such a way
|
||
// that old incompatible systems interpret it as a NULL mouse
|
||
// move.
|
||
//
|
||
if (pIMEventIn->data.mouse.flags & IM_FLAG_MOUSE_WHEEL)
|
||
{
|
||
//
|
||
// This is a wheel rotatation.
|
||
//
|
||
// We massage this event so that new systems can see it for
|
||
// what it truly is - a wheel rotation, but old systems
|
||
// (which check the MOUSE_MOVE flag first, and ignore all
|
||
// other flags if set) see it as a mouse move.
|
||
//
|
||
// We did not set the MOUSE_MOVE flag when we first
|
||
// generated this event, since we did not want to trigger
|
||
// any of the sending side mouse move processing which
|
||
// would otherwise have been invoked.
|
||
//
|
||
pIMEventIn->data.mouse.flags |= IM_FLAG_MOUSE_MOVE;
|
||
}
|
||
|
||
//
|
||
// Forward the event
|
||
//
|
||
m_aimInControlEventsToReturn[m_imInControlNumEventsPending++] =
|
||
IEM_EVENT_FORWARD;
|
||
}
|
||
|
||
//
|
||
// Now we are going into a loop to return the m_iemLocalEvents we
|
||
// have queued up. We will return the first one below and then be
|
||
// called again until we have returned them all and return FALSE.
|
||
//
|
||
m_imfInControlNewEvent = FALSE;
|
||
m_imInControlNumEventsReturned = 0;
|
||
}
|
||
|
||
if (!m_imfInControlNewEvent)
|
||
{
|
||
if (m_imInControlNumEventsReturned == m_imInControlNumEventsPending)
|
||
{
|
||
//
|
||
// There are no more m_aiemLocalEvents to return.
|
||
//
|
||
TRACE_OUT(( "NO MORE EVENTS"));
|
||
m_imfInControlNewEvent = TRUE;
|
||
DC_QUIT;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Return the next event.
|
||
//
|
||
|
||
if (m_aimInControlEventsToReturn[m_imInControlNumEventsReturned] >=
|
||
IEM_EVENT_HOTKEY_BASE)
|
||
{
|
||
TRACE_OUT(( "HOTKEY "));
|
||
//
|
||
// Return a hotkey event.
|
||
//
|
||
pIMEventOut->type = IM_TYPE_VK2;
|
||
pIMEventOut->data.keyboard.keyCode = (TSHR_UINT16)
|
||
(m_aimInControlEventsToReturn[m_imInControlNumEventsReturned] -
|
||
IEM_EVENT_HOTKEY_BASE);
|
||
pIMEventOut->data.keyboard.flags = 0;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Return a non-hotkey event.
|
||
//
|
||
switch (m_aimInControlEventsToReturn[m_imInControlNumEventsReturned])
|
||
{
|
||
case IEM_EVENT_CTRL_DOWN:
|
||
TRACE_OUT(( "CTRL DWN"));
|
||
//
|
||
// Set up a Ctrl down event.
|
||
//
|
||
pIMEventOut->type = IM_TYPE_VK1;
|
||
pIMEventOut->data.keyboard.keyCode = VK_CONTROL;
|
||
pIMEventOut->data.keyboard.flags = 0;
|
||
break;
|
||
|
||
case IEM_EVENT_CTRL_UP:
|
||
TRACE_OUT(( "CTRL UP "));
|
||
//
|
||
// Set up a Ctrl up event with the quiet flag set
|
||
// - this means it should have no effect (other
|
||
// than to release the control key).
|
||
//
|
||
pIMEventOut->type = IM_TYPE_VK1;
|
||
pIMEventOut->data.keyboard.keyCode = VK_CONTROL;
|
||
pIMEventOut->data.keyboard.flags =
|
||
IM_FLAG_KEYBOARD_DOWN |
|
||
IM_FLAG_KEYBOARD_RELEASE |
|
||
IM_FLAG_KEYBOARD_QUIET;
|
||
break;
|
||
|
||
case IEM_EVENT_SHIFT_DOWN:
|
||
TRACE_OUT(( "SHFT DWN"));
|
||
//
|
||
// Set up a Shift down event.
|
||
//
|
||
pIMEventOut->type = IM_TYPE_VK1;
|
||
pIMEventOut->data.keyboard.keyCode = VK_SHIFT;
|
||
pIMEventOut->data.keyboard.flags = 0;
|
||
break;
|
||
|
||
case IEM_EVENT_SHIFT_UP:
|
||
TRACE_OUT(( "SHFT UP "));
|
||
//
|
||
// Set up a Shift up event with the quiet flag set
|
||
// - this means it should have no effect (other
|
||
// than to release the shift key).
|
||
//
|
||
pIMEventOut->type = IM_TYPE_VK1;
|
||
pIMEventOut->data.keyboard.keyCode = VK_SHIFT;
|
||
pIMEventOut->data.keyboard.flags =
|
||
IM_FLAG_KEYBOARD_DOWN |
|
||
IM_FLAG_KEYBOARD_RELEASE |
|
||
IM_FLAG_KEYBOARD_QUIET;
|
||
break;
|
||
|
||
case IEM_EVENT_MENU_DOWN:
|
||
TRACE_OUT(( "MENU DWN"));
|
||
//
|
||
// Set up a Menu down event.
|
||
//
|
||
pIMEventOut->type = IM_TYPE_VK1;
|
||
pIMEventOut->data.keyboard.keyCode = VK_MENU;
|
||
break;
|
||
|
||
case IEM_EVENT_MENU_UP:
|
||
TRACE_OUT(( "MENU UP "));
|
||
//
|
||
// Set up a Ctrl down event with the quiet flag set
|
||
// - ths is means it should have no effect (other
|
||
// than to release the menu key).
|
||
//
|
||
pIMEventOut->type = IM_TYPE_VK1;
|
||
pIMEventOut->data.keyboard.keyCode = VK_MENU;
|
||
pIMEventOut->data.keyboard.flags =
|
||
IM_FLAG_KEYBOARD_DOWN |
|
||
IM_FLAG_KEYBOARD_RELEASE |
|
||
IM_FLAG_KEYBOARD_QUIET;
|
||
break;
|
||
|
||
case IEM_EVENT_CAPS_LOCK_DOWN:
|
||
TRACE_OUT(( "CAPS DWN"));
|
||
//
|
||
// Send a caps lock down.
|
||
//
|
||
pIMEventOut->type = IM_TYPE_VK1;
|
||
pIMEventOut->data.keyboard.keyCode = VK_CAPITAL;
|
||
pIMEventOut->data.keyboard.flags = 0;
|
||
break;
|
||
|
||
case IEM_EVENT_CAPS_LOCK_UP:
|
||
TRACE_OUT(( "CAPS UP "));
|
||
//
|
||
// Send a caps lock up.
|
||
//
|
||
pIMEventOut->type = IM_TYPE_VK1;
|
||
pIMEventOut->data.keyboard.keyCode = VK_CAPITAL;
|
||
pIMEventOut->data.keyboard.flags =
|
||
IM_FLAG_KEYBOARD_DOWN |
|
||
IM_FLAG_KEYBOARD_RELEASE;
|
||
break;
|
||
|
||
case IEM_EVENT_NUM_LOCK_DOWN:
|
||
TRACE_OUT(( "NUM DOWN"));
|
||
//
|
||
// Send a num lock down - num lock is an extended
|
||
// key.
|
||
//
|
||
pIMEventOut->type = IM_TYPE_VK1;
|
||
pIMEventOut->data.keyboard.keyCode = VK_NUMLOCK;
|
||
pIMEventOut->data.keyboard.flags =
|
||
IM_FLAG_KEYBOARD_EXTENDED;
|
||
break;
|
||
|
||
case IEM_EVENT_NUM_LOCK_UP:
|
||
//
|
||
// Send a num lock up - num lock is an extended
|
||
// key.
|
||
//
|
||
TRACE_OUT(( "NUM UP "));
|
||
pIMEventOut->type = IM_TYPE_VK1;
|
||
pIMEventOut->data.keyboard.keyCode = VK_NUMLOCK;
|
||
pIMEventOut->data.keyboard.flags =
|
||
IM_FLAG_KEYBOARD_DOWN |
|
||
IM_FLAG_KEYBOARD_RELEASE |
|
||
IM_FLAG_KEYBOARD_EXTENDED;
|
||
break;
|
||
|
||
case IEM_EVENT_SCROLL_LOCK_DOWN:
|
||
//
|
||
// Send a scroll lock down.
|
||
//
|
||
TRACE_OUT(( "SCROLDWN"));
|
||
pIMEventOut->type = IM_TYPE_VK1;
|
||
pIMEventOut->data.keyboard.keyCode = VK_SCROLL;
|
||
pIMEventOut->data.keyboard.flags = 0;
|
||
break;
|
||
|
||
case IEM_EVENT_SCROLL_LOCK_UP:
|
||
//
|
||
// Send a scroll lock up.
|
||
//
|
||
TRACE_OUT(( "SCROLLUP"));
|
||
pIMEventOut->type = IM_TYPE_VK1;
|
||
pIMEventOut->data.keyboard.keyCode = VK_SCROLL;
|
||
pIMEventOut->data.keyboard.flags =
|
||
IM_FLAG_KEYBOARD_DOWN |
|
||
IM_FLAG_KEYBOARD_RELEASE;
|
||
break;
|
||
|
||
case IEM_EVENT_FORWARD:
|
||
//
|
||
// Just copy the packet.
|
||
//
|
||
TRACE_OUT(( "FORWARD"));
|
||
*pIMEventOut = *pIMEventIn;
|
||
break;
|
||
|
||
case IEM_EVENT_CONSUMED:
|
||
//
|
||
// Copy the packet and set the flag.
|
||
//
|
||
TRACE_OUT(( "CONSUMED"));
|
||
*pIMEventOut = *pIMEventIn;
|
||
pIMEventOut->data.keyboard.flags |=
|
||
IM_FLAG_KEYBOARD_QUIET;
|
||
break;
|
||
|
||
default:
|
||
ERROR_OUT(( "Invalid code path"));
|
||
break;
|
||
}
|
||
}
|
||
m_imInControlNumEventsReturned++;
|
||
|
||
//
|
||
// Do tracing
|
||
//
|
||
if (pIMEventOut->type == IM_TYPE_ASCII)
|
||
{
|
||
TRACE_OUT(( "OUT ASCII code %04X, flags %04X",
|
||
pIMEventOut->data.keyboard.keyCode, pIMEventOut->data.keyboard.flags));
|
||
}
|
||
else if (pIMEventOut->type == IM_TYPE_VK1)
|
||
{
|
||
TRACE_OUT(( "OUT VK1 code %04X, flags %04X",
|
||
pIMEventOut->data.keyboard.keyCode, pIMEventOut->data.keyboard.flags));
|
||
}
|
||
else if (pIMEventOut->type == IM_TYPE_VK2)
|
||
{
|
||
TRACE_OUT(( "OUT VK2 code - %04X, flags - %04X",
|
||
pIMEventOut->data.keyboard.keyCode, pIMEventOut->data.keyboard.flags));
|
||
}
|
||
else if ((pIMEventOut->type == IM_TYPE_3BUTTON) &&
|
||
!(pIMEventOut->data.mouse.flags & IM_FLAG_MOUSE_MOVE))
|
||
{
|
||
TRACE_OUT(( "OUT 3BTTN flags - %04X (%d,%d)",
|
||
pIMEventOut->data.mouse.flags, pIMEventOut->data.mouse.x,
|
||
pIMEventOut->data.mouse.y));
|
||
}
|
||
else if (pIMEventOut->type == IM_TYPE_3BUTTON)
|
||
{
|
||
TRACE_OUT(( "OUT 3BTTN flags - %04X (%d,%d)",
|
||
pIMEventOut->data.mouse.flags, pIMEventOut->data.mouse.x,
|
||
pIMEventOut->data.mouse.y));
|
||
}
|
||
else
|
||
{
|
||
ERROR_OUT(("Invalid IM type %d", pIMEventOut->type));
|
||
}
|
||
|
||
rc = TRUE;
|
||
}
|
||
}
|
||
|
||
DC_EXIT_POINT:
|
||
DebugExitVOID(ASShare::IMTranslateOutgoing);
|
||
return(rc);
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// IMTranslateIncoming()
|
||
//
|
||
// DESCRIPTION:
|
||
//
|
||
// Converts remotely generated sequences of IMEVENTs into sequences of
|
||
// IMEVENTs for replay. Does a 1 to (0-n) translation. Handles faking
|
||
// keys using ALT and keypad.
|
||
//
|
||
// When an IMEVENT is received and is ready to be replayed this function
|
||
// is called with a pointer to that packet in pIMEventIn.
|
||
// IMTranslateIncoming can then return TRUE and fill in the packet at
|
||
// pIMEventOut or return FALSE. If IMTranslateIncoming returns TRUE the
|
||
// IM will call it again with the same packet. The IMEVENTs returned are
|
||
// played back on the local machine using the journal playback hook by the
|
||
// IM.
|
||
//
|
||
// PARAMETERS:
|
||
//
|
||
// pIMEventIn - pointer to IMEVENT
|
||
//
|
||
// pIMEventOut - pointer to IMEVENT
|
||
//
|
||
// personID - the ID of the person this event was received from
|
||
//
|
||
// RETURNS:
|
||
//
|
||
// TRUE - packet returned (call function again)
|
||
//
|
||
// FALSE - no packet returned (don't call function again)
|
||
//
|
||
//
|
||
//
|
||
BOOL ASShare::IMTranslateIncoming
|
||
(
|
||
PIMEVENT pIMEventIn,
|
||
PIMEVENT pIMEventOut
|
||
)
|
||
{
|
||
BYTE curKbState;
|
||
BYTE rcVkKeyScanKbState;
|
||
UINT keyCode;
|
||
TSHR_UINT16 rcVkKeyScan;
|
||
BOOL bTranslateOEM;
|
||
char chAnsi;
|
||
char chOEM;
|
||
char chNewAnsi;
|
||
UINT position;
|
||
UINT digit;
|
||
UINT i;
|
||
|
||
DebugEntry(ASShare::IMTranslateIncoming);
|
||
|
||
//
|
||
// In this function we will receive several types of events
|
||
//
|
||
// IM_TYPE_VK1 - processed
|
||
// IM_TYPE_ASCII - processed
|
||
// IM_TYPE_VK2 - ignored (discarded)
|
||
// IM_TYPE_3BUTTON - processed
|
||
//
|
||
// For IM_TYPE_VK1:
|
||
//
|
||
// If it has the consumed locally flag set then try and play it back
|
||
// without anything happening. This means that for an Alt up we make
|
||
// sure that there have been some keyboard events between the Alt down
|
||
// and this event.
|
||
//
|
||
// For IM_TYPE_ASCII:
|
||
//
|
||
// Try to convert this to a VK to playback. If we are succesful then
|
||
// playback one or more key strokes to get into the correct shift state
|
||
// then play back the VK and then undo any shift states. If we can't
|
||
// convert to a VK then fake a sequence of Alt + numeric keypad keys to
|
||
// get the key in.
|
||
//
|
||
// For IM_TYPE_VK2:
|
||
//
|
||
// Discard unceremoniously.
|
||
//
|
||
// For IM_TYPE_3BUTTON:
|
||
//
|
||
// Play back directly.
|
||
//
|
||
//
|
||
keyCode = pIMEventIn->data.keyboard.keyCode;
|
||
|
||
if (m_imfControlledNewEvent)
|
||
{
|
||
//
|
||
// The first time we have seen a new event - accumulate an array
|
||
// of events we want to return.
|
||
//
|
||
|
||
//
|
||
// Start from the beginning of our returned events array.
|
||
//
|
||
m_imControlledNumEventsPending = 0;
|
||
m_imControlledNumEventsReturned = 0;
|
||
|
||
if (pIMEventIn->type == IM_TYPE_VK1)
|
||
{
|
||
//
|
||
// Handle VK1s first. Special cases are VK_MENU, VK_TAB and
|
||
// VK_ESC. We recognise VK_MENU down key strokes and remember
|
||
// when they happened so that we can possibly fiddle with
|
||
// VK_MENU up keystrokes later to go into menu mode. We check
|
||
// on VK_TAB for the IM_FLAG_KEYBOARD_QUIET flag and if it is
|
||
// set then we don't replay anything
|
||
// First translate the virtual key code from the DC-Share
|
||
// protocol code to the OS virtual key code
|
||
//
|
||
if (keyCode == VK_MENU)
|
||
{
|
||
if (!IS_IM_KEY_RELEASE(pIMEventIn->data.keyboard.flags))
|
||
{
|
||
//
|
||
// This is a VK_MENU press - return it without
|
||
// interfering.
|
||
//
|
||
m_aimControlledEventsToReturn[m_imControlledNumEventsPending++] =
|
||
IEM_EVENT_REPLAY;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Handle VK_MENU up events
|
||
//
|
||
// If the menu up has the `quiet' flag set then
|
||
// insert a couple of shift key events to prevent it
|
||
// having any effect. There are two cases we're
|
||
// covering here where an Alt-UP can have some effect.
|
||
//
|
||
// 1. Alt-Down, Alt-Up causes the system menu button to
|
||
// be highlighted.
|
||
//
|
||
// 2. Entering characters from the numeric keypad takes
|
||
// effect on the Alt-Up.
|
||
//
|
||
// Both of these effects can be negated by adding the
|
||
// shift key strokes.
|
||
//
|
||
if (pIMEventIn->data.keyboard.flags &
|
||
IM_FLAG_KEYBOARD_QUIET)
|
||
{
|
||
//
|
||
// We need to `silence' this key - to do this we
|
||
// will insert to shift key strokes first
|
||
//
|
||
if (m_aimControlledControllerKeyStates[VK_SHIFT] & 0x80)
|
||
{
|
||
//
|
||
// Shift is currently down - insert an up then
|
||
// a down
|
||
//
|
||
m_aimControlledEventsToReturn[m_imControlledNumEventsPending++] =
|
||
IEM_EVENT_SHIFT_UP;
|
||
m_aimControlledEventsToReturn[m_imControlledNumEventsPending++] =
|
||
IEM_EVENT_SHIFT_DOWN;
|
||
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Shift is currently up - insert a down then
|
||
// an up
|
||
//
|
||
m_aimControlledEventsToReturn[m_imControlledNumEventsPending++] =
|
||
IEM_EVENT_SHIFT_DOWN;
|
||
m_aimControlledEventsToReturn[m_imControlledNumEventsPending++] =
|
||
IEM_EVENT_SHIFT_UP;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Replay the menu up key stroke.
|
||
//
|
||
m_aimControlledEventsToReturn[m_imControlledNumEventsPending++] =
|
||
IEM_EVENT_REPLAY;
|
||
|
||
}
|
||
}
|
||
else if ((pIMEventIn->data.keyboard.flags &
|
||
IM_FLAG_KEYBOARD_QUIET) &&
|
||
((keyCode == VK_TAB) ||
|
||
(keyCode == VK_ESCAPE)))
|
||
{
|
||
//
|
||
// Just get out of here - we don't want to play this back
|
||
//
|
||
return(FALSE);
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// All other VKs just get replayed
|
||
//
|
||
m_aimControlledEventsToReturn[m_imControlledNumEventsPending++] =
|
||
IEM_EVENT_REPLAY;
|
||
}
|
||
}
|
||
else if (pIMEventIn->type == IM_TYPE_ASCII)
|
||
{
|
||
//
|
||
// For ASCII packets we need to find out how we can replay them
|
||
// on our local keyboard. If we can replay them directly or
|
||
// with shift or ctrl (but not with ALT), then we will do so,
|
||
// otherwise we will simulate Alt + numeric keypad to replay
|
||
// them. If we have to generate fake modifier key strokes
|
||
// ourselves then we will replay the whole key stroke on the
|
||
// incoming key down. If we don't need to generate fake key
|
||
// strokes then we will play the down and up keystrokes as they
|
||
// come in.
|
||
//
|
||
// We do not allow VK combinations involving ALT as this messes
|
||
// up remote international keyboard support. For example, if
|
||
// the remote keyboard is UK and we are (say) Spanish,
|
||
// VKKeyScan says we can do the "UK pound" character as
|
||
// Ctrl+Alt+3. While this works in Windows, and for DOS Boxes
|
||
// on standard keyboards, DOS Boxes with enhanced keyboards
|
||
// require ALTGR+3 (nb Windows seems to treat ALTGR as Ctrl+Alt
|
||
// anyway - at least for VKs and Async state). There is no VK
|
||
// for ALTGR, so do an ALT-nnn sequence for these cases.
|
||
//
|
||
rcVkKeyScan = VkKeyScan((char)keyCode);
|
||
TRACE_OUT(( "co_vk_key_scan of X%02x returns rcVkKeyScan X%02x",
|
||
keyCode, rcVkKeyScan));
|
||
if ((rcVkKeyScan != 0xffff) && !(rcVkKeyScan & 0x0400))
|
||
{
|
||
//
|
||
// This can be replayed using a combination of modifiers on
|
||
// this keyboard.
|
||
//
|
||
rcVkKeyScanKbState = HIBYTE(rcVkKeyScan);
|
||
|
||
//
|
||
// The high byte of rcVkKeyScan contains three bit flags
|
||
// which signify which modifiers ar required to generate
|
||
// this character. They are
|
||
//
|
||
// bit 0 - Shift
|
||
// bit 1 - Ctrl
|
||
// bit 2 - Alt (Menu)
|
||
//
|
||
// We will construct an equivalent set of flags which
|
||
// describes the current state of these modifiers.
|
||
//
|
||
curKbState = 0;
|
||
|
||
if (m_aimControlledControllerKeyStates[VK_SHIFT] & 0x80)
|
||
{
|
||
curKbState |= IEM_SHIFT_DOWN;
|
||
}
|
||
|
||
if (m_aimControlledControllerKeyStates[VK_CONTROL] & 0x80)
|
||
{
|
||
curKbState |= IEM_CTRL_DOWN;
|
||
}
|
||
|
||
if (m_aimControlledControllerKeyStates[VK_MENU] & 0x80)
|
||
{
|
||
curKbState |= IEM_MENU_DOWN;
|
||
|
||
//
|
||
// If the Alt key is down currently in this person's
|
||
// context then (in general
|
||
// it. This means accelerators which need to be
|
||
// shifted will work as we won't release the Alt key in
|
||
// order to generate the key strokes.
|
||
//
|
||
// However, if the ALT key is being held down in
|
||
// combination with SHIFT and CTRL to generate a
|
||
// character (e.g. CTRL-ALT-SHIFT-4 on a US keyboard
|
||
// to generate a <20> character) then we will allow the
|
||
// ALT key up before we play back the true character.
|
||
//
|
||
if ((curKbState & (IEM_SHIFT_DOWN | IEM_CTRL_DOWN)) !=
|
||
(IEM_SHIFT_DOWN | IEM_CTRL_DOWN))
|
||
{
|
||
rcVkKeyScanKbState |= IEM_MENU_DOWN;
|
||
}
|
||
}
|
||
|
||
if ((m_aimControlledControllerKeyStates[VK_CAPITAL] & 0x01) &&
|
||
((LOBYTE(rcVkKeyScan) >= 'A') &&
|
||
((LOBYTE(rcVkKeyScan) <= 'Z'))))
|
||
{
|
||
//
|
||
// If caps-lock is enabled then the effect of a shift
|
||
// down on VKs A thru Z is reversed. This logic ( 'A'
|
||
// <= x <= 'Z' is encoded in the keyboard.drv so it
|
||
// should be pretty safe).
|
||
//
|
||
curKbState ^= IEM_SHIFT_DOWN;
|
||
}
|
||
|
||
if (curKbState == rcVkKeyScanKbState)
|
||
{
|
||
//
|
||
// We are already in the correct shift state so just
|
||
// replay the VK.
|
||
//
|
||
m_aimControlledEventsToReturn[m_imControlledNumEventsPending++] =
|
||
IEM_EVENT_REPLAY_VK;
|
||
m_imControlledVKToReplay = LOBYTE(rcVkKeyScan);
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// We need to generate some fake modifiers - only do
|
||
// this on a key press.
|
||
//
|
||
if (pIMEventIn->data.keyboard.flags &
|
||
IM_FLAG_KEYBOARD_RELEASE)
|
||
{
|
||
return(FALSE);
|
||
}
|
||
|
||
//
|
||
// Insert modifiers to get into the correct state.
|
||
//
|
||
m_imControlledNumEventsPending += IMInsertModifierKeystrokes(
|
||
curKbState,
|
||
rcVkKeyScanKbState,
|
||
&(m_aimControlledEventsToReturn[m_imControlledNumEventsPending]));
|
||
|
||
//
|
||
// Now insert the VK itself - a down and up.
|
||
//
|
||
m_aimControlledEventsToReturn[m_imControlledNumEventsPending++] =
|
||
IEM_EVENT_REPLAY_VK_DOWN;
|
||
m_aimControlledEventsToReturn[m_imControlledNumEventsPending++] =
|
||
IEM_EVENT_REPLAY_VK_UP;
|
||
|
||
//
|
||
// Remeber the VK we want to replay when we come across
|
||
// IEM_EVENT_REPLAY_VK_DOWN/UP.
|
||
//
|
||
m_imControlledVKToReplay = LOBYTE(rcVkKeyScan);
|
||
|
||
//
|
||
// Now insert the modifiers to get back to the current
|
||
// state.
|
||
//
|
||
m_imControlledNumEventsPending += IMInsertModifierKeystrokes(
|
||
rcVkKeyScanKbState,
|
||
curKbState,
|
||
&(m_aimControlledEventsToReturn[m_imControlledNumEventsPending]));
|
||
|
||
//
|
||
// Now we have a complete set of events ready to replay
|
||
// so go for it.
|
||
//
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// We can't replay directly, so will have to simulate an
|
||
// Alt+keypad sequence.
|
||
//
|
||
TRACE_OUT(( "FAKE AN ALT-nnn SEQUENCE IF WINDOWS"));
|
||
//
|
||
// We only do this sort of stuff on a key-press.
|
||
//
|
||
if (pIMEventIn->data.keyboard.flags &
|
||
IM_FLAG_KEYBOARD_RELEASE)
|
||
{
|
||
return(FALSE);
|
||
}
|
||
|
||
//
|
||
// The following code relies on keyCode being less than 999
|
||
// and we should receive a keycode > 255 so get out now if
|
||
// we have.
|
||
//
|
||
if (keyCode > 255)
|
||
{
|
||
return(FALSE);
|
||
}
|
||
|
||
//
|
||
// First get modifiers into correct state - create bit
|
||
// flags for current modifier state.
|
||
//
|
||
curKbState = 0;
|
||
|
||
//
|
||
// For windows we have a character to input that cannot
|
||
// be replayed by pressing a key...replay by injecting
|
||
// alt-nnn.
|
||
//
|
||
if (m_aimControlledControllerKeyStates[VK_SHIFT] & 0x80)
|
||
{
|
||
curKbState |= IEM_SHIFT_DOWN;
|
||
}
|
||
|
||
if (m_aimControlledControllerKeyStates[VK_CONTROL] & 0x80)
|
||
{
|
||
curKbState |= IEM_CTRL_DOWN;
|
||
}
|
||
|
||
if (m_aimControlledControllerKeyStates[VK_MENU] & 0x80)
|
||
{
|
||
curKbState |= IEM_MENU_DOWN;
|
||
}
|
||
|
||
//
|
||
// If necessary, reset all modifiers.
|
||
//
|
||
if (curKbState)
|
||
{
|
||
m_imControlledNumEventsPending += IMInsertModifierKeystrokes(
|
||
curKbState,
|
||
0,
|
||
&(m_aimControlledEventsToReturn[m_imControlledNumEventsPending]));
|
||
}
|
||
|
||
//
|
||
// Now determine whether we can do the ALT-nnn keypad
|
||
// sequence using an OEM keycode or whether we have to use
|
||
// an ANSI (Windows) keycode.
|
||
//
|
||
// The issue here is that:
|
||
//
|
||
// - hosted Windows applications (or rather Windows itself)
|
||
// can distinguish between, and handle correctly, ANSI
|
||
// keycodes and OEM keycodes (where the latter vary
|
||
// depending on the keyboard type). For example,
|
||
// ALT-0163 is the ANSI "UK pound" on all keyboards,
|
||
// and on US national keyboards ALT-156 is the OEM
|
||
// keycode for "UK pound".
|
||
//
|
||
// - hosted DOS Boxes only understand OEM keycodes.
|
||
//
|
||
// So (for example), if we have a remote UK keyboard
|
||
// controlling local Windows and DOS Box applications, and
|
||
// we generate ALT-nnn using the OEM keycode (and without a
|
||
// leading zero), both the Windows and DOS Box applications
|
||
// interpret it as "UK pound" (Hoorah!). In contrast, if
|
||
// we generate ALT-nnn using the ANSI keycode (with a
|
||
// leading zero), the Windows applications still do "UK
|
||
// pound", BUT the DOS Box does an "u acute".
|
||
//
|
||
// As far as we can tell (eg by examining the DDK keyboard
|
||
// driver source for AnsiToOem), there should always be a
|
||
// translation. However, it is possible that the ANSI to
|
||
// OEM translation is not 1<->1. We therefore check this
|
||
// by doing a second translation back from OEM to ANSI. If
|
||
// this does not give us the original character we use the
|
||
// original ANSI code and play it back with a ALT-0nnn
|
||
// sequence.
|
||
//
|
||
chAnsi = (char)pIMEventIn->data.keyboard.keyCode;
|
||
|
||
AnsiToOemBuff(&chAnsi, &chOEM, 1);
|
||
OemToAnsiBuff(&chOEM, &chNewAnsi, 1);
|
||
TRACE_OUT(( "Ansi: %02x OEM: %02x NewAnsi: %02x",
|
||
(BYTE)chAnsi,
|
||
(BYTE)chOEM,
|
||
(BYTE)chNewAnsi ));
|
||
|
||
bTranslateOEM = (chAnsi == chNewAnsi);
|
||
|
||
keyCode = (bTranslateOEM)
|
||
? (UINT)(BYTE)chOEM
|
||
: pIMEventIn->data.keyboard.keyCode;
|
||
|
||
//
|
||
// Now insert a VK_MENU down.
|
||
//
|
||
m_aimControlledEventsToReturn[m_imControlledNumEventsPending++] =
|
||
IEM_EVENT_MENU_DOWN;
|
||
|
||
//
|
||
// Now insert the numeric keypad keystrokes. If we're
|
||
// doing an ANSI ALT
|
||
//
|
||
if (!bTranslateOEM)
|
||
{
|
||
m_aimControlledEventsToReturn[m_imControlledNumEventsPending++] =
|
||
IEM_EVENT_KEYPAD0_DOWN;
|
||
m_aimControlledEventsToReturn[m_imControlledNumEventsPending++] =
|
||
IEM_EVENT_KEYPAD0_UP;
|
||
}
|
||
|
||
|
||
//
|
||
// Add keystrokes for hundreds, tens and units, taking care
|
||
// to discard leading (but not trailing) zeros if we're
|
||
// doing an OEM sequence (which would confuse Windows into
|
||
// thinking an OEM ALT-nnn sequence was an ANSI sequence).
|
||
//
|
||
position = 100;
|
||
for (i=0 ; i<3 ; i++)
|
||
{
|
||
//
|
||
// Insert the correct digit for this position.
|
||
//
|
||
digit = keyCode / position;
|
||
|
||
if (!(digit == 0 && bTranslateOEM))
|
||
{
|
||
bTranslateOEM = FALSE;
|
||
m_aimControlledEventsToReturn[m_imControlledNumEventsPending++] =
|
||
IEM_EVENT_KEYPAD0_DOWN + digit;
|
||
m_aimControlledEventsToReturn[m_imControlledNumEventsPending++] =
|
||
IEM_EVENT_KEYPAD0_UP + digit;
|
||
}
|
||
|
||
//
|
||
// Move to next position.
|
||
//
|
||
keyCode %= position;
|
||
position /= 10;
|
||
}
|
||
|
||
//
|
||
// Now insert a VK_MENU up.
|
||
//
|
||
m_aimControlledEventsToReturn[m_imControlledNumEventsPending++] =
|
||
IEM_EVENT_MENU_UP;
|
||
|
||
|
||
//
|
||
// If necessary, get the modifiers back to the state they
|
||
// were in previously.
|
||
//
|
||
if (curKbState != 0)
|
||
{
|
||
m_imControlledNumEventsPending += IMInsertModifierKeystrokes(
|
||
0,
|
||
curKbState,
|
||
&(m_aimControlledEventsToReturn[m_imControlledNumEventsPending]));
|
||
}
|
||
|
||
//
|
||
// Now we have a buffer full of keystrokes - go for it.
|
||
//
|
||
}
|
||
}
|
||
else if (pIMEventIn->type == IM_TYPE_VK2)
|
||
{
|
||
//
|
||
// Hot keys are thrown away - this is easy.
|
||
//
|
||
return(FALSE);
|
||
}
|
||
else if (pIMEventIn->type == IM_TYPE_3BUTTON)
|
||
{
|
||
//
|
||
// Mouse events are just replayed.
|
||
//
|
||
m_aimControlledEventsToReturn[m_imControlledNumEventsPending++] = IEM_EVENT_REPLAY;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Unknown events are thrown away - this is easy.
|
||
//
|
||
return(FALSE);
|
||
}
|
||
|
||
//
|
||
// Now we have events to return.
|
||
//
|
||
m_imfControlledNewEvent = FALSE;
|
||
m_imControlledNumEventsReturned = 0;
|
||
}
|
||
|
||
if (!m_imfControlledNewEvent)
|
||
{
|
||
if (m_imControlledNumEventsReturned == m_imControlledNumEventsPending)
|
||
{
|
||
//
|
||
// There are no more events to return.
|
||
//
|
||
m_imfControlledNewEvent = TRUE;
|
||
return(FALSE);
|
||
}
|
||
else
|
||
{
|
||
TRACE_OUT(("Event to return: %u",
|
||
m_aimControlledEventsToReturn[m_imControlledNumEventsReturned]));
|
||
if ((m_aimControlledEventsToReturn[m_imControlledNumEventsReturned] >=
|
||
IEM_EVENT_KEYPAD0_DOWN) &&
|
||
(m_aimControlledEventsToReturn[m_imControlledNumEventsReturned] <=
|
||
(IEM_EVENT_KEYPAD0_DOWN+9)))
|
||
{
|
||
//
|
||
// Return a keypad down event.
|
||
//
|
||
pIMEventOut->type = IM_TYPE_VK1;
|
||
pIMEventOut->data.keyboard.keyCode = (TSHR_UINT16)
|
||
(VK_NUMPAD0 +
|
||
(m_aimControlledEventsToReturn[m_imControlledNumEventsReturned] -
|
||
IEM_EVENT_KEYPAD0_DOWN));
|
||
pIMEventOut->data.keyboard.flags = IM_FLAG_KEYBOARD_ALT_DOWN;
|
||
}
|
||
else if ((m_aimControlledEventsToReturn[m_imControlledNumEventsReturned] >=
|
||
IEM_EVENT_KEYPAD0_UP) &&
|
||
(m_aimControlledEventsToReturn[m_imControlledNumEventsReturned] <=
|
||
(IEM_EVENT_KEYPAD0_UP+9)))
|
||
{
|
||
//
|
||
// Return a keypad up event.
|
||
//
|
||
pIMEventOut->type = IM_TYPE_VK1;
|
||
pIMEventOut->data.keyboard.keyCode = (TSHR_UINT16)
|
||
(VK_NUMPAD0 +
|
||
(m_aimControlledEventsToReturn[m_imControlledNumEventsReturned] -
|
||
IEM_EVENT_KEYPAD0_UP));
|
||
pIMEventOut->data.keyboard.flags = IM_FLAG_KEYBOARD_DOWN |
|
||
IM_FLAG_KEYBOARD_RELEASE |
|
||
IM_FLAG_KEYBOARD_ALT_DOWN;
|
||
}
|
||
else
|
||
{
|
||
switch (m_aimControlledEventsToReturn[m_imControlledNumEventsReturned])
|
||
{
|
||
case IEM_EVENT_CTRL_DOWN:
|
||
//
|
||
// Set up a Ctrl down event.
|
||
//
|
||
pIMEventOut->type = IM_TYPE_VK1;
|
||
pIMEventOut->data.keyboard.keyCode =
|
||
VK_CONTROL;
|
||
pIMEventOut->data.keyboard.flags = 0;
|
||
break;
|
||
|
||
case IEM_EVENT_CTRL_UP:
|
||
//
|
||
// Set up a Ctrl up event.
|
||
//
|
||
pIMEventOut->type = IM_TYPE_VK1;
|
||
pIMEventOut->data.keyboard.keyCode =
|
||
VK_CONTROL;
|
||
pIMEventOut->data.keyboard.flags =
|
||
IM_FLAG_KEYBOARD_DOWN | IM_FLAG_KEYBOARD_RELEASE;
|
||
break;
|
||
|
||
case IEM_EVENT_SHIFT_DOWN:
|
||
//
|
||
// Set up a Shift down event.
|
||
//
|
||
pIMEventOut->type = IM_TYPE_VK1;
|
||
pIMEventOut->data.keyboard.keyCode =
|
||
VK_SHIFT;
|
||
pIMEventOut->data.keyboard.flags = 0;
|
||
break;
|
||
|
||
case IEM_EVENT_SHIFT_UP:
|
||
//
|
||
// Set up a Shift up event.
|
||
//
|
||
pIMEventOut->type = IM_TYPE_VK1;
|
||
pIMEventOut->data.keyboard.keyCode =
|
||
VK_SHIFT;
|
||
pIMEventOut->data.keyboard.flags =
|
||
IM_FLAG_KEYBOARD_DOWN | IM_FLAG_KEYBOARD_RELEASE;
|
||
break;
|
||
|
||
case IEM_EVENT_MENU_DOWN:
|
||
//
|
||
// Set up a Menu down event.
|
||
//
|
||
pIMEventOut->type = IM_TYPE_VK1;
|
||
pIMEventOut->data.keyboard.keyCode = VK_MENU;
|
||
pIMEventOut->data.keyboard.flags = 0;
|
||
break;
|
||
|
||
case IEM_EVENT_MENU_UP:
|
||
//
|
||
// Set up a Menu up event.
|
||
//
|
||
pIMEventOut->type = IM_TYPE_VK1;
|
||
pIMEventOut->data.keyboard.keyCode = VK_MENU;
|
||
pIMEventOut->data.keyboard.flags =
|
||
IM_FLAG_KEYBOARD_DOWN | IM_FLAG_KEYBOARD_RELEASE;
|
||
break;
|
||
|
||
case IEM_EVENT_REPLAY:
|
||
//
|
||
// Just copy the packet.
|
||
//
|
||
*pIMEventOut = *pIMEventIn;
|
||
break;
|
||
|
||
case IEM_EVENT_REPLAY_VK:
|
||
//
|
||
// Replay the VK from m_imControlledVKToReplay using the
|
||
// flags on the incoming packet.
|
||
//
|
||
*pIMEventOut = *pIMEventIn;
|
||
pIMEventOut->type = IM_TYPE_VK1;
|
||
pIMEventOut->data.keyboard.keyCode = (TSHR_UINT16)
|
||
m_imControlledVKToReplay;
|
||
break;
|
||
|
||
case IEM_EVENT_REPLAY_VK_UP:
|
||
//
|
||
// Replay an up key event for the VK in
|
||
// m_imControlledVKToReplay.
|
||
//
|
||
pIMEventOut->type = IM_TYPE_VK1;
|
||
pIMEventOut->data.keyboard.keyCode = (TSHR_UINT16)
|
||
m_imControlledVKToReplay;
|
||
pIMEventOut->data.keyboard.flags =
|
||
IM_FLAG_KEYBOARD_DOWN | IM_FLAG_KEYBOARD_RELEASE;
|
||
break;
|
||
|
||
case IEM_EVENT_REPLAY_VK_DOWN:
|
||
//
|
||
// Replay a down key event for the VK in
|
||
// m_imControlledVKToReplay.
|
||
//
|
||
pIMEventOut->type = IM_TYPE_VK1;
|
||
pIMEventOut->data.keyboard.keyCode = (TSHR_UINT16)
|
||
m_imControlledVKToReplay;
|
||
pIMEventOut->data.keyboard.flags = 0;
|
||
break;
|
||
|
||
case IEM_EVENT_NORMAL:
|
||
//
|
||
// Play back the event but force it to be normal.
|
||
//
|
||
*pIMEventOut = *pIMEventIn;
|
||
pIMEventOut->data.keyboard.flags &=
|
||
(TSHR_UINT16)~IM_FLAG_KEYBOARD_ALT_DOWN;
|
||
break;
|
||
|
||
case IEM_EVENT_SYSTEM:
|
||
//
|
||
// Play back the event but force it to be system.
|
||
//
|
||
*pIMEventOut = *pIMEventIn;
|
||
pIMEventOut->data.keyboard.flags |=
|
||
IM_FLAG_KEYBOARD_ALT_DOWN;
|
||
break;
|
||
|
||
default:
|
||
ERROR_OUT(( "Invalid code path"));
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
m_imControlledNumEventsReturned++;
|
||
|
||
//
|
||
// If we're going to playback a NUMLOCK event, make sure we force
|
||
// the keyboard LEDs to be accurate.
|
||
//
|
||
if ((pIMEventOut->type == IM_TYPE_VK1) &&
|
||
(pIMEventOut->data.keyboard.keyCode == VK_NUMLOCK) &&
|
||
IS_IM_KEY_PRESS(pIMEventOut->data.keyboard.flags))
|
||
{
|
||
TRACE_OUT(("Playing back NUMLOCK; add IM_FLAG_KEYBOARD_UPDATESTATE"));
|
||
pIMEventOut->data.keyboard.flags |= IM_FLAG_KEYBOARD_UPDATESTATE;
|
||
}
|
||
|
||
return(TRUE);
|
||
}
|
||
|
||
DebugExitBOOL(ASShare::IMTranslateIncoming, FALSE);
|
||
return(FALSE);
|
||
}
|
||
|
||
|
||
//
|
||
// FUNCTION: IMInsertModifierKeystrokes
|
||
//
|
||
// DESCRIPTION:
|
||
//
|
||
// This function inserts various modifier keystrokes into the supplied
|
||
// buffer to move from one modifier state to another.
|
||
//
|
||
// PARAMETERS:
|
||
//
|
||
// curKbState - the current modifier state (bit 0 - Shift, bit 1 - Ctrl,
|
||
// bit 2 - Menu).
|
||
//
|
||
// targetKbState - the state we want the modifiers to be in
|
||
//
|
||
// pEventQueue - a pointer to an array where the required events can be
|
||
// inserted
|
||
//
|
||
// RETURNS: the number of events inserted
|
||
//
|
||
//
|
||
UINT ASShare::IMInsertModifierKeystrokes
|
||
(
|
||
BYTE curKbState,
|
||
BYTE targetKbState,
|
||
LPUINT pEventQueue
|
||
)
|
||
{
|
||
|
||
UINT kbDelta;
|
||
UINT events = 0;
|
||
|
||
DebugEntry(ASShare::IMInsertModifierKeystrokes);
|
||
|
||
//
|
||
// Find out which modifiers are different.
|
||
//
|
||
kbDelta = curKbState ^ targetKbState;
|
||
TRACE_OUT(( "Keyboard delat %x", kbDelta));
|
||
|
||
//
|
||
// Now generate the right events to get us into the correct modifier
|
||
// state.
|
||
//
|
||
if (kbDelta & IEM_SHIFT_DOWN)
|
||
{
|
||
//
|
||
// Shift state is different - do we need an up or down.
|
||
//
|
||
if (curKbState & IEM_SHIFT_DOWN)
|
||
{
|
||
//
|
||
// We need an up.
|
||
//
|
||
pEventQueue[events++] = IEM_EVENT_SHIFT_UP;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// We need a down.
|
||
//
|
||
pEventQueue[events++] = IEM_EVENT_SHIFT_DOWN;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Same process for Ctrl and Alt.
|
||
//
|
||
if (kbDelta & IEM_CTRL_DOWN)
|
||
{
|
||
if (curKbState & IEM_CTRL_DOWN)
|
||
{
|
||
pEventQueue[events++] = IEM_EVENT_CTRL_UP;
|
||
}
|
||
else
|
||
{
|
||
pEventQueue[events++] = IEM_EVENT_CTRL_DOWN;
|
||
}
|
||
}
|
||
|
||
if (kbDelta & IEM_MENU_DOWN)
|
||
{
|
||
if (curKbState & IEM_MENU_DOWN)
|
||
{
|
||
pEventQueue[events++] = IEM_EVENT_MENU_UP;
|
||
}
|
||
else
|
||
{
|
||
pEventQueue[events++] = IEM_EVENT_MENU_DOWN;
|
||
}
|
||
}
|
||
|
||
DebugExitDWORD(ASShare::IMInsertModifierKeystrokes, events);
|
||
return(events);
|
||
}
|
||
|
||
|
||
//
|
||
// IMInjectEvent()
|
||
//
|
||
// DESCRIPTION:
|
||
//
|
||
// Called by IMMaybeInjectEvents when it is ready to inject an event.
|
||
// Given a pointer to a IMOSEVENT this function formats it correctly and
|
||
// calls the appropriate USER callback. It also updates the async key
|
||
// state arrays for the source queue and USER and sets m_imLastInjectTime to
|
||
// the tick count at which the event was injected. We protect against
|
||
// injecting up key strokes/mouse buttons when USER does not think the
|
||
// key/button is down in this function. It is quite possible (given the
|
||
// potential variety of CAs) that the IM will be asked to inject an up
|
||
// event when there has been no corresponding down event. This should be
|
||
// harmless as it is possible for this to happen in real life (ie the
|
||
// system message queue is full when the down event happens but there is
|
||
// space when the up event happens). However, it is quite unlikely and it
|
||
// is more likely that injecting these unmatched events will confuse
|
||
// applications.
|
||
//
|
||
// PARAMETERS:
|
||
//
|
||
// pEvent - pointer to an IMOSEVENT.
|
||
//
|
||
// THIS WORKS FOR NT AND WIN95.
|
||
//
|
||
BOOL ASShare::IMInjectEvent(LPIMOSEVENT pEvent)
|
||
{
|
||
UINT clickTime;
|
||
TSHR_UINT16 flags;
|
||
TSHR_UINT16 flagsAfter;
|
||
LPMSEV pMouseEvent;
|
||
|
||
DebugEntry(IMInjectEvent);
|
||
|
||
//
|
||
// Now inject the event.
|
||
//
|
||
switch (pEvent->type)
|
||
{
|
||
case IM_MOUSE_EVENT:
|
||
//
|
||
// Set up a pointer to the mouse event data.
|
||
//
|
||
pMouseEvent = &(pEvent->event.mouse);
|
||
|
||
//
|
||
// Check whether this is an unmatched up event
|
||
//
|
||
if ((IM_MEV_BUTTON1_UP(*pEvent) &&
|
||
IM_KEY_STATE_IS_UP(m_aimControlledKeyStates[VK_LBUTTON])) ||
|
||
(IM_MEV_BUTTON2_UP(*pEvent) &&
|
||
IM_KEY_STATE_IS_UP(m_aimControlledKeyStates[VK_RBUTTON])) ||
|
||
(IM_MEV_BUTTON3_UP(*pEvent) &&
|
||
IM_KEY_STATE_IS_UP(m_aimControlledKeyStates[VK_MBUTTON])))
|
||
{
|
||
//
|
||
// This is an unmatched up event so just discard it here
|
||
//
|
||
TRACE_OUT(("IMInjectEvent: discarding unmatched mouse up event"));
|
||
DC_QUIT;
|
||
}
|
||
|
||
//
|
||
// Store the injection time of this event.
|
||
//
|
||
m_imControlledLastLowLevelMouseEventTime = GetTickCount();
|
||
|
||
//
|
||
// Store the mouse position - only consider absolute mouse
|
||
// moves. (Note that for the cases in which we inject a
|
||
// relative mouse event we always set the co-ordinate change to
|
||
// 0).
|
||
//
|
||
if (pMouseEvent->flags & MOUSEEVENTF_ABSOLUTE)
|
||
{
|
||
m_imControlledLastMousePos.x = pMouseEvent->pt.x;
|
||
m_imControlledLastMousePos.y = pMouseEvent->pt.y;
|
||
|
||
TRACE_OUT(( "Updating mouse position (%d:%d)",
|
||
m_imControlledLastMousePos.x,
|
||
m_imControlledLastMousePos.y));
|
||
}
|
||
|
||
//
|
||
// Inject the event.
|
||
//
|
||
TRACE_OUT(("IMInjectEvent: MOUSE parameters are:"));
|
||
TRACE_OUT((" flags 0x%08x", pMouseEvent->flags));
|
||
TRACE_OUT((" time 0x%08x", m_imControlledLastLowLevelMouseEventTime));
|
||
TRACE_OUT((" position (%d, %d)", pMouseEvent->pt.x, pMouseEvent->pt.y));
|
||
TRACE_OUT((" mouseData %d", pMouseEvent->mouseData));
|
||
TRACE_OUT((" dwExtra %d", pMouseEvent->dwExtraInfo));
|
||
|
||
//
|
||
// Finally scale the logical screen co-ordinates to the full
|
||
// 16-bit range (0..65535).
|
||
//
|
||
|
||
ASSERT(m_pasLocal->cpcCaps.screen.capsScreenWidth);
|
||
ASSERT(m_pasLocal->cpcCaps.screen.capsScreenHeight);
|
||
|
||
pMouseEvent->pt.x = IM_MOUSEPOS_LOG_TO_OS(pMouseEvent->pt.x,
|
||
m_pasLocal->cpcCaps.screen.capsScreenWidth);
|
||
pMouseEvent->pt.y = IM_MOUSEPOS_LOG_TO_OS(pMouseEvent->pt.y,
|
||
m_pasLocal->cpcCaps.screen.capsScreenHeight);
|
||
|
||
OSI_InjectMouseEvent(pMouseEvent->flags, pMouseEvent->pt.x,
|
||
pMouseEvent->pt.y, pMouseEvent->mouseData, pMouseEvent->dwExtraInfo);
|
||
break;
|
||
|
||
case IM_KEYBOARD_EVENT:
|
||
//
|
||
// Check whether this is an unmatched up event
|
||
//
|
||
if (IM_KEV_KEYUP(*pEvent) &&
|
||
IM_KEY_STATE_IS_UP(m_aimControlledKeyStates[IM_KEV_VKCODE(*pEvent)]))
|
||
{
|
||
//
|
||
// This is an unmatched up event so just discard it.
|
||
//
|
||
TRACE_OUT(("IMInjectEvent: discarding unmatched key up event %04hX",
|
||
IM_KEV_VKCODE(*pEvent)));
|
||
DC_QUIT;
|
||
}
|
||
|
||
//
|
||
// Inject the event.
|
||
//
|
||
TRACE_OUT(("IMInjectEvent: KEYBD parameters are:"));
|
||
TRACE_OUT((" flags 0x%08x", pEvent->event.keyboard.flags));
|
||
TRACE_OUT((" virtkey %u", pEvent->event.keyboard.vkCode));
|
||
TRACE_OUT((" scan code %u", pEvent->event.keyboard.scanCode));
|
||
|
||
OSI_InjectKeyboardEvent(pEvent->event.keyboard.flags,
|
||
pEvent->event.keyboard.vkCode, pEvent->event.keyboard.scanCode,
|
||
pEvent->event.keyboard.dwExtraInfo);
|
||
|
||
if (pEvent->flags & IM_FLAG_UPDATESTATE)
|
||
{
|
||
BYTE kbState[256];
|
||
|
||
TRACE_OUT(("Updating keyboard LED state after playing back toggle"));
|
||
|
||
GetKeyboardState(kbState);
|
||
SetKeyboardState(kbState);
|
||
}
|
||
break;
|
||
|
||
default:
|
||
//
|
||
// We do nothing for unexpected events - this allow us to add
|
||
// more events later that can be sent to back level systems
|
||
// where they will be safely ignored
|
||
//
|
||
TRACE_OUT(( "Unexpected event %d", pEvent->type));
|
||
DC_QUIT;
|
||
}
|
||
|
||
//
|
||
// If we get here successfully then we want to update our copy of the
|
||
// async key state so set the flag.
|
||
//
|
||
IMUpdateAsyncArray(m_aimControlledKeyStates, pEvent);
|
||
|
||
DC_EXIT_POINT:
|
||
|
||
DebugExitBOOL(ASShare::IMInjectEvent, TRUE);
|
||
return(TRUE);
|
||
}
|
||
|
||
|
||
//
|
||
// FUNCTION: IMInjectingEvents
|
||
//
|
||
BOOL ASShare::IMInjectingEvents(void)
|
||
{
|
||
LPIMOSEVENT pNextEvent;
|
||
IMOSEVENT mouseMoveEvent;
|
||
UINT tick;
|
||
UINT targetTime;
|
||
UINT targetDelta;
|
||
BOOL rc = TRUE;
|
||
|
||
DebugEntry(ASShare::IMInjectingEvents);
|
||
|
||
if (m_pasLocal->m_caControlledBy && m_imControlledOSQ.numEvents)
|
||
{
|
||
pNextEvent = m_imControlledOSQ.events + m_imControlledOSQ.head;
|
||
|
||
//
|
||
// First check if this is a remote mouse event being injected too
|
||
// soon after the previous one. We used to only do this for mouse
|
||
// move events to prevent them all being spoiled if they were
|
||
// injected too quickly. However, we now do it for all mouse
|
||
// events because of a bug in Windows USER whereby if the mouse
|
||
// press which brings up a menu is processed after the
|
||
// corresponding mouse release has been passed to USER (so that the
|
||
// async state of the mouse button is up) then the menu is brought
|
||
// up in the position it is brought up in if it is selected via the
|
||
// keyboard rather than the position it is brought up in if it is
|
||
// selected by the mouse. (These positions are only different when
|
||
// the menu cannot be placed completely below or above the menu
|
||
// bar). This can then lead to the mouse release selecting an item
|
||
// from the menu.
|
||
//
|
||
tick = GetTickCount();
|
||
if (m_imfControlledPaceInjection &&
|
||
(pNextEvent->type == IM_MOUSE_EVENT))
|
||
{
|
||
//
|
||
// This is a remote mouse event so check that now is a good
|
||
// time to inject it Smooth out the backlog adjustment so that
|
||
// packet bursts do not get spoiled too much. Set an absolute
|
||
// lg_lpimSharedData->imit on injection delay of the low sample rate so that
|
||
// timestamp anomolies do not cause us to withhold messages
|
||
//
|
||
|
||
//
|
||
// The target delta between last and current events is
|
||
// calculated from the remote timestamps
|
||
//
|
||
targetDelta = abs((int)(pNextEvent->time -
|
||
m_imControlledLastMouseRemoteTime));
|
||
if (targetDelta > IM_LOCAL_MOUSE_SAMPLING_GAP_LOW_MS)
|
||
{
|
||
targetDelta = IM_LOCAL_MOUSE_SAMPLING_GAP_LOW_MS;
|
||
}
|
||
|
||
//
|
||
// The target injection time is based on the last injection
|
||
// time and our target delta, adjusted for any backlog we are
|
||
// seeing. Because packeting gives a jerky backlog we need to
|
||
// smooth our adjustment out (only modify by backlog/8)
|
||
//
|
||
targetTime = m_imControlledLastMouseLocalTime +
|
||
targetDelta - (m_imControlledMouseBacklog/8);
|
||
|
||
TRACE_OUT(( "Last tremote %#lx, this tremote %#lx, backlog %#lx",
|
||
m_imControlledLastMouseRemoteTime,
|
||
pNextEvent->time,
|
||
m_imControlledMouseBacklog));
|
||
TRACE_OUT(( "Last tlocal %#lx, tick %#lx, targetTime %#lx",
|
||
m_imControlledLastMouseLocalTime,
|
||
tick,
|
||
targetTime));
|
||
|
||
//
|
||
// Now inject the events - ignore them if they are too early
|
||
//
|
||
if (IM_MEV_ABS_MOVE(*pNextEvent) && (tick < targetTime))
|
||
{
|
||
//
|
||
// If values seem wild (for example this is the first mouse
|
||
// event ever) then reset them
|
||
//
|
||
if (targetTime > tick + 1000)
|
||
{
|
||
m_imControlledLastMouseRemoteTime = pNextEvent->time;
|
||
m_imControlledLastMouseLocalTime = tick;
|
||
m_imControlledMouseBacklog = 0;
|
||
TRACE_OUT(( "Wild values - reset"));
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// This is too early - get out of the loop.
|
||
//
|
||
rc = FALSE;
|
||
DC_QUIT;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// We will inject this event (and remember when we did it
|
||
// so we don't inject the next one to quickly). Calculate
|
||
// the backlog because we may have to make up for a
|
||
// processing delay If this event is long (1000 mS) after
|
||
// our projected event time then assume a pause in movement
|
||
// and reset the backlog to avoid progressive erosion.
|
||
// Otherwise calculate the new backlog.
|
||
//
|
||
// Perf - don't reset backlog unless the time has expired.
|
||
// Restting just because we see a click means that we
|
||
// actually increase the latency by assuming that mouse
|
||
// messages queued behind the tick are not backlogged.
|
||
//
|
||
if (tick < (targetTime + 1000))
|
||
{
|
||
m_imControlledMouseBacklog += ( tick -
|
||
m_imControlledLastMouseLocalTime -
|
||
targetDelta );
|
||
}
|
||
else
|
||
{
|
||
m_imControlledMouseBacklog = 0;
|
||
TRACE_OUT(( "Non move/big gap in move"));
|
||
}
|
||
m_imControlledLastMouseRemoteTime = pNextEvent->time;
|
||
m_imControlledLastMouseLocalTime = tick;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// This is not a remote mouse event. Reset the
|
||
// m_imNextRemoteMouseEvent to zero so we don't hold up the next
|
||
// remote mouse event.
|
||
//
|
||
m_imControlledLastMouseRemoteTime = pNextEvent->time;
|
||
m_imControlledLastMouseLocalTime = tick;
|
||
m_imControlledMouseBacklog = 0;
|
||
TRACE_OUT(( "Local/non-paced/non-mouse - reset"));
|
||
}
|
||
|
||
//
|
||
// Only inject the event if IM_FLAG_DONT_REPLAY is not set
|
||
//
|
||
if (!(pNextEvent->flags & IM_FLAG_DONT_REPLAY))
|
||
{
|
||
//
|
||
// If the event is a mouse click then we always inject a mouse
|
||
// move event g_lpimSharedData->immediately before it to ensure that the current
|
||
// position is correct before the click is injected.
|
||
//
|
||
// This is because USER does not handle combined "move and
|
||
// click" events correctly (it appears to treat them as "click
|
||
// and move", generating a mouse move event AFTER the click
|
||
// event, rather than before). Under normal Windows operation
|
||
// it appears (from observation) that movement events and click
|
||
// events are generated separately (i.e. a click event will
|
||
// never have the movement flag set). However, incoming mouse
|
||
// click events may have positions that are different from the
|
||
// last mouse move event so we must inject the extra move event
|
||
// to keep USER happy.
|
||
//
|
||
if ( (pNextEvent->type == IM_MOUSE_EVENT) &&
|
||
(IM_MEV_BUTTON_DOWN(*pNextEvent) ||
|
||
IM_MEV_BUTTON_UP(*pNextEvent)) )
|
||
{
|
||
TRACE_OUT(( "Mouse clk: injecting extra"));
|
||
|
||
//
|
||
// Take a copy of the event.
|
||
//
|
||
mouseMoveEvent = *pNextEvent;
|
||
|
||
//
|
||
// Turn the mouse click event into a mouse move event with
|
||
// the absolute/relative flag unchanged.
|
||
//
|
||
mouseMoveEvent.event.mouse.flags &= MOUSEEVENTF_ABSOLUTE;
|
||
mouseMoveEvent.event.mouse.flags |= MOUSEEVENTF_MOVE;
|
||
|
||
//
|
||
// Inject the additional move event.
|
||
//
|
||
IMInjectEvent(&mouseMoveEvent);
|
||
|
||
//
|
||
// As the position is now correct, we turn the click into a
|
||
// relative event with an unchanged position.
|
||
//
|
||
pNextEvent->event.mouse.flags &= ~MOUSEEVENTF_ABSOLUTE;
|
||
pNextEvent->event.mouse.pt.x = 0;
|
||
pNextEvent->event.mouse.pt.y = 0;
|
||
|
||
//
|
||
// If this is a mouse down click then flag the injection
|
||
// heuristic as active. We deactivate the heuristic when
|
||
// the mouse is released so that dragging over menus can be
|
||
// done without delay. (We keep the heuristic active when
|
||
// mouse is depressed because most drawing apps perform
|
||
// freehand drawing in this way.
|
||
//
|
||
if (IM_MEV_BUTTON_DOWN(*pNextEvent))
|
||
{
|
||
TRACE_OUT(( "Injection pacing active"));
|
||
m_imfControlledPaceInjection = TRUE;
|
||
}
|
||
else
|
||
{
|
||
TRACE_OUT(( "Injection pacing inactive"));
|
||
m_imfControlledPaceInjection = FALSE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Inject the real event.
|
||
//
|
||
TRACE_OUT(( "Injecting the evnt now"));
|
||
IMInjectEvent(pNextEvent);
|
||
}
|
||
|
||
IMUpdateAsyncArray(m_aimControlledControllerKeyStates, pNextEvent);
|
||
|
||
ASSERT(m_imControlledOSQ.numEvents);
|
||
m_imControlledOSQ.numEvents--;
|
||
m_imControlledOSQ.head = CIRCULAR_INDEX(m_imControlledOSQ.head, 1,
|
||
IM_SIZE_OSQ);
|
||
|
||
//
|
||
// We only inject a single keyboard event per pass to prevent
|
||
// excessive spoiling of repeated events. Having got them here it
|
||
// seems a shame to spoil them. Spoil down to 5 so we don't get
|
||
// excessive overrun following a key repeat sequence.
|
||
//
|
||
if ((pNextEvent->type == IM_KEYBOARD_EVENT) &&
|
||
(m_imControlledOSQ.numEvents < 5))
|
||
{
|
||
TRACE_OUT(( "Keyboard event so leaving loop"));
|
||
rc = FALSE;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// We're done.
|
||
//
|
||
rc = FALSE;
|
||
}
|
||
|
||
DC_EXIT_POINT:
|
||
DebugExitBOOL(ASShare::IMInjectingEvents, rc);
|
||
return(rc);
|
||
}
|
||
|
||
|
||
|
||
|
||
//
|
||
// IMMaybeInjectEvents()
|
||
//
|
||
// DESCRIPTION:
|
||
//
|
||
// This is called whenever the IM believes there may be an opportunity to
|
||
// inject more events into USER via the input event callbacks. The two
|
||
// main reasons for this are:
|
||
//
|
||
// 1. We have received a new event in the mouse or keyboard hooks. This
|
||
// will normally imply that an event has been removed from the system
|
||
// message queue so there will be at least one free slot on it.
|
||
//
|
||
// 2. We have added a new event (or events) to either the local or remote
|
||
// USER event queues. This means there will be at least one event waiting
|
||
// to be injected.
|
||
//
|
||
// This function is also called periodically (via IM_Periodic) to keep
|
||
// things moving.
|
||
//
|
||
// In order for an event to be injected there must be
|
||
//
|
||
// - an event waiting (with IM_FLAG_DONT_REPLAY reset)
|
||
// - a space on the USER system message queue
|
||
// - a new time stamp (if we are switching event sources).
|
||
//
|
||
// This function works as a state machine. It always starts in a specified
|
||
// state and will then take various actions and then possibly enter a new
|
||
// state. It continues to loop through this process until it cannot take
|
||
// any actions in one of its states at which point it returns.
|
||
//
|
||
// There are four states (each of which is further qualified by whether it
|
||
// refers to local or remote events). The states are:
|
||
//
|
||
// IM_INJECTING_EVENTS - we are injecting events into USER from the
|
||
// appropriate queue.
|
||
//
|
||
// IM_WAITING_FOR_TICK - we are waiting for a timer tick to give us a new
|
||
// timestamp before injecting events
|
||
//
|
||
// IM_DEVICE_TO_NEW_SOURCE - we are injecting fake events to bring the
|
||
// state of the keyboard and mouse (as seen by USER) into line with the
|
||
// state of the new source of input.
|
||
//
|
||
void ASShare::IMMaybeInjectEvents(void)
|
||
{
|
||
IMEVENT eventIn;
|
||
IMEVENT eventOut;
|
||
IMOSEVENT OSEvent;
|
||
BOOL replay;
|
||
UINT rcConvert;
|
||
UINT now;
|
||
HWND hwndDest;
|
||
HWND hwndParent;
|
||
POINT ptMousePos;
|
||
LPIMOSEVENT pNextEvent;
|
||
|
||
DebugEntry(IMMaybeInjectEvents);
|
||
|
||
ASSERT(m_pasLocal->m_caControlledBy);
|
||
|
||
//
|
||
// Check whether we should wait before converting events. We need to
|
||
// do this to prevent us being swamped with mouse move events when
|
||
// we're waiting for the desktop to scroll.
|
||
//
|
||
now = GetTickCount();
|
||
if (IN_TIME_RANGE(m_imControlledLastIncompleteConversion,
|
||
m_imControlledLastIncompleteConversion + IM_MIN_RECONVERSION_INTERVAL_MS, now))
|
||
{
|
||
goto IM_DISCARD;
|
||
}
|
||
|
||
//
|
||
// NOW TRANSLATE NETWORK EVENTS TO OS EVENTS
|
||
// We'll discard or inject them when the time is right.
|
||
// But don't do translation if there are still OS events left
|
||
// waiting to be injected from the previous packet.
|
||
//
|
||
if (m_imControlledEventQ.numEvents && !m_imControlledOSQ.numEvents)
|
||
{
|
||
//
|
||
// Get the event from the front of the network event queue.
|
||
//
|
||
eventIn = m_imControlledEventQ.events[0];
|
||
|
||
replay = FALSE;
|
||
switch (eventIn.type)
|
||
{
|
||
case IM_TYPE_3BUTTON:
|
||
case IM_TYPE_VK1:
|
||
case IM_TYPE_VK2:
|
||
case IM_TYPE_ASCII:
|
||
{
|
||
replay = TRUE;
|
||
break;
|
||
}
|
||
|
||
default:
|
||
ERROR_OUT(("Bogus NETWORK event being translated"));
|
||
break;
|
||
}
|
||
|
||
//
|
||
// After this while loop we test rcConvert to see whether the
|
||
// input packet can now be removed (has been fully processed).
|
||
// We only SET rcConvert if IMTranslateIncoming returns TRUE,
|
||
// yet IM_TR specifically returns FALSE to indicate that the
|
||
// input packet does not contain an event and is to be
|
||
// discarded. To fix this - set rcConvert here.
|
||
//
|
||
rcConvert = IM_IMQUEUEREMOVE;
|
||
while (IMTranslateIncoming(&eventIn, &eventOut))
|
||
{
|
||
rcConvert = IMConvertIMEventToOSEvent(&eventOut, &OSEvent);
|
||
|
||
//
|
||
// Inject the event into the OS queue (if required).
|
||
//
|
||
if (rcConvert & IM_OSQUEUEINJECT)
|
||
{
|
||
if (!replay)
|
||
{
|
||
OSEvent.flags |= IM_FLAG_DONT_REPLAY;
|
||
}
|
||
|
||
// Add to playback queue
|
||
|
||
// Is the queue filled up?
|
||
if (m_imControlledOSQ.numEvents == IM_SIZE_OSQ)
|
||
{
|
||
ERROR_OUT(("Failed to add OS event to queue"));
|
||
}
|
||
else
|
||
{
|
||
// Put this element at the tail.
|
||
m_imControlledOSQ.events[CIRCULAR_INDEX(m_imControlledOSQ.head,
|
||
m_imControlledOSQ.numEvents, IM_SIZE_OSQ)] =
|
||
OSEvent;
|
||
m_imControlledOSQ.numEvents++;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// The following test is not ideal as it relies on the fact
|
||
// that any events for which IMConvertIMEventToUSEREvent does
|
||
// not set IM_IMQUEUEREMOVE had a one-one mapping.
|
||
//
|
||
// However, we know that this is always the case with mouse
|
||
// events, which are the only events that will be cause this
|
||
// flag to be unset.
|
||
//
|
||
if (rcConvert & IM_IMQUEUEREMOVE)
|
||
{
|
||
//
|
||
// Remove this from the network queue
|
||
//
|
||
m_imControlledEventQ.numEvents--;
|
||
UT_MoveMemory(&(m_imControlledEventQ.events[0]),
|
||
&(m_imControlledEventQ.events[1]),
|
||
sizeof(IMEVENT) * m_imControlledEventQ.numEvents);
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Remember this so we don't flood the input injection with
|
||
// events when we don't remove the network event from the
|
||
// queue.
|
||
//
|
||
TRACE_OUT(( "do not shuffle"));
|
||
m_imControlledLastIncompleteConversion = GetTickCount();
|
||
}
|
||
}
|
||
|
||
IM_DISCARD:
|
||
//
|
||
// Get rid of all discarded events. Update the remote controller's
|
||
// key state array to reflect it. But since we aren't going to replay
|
||
// these, don't update our local key state table.
|
||
//
|
||
|
||
while (m_imControlledOSQ.numEvents > 0)
|
||
{
|
||
pNextEvent = m_imControlledOSQ.events + m_imControlledOSQ.head;
|
||
if (!(pNextEvent->flags & IM_FLAG_DONT_REPLAY))
|
||
{
|
||
// We're done.
|
||
break;
|
||
}
|
||
|
||
IMUpdateAsyncArray(m_aimControlledControllerKeyStates, pNextEvent);
|
||
|
||
ASSERT(m_imControlledOSQ.numEvents);
|
||
m_imControlledOSQ.numEvents--;
|
||
m_imControlledOSQ.head = CIRCULAR_INDEX(m_imControlledOSQ.head, 1,
|
||
IM_SIZE_OSQ);
|
||
}
|
||
|
||
|
||
//
|
||
// NOW INJECT OS EVENTS into system
|
||
//
|
||
while (IMInjectingEvents())
|
||
{
|
||
;
|
||
}
|
||
|
||
DebugExitVOID(ASShare::IMMaybeInjectEvents);
|
||
}
|
||
|
||
|
||
//
|
||
// FUNCTION: IMUpdateAsyncArray
|
||
//
|
||
// DESCRIPTION:
|
||
//
|
||
// Called with the address of one of our async key state arrays and a
|
||
// IMOSEVENT this function updates the async key state array according to
|
||
// the contents of the IMOSEVENT.
|
||
//
|
||
// PARAMETERS:
|
||
//
|
||
// paimKeyStates - pointer to async key state array.
|
||
//
|
||
// pEvent - pointer to IMOSEVENT.
|
||
//
|
||
// RETURNS: NONE
|
||
//
|
||
//
|
||
void ASShare::IMUpdateAsyncArray
|
||
(
|
||
LPBYTE paimKeyStates,
|
||
LPIMOSEVENT pEvent
|
||
)
|
||
{
|
||
UINT flags;
|
||
UINT vkCode;
|
||
|
||
DebugEntry(ASShare::IMUpdateAsyncArray);
|
||
|
||
switch (pEvent->type)
|
||
{
|
||
case IM_MOUSE_EVENT:
|
||
//
|
||
// Update the async key state arrays for this event. Note that
|
||
// we treat each event as independent - this is how Windows
|
||
// treats them and if all the up/down flags are set Windows
|
||
// will generate six mouse message! (and in down,up order).
|
||
//
|
||
flags = pEvent->event.mouse.flags;
|
||
|
||
if (flags & MOUSEEVENTF_LEFTDOWN)
|
||
{
|
||
IM_SET_VK_DOWN(paimKeyStates[VK_LBUTTON]);
|
||
}
|
||
|
||
if (flags & MOUSEEVENTF_LEFTUP)
|
||
{
|
||
IM_SET_VK_UP(paimKeyStates[VK_LBUTTON]);
|
||
}
|
||
|
||
if (flags & MOUSEEVENTF_RIGHTDOWN)
|
||
{
|
||
IM_SET_VK_DOWN(paimKeyStates[VK_RBUTTON]);
|
||
}
|
||
|
||
if (flags & MOUSEEVENTF_RIGHTUP)
|
||
{
|
||
IM_SET_VK_UP(paimKeyStates[VK_RBUTTON]);
|
||
}
|
||
|
||
if (flags & MOUSEEVENTF_MIDDLEDOWN)
|
||
{
|
||
IM_SET_VK_DOWN(paimKeyStates[VK_MBUTTON]);
|
||
}
|
||
|
||
if (flags & MOUSEEVENTF_MIDDLEUP)
|
||
{
|
||
IM_SET_VK_UP(paimKeyStates[VK_MBUTTON]);
|
||
}
|
||
break;
|
||
|
||
case IM_KEYBOARD_EVENT:
|
||
//
|
||
// Update the async key state arrays.
|
||
//
|
||
vkCode = IM_KEV_VKCODE(*pEvent);
|
||
|
||
if (IM_KEV_KEYUP(*pEvent))
|
||
{
|
||
IM_SET_VK_UP(paimKeyStates[vkCode]);
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// This is a key down event - check if it is a press or a
|
||
// repeat.
|
||
//
|
||
if (IM_KEY_STATE_IS_UP(paimKeyStates[vkCode]))
|
||
{
|
||
//
|
||
// This is a key press as the key was previously up -
|
||
// alter the toggle state. We keep the toggle state
|
||
// for all keys although we currently only worry about
|
||
// it for the `known' toggles.
|
||
//
|
||
IM_TOGGLE_VK(paimKeyStates[vkCode]);
|
||
}
|
||
|
||
IM_SET_VK_DOWN(paimKeyStates[vkCode]);
|
||
}
|
||
break;
|
||
|
||
default:
|
||
//
|
||
// Just ignore unexpected events.
|
||
//
|
||
ERROR_OUT(( "Unexpected event %u", pEvent->type));
|
||
break;
|
||
}
|
||
|
||
DebugExitVOID(ASShare::IMUpdateAsyncArray);
|
||
}
|
||
|