|
|
#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; if (ERROR_SUCCESS == RegOpenKey(HKEY_CURRENT_USER,AUDIO_KEY,&hkeyBandwidth)) { DWORD dwBandwidth = BW_DEFAULT; DWORD dwType = REG_DWORD; DWORD cbData = sizeof(dwBandwidth);
if ( ERROR_SUCCESS == RegQueryValueEx(hkeyBandwidth, REGVAL_TYPICALBANDWIDTH, NULL, &dwType, (LPBYTE)&dwBandwidth, &cbData) ) { if ( BW_144KBS == dwBandwidth ) { m_imInControlMouseWithhold = IM_LOCAL_MOUSE_WITHHOLD; } }
RegCloseKey(hkeyBandwidth); }
//
// 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 = NmLoadLibrary("imm32.dll",TRUE); 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
if(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); } else { WARNING_OUT(("Error %d getting keyboard state", GetLastError())); } //
// 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); }
// Install controlled input hooks
rc = OSI_InstallControlledHooks((pasControlledBy != NULL), (m_pasLocal->hetCount == HET_DESKTOPSHARED)); 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
if(!GetKeyboardState(m_aimInControlKeyStates)) { WARNING_OUT(("Error %d getting keyboard state", GetLastError())); }
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) { PIMEVENT pimEvent;
if (pasFrom->cpcCaps.general.version >= CAPS_VERSION_30) { WARNING_OUT(("Ignoring IM packet from [%d], not in control of us", pasFrom->mcsID)); DC_QUIT; }
//
// 2.x COMPAT: not-controlled folks send IM packets as broadcasts.
// Fake a mouse move move. Skip through all the events in the
// packet to the last mouse move/click/pos info.
//
// Note that we don't have to fill in all the S20, S20DATAPACKET,
// and DATAPACKET header info.
//
pimEvent = NULL; for (i = 0; i < pIMPacket->numEvents; i++) { if (pIMPacket->aEvents[i].type == IM_TYPE_3BUTTON) { pimEvent = &(pIMPacket->aEvents[i]); }
}
if (pimEvent) { // Pass fake packet with mouse pos to cursor manager
TRACE_OUT(("Handling 2.x mouse event to {%04d, %04d}", pimEvent->data.mouse.x, pimEvent->data.mouse.y)); CM_UpdateShadowCursor(pasFrom, pasFrom->cmShadowOff, pimEvent->data.mouse.x, pimEvent->data.mouse.y, pasFrom->cmHotSpot.x, pasFrom->cmHotSpot.y); }
// Now we're done.
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_PTR 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); ASSERT(!pasHost->m_caControlPaused);
if(!GetKeyboardState(m_aimInControlKeyStates)) { WARNING_OUT(("Error %d getting keyboard state", GetLastError())); }
//
// 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); ASSERT(!pasHost->m_caControlPaused);
if(!GetKeyboardState(m_aimInControlKeyStates)) { WARNING_OUT(("Error %d getting keyboard state", GetLastError())); }
//
// 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 � 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"));
if(GetKeyboardState(kbState)) { SetKeyboardState(kbState); } else { WARNING_OUT(("Error %d getting keyboard state", GetLastError())); } } 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: { // Always allow mouse moves
if (!(eventIn.data.mouse.flags & IM_FLAG_MOUSE_DOWN)) { replay = TRUE; } else { //
// Allow click events to shared windows or
// if a different desktop/screensaver is around
//
ptMousePos.x = eventIn.data.mouse.x; ptMousePos.y = eventIn.data.mouse.y;
hwndDest = WindowFromPoint(ptMousePos);
if (HET_WindowIsHosted(hwndDest) || OSI_IsWindowScreenSaver(hwndDest)) { replay = TRUE; } }
break; }
case IM_TYPE_VK1: case IM_TYPE_VK2: case IM_TYPE_ASCII: { hwndDest = GetForegroundWindow();
if (HET_WindowIsHosted(hwndDest) || OSI_IsWindowScreenSaver(hwndDest)) { 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); }
|