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 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 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 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 void

View File

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

View File

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

View File

@ -521,9 +521,11 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event)
// process as if it were a key up // process as if it were a key up
KeyModifierMask mask; KeyModifierMask mask;
KeyButton button = static_cast<KeyButton>(
(lParam & 0x00ff0000u) >> 16);
const KeyID key = mapKey(wParam, lParam, &mask); const KeyID key = mapKey(wParam, lParam, &mask);
LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x", 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); m_receiver->onKeyUp(key, mask, button);
updateKey(wParam, false); updateKey(wParam, false);
} }
if ((m_keys[VK_RWIN] & 0x80) != 0 && if ((m_keys[VK_RWIN] & 0x80) != 0 &&
@ -536,9 +538,11 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event)
// process as if it were a key up // process as if it were a key up
KeyModifierMask mask; KeyModifierMask mask;
KeyButton button = static_cast<KeyButton>(
(lParam & 0x00ff0000u) >> 16);
const KeyID key = mapKey(wParam, lParam, &mask); const KeyID key = mapKey(wParam, lParam, &mask);
LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x", 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); m_receiver->onKeyUp(key, mask, button);
updateKey(wParam, false); updateKey(wParam, false);
} }
} }
@ -554,18 +558,20 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event)
if (!ignore()) { if (!ignore()) {
KeyModifierMask mask; KeyModifierMask mask;
const KeyID key = mapKey(msg->wParam, msg->lParam, &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) { if ((msg->lParam & 0x80000000) == 0) {
// key press // key press
const bool wasDown = ((msg->lParam & 0x40000000) != 0); const bool wasDown = ((msg->lParam & 0x40000000) != 0);
const SInt32 repeat = (SInt32)(msg->lParam & 0xffff); const SInt32 repeat = (SInt32)(msg->lParam & 0xffff);
if (repeat >= 2 || wasDown) { if (repeat >= 2 || wasDown) {
LOG((CLOG_DEBUG1 "event: key repeat key=%d mask=0x%04x count=%d", 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); m_receiver->onKeyRepeat(key, mask, repeat, button);
} }
else { else {
LOG((CLOG_DEBUG1 "event: key press key=%d mask=0x%04x", key, mask)); LOG((CLOG_DEBUG1 "event: key press key=%d mask=0x%04x button=0x%04x", key, mask, button));
m_receiver->onKeyDown(key, mask); m_receiver->onKeyDown(key, mask, button);
} }
// update key state // update key state
@ -580,14 +586,14 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event)
// keys like alt+tab, ctrl+esc, etc. // keys like alt+tab, ctrl+esc, etc.
if (m_is95Family && !isModifier(msg->wParam) && if (m_is95Family && !isModifier(msg->wParam) &&
(m_keys[msg->wParam] & 0x80) == 0) { (m_keys[msg->wParam] & 0x80) == 0) {
LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x", 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); m_receiver->onKeyDown(key, mask, button);
updateKey(msg->wParam, true); updateKey(msg->wParam, true);
} }
// do key up // do key up
LOG((CLOG_DEBUG1 "event: key release key=%d mask=0x%04x", key, mask)); LOG((CLOG_DEBUG1 "event: key release key=%d mask=0x%04x button=0x%04x", key, mask, button));
m_receiver->onKeyUp(key, mask); m_receiver->onKeyUp(key, mask, button);
// update key state // update key state
updateKey(msg->wParam, false); updateKey(msg->wParam, false);
@ -1408,11 +1414,6 @@ CMSWindowsPrimaryScreen::mapKey(
return id; return id;
} }
// check for dead keys
if (MapVirtualKey(vkCode, 2) >= 0x8000) {
return kKeyMultiKey;
}
// save the control state then clear it. ToAscii() maps ctrl+letter // save the control state then clear it. ToAscii() maps ctrl+letter
// to the corresponding control code and ctrl+backspace to delete. // to the corresponding control code and ctrl+backspace to delete.
// we don't want that translation so we clear the control modifier // 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 lControl = m_keys[VK_LCONTROL];
BYTE rControl = m_keys[VK_RCONTROL]; BYTE rControl = m_keys[VK_RCONTROL];
BYTE control = m_keys[VK_CONTROL]; BYTE control = m_keys[VK_CONTROL];
BYTE lMenu = m_keys[VK_LMENU];
BYTE menu = m_keys[VK_MENU];
if ((mask & KeyModifierModeSwitch) == 0) { if ((mask & KeyModifierModeSwitch) == 0) {
m_keys[VK_LCONTROL] = 0; m_keys[VK_LCONTROL] = 0;
m_keys[VK_RCONTROL] = 0; m_keys[VK_RCONTROL] = 0;
m_keys[VK_CONTROL] = 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 // convert to ascii
// FIXME -- support unicode
WORD ascii; WORD ascii;
int result = ToAscii(vkCode, scanCode, m_keys, &ascii, 0); int result = ToAscii(vkCode, scanCode, m_keys, &ascii, 0);
@ -1436,23 +1444,45 @@ CMSWindowsPrimaryScreen::mapKey(
m_keys[VK_LCONTROL] = lControl; m_keys[VK_LCONTROL] = lControl;
m_keys[VK_RCONTROL] = rControl; m_keys[VK_RCONTROL] = rControl;
m_keys[VK_CONTROL] = control; 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 // if result is less than zero then it was a dead key. leave it
// is remembered by the keyboard which we don't want. remove it // there.
// by calling ToAscii() again with arbitrary arguments.
if (result < 0) { if (result < 0) {
ToAscii(vkCode, scanCode, m_keys, &ascii, 0);
return kKeyMultiKey; return kKeyMultiKey;
} }
// if result is 1 then the key was succesfully converted // if result is 1 then the key was succesfully converted
else if (result == 1) { 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); return static_cast<KeyID>(ascii & 0x00ff);
} }
// if result is 2 then a previous dead key could not be composed. // if result is 2 then a previous dead key could not be composed.
// put the old dead key back.
else if (result == 2) { 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 // get the scan code of the dead key and the shift state
// required to generate it. // required to generate it.
vkCode = VkKeyScan(static_cast<TCHAR>(ascii & 0x00ff)); vkCode = VkKeyScan(static_cast<TCHAR>(ascii & 0x00ff));

View File

@ -49,7 +49,8 @@ CMSWindowsSecondaryScreen::~CMSWindowsSecondaryScreen()
} }
void void
CMSWindowsSecondaryScreen::keyDown(KeyID key, KeyModifierMask mask) CMSWindowsSecondaryScreen::keyDown(KeyID key,
KeyModifierMask mask, KeyButton button)
{ {
Keystrokes keys; Keystrokes keys;
UINT virtualKey; UINT virtualKey;
@ -89,11 +90,14 @@ CMSWindowsSecondaryScreen::keyDown(KeyID key, KeyModifierMask mask)
m_fakeKeys[VK_MENU] |= 0x80; m_fakeKeys[VK_MENU] |= 0x80;
break; break;
} }
// note which server key generated this key
m_serverKeyMap[button] = virtualKey;
} }
void void
CMSWindowsSecondaryScreen::keyRepeat(KeyID key, CMSWindowsSecondaryScreen::keyRepeat(KeyID key,
KeyModifierMask mask, SInt32 count) KeyModifierMask mask, SInt32 count, KeyButton button)
{ {
Keystrokes keys; Keystrokes keys;
UINT virtualKey; UINT virtualKey;
@ -101,6 +105,12 @@ CMSWindowsSecondaryScreen::keyRepeat(KeyID key,
CLock lock(&m_mutex); CLock lock(&m_mutex);
m_screen->syncDesktop(); 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 // get the sequence of keys to simulate key repeat and the final
// modifier state. // modifier state.
m_mask = mapKey(keys, virtualKey, key, mask, kRepeat); m_mask = mapKey(keys, virtualKey, key, mask, kRepeat);
@ -108,12 +118,40 @@ CMSWindowsSecondaryScreen::keyRepeat(KeyID key,
return; 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 // generate key events
doKeystrokes(keys, count); doKeystrokes(keys, count);
} }
void void
CMSWindowsSecondaryScreen::keyUp(KeyID key, KeyModifierMask mask) CMSWindowsSecondaryScreen::keyUp(KeyID key,
KeyModifierMask mask, KeyButton button)
{ {
Keystrokes keys; Keystrokes keys;
UINT virtualKey; UINT virtualKey;
@ -121,11 +159,41 @@ CMSWindowsSecondaryScreen::keyUp(KeyID key, KeyModifierMask mask)
CLock lock(&m_mutex); CLock lock(&m_mutex);
m_screen->syncDesktop(); 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 // get the sequence of keys to simulate key release and the final
// modifier state. // modifier state.
m_mask = mapKey(keys, virtualKey, key, mask, kRelease); 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()) { 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 // generate key events
@ -177,6 +245,11 @@ CMSWindowsSecondaryScreen::keyUp(KeyID key, KeyModifierMask mask)
} }
break; break;
} }
// remove server key from map
if (index != m_serverKeyMap.end()) {
m_serverKeyMap.erase(index);
}
} }
void void
@ -716,8 +789,20 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey,
// if not in map then ask system to convert character // if not in map then ask system to convert character
if (virtualKey == 0) { if (virtualKey == 0) {
// translate. return no keys if unknown key. // translate. return no keys if unknown key.
// FIXME -- handle unicode char ascii;
TCHAR ascii = static_cast<TCHAR>(id & 0x000000ff); 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); SHORT vk = VkKeyScan(ascii);
if (vk == 0xffff) { if (vk == 0xffff) {
LOG((CLOG_DEBUG2 "no virtual key for character %d", id)); LOG((CLOG_DEBUG2 "no virtual key for character %d", id));

View File

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

View File

@ -241,12 +241,15 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event)
const KeyModifierMask mask = mapModifier(xevent.xkey.state); const KeyModifierMask mask = mapModifier(xevent.xkey.state);
const KeyID key = mapKey(&xevent.xkey); const KeyID key = mapKey(&xevent.xkey);
if (key != kKeyNone) { if (key != kKeyNone) {
m_receiver->onKeyDown(key, mask); m_receiver->onKeyDown(key, mask,
static_cast<KeyButton>(xevent.xkey.keycode));
if (key == kKeyCapsLock && m_capsLockHalfDuplex) { 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) { 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 // 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)); LOG((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state));
if (key == kKeyCapsLock && m_capsLockHalfDuplex) { 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) { 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 { else {
// found a press event following so it's a repeat. // 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. // repeats but we'll just send a repeat of 1.
// note that we discard the press event. // note that we discard the press event.
LOG((CLOG_DEBUG1 "event: repeat code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); 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 void
CXWindowsSecondaryScreen::keyDown(KeyID key, KeyModifierMask mask) CXWindowsSecondaryScreen::keyDown(KeyID key,
KeyModifierMask mask, KeyButton button)
{ {
Keystrokes keys; Keystrokes keys;
KeyCode keycode; KeyCode keycode;
@ -113,15 +114,24 @@ CXWindowsSecondaryScreen::keyDown(KeyID key, KeyModifierMask mask)
// note that key is now down // note that key is now down
m_keys[keycode] = true; m_keys[keycode] = true;
m_fakeKeys[keycode] = true; m_fakeKeys[keycode] = true;
// note which server key generated this key
m_serverKeyMap[button] = keycode;
} }
void void
CXWindowsSecondaryScreen::keyRepeat(KeyID key, CXWindowsSecondaryScreen::keyRepeat(KeyID key,
KeyModifierMask mask, SInt32 count) KeyModifierMask mask, SInt32 count, KeyButton button)
{ {
Keystrokes keys; Keystrokes keys;
KeyCode keycode; 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 // get the sequence of keys to simulate key repeat and the final
// modifier state. // modifier state.
m_mask = mapKey(keys, keycode, key, mask, kRepeat); m_mask = mapKey(keys, keycode, key, mask, kRepeat);
@ -129,21 +139,78 @@ CXWindowsSecondaryScreen::keyRepeat(KeyID key,
return; 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 // generate key events
doKeystrokes(keys, count); doKeystrokes(keys, count);
} }
void void
CXWindowsSecondaryScreen::keyUp(KeyID key, KeyModifierMask mask) CXWindowsSecondaryScreen::keyUp(KeyID key,
KeyModifierMask mask, KeyButton button)
{ {
Keystrokes keys; Keystrokes keys;
KeyCode keycode; 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 // get the sequence of keys to simulate key release and the final
// modifier state. // modifier state.
m_mask = mapKey(keys, keycode, key, mask, kRelease); 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()) { 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 // generate key events
@ -152,6 +219,11 @@ CXWindowsSecondaryScreen::keyUp(KeyID key, KeyModifierMask mask)
// note that key is now up // note that key is now up
m_keys[keycode] = false; m_keys[keycode] = false;
m_fakeKeys[keycode] = false; m_fakeKeys[keycode] = false;
// remove server key from map
if (index != m_serverKeyMap.end()) {
m_serverKeyMap.erase(index);
}
} }
void void

View File

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

View File

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

View File

@ -200,24 +200,25 @@ CClientProxy1_0::setClipboardDirty(ClipboardID id, bool dirty)
} }
void 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)); 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 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)); 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 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)); 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 void

View File

@ -37,9 +37,10 @@ public:
virtual void setClipboard(ClipboardID, const CString&); virtual void setClipboard(ClipboardID, const CString&);
virtual void grabClipboard(ClipboardID); virtual void grabClipboard(ClipboardID);
virtual void setClipboardDirty(ClipboardID, bool); virtual void setClipboardDirty(ClipboardID, bool);
virtual void keyDown(KeyID, KeyModifierMask); virtual void keyDown(KeyID, KeyModifierMask, KeyButton);
virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count); virtual void keyRepeat(KeyID, KeyModifierMask,
virtual void keyUp(KeyID, KeyModifierMask); SInt32 count, KeyButton);
virtual void keyUp(KeyID, KeyModifierMask, KeyButton);
virtual void mouseDown(ButtonID); virtual void mouseDown(ButtonID);
virtual void mouseUp(ButtonID); virtual void mouseUp(ButtonID);
virtual void mouseMove(SInt32 xAbs, SInt32 yAbs); 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 void
CPrimaryClient::keyDown(KeyID, KeyModifierMask) CPrimaryClient::keyDown(KeyID, KeyModifierMask, KeyButton)
{ {
// ignore // ignore
} }
void void
CPrimaryClient::keyRepeat(KeyID, KeyModifierMask, SInt32) CPrimaryClient::keyRepeat(KeyID, KeyModifierMask, SInt32, KeyButton)
{ {
// ignore // ignore
} }
void void
CPrimaryClient::keyUp(KeyID, KeyModifierMask) CPrimaryClient::keyUp(KeyID, KeyModifierMask, KeyButton)
{ {
// ignore // ignore
} }

View File

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

View File

@ -20,6 +20,7 @@
#include "COutputPacketStream.h" #include "COutputPacketStream.h"
#include "CProtocolUtil.h" #include "CProtocolUtil.h"
#include "CClientProxy1_0.h" #include "CClientProxy1_0.h"
#include "CClientProxy1_1.h"
#include "OptionTypes.h" #include "OptionTypes.h"
#include "ProtocolTypes.h" #include "ProtocolTypes.h"
#include "XScreen.h" #include "XScreen.h"
@ -615,9 +616,9 @@ CServer::onOneShotTimerExpired(UInt32 id)
} }
void 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); CLock lock(&m_mutex);
assert(m_active != NULL); assert(m_active != NULL);
@ -627,13 +628,13 @@ CServer::onKeyDown(KeyID id, KeyModifierMask mask)
} }
// relay // relay
m_active->keyDown(id, mask); m_active->keyDown(id, mask, button);
} }
void 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); CLock lock(&m_mutex);
assert(m_active != NULL); assert(m_active != NULL);
@ -643,13 +644,14 @@ CServer::onKeyUp(KeyID id, KeyModifierMask mask)
} }
// relay // relay
m_active->keyUp(id, mask); m_active->keyUp(id, mask, button);
} }
void 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); CLock lock(&m_mutex);
assert(m_active != NULL); assert(m_active != NULL);
@ -660,7 +662,7 @@ CServer::onKeyRepeat(KeyID id, KeyModifierMask mask, SInt32 count)
} }
// relay // relay
m_active->keyRepeat(id, mask, count); m_active->keyRepeat(id, mask, count, button);
} }
void void
@ -1698,18 +1700,7 @@ CServer::handshakeClient(IDataSocket* socket)
} }
// disallow invalid version numbers // disallow invalid version numbers
if (major < 0 || minor < 0) { 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)) {
throw XIncompatibleClient(major, minor); throw XIncompatibleClient(major, minor);
} }
@ -1720,7 +1711,22 @@ CServer::handshakeClient(IDataSocket* socket)
// create client proxy for highest version supported by the client // 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)); 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 // negotiate
// FIXME // FIXME

View File

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

View File

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

View File

@ -124,23 +124,28 @@ public:
//! Notify of key press //! Notify of key press
/*! /*!
Synthesize key events to generate a press of key \c id. If possible 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 //! Notify of key repeat
/*! /*!
Synthesize key events to generate a press and release of key \c id Synthesize key events to generate a press and release of key \c id
\c count times. If possible match the given modifier mask. \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 //! Notify of key release
/*! /*!
Synthesize key events to generate a release of key \c id. If possible Synthesize key events to generate a release of key \c id. If possible
match the given modifier mask. match the given modifier mask.
*/ */
virtual void keyUp(KeyID id, KeyModifierMask) = 0; virtual void keyUp(KeyID id, KeyModifierMask, KeyButton) = 0;
//! Notify of mouse press //! Notify of mouse press
/*! /*!

View File

@ -103,23 +103,28 @@ public:
//! Notify of key press //! Notify of key press
/*! /*!
Synthesize key events to generate a press of key \c id. If possible 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 //! Notify of key repeat
/*! /*!
Synthesize key events to generate a press and release of key \c id Synthesize key events to generate a press and release of key \c id
\c count times. If possible match the given modifier mask. \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 //! Notify of key release
/*! /*!
Synthesize key events to generate a release of key \c id. If possible Synthesize key events to generate a release of key \c id. If possible
match the given modifier mask. match the given modifier mask.
*/ */
virtual void keyUp(KeyID id, KeyModifierMask) = 0; virtual void keyUp(KeyID id, KeyModifierMask, KeyButton) = 0;
//! Notify of mouse press //! Notify of mouse press
/*! /*!

View File

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

View File

@ -19,12 +19,19 @@
//! Key ID //! 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 U+E000 through U+EFFF for the various control keys (e.g. arrow
keys, function keys, modifier keys, etc). keys, function keys, modifier keys, etc).
*/ */
typedef UInt32 KeyID; 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 //! Modifier key mask
/*! /*!
Type to hold a bitmask of key modifiers (e.g. shift keys). Type to hold a bitmask of key modifiers (e.g. shift keys).

View File

@ -18,8 +18,10 @@
#include "BasicTypes.h" #include "BasicTypes.h"
// protocol version number // 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 kProtocolMajorVersion = 1;
static const SInt16 kProtocolMinorVersion = 0; static const SInt16 kProtocolMinorVersion = 1;
// default contact port number // default contact port number
static const UInt16 kDefaultPort = 24800; static const UInt16 kDefaultPort = 24800;
@ -137,16 +139,34 @@ static const char kMsgCInfoAck[] = "CIAK";
// //
// key pressed: primary -> secondary // key pressed: primary -> secondary
// $1 = KeyID, $2 = KeyModifierMask // $1 = KeyID, $2 = KeyModifierMask, $3 = KeyButton
static const char kMsgDKeyDown[] = "DKDN%2i%2i"; // 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 // key auto-repeat: primary -> secondary
// $1 = KeyID, $2 = KeyModifierMask, $3 = number of repeats // $1 = KeyID, $2 = KeyModifierMask, $3 = number of repeats, $4 = KeyButton
static const char kMsgDKeyRepeat[] = "DKRP%2i%2i%2i"; 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 // key released: primary -> secondary
// $1 = KeyID, $2 = KeyModifierMask // $1 = KeyID, $2 = KeyModifierMask, $3 = KeyButton
static const char kMsgDKeyUp[] = "DKUP%2i%2i"; 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 // mouse button pressed: primary -> secondary
// $1 = ButtonID // $1 = ButtonID