Skip to content

Commit

Permalink
Track keys using system key code
Browse files Browse the repository at this point in the history
We might not always be able to map to an RFB key code, so let's use the
platform specific code to track which keys are pressed.
  • Loading branch information
CendioOssman committed Oct 16, 2024
1 parent a4c86a7 commit 16f9878
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 75 deletions.
136 changes: 65 additions & 71 deletions vncviewer/Viewport.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,14 @@ enum { ID_DISCONNECT, ID_FULLSCREEN, ID_MINIMIZE, ID_RESIZE,
static const WORD SCAN_FAKE = 0xaa;
#endif

// Used for fake key presses from the menu
static const int FAKE_CTRL_KEY_CODE = 0x10001;
static const int FAKE_ALT_KEY_CODE = 0x10002;
static const int FAKE_DEL_KEY_CODE = 0x10003;

// Used for fake key presses for lock key sync
static const int FAKE_KEY_CODE = 0xffff;

Viewport::Viewport(int w, int h, const rfb::PixelFormat& /*serverPF*/, CConn* cc_)
: Fl_Widget(0, 0, w, h), cc(cc_), frameBuffer(nullptr),
lastPointerPos(0, 0), lastButtonMask(0),
Expand Down Expand Up @@ -499,18 +507,18 @@ void Viewport::pushLEDState()

if ((ledState & ledCapsLock) != (cc->server.ledState() & ledCapsLock)) {
vlog.debug("Inserting fake CapsLock to get in sync with server");
handleKeyPress(0x3a, XK_Caps_Lock);
handleKeyRelease(0x3a);
handleKeyPress(FAKE_KEY_CODE, 0x3a, XK_Caps_Lock);
handleKeyRelease(FAKE_KEY_CODE);
}
if ((ledState & ledNumLock) != (cc->server.ledState() & ledNumLock)) {
vlog.debug("Inserting fake NumLock to get in sync with server");
handleKeyPress(0x45, XK_Num_Lock);
handleKeyRelease(0x45);
handleKeyPress(FAKE_KEY_CODE, 0x45, XK_Num_Lock);
handleKeyRelease(FAKE_KEY_CODE);
}
if ((ledState & ledScrollLock) != (cc->server.ledState() & ledScrollLock)) {
vlog.debug("Inserting fake ScrollLock to get in sync with server");
handleKeyPress(0x46, XK_Scroll_Lock);
handleKeyRelease(0x46);
handleKeyPress(FAKE_KEY_CODE, 0x46, XK_Scroll_Lock);
handleKeyRelease(FAKE_KEY_CODE);
}
}

Expand Down Expand Up @@ -637,9 +645,9 @@ int Viewport::handle(int event)

// Resend Ctrl/Alt if needed
if (menuCtrlKey)
handleKeyPress(0x1d, XK_Control_L);
handleKeyPress(FAKE_CTRL_KEY_CODE, 0x1d, XK_Control_L);
if (menuAltKey)
handleKeyPress(0x38, XK_Alt_L);
handleKeyPress(FAKE_ALT_KEY_CODE, 0x38, XK_Alt_L);

// Yes, we would like some focus please!
return 1;
Expand Down Expand Up @@ -822,12 +830,13 @@ void Viewport::handlePointerTimeout(void *data)

void Viewport::resetKeyboard()
{
while (!downKeySym.empty())
handleKeyRelease(downKeySym.begin()->first);
while (!downKeys.empty())
handleKeyRelease(downKeys.begin()->first);
}


void Viewport::handleKeyPress(int keyCode, uint32_t keySym)
void Viewport::handleKeyPress(int systemKeyCode,
uint32_t keyCode, uint32_t keySym)
{
static bool menuRecursion = false;

Expand All @@ -843,11 +852,6 @@ void Viewport::handleKeyPress(int keyCode, uint32_t keySym)
if (viewOnly)
return;

if (keyCode == 0) {
vlog.error(_("No key code specified on key press"));
return;
}

#ifdef __APPLE__
// Alt on OS X behaves more like AltGr on other systems, and to get
// sane behaviour we should translate things in that manner for the
Expand All @@ -874,53 +878,49 @@ void Viewport::handleKeyPress(int keyCode, uint32_t keySym)
// symbol on release as when pressed. This breaks the VNC protocol however,
// so we need to keep track of what keysym a key _code_ generated on press
// and send the same on release.
downKeySym[keyCode] = keySym;
downKeys[systemKeyCode].keyCode = keyCode;
downKeys[systemKeyCode].keySym = keySym;

vlog.debug("Key pressed: 0x%04x => XK_%s (0x%04x)",
keyCode, KeySymName(keySym), keySym);
vlog.debug("Key pressed: %d => 0x%02x / XK_%s (0x%04x)",
systemKeyCode, keyCode, KeySymName(keySym), keySym);

try {
// Fake keycode?
if (keyCode > 0xff)
cc->writer()->writeKeyEvent(keySym, 0, true);
else
cc->writer()->writeKeyEvent(keySym, keyCode, true);
cc->writer()->writeKeyEvent(keySym, keyCode, true);
} catch (rdr::Exception& e) {
vlog.error("%s", e.str());
abort_connection_with_unexpected_error(e);
}
}


void Viewport::handleKeyRelease(int keyCode)
void Viewport::handleKeyRelease(int systemKeyCode)
{
DownMap::iterator iter;

if (viewOnly)
return;

iter = downKeySym.find(keyCode);
if (iter == downKeySym.end()) {
iter = downKeys.find(systemKeyCode);
if (iter == downKeys.end()) {
// These occur somewhat frequently so let's not spam them unless
// logging is turned up.
vlog.debug("Unexpected release of key code %d", keyCode);
vlog.debug("Unexpected release of key code %d", systemKeyCode);
return;
}

vlog.debug("Key released: 0x%04x => XK_%s (0x%04x)",
keyCode, KeySymName(iter->second), iter->second);
vlog.debug("Key released: %d => 0x%02x / XK_%s (0x%04x)",
systemKeyCode, iter->second.keyCode,
KeySymName(iter->second.keySym), iter->second.keySym);

try {
if (keyCode > 0xff)
cc->writer()->writeKeyEvent(iter->second, 0, false);
else
cc->writer()->writeKeyEvent(iter->second, keyCode, false);
cc->writer()->writeKeyEvent(iter->second.keySym,
iter->second.keyCode, false);
} catch (rdr::Exception& e) {
vlog.error("%s", e.str());
abort_connection_with_unexpected_error(e);
}

downKeySym.erase(iter);
downKeys.erase(iter);
}


Expand Down Expand Up @@ -1057,7 +1057,7 @@ int Viewport::handleSystemEvent(void *event, void *data)
}
}

self->handleKeyPress(keyCode, keySym);
self->handleKeyPress(keyCode, keyCode, keySym);

// We don't get reliable WM_KEYUP for these
switch (keySym) {
Expand Down Expand Up @@ -1112,9 +1112,9 @@ int Viewport::handleSystemEvent(void *event, void *data)
// Windows has a rather nasty bug where it won't send key release
// events for a Shift button if the other Shift is still pressed
if ((keyCode == 0x2a) || (keyCode == 0x36)) {
if (self->downKeySym.count(0x2a))
if (self->downKeys.count(0x2a))
self->handleKeyRelease(0x2a);
if (self->downKeySym.count(0x36))
if (self->downKeys.count(0x36))
self->handleKeyRelease(0x36);
}

Expand All @@ -1128,31 +1128,33 @@ int Viewport::handleSystemEvent(void *event, void *data)
}

if (cocoa_is_keyboard_event(event)) {
int keyCode;
int systemKeyCode;

keyCode = cocoa_event_keycode(event);
if ((unsigned)keyCode >= code_map_osx_to_qnum_len)
keyCode = 0;
else
keyCode = code_map_osx_to_qnum[keyCode];
systemKeyCode = cocoa_event_keycode(event);

if (cocoa_is_key_press(event)) {
uint32_t keyCode;
uint32_t keySym;

if ((unsigned)systemKeyCode >= code_map_osx_to_qnum_len)
keyCode = 0;
else
keyCode = code_map_osx_to_qnum[systemKeyCode];

keySym = cocoa_event_keysym(event);
if (keySym == NoSymbol) {
vlog.error(_("No symbol for key code 0x%02x (in the current state)"),
(int)keyCode);
}

self->handleKeyPress(keyCode, keySym);
self->handleKeyPress(systemKeyCode, keyCode, keySym);

// We don't get any release events for CapsLock, so we have to
// send the release right away.
if (keySym == XK_Caps_Lock)
self->handleKeyRelease(keyCode);
self->handleKeyRelease(systemKeyCode);
} else {
self->handleKeyRelease(keyCode);
self->handleKeyRelease(systemKeyCode);
}

return 1;
Expand All @@ -1167,11 +1169,6 @@ int Viewport::handleSystemEvent(void *event, void *data)

keycode = code_map_keycode_to_qnum[xevent->xkey.keycode];

// Generate a fake keycode just for tracking if we can't figure
// out the proper one
if (keycode == 0)
keycode = 0x100 | xevent->xkey.keycode;

XLookupString(&xevent->xkey, &str, 1, &keysym, nullptr);
if (keysym == NoSymbol) {
vlog.error(_("No symbol for key code %d (in the current state)"),
Expand All @@ -1195,13 +1192,10 @@ int Viewport::handleSystemEvent(void *event, void *data)
break;
}

self->handleKeyPress(keycode, keysym);
self->handleKeyPress(xevent->xkey.keycode, keycode, keysym);
return 1;
} else if (xevent->type == KeyRelease) {
int keycode = code_map_keycode_to_qnum[xevent->xkey.keycode];
if (keycode == 0)
keycode = 0x100 | xevent->xkey.keycode;
self->handleKeyRelease(keycode);
self->handleKeyRelease(xevent->xkey.keycode);
return 1;
}
#endif
Expand All @@ -1217,7 +1211,7 @@ void Viewport::handleAltGrTimeout(void *data)
assert(self);

self->altGrArmed = false;
self->handleKeyPress(0x1d, XK_Control_L);
self->handleKeyPress(0x1d, 0x1d, XK_Control_L);
}

void Viewport::resolveAltGrDetection(bool isAltGrSequence)
Expand All @@ -1226,7 +1220,7 @@ void Viewport::resolveAltGrDetection(bool isAltGrSequence)
Fl::remove_timeout(handleAltGrTimeout);
// when it's not an AltGr sequence we can't supress the Ctrl anymore
if (!isAltGrSequence)
handleKeyPress(0x1d, XK_Control_L);
handleKeyPress(0x1d, 0x1d, XK_Control_L);
}
#endif

Expand Down Expand Up @@ -1338,30 +1332,30 @@ void Viewport::popupContextMenu()
break;
case ID_CTRL:
if (m->value())
handleKeyPress(0x1d, XK_Control_L);
handleKeyPress(FAKE_CTRL_KEY_CODE, 0x1d, XK_Control_L);
else
handleKeyRelease(0x1d);
handleKeyRelease(FAKE_CTRL_KEY_CODE);
menuCtrlKey = !menuCtrlKey;
break;
case ID_ALT:
if (m->value())
handleKeyPress(0x38, XK_Alt_L);
handleKeyPress(FAKE_ALT_KEY_CODE, 0x38, XK_Alt_L);
else
handleKeyRelease(0x38);
handleKeyRelease(FAKE_ALT_KEY_CODE);
menuAltKey = !menuAltKey;
break;
case ID_MENUKEY:
handleKeyPress(menuKeyCode, menuKeySym);
handleKeyRelease(menuKeyCode);
handleKeyPress(FAKE_KEY_CODE, menuKeyCode, menuKeySym);
handleKeyRelease(FAKE_KEY_CODE);
break;
case ID_CTRLALTDEL:
handleKeyPress(0x1d, XK_Control_L);
handleKeyPress(0x38, XK_Alt_L);
handleKeyPress(0xd3, XK_Delete);
handleKeyPress(FAKE_CTRL_KEY_CODE, 0x1d, XK_Control_L);
handleKeyPress(FAKE_ALT_KEY_CODE, 0x38, XK_Alt_L);
handleKeyPress(FAKE_DEL_KEY_CODE, 0xd3, XK_Delete);

handleKeyRelease(0xd3);
handleKeyRelease(0x38);
handleKeyRelease(0x1d);
handleKeyRelease(FAKE_DEL_KEY_CODE);
handleKeyRelease(FAKE_ALT_KEY_CODE);
handleKeyRelease(FAKE_CTRL_KEY_CODE);
break;
case ID_REFRESH:
cc->refreshFramebuffer();
Expand Down
13 changes: 9 additions & 4 deletions vncviewer/Viewport.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,9 @@ class Viewport : public Fl_Widget, public EmulateMB {

void resetKeyboard();

void handleKeyPress(int keyCode, uint32_t keySym);
void handleKeyRelease(int keyCode);
void handleKeyPress(int systemKeyCode,
uint32_t keyCode, uint32_t keySym);
void handleKeyRelease(int systemKeyCode);

static int handleSystemEvent(void *event, void *data);

Expand All @@ -113,8 +114,12 @@ class Viewport : public Fl_Widget, public EmulateMB {
rfb::Point lastPointerPos;
uint8_t lastButtonMask;

typedef std::map<int, uint32_t> DownMap;
DownMap downKeySym;
struct DownKey {
uint32_t keyCode;
uint32_t keySym;
};
typedef std::map<int, DownKey> DownMap;
DownMap downKeys;

#ifdef WIN32
bool altGrArmed;
Expand Down

0 comments on commit 16f9878

Please sign in to comment.