Checkpointing improved key handling. This change adds non-ASCII

key handling to win32 on both client and server.  It also changes
the protocol and adds code to ensure every key pressed also gets
released and that that doesn't get confused when the KeyID for
the press is different from the KeyID of the release (or repeat).
This commit is contained in:
crs 2003-04-27 17:01:14 +00:00
parent cf7ab3459d
commit 11f90022e0
24 changed files with 479 additions and 131 deletions

View File

@ -365,21 +365,22 @@ CClient::setClipboardDirty(ClipboardID, bool)
}
void
CClient::keyDown(KeyID id, KeyModifierMask mask)
CClient::keyDown(KeyID id, KeyModifierMask mask, KeyButton button)
{
m_screen->keyDown(id, mask);
m_screen->keyDown(id, mask, button);
}
void
CClient::keyRepeat(KeyID id, KeyModifierMask mask, SInt32 count)
CClient::keyRepeat(KeyID id, KeyModifierMask mask,
SInt32 count, KeyButton button)
{
m_screen->keyRepeat(id, mask, count);
m_screen->keyRepeat(id, mask, count, button);
}
void
CClient::keyUp(KeyID id, KeyModifierMask mask)
CClient::keyUp(KeyID id, KeyModifierMask mask, KeyButton button)
{
m_screen->keyUp(id, mask);
m_screen->keyUp(id, mask, button);
}
void

View File

@ -152,9 +152,10 @@ public:
virtual void setClipboard(ClipboardID, const CString&);
virtual void grabClipboard(ClipboardID);
virtual void setClipboardDirty(ClipboardID, bool dirty);
virtual void keyDown(KeyID, KeyModifierMask);
virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count);
virtual void keyUp(KeyID, KeyModifierMask);
virtual void keyDown(KeyID, KeyModifierMask, KeyButton);
virtual void keyRepeat(KeyID, KeyModifierMask,
SInt32 count, KeyButton);
virtual void keyUp(KeyID, KeyModifierMask, KeyButton);
virtual void mouseDown(ButtonID);
virtual void mouseUp(ButtonID);
virtual void mouseMove(SInt32 xAbs, SInt32 yAbs);

View File

@ -503,9 +503,10 @@ CServerProxy::keyDown()
flushCompressedMouse();
// parse
UInt16 id, mask;
CProtocolUtil::readf(getInputStream(), kMsgDKeyDown + 4, &id, &mask);
LOG((CLOG_DEBUG1 "recv key down id=%d, mask=0x%04x", id, mask));
UInt16 id, mask, button;
CProtocolUtil::readf(getInputStream(), kMsgDKeyDown + 4,
&id, &mask, &button);
LOG((CLOG_DEBUG1 "recv key down id=%d, mask=0x%04x, button=0x%04x", id, mask, button));
// translate
KeyID id2 = translateKey(static_cast<KeyID>(id));
@ -516,7 +517,7 @@ CServerProxy::keyDown()
LOG((CLOG_DEBUG1 "key down translated to id=%d, mask=0x%04x", id2, mask2));
// forward
getClient()->keyDown(id2, mask2);
getClient()->keyDown(id2, mask2, button);
}
void
@ -526,10 +527,10 @@ CServerProxy::keyRepeat()
flushCompressedMouse();
// parse
UInt16 id, mask, count;
CProtocolUtil::readf(getInputStream(),
kMsgDKeyRepeat + 4, &id, &mask, &count);
LOG((CLOG_DEBUG1 "recv key repeat id=%d, mask=0x%04x, count=%d", id, mask, count));
UInt16 id, mask, count, button;
CProtocolUtil::readf(getInputStream(), kMsgDKeyRepeat + 4,
&id, &mask, &count, &button);
LOG((CLOG_DEBUG1 "recv key repeat id=%d, mask=0x%04x, count=%d, button=0x%04x", id, mask, count, button));
// translate
KeyID id2 = translateKey(static_cast<KeyID>(id));
@ -540,7 +541,7 @@ CServerProxy::keyRepeat()
LOG((CLOG_DEBUG1 "key down translated to id=%d, mask=0x%04x", id2, mask2));
// forward
getClient()->keyRepeat(id2, mask2, count);
getClient()->keyRepeat(id2, mask2, count, button);
}
void
@ -550,9 +551,9 @@ CServerProxy::keyUp()
flushCompressedMouse();
// parse
UInt16 id, mask;
CProtocolUtil::readf(getInputStream(), kMsgDKeyUp + 4, &id, &mask);
LOG((CLOG_DEBUG1 "recv key up id=%d, mask=0x%04x", id, mask));
UInt16 id, mask, button;
CProtocolUtil::readf(getInputStream(), kMsgDKeyUp + 4, &id, &mask, &button);
LOG((CLOG_DEBUG1 "recv key up id=%d, mask=0x%04x, button=0x%04x", id, mask, button));
// translate
KeyID id2 = translateKey(static_cast<KeyID>(id));
@ -563,7 +564,7 @@ CServerProxy::keyUp()
LOG((CLOG_DEBUG1 "key down translated to id=%d, mask=0x%04x", id2, mask2));
// forward
getClient()->keyUp(id2, mask2);
getClient()->keyUp(id2, mask2, button);
}
void

View File

@ -521,9 +521,11 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event)
// process as if it were a key up
KeyModifierMask mask;
KeyButton button = static_cast<KeyButton>(
(lParam & 0x00ff0000u) >> 16);
const KeyID key = mapKey(wParam, lParam, &mask);
LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x", key, mask));
m_receiver->onKeyUp(key, mask);
LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button));
m_receiver->onKeyUp(key, mask, button);
updateKey(wParam, false);
}
if ((m_keys[VK_RWIN] & 0x80) != 0 &&
@ -536,9 +538,11 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event)
// process as if it were a key up
KeyModifierMask mask;
KeyButton button = static_cast<KeyButton>(
(lParam & 0x00ff0000u) >> 16);
const KeyID key = mapKey(wParam, lParam, &mask);
LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x", key, mask));
m_receiver->onKeyUp(key, mask);
LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button));
m_receiver->onKeyUp(key, mask, button);
updateKey(wParam, false);
}
}
@ -554,18 +558,20 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event)
if (!ignore()) {
KeyModifierMask mask;
const KeyID key = mapKey(msg->wParam, msg->lParam, &mask);
if (key != kKeyNone) {
KeyButton button = static_cast<KeyButton>(
(msg->lParam & 0x00ff0000u) >> 16);
if (key != kKeyNone && key != kKeyMultiKey) {
if ((msg->lParam & 0x80000000) == 0) {
// key press
const bool wasDown = ((msg->lParam & 0x40000000) != 0);
const SInt32 repeat = (SInt32)(msg->lParam & 0xffff);
if (repeat >= 2 || wasDown) {
LOG((CLOG_DEBUG1 "event: key repeat key=%d mask=0x%04x count=%d", key, mask, repeat));
m_receiver->onKeyRepeat(key, mask, repeat);
LOG((CLOG_DEBUG1 "event: key repeat key=%d mask=0x%04x count=%d button=0x%04x", key, mask, repeat, button));
m_receiver->onKeyRepeat(key, mask, repeat, button);
}
else {
LOG((CLOG_DEBUG1 "event: key press key=%d mask=0x%04x", key, mask));
m_receiver->onKeyDown(key, mask);
LOG((CLOG_DEBUG1 "event: key press key=%d mask=0x%04x button=0x%04x", key, mask, button));
m_receiver->onKeyDown(key, mask, button);
}
// update key state
@ -580,14 +586,14 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event)
// keys like alt+tab, ctrl+esc, etc.
if (m_is95Family && !isModifier(msg->wParam) &&
(m_keys[msg->wParam] & 0x80) == 0) {
LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x", key, mask));
m_receiver->onKeyDown(key, mask);
LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask, button));
m_receiver->onKeyDown(key, mask, button);
updateKey(msg->wParam, true);
}
// do key up
LOG((CLOG_DEBUG1 "event: key release key=%d mask=0x%04x", key, mask));
m_receiver->onKeyUp(key, mask);
LOG((CLOG_DEBUG1 "event: key release key=%d mask=0x%04x button=0x%04x", key, mask, button));
m_receiver->onKeyUp(key, mask, button);
// update key state
updateKey(msg->wParam, false);
@ -1408,11 +1414,6 @@ CMSWindowsPrimaryScreen::mapKey(
return id;
}
// check for dead keys
if (MapVirtualKey(vkCode, 2) >= 0x8000) {
return kKeyMultiKey;
}
// save the control state then clear it. ToAscii() maps ctrl+letter
// to the corresponding control code and ctrl+backspace to delete.
// we don't want that translation so we clear the control modifier
@ -1421,14 +1422,21 @@ CMSWindowsPrimaryScreen::mapKey(
BYTE lControl = m_keys[VK_LCONTROL];
BYTE rControl = m_keys[VK_RCONTROL];
BYTE control = m_keys[VK_CONTROL];
BYTE lMenu = m_keys[VK_LMENU];
BYTE menu = m_keys[VK_MENU];
if ((mask & KeyModifierModeSwitch) == 0) {
m_keys[VK_LCONTROL] = 0;
m_keys[VK_RCONTROL] = 0;
m_keys[VK_CONTROL] = 0;
}
else {
m_keys[VK_LCONTROL] = 0x80;
m_keys[VK_CONTROL] = 0x80;
m_keys[VK_LMENU] = 0x80;
m_keys[VK_MENU] = 0x80;
}
// convert to ascii
// FIXME -- support unicode
WORD ascii;
int result = ToAscii(vkCode, scanCode, m_keys, &ascii, 0);
@ -1436,23 +1444,45 @@ CMSWindowsPrimaryScreen::mapKey(
m_keys[VK_LCONTROL] = lControl;
m_keys[VK_RCONTROL] = rControl;
m_keys[VK_CONTROL] = control;
m_keys[VK_LMENU] = lMenu;
m_keys[VK_MENU] = menu;
// if result is less than zero then it was a dead key. that key
// is remembered by the keyboard which we don't want. remove it
// by calling ToAscii() again with arbitrary arguments.
// if result is less than zero then it was a dead key. leave it
// there.
if (result < 0) {
ToAscii(vkCode, scanCode, m_keys, &ascii, 0);
return kKeyMultiKey;
}
// if result is 1 then the key was succesfully converted
else if (result == 1) {
if (ascii >= 0x80) {
// character is not really ASCII. instead it's some
// character in the current ANSI code page. try to
// convert that to a Unicode character. if we fail
// then use the single byte character as is.
char src = static_cast<char>(ascii & 0xff);
wchar_t unicode;
if (MultiByteToWideChar(CP_THREAD_ACP, MB_PRECOMPOSED,
&src, 1, &unicode, 1) > 0) {
return static_cast<KeyID>(unicode);
}
}
return static_cast<KeyID>(ascii & 0x00ff);
}
// if result is 2 then a previous dead key could not be composed.
// put the old dead key back.
else if (result == 2) {
// if the two characters are the same and this is a key release
// then this event is the dead key being released. we put the
// dead key back in that case, otherwise we discard both key
// events because we can't compose the character. alternatively
// we could generate key events for both keys.
if (((ascii & 0xff00) >> 8) != (ascii & 0x00ff) ||
(info & 0x80000000) == 0) {
// cannot compose key
return kKeyNone;
}
// get the scan code of the dead key and the shift state
// required to generate it.
vkCode = VkKeyScan(static_cast<TCHAR>(ascii & 0x00ff));

View File

@ -49,7 +49,8 @@ CMSWindowsSecondaryScreen::~CMSWindowsSecondaryScreen()
}
void
CMSWindowsSecondaryScreen::keyDown(KeyID key, KeyModifierMask mask)
CMSWindowsSecondaryScreen::keyDown(KeyID key,
KeyModifierMask mask, KeyButton button)
{
Keystrokes keys;
UINT virtualKey;
@ -89,11 +90,14 @@ CMSWindowsSecondaryScreen::keyDown(KeyID key, KeyModifierMask mask)
m_fakeKeys[VK_MENU] |= 0x80;
break;
}
// note which server key generated this key
m_serverKeyMap[button] = virtualKey;
}
void
CMSWindowsSecondaryScreen::keyRepeat(KeyID key,
KeyModifierMask mask, SInt32 count)
KeyModifierMask mask, SInt32 count, KeyButton button)
{
Keystrokes keys;
UINT virtualKey;
@ -101,6 +105,12 @@ CMSWindowsSecondaryScreen::keyRepeat(KeyID key,
CLock lock(&m_mutex);
m_screen->syncDesktop();
// if we haven't seen this button go down then ignore it
ServerKeyMap::iterator index = m_serverKeyMap.find(button);
if (index == m_serverKeyMap.end()) {
return;
}
// get the sequence of keys to simulate key repeat and the final
// modifier state.
m_mask = mapKey(keys, virtualKey, key, mask, kRepeat);
@ -108,12 +118,40 @@ CMSWindowsSecondaryScreen::keyRepeat(KeyID key,
return;
}
// if we've seen this button (and we should have) then make sure
// we release the same key we pressed when we saw it.
ServerKeyMap::iterator index = m_serverKeyMap.find(button);
if (index != m_serverKeyMap.end() && virtualKey != index->second) {
// replace key up with previous keycode but leave key down
// alone so it uses the new keycode and store that keycode
// in the server key map.
for (Keystrokes::iterator index2 = keys.begin();
index2 != keys.end(); ++index2) {
if (index2->m_virtualKey == index->second) {
index2->m_virtualKey = index->second;
break;
}
}
// note that old key is now up
m_keys[index->second] = false;
m_fakeKeys[index->second] = false;
// map server key to new key
index->second = virtualKey;
// note that new key is now down
m_keys[index->second] = true;
m_fakeKeys[index->second] = true;
}
// generate key events
doKeystrokes(keys, count);
}
void
CMSWindowsSecondaryScreen::keyUp(KeyID key, KeyModifierMask mask)
CMSWindowsSecondaryScreen::keyUp(KeyID key,
KeyModifierMask mask, KeyButton button)
{
Keystrokes keys;
UINT virtualKey;
@ -121,11 +159,41 @@ CMSWindowsSecondaryScreen::keyUp(KeyID key, KeyModifierMask mask)
CLock lock(&m_mutex);
m_screen->syncDesktop();
// if we haven't seen this button go down then ignore it
ServerKeyMap::iterator index = m_serverKeyMap.find(button);
if (index == m_serverKeyMap.end()) {
return;
}
// get the sequence of keys to simulate key release and the final
// modifier state.
m_mask = mapKey(keys, virtualKey, key, mask, kRelease);
// if there are no keys to generate then we should at least generate
// a key release for the key we pressed.
if (keys.empty()) {
return;
Keystroke keystroke;
virtualKey = index->second;
keystroke.m_virtualKey = virtualKey;
keystroke.m_press = false;
keystroke.m_repeat = false;
keys.push_back(keystroke);
}
// if we've seen this button (and we should have) then make sure
// we release the same key we pressed when we saw it.
if (index != m_serverKeyMap.end() && virtualKey != index->second) {
// replace key up with previous virtual key
for (Keystrokes::iterator index2 = keys.begin();
index2 != keys.end(); ++index2) {
if (index2->m_virtualKey == virtualKey) {
index2->m_virtualKey = index->second;
break;
}
}
// use old virtual key
virtualKey = index->second;
}
// generate key events
@ -177,6 +245,11 @@ CMSWindowsSecondaryScreen::keyUp(KeyID key, KeyModifierMask mask)
}
break;
}
// remove server key from map
if (index != m_serverKeyMap.end()) {
m_serverKeyMap.erase(index);
}
}
void
@ -716,8 +789,20 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey,
// if not in map then ask system to convert character
if (virtualKey == 0) {
// translate. return no keys if unknown key.
// FIXME -- handle unicode
TCHAR ascii = static_cast<TCHAR>(id & 0x000000ff);
char ascii;
wchar_t unicode = static_cast<wchar_t>(id & 0x0000ffff);
BOOL error;
if (WideCharToMultiByte(CP_THREAD_ACP,
#if defined(WC_NO_BEST_FIT_CHARS)
WC_NO_BEST_FIT_CHARS |
#endif
WC_COMPOSITECHECK |
WC_DEFAULTCHAR,
&unicode, 1,
&ascii, 1, NULL, &error) == 0 || error) {
LOG((CLOG_DEBUG2 "character %d not in code page", id));
return m_mask;
}
SHORT vk = VkKeyScan(ascii);
if (vk == 0xffff) {
LOG((CLOG_DEBUG2 "no virtual key for character %d", id));

View File

@ -38,9 +38,10 @@ public:
virtual ~CMSWindowsSecondaryScreen();
// CSecondaryScreen overrides
virtual void keyDown(KeyID, KeyModifierMask);
virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count);
virtual void keyUp(KeyID, KeyModifierMask);
virtual void keyDown(KeyID, KeyModifierMask, KeyButton);
virtual void keyRepeat(KeyID, KeyModifierMask,
SInt32 count, KeyButton);
virtual void keyUp(KeyID, KeyModifierMask, KeyButton);
virtual void mouseDown(ButtonID);
virtual void mouseUp(ButtonID);
virtual void mouseMove(SInt32 xAbsolute, SInt32 yAbsolute);
@ -83,6 +84,7 @@ private:
bool m_repeat;
};
typedef std::vector<Keystroke> Keystrokes;
typedef std::map<KeyButton, UINT> ServerKeyMap;
// open/close desktop (for windows 95/98/me)
bool openDesktop();
@ -123,6 +125,9 @@ private:
// current active modifiers
KeyModifierMask m_mask;
// map server key buttons to local virtual keys
ServerKeyMap m_serverKeyMap;
};
#endif

View File

@ -241,12 +241,15 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event)
const KeyModifierMask mask = mapModifier(xevent.xkey.state);
const KeyID key = mapKey(&xevent.xkey);
if (key != kKeyNone) {
m_receiver->onKeyDown(key, mask);
m_receiver->onKeyDown(key, mask,
static_cast<KeyButton>(xevent.xkey.keycode));
if (key == kKeyCapsLock && m_capsLockHalfDuplex) {
m_receiver->onKeyUp(key, mask | KeyModifierCapsLock);
m_receiver->onKeyUp(key, mask | KeyModifierCapsLock,
static_cast<KeyButton>(xevent.xkey.keycode));
}
else if (key == kKeyNumLock && m_numLockHalfDuplex) {
m_receiver->onKeyUp(key, mask | KeyModifierNumLock);
m_receiver->onKeyUp(key, mask | KeyModifierNumLock,
static_cast<KeyButton>(xevent.xkey.keycode));
}
}
}
@ -280,12 +283,15 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event)
// no press event follows so it's a plain release
LOG((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state));
if (key == kKeyCapsLock && m_capsLockHalfDuplex) {
m_receiver->onKeyDown(key, mask);
m_receiver->onKeyDown(key, mask,
static_cast<KeyButton>(xevent.xkey.keycode));
}
else if (key == kKeyNumLock && m_numLockHalfDuplex) {
m_receiver->onKeyDown(key, mask);
m_receiver->onKeyDown(key, mask,
static_cast<KeyButton>(xevent.xkey.keycode));
}
m_receiver->onKeyUp(key, mask);
m_receiver->onKeyUp(key, mask,
static_cast<KeyButton>(xevent.xkey.keycode));
}
else {
// found a press event following so it's a repeat.
@ -293,7 +299,8 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event)
// repeats but we'll just send a repeat of 1.
// note that we discard the press event.
LOG((CLOG_DEBUG1 "event: repeat code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state));
m_receiver->onKeyRepeat(key, mask, 1);
m_receiver->onKeyRepeat(key, mask, 1,
static_cast<KeyButton>(xevent.xkey.keycode));
}
}
}

View File

@ -95,7 +95,8 @@ CXWindowsSecondaryScreen::~CXWindowsSecondaryScreen()
}
void
CXWindowsSecondaryScreen::keyDown(KeyID key, KeyModifierMask mask)
CXWindowsSecondaryScreen::keyDown(KeyID key,
KeyModifierMask mask, KeyButton button)
{
Keystrokes keys;
KeyCode keycode;
@ -113,15 +114,24 @@ CXWindowsSecondaryScreen::keyDown(KeyID key, KeyModifierMask mask)
// note that key is now down
m_keys[keycode] = true;
m_fakeKeys[keycode] = true;
// note which server key generated this key
m_serverKeyMap[button] = keycode;
}
void
CXWindowsSecondaryScreen::keyRepeat(KeyID key,
KeyModifierMask mask, SInt32 count)
KeyModifierMask mask, SInt32 count, KeyButton button)
{
Keystrokes keys;
KeyCode keycode;
// if we haven't seen this button go down then ignore it
ServerKeyMap::iterator index = m_serverKeyMap.find(button);
if (index == m_serverKeyMap.end()) {
return;
}
// get the sequence of keys to simulate key repeat and the final
// modifier state.
m_mask = mapKey(keys, keycode, key, mask, kRepeat);
@ -129,21 +139,78 @@ CXWindowsSecondaryScreen::keyRepeat(KeyID key,
return;
}
// if we've seen this button (and we should have) then make sure
// we release the same key we pressed when we saw it.
if (index != m_serverKeyMap.end() && keycode != index->second) {
// replace key up with previous keycode but leave key down
// alone so it uses the new keycode and store that keycode
// in the server key map.
for (Keystrokes::iterator index2 = keys.begin();
index2 != keys.end(); ++index2) {
if (index2->m_keycode == index->second) {
index2->m_keycode = index->second;
break;
}
}
// note that old key is now up
m_keys[index->second] = false;
m_fakeKeys[index->second] = false;
// map server key to new key
index->second = keycode;
// note that new key is now down
m_keys[index->second] = true;
m_fakeKeys[index->second] = true;
}
// generate key events
doKeystrokes(keys, count);
}
void
CXWindowsSecondaryScreen::keyUp(KeyID key, KeyModifierMask mask)
CXWindowsSecondaryScreen::keyUp(KeyID key,
KeyModifierMask mask, KeyButton button)
{
Keystrokes keys;
KeyCode keycode;
// if we haven't seen this button go down then ignore it
ServerKeyMap::iterator index = m_serverKeyMap.find(button);
if (index == m_serverKeyMap.end()) {
return;
}
// get the sequence of keys to simulate key release and the final
// modifier state.
m_mask = mapKey(keys, keycode, key, mask, kRelease);
// if there are no keys to generate then we should at least generate
// a key release for the key we pressed.
if (keys.empty()) {
return;
Keystroke keystroke;
keycode = index->second;
keystroke.m_keycode = keycode;
keystroke.m_press = False;
keystroke.m_repeat = false;
keys.push_back(keystroke);
}
// if we've seen this button (and we should have) then make sure
// we release the same key we pressed when we saw it.
if (index != m_serverKeyMap.end() && keycode != index->second) {
// replace key up with previous keycode
for (Keystrokes::iterator index2 = keys.begin();
index2 != keys.end(); ++index2) {
if (index2->m_keycode == keycode) {
index2->m_keycode = index->second;
break;
}
}
// use old keycode
keycode = index->second;
}
// generate key events
@ -152,6 +219,11 @@ CXWindowsSecondaryScreen::keyUp(KeyID key, KeyModifierMask mask)
// note that key is now up
m_keys[keycode] = false;
m_fakeKeys[keycode] = false;
// remove server key from map
if (index != m_serverKeyMap.end()) {
m_serverKeyMap.erase(index);
}
}
void

View File

@ -37,9 +37,10 @@ public:
virtual ~CXWindowsSecondaryScreen();
// CSecondaryScreen overrides
virtual void keyDown(KeyID, KeyModifierMask);
virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count);
virtual void keyUp(KeyID, KeyModifierMask);
virtual void keyDown(KeyID, KeyModifierMask, KeyButton);
virtual void keyRepeat(KeyID, KeyModifierMask,
SInt32 count, KeyButton);
virtual void keyUp(KeyID, KeyModifierMask, KeyButton);
virtual void mouseDown(ButtonID);
virtual void mouseUp(ButtonID);
virtual void mouseMove(SInt32 x, SInt32 y);
@ -91,6 +92,7 @@ private:
typedef std::map<KeySym, KeyCodeMask> KeyCodeMap;
typedef KeyCodeMap::const_iterator KeyCodeIndex;
typedef std::map<KeyCode, unsigned int> ModifierMap;
typedef std::map<KeyButton, KeyCode> ServerKeyMap;
unsigned int mapButton(ButtonID button) const;
@ -165,6 +167,9 @@ private:
// maps keycodes to modifier indices
ModifierMap m_keycodeToModifier;
// map server key buttons to local keycodes
ServerKeyMap m_serverKeyMap;
};
#endif

View File

@ -68,9 +68,10 @@ public:
virtual void setClipboard(ClipboardID, const CString&) = 0;
virtual void grabClipboard(ClipboardID) = 0;
virtual void setClipboardDirty(ClipboardID, bool) = 0;
virtual void keyDown(KeyID, KeyModifierMask) = 0;
virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0;
virtual void keyUp(KeyID, KeyModifierMask) = 0;
virtual void keyDown(KeyID, KeyModifierMask, KeyButton) = 0;
virtual void keyRepeat(KeyID, KeyModifierMask,
SInt32 count, KeyButton) = 0;
virtual void keyUp(KeyID, KeyModifierMask, KeyButton) = 0;
virtual void mouseDown(ButtonID) = 0;
virtual void mouseUp(ButtonID) = 0;
virtual void mouseMove(SInt32 xAbs, SInt32 yAbs) = 0;

View File

@ -200,24 +200,25 @@ CClientProxy1_0::setClipboardDirty(ClipboardID id, bool dirty)
}
void
CClientProxy1_0::keyDown(KeyID key, KeyModifierMask mask)
CClientProxy1_0::keyDown(KeyID key, KeyModifierMask mask, KeyButton)
{
LOG((CLOG_DEBUG1 "send key down to \"%s\" id=%d, mask=0x%04x", getName().c_str(), key, mask));
CProtocolUtil::writef(getOutputStream(), kMsgDKeyDown, key, mask);
CProtocolUtil::writef(getOutputStream(), kMsgDKeyDown1_0, key, mask);
}
void
CClientProxy1_0::keyRepeat(KeyID key, KeyModifierMask mask, SInt32 count)
CClientProxy1_0::keyRepeat(KeyID key, KeyModifierMask mask,
SInt32 count, KeyButton)
{
LOG((CLOG_DEBUG1 "send key repeat to \"%s\" id=%d, mask=0x%04x, count=%d", getName().c_str(), key, mask, count));
CProtocolUtil::writef(getOutputStream(), kMsgDKeyRepeat, key, mask, count);
CProtocolUtil::writef(getOutputStream(), kMsgDKeyRepeat1_0, key, mask, count);
}
void
CClientProxy1_0::keyUp(KeyID key, KeyModifierMask mask)
CClientProxy1_0::keyUp(KeyID key, KeyModifierMask mask, KeyButton)
{
LOG((CLOG_DEBUG1 "send key up to \"%s\" id=%d, mask=0x%04x", getName().c_str(), key, mask));
CProtocolUtil::writef(getOutputStream(), kMsgDKeyUp, key, mask);
CProtocolUtil::writef(getOutputStream(), kMsgDKeyUp1_0, key, mask);
}
void

View File

@ -37,9 +37,10 @@ public:
virtual void setClipboard(ClipboardID, const CString&);
virtual void grabClipboard(ClipboardID);
virtual void setClipboardDirty(ClipboardID, bool);
virtual void keyDown(KeyID, KeyModifierMask);
virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count);
virtual void keyUp(KeyID, KeyModifierMask);
virtual void keyDown(KeyID, KeyModifierMask, KeyButton);
virtual void keyRepeat(KeyID, KeyModifierMask,
SInt32 count, KeyButton);
virtual void keyUp(KeyID, KeyModifierMask, KeyButton);
virtual void mouseDown(ButtonID);
virtual void mouseUp(ButtonID);
virtual void mouseMove(SInt32 xAbs, SInt32 yAbs);

View File

@ -0,0 +1,56 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "CClientProxy1_1.h"
#include "CProtocolUtil.h"
#include "CLog.h"
#include <cstring>
//
// CClientProxy1_1
//
CClientProxy1_1::CClientProxy1_1(IServer* server, const CString& name,
IInputStream* input, IOutputStream* output) :
CClientProxy1_0(server, name, input, output)
{
// do nothing
}
CClientProxy1_1::~CClientProxy1_1()
{
// do nothing
}
void
CClientProxy1_1::keyDown(KeyID key, KeyModifierMask mask, KeyButton button)
{
LOG((CLOG_DEBUG1 "send key down to \"%s\" id=%d, mask=0x%04x, button=0x%04x", getName().c_str(), key, mask, button));
CProtocolUtil::writef(getOutputStream(), kMsgDKeyDown, key, mask, button);
}
void
CClientProxy1_1::keyRepeat(KeyID key, KeyModifierMask mask,
SInt32 count, KeyButton button)
{
LOG((CLOG_DEBUG1 "send key repeat to \"%s\" id=%d, mask=0x%04x, count=%d, button=0x%04x", getName().c_str(), key, mask, count, button));
CProtocolUtil::writef(getOutputStream(), kMsgDKeyRepeat, key, mask, count, button);
}
void
CClientProxy1_1::keyUp(KeyID key, KeyModifierMask mask, KeyButton button)
{
LOG((CLOG_DEBUG1 "send key up to \"%s\" id=%d, mask=0x%04x, button=0x%04x", getName().c_str(), key, mask, button));
CProtocolUtil::writef(getOutputStream(), kMsgDKeyUp, key, mask, button);
}

View File

@ -0,0 +1,35 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef CCLIENTPROXY1_1_H
#define CCLIENTPROXY1_1_H
#include "CClientProxy1_0.h"
//! Proxy for client implementing protocol version 1.1
class CClientProxy1_1 : public CClientProxy1_0 {
public:
CClientProxy1_1(IServer* server, const CString& name,
IInputStream* adoptedInput,
IOutputStream* adoptedOutput);
~CClientProxy1_1();
// IClient overrides
virtual void keyDown(KeyID, KeyModifierMask, KeyButton);
virtual void keyRepeat(KeyID, KeyModifierMask,
SInt32 count, KeyButton);
virtual void keyUp(KeyID, KeyModifierMask, KeyButton);
};
#endif

View File

@ -201,19 +201,19 @@ CPrimaryClient::setClipboardDirty(ClipboardID id, bool dirty)
}
void
CPrimaryClient::keyDown(KeyID, KeyModifierMask)
CPrimaryClient::keyDown(KeyID, KeyModifierMask, KeyButton)
{
// ignore
}
void
CPrimaryClient::keyRepeat(KeyID, KeyModifierMask, SInt32)
CPrimaryClient::keyRepeat(KeyID, KeyModifierMask, SInt32, KeyButton)
{
// ignore
}
void
CPrimaryClient::keyUp(KeyID, KeyModifierMask)
CPrimaryClient::keyUp(KeyID, KeyModifierMask, KeyButton)
{
// ignore
}

View File

@ -107,9 +107,10 @@ public:
virtual void setClipboard(ClipboardID, const CString&);
virtual void grabClipboard(ClipboardID);
virtual void setClipboardDirty(ClipboardID, bool);
virtual void keyDown(KeyID, KeyModifierMask);
virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count);
virtual void keyUp(KeyID, KeyModifierMask);
virtual void keyDown(KeyID, KeyModifierMask, KeyButton);
virtual void keyRepeat(KeyID, KeyModifierMask,
SInt32 count, KeyButton);
virtual void keyUp(KeyID, KeyModifierMask, KeyButton);
virtual void mouseDown(ButtonID);
virtual void mouseUp(ButtonID);
virtual void mouseMove(SInt32 xAbs, SInt32 yAbs);

View File

@ -20,6 +20,7 @@
#include "COutputPacketStream.h"
#include "CProtocolUtil.h"
#include "CClientProxy1_0.h"
#include "CClientProxy1_1.h"
#include "OptionTypes.h"
#include "ProtocolTypes.h"
#include "XScreen.h"
@ -615,9 +616,9 @@ CServer::onOneShotTimerExpired(UInt32 id)
}
void
CServer::onKeyDown(KeyID id, KeyModifierMask mask)
CServer::onKeyDown(KeyID id, KeyModifierMask mask, KeyButton button)
{
LOG((CLOG_DEBUG1 "onKeyDown id=%d mask=0x%04x", id, mask));
LOG((CLOG_DEBUG1 "onKeyDown id=%d mask=0x%04x button=0x%04x", id, mask, button));
CLock lock(&m_mutex);
assert(m_active != NULL);
@ -627,13 +628,13 @@ CServer::onKeyDown(KeyID id, KeyModifierMask mask)
}
// relay
m_active->keyDown(id, mask);
m_active->keyDown(id, mask, button);
}
void
CServer::onKeyUp(KeyID id, KeyModifierMask mask)
CServer::onKeyUp(KeyID id, KeyModifierMask mask, KeyButton button)
{
LOG((CLOG_DEBUG1 "onKeyUp id=%d mask=0x%04x", id, mask));
LOG((CLOG_DEBUG1 "onKeyUp id=%d mask=0x%04x button=0x%04x", id, mask, button));
CLock lock(&m_mutex);
assert(m_active != NULL);
@ -643,13 +644,14 @@ CServer::onKeyUp(KeyID id, KeyModifierMask mask)
}
// relay
m_active->keyUp(id, mask);
m_active->keyUp(id, mask, button);
}
void
CServer::onKeyRepeat(KeyID id, KeyModifierMask mask, SInt32 count)
CServer::onKeyRepeat(KeyID id, KeyModifierMask mask,
SInt32 count, KeyButton button)
{
LOG((CLOG_DEBUG1 "onKeyRepeat id=%d mask=0x%04x count=%d", id, mask, count));
LOG((CLOG_DEBUG1 "onKeyRepeat id=%d mask=0x%04x count=%d button=0x%04x", id, mask, count, button));
CLock lock(&m_mutex);
assert(m_active != NULL);
@ -660,7 +662,7 @@ CServer::onKeyRepeat(KeyID id, KeyModifierMask mask, SInt32 count)
}
// relay
m_active->keyRepeat(id, mask, count);
m_active->keyRepeat(id, mask, count, button);
}
void
@ -1698,18 +1700,7 @@ CServer::handshakeClient(IDataSocket* socket)
}
// disallow invalid version numbers
if (major < 0 || minor < 0) {
throw XIncompatibleClient(major, minor);
}
// disallow connection from test versions to release versions
if (major == 0 && kProtocolMajorVersion != 0) {
throw XIncompatibleClient(major, minor);
}
// hangup (with error) if version isn't supported
if (major > kProtocolMajorVersion ||
(major == kProtocolMajorVersion && minor > kProtocolMinorVersion)) {
if (major <= 0 || minor < 0) {
throw XIncompatibleClient(major, minor);
}
@ -1720,7 +1711,22 @@ CServer::handshakeClient(IDataSocket* socket)
// create client proxy for highest version supported by the client
LOG((CLOG_DEBUG1 "creating proxy for client \"%s\" version %d.%d", name.c_str(), major, minor));
proxy = new CClientProxy1_0(this, name, input, output);
if (major == 1) {
switch (minor) {
case 0:
proxy = new CClientProxy1_0(this, name, input, output);
break;
case 1:
proxy = new CClientProxy1_1(this, name, input, output);
break;
}
}
// hangup (with error) if version isn't supported
if (proxy == NULL) {
throw XIncompatibleClient(major, minor);
}
// negotiate
// FIXME

View File

@ -187,9 +187,10 @@ public:
// IPrimaryScreenReceiver overrides
virtual void onScreensaver(bool activated);
virtual void onOneShotTimerExpired(UInt32 id);
virtual void onKeyDown(KeyID, KeyModifierMask);
virtual void onKeyUp(KeyID, KeyModifierMask);
virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count);
virtual void onKeyDown(KeyID, KeyModifierMask, KeyButton);
virtual void onKeyUp(KeyID, KeyModifierMask, KeyButton);
virtual void onKeyRepeat(KeyID, KeyModifierMask,
SInt32 count, KeyButton);
virtual void onMouseDown(ButtonID);
virtual void onMouseUp(ButtonID);
virtual bool onMouseMovePrimary(SInt32 x, SInt32 y);

View File

@ -27,6 +27,7 @@ noinst_LIBRARIES = libserver.a
libserver_a_SOURCES = \
CClientProxy.cpp \
CClientProxy1_0.cpp \
CClientProxy1_1.cpp \
CConfig.cpp \
CHTTPServer.cpp \
CPrimaryClient.cpp \

View File

@ -124,23 +124,28 @@ public:
//! Notify of key press
/*!
Synthesize key events to generate a press of key \c id. If possible
match the given modifier mask.
match the given modifier mask. The KeyButton identifies the physical
key on the server that generated this key down. The client must
ensure that a key up or key repeat that uses the same KeyButton will
synthesize an up or repeat for the same client key synthesized by
keyDown().
*/
virtual void keyDown(KeyID id, KeyModifierMask) = 0;
virtual void keyDown(KeyID id, KeyModifierMask, KeyButton) = 0;
//! Notify of key repeat
/*!
Synthesize key events to generate a press and release of key \c id
\c count times. If possible match the given modifier mask.
*/
virtual void keyRepeat(KeyID id, KeyModifierMask, SInt32 count) = 0;
virtual void keyRepeat(KeyID id, KeyModifierMask,
SInt32 count, KeyButton) = 0;
//! Notify of key release
/*!
Synthesize key events to generate a release of key \c id. If possible
match the given modifier mask.
*/
virtual void keyUp(KeyID id, KeyModifierMask) = 0;
virtual void keyUp(KeyID id, KeyModifierMask, KeyButton) = 0;
//! Notify of mouse press
/*!

View File

@ -103,23 +103,28 @@ public:
//! Notify of key press
/*!
Synthesize key events to generate a press of key \c id. If possible
match the given modifier mask.
match the given modifier mask. The KeyButton identifies the physical
key on the server that generated this key down. The client must
ensure that a key up or key repeat that uses the same KeyButton will
synthesize an up or repeat for the same client key synthesized by
keyDown().
*/
virtual void keyDown(KeyID id, KeyModifierMask) = 0;
virtual void keyDown(KeyID id, KeyModifierMask, KeyButton) = 0;
//! Notify of key repeat
/*!
Synthesize key events to generate a press and release of key \c id
\c count times. If possible match the given modifier mask.
*/
virtual void keyRepeat(KeyID id, KeyModifierMask, SInt32 count) = 0;
virtual void keyRepeat(KeyID id, KeyModifierMask,
SInt32 count, KeyButton) = 0;
//! Notify of key release
/*!
Synthesize key events to generate a release of key \c id. If possible
match the given modifier mask.
*/
virtual void keyUp(KeyID id, KeyModifierMask) = 0;
virtual void keyUp(KeyID id, KeyModifierMask, KeyButton) = 0;
//! Notify of mouse press
/*!

View File

@ -43,11 +43,12 @@ public:
// call to notify of events. onMouseMovePrimary() returns
// true iff the mouse enters a jump zone and jumps.
//! Notify of key press
virtual void onKeyDown(KeyID, KeyModifierMask) = 0;
virtual void onKeyDown(KeyID, KeyModifierMask, KeyButton) = 0;
//! Notify of key release
virtual void onKeyUp(KeyID, KeyModifierMask) = 0;
virtual void onKeyUp(KeyID, KeyModifierMask, KeyButton) = 0;
//! Notify of key repeat
virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0;
virtual void onKeyRepeat(KeyID, KeyModifierMask,
SInt32 count, KeyButton) = 0;
//! Notify of mouse button press
virtual void onMouseDown(ButtonID) = 0;
//! Notify of mouse button release

View File

@ -19,12 +19,19 @@
//! Key ID
/*!
Type to hold a key identifier. The encoding is UTF-32, using
Type to hold a key symbol identifier. The encoding is UTF-32, using
U+E000 through U+EFFF for the various control keys (e.g. arrow
keys, function keys, modifier keys, etc).
*/
typedef UInt32 KeyID;
//! Key Code
/*!
Type to hold a physical key identifier. That is, it identifies a
physical key on the keyboard.
*/
typedef UInt16 KeyButton;
//! Modifier key mask
/*!
Type to hold a bitmask of key modifiers (e.g. shift keys).

View File

@ -18,8 +18,10 @@
#include "BasicTypes.h"
// protocol version number
// 1.0: initial protocol
// 1.1: adds KeyCode to key press, release, and repeat
static const SInt16 kProtocolMajorVersion = 1;
static const SInt16 kProtocolMinorVersion = 0;
static const SInt16 kProtocolMinorVersion = 1;
// default contact port number
static const UInt16 kDefaultPort = 24800;
@ -137,16 +139,34 @@ static const char kMsgCInfoAck[] = "CIAK";
//
// key pressed: primary -> secondary
// $1 = KeyID, $2 = KeyModifierMask
static const char kMsgDKeyDown[] = "DKDN%2i%2i";
// $1 = KeyID, $2 = KeyModifierMask, $3 = KeyButton
// the KeyButton identifies the physical key on the primary used to
// generate this key. the secondary should note the KeyButton along
// with the physical key it uses to generate the key press. on
// release, the secondary can then use the primary's KeyButton to
// find its corresponding physical key and release it. this is
// necessary because the KeyID on release may not be the KeyID of
// the press. this can happen with combining (dead) keys or if
// the keyboard layouts are not identical and the user releases
// a modifier key before releasing the modified key.
static const char kMsgDKeyDown[] = "DKDN%2i%2i%2i";
// key pressed 1.0: same as above but without KeyButton
static const char kMsgDKeyDown1_0[] = "DKDN%2i%2i";
// key auto-repeat: primary -> secondary
// $1 = KeyID, $2 = KeyModifierMask, $3 = number of repeats
static const char kMsgDKeyRepeat[] = "DKRP%2i%2i%2i";
// $1 = KeyID, $2 = KeyModifierMask, $3 = number of repeats, $4 = KeyButton
static const char kMsgDKeyRepeat[] = "DKRP%2i%2i%2i%2i";
// key auto-repeat 1.0: same as above but without KeyButton
static const char kMsgDKeyRepeat1_0[] = "DKRP%2i%2i%2i";
// key released: primary -> secondary
// $1 = KeyID, $2 = KeyModifierMask
static const char kMsgDKeyUp[] = "DKUP%2i%2i";
// $1 = KeyID, $2 = KeyModifierMask, $3 = KeyButton
static const char kMsgDKeyUp[] = "DKUP%2i%2i%2i";
// key released 1.0: same as above but without KeyButton
static const char kMsgDKeyUp1_0[] = "DKUP%2i%2i";
// mouse button pressed: primary -> secondary
// $1 = ButtonID