Add support for raw keyboard in vncviewer
Make sure it can map between the key codes of the local system
in to the key codes used by the protocol.
diff --git a/vncviewer/Viewport.cxx b/vncviewer/Viewport.cxx
index 1923f53..331efbc 100644
--- a/vncviewer/Viewport.cxx
+++ b/vncviewer/Viewport.cxx
@@ -74,8 +74,21 @@
#include <FL/Fl_Menu.H>
#include <FL/Fl_Menu_Button.H>
+#if !defined(WIN32) && !defined(__APPLE__)
+#include <X11/XKBlib.h>
+extern const struct _code_map_xkb_to_qnum {
+ const char * from;
+ const unsigned short to;
+} code_map_xkb_to_qnum[];
+extern const unsigned int code_map_xkb_to_qnum_len;
+
+static int code_map_keycode_to_qnum[256];
+#endif
+
#ifdef __APPLE__
#include "cocoa.h"
+extern const unsigned short code_map_osx_to_qnum[];
+extern const unsigned int code_map_osx_to_qnum_len;
#endif
#ifdef WIN32
@@ -94,9 +107,6 @@
ID_CTRL, ID_ALT, ID_MENUKEY, ID_CTRLALTDEL,
ID_REFRESH, ID_OPTIONS, ID_INFO, ID_ABOUT, ID_DISMISS };
-// Fake key presses use this value and above
-static const int fakeKeyBase = 0x200;
-
// Used to detect fake input (0xaa is not a real key)
#ifdef WIN32
static const WORD SCAN_FAKE = 0xaa;
@@ -107,6 +117,45 @@
lastPointerPos(0, 0), lastButtonMask(0),
menuCtrlKey(false), menuAltKey(false), cursor(NULL)
{
+#if !defined(WIN32) && !defined(__APPLE__)
+ XkbDescPtr xkb;
+ Status status;
+
+ xkb = XkbGetMap(fl_display, 0, XkbUseCoreKbd);
+ if (!xkb)
+ throw Exception("XkbGetMap");
+
+ status = XkbGetNames(fl_display, XkbKeyNamesMask, xkb);
+ if (status != Success)
+ throw Exception("XkbGetNames");
+
+ memset(code_map_keycode_to_qnum, 0, sizeof(code_map_keycode_to_qnum));
+ for (KeyCode keycode = xkb->min_key_code;
+ keycode < xkb->max_key_code;
+ keycode++) {
+ const char *keyname = xkb->names->keys[keycode].name;
+ unsigned short rfbcode;
+
+ if (keyname[0] == '\0')
+ continue;
+
+ rfbcode = 0;
+ for (unsigned i = 0;i < code_map_xkb_to_qnum_len;i++) {
+ if (strncmp(code_map_xkb_to_qnum[i].from,
+ keyname, XkbKeyNameLength) == 0) {
+ rfbcode = code_map_xkb_to_qnum[i].to;
+ break;
+ }
+ }
+ if (rfbcode != 0)
+ code_map_keycode_to_qnum[keycode] = rfbcode;
+ else
+ vlog.debug("No key mapping for key %.4s", keyname);
+ }
+
+ XkbFreeKeyboard(xkb, 0, True);
+#endif
+
Fl::add_clipboard_notify(handleClipboardChange, this);
// We need to intercept keyboard events early
@@ -395,18 +444,18 @@
if ((state & ledCapsLock) != (cc->cp.ledState() & ledCapsLock)) {
vlog.debug("Inserting fake CapsLock to get in sync with server");
- handleKeyPress(fakeKeyBase + 100, XK_Caps_Lock);
- handleKeyRelease(fakeKeyBase + 100);
+ handleKeyPress(0x3a, XK_Caps_Lock);
+ handleKeyRelease(0x3a);
}
if ((state & ledNumLock) != (cc->cp.ledState() & ledNumLock)) {
vlog.debug("Inserting fake NumLock to get in sync with server");
- handleKeyPress(fakeKeyBase + 101, XK_Num_Lock);
- handleKeyRelease(fakeKeyBase + 101);
+ handleKeyPress(0x45, XK_Num_Lock);
+ handleKeyRelease(0x45);
}
if ((state & ledScrollLock) != (cc->cp.ledState() & ledScrollLock)) {
vlog.debug("Inserting fake ScrollLock to get in sync with server");
- handleKeyPress(fakeKeyBase + 102, XK_Scroll_Lock);
- handleKeyRelease(fakeKeyBase + 102);
+ handleKeyPress(0x46, XK_Scroll_Lock);
+ handleKeyRelease(0x46);
}
}
@@ -664,6 +713,11 @@
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
@@ -694,23 +748,11 @@
// Ctrl+Alt+AltGr, which we usually end up with when Xvnc tries to
// get everything in the correct state. Cheat and temporarily release
// Ctrl and Alt when we send some other symbol.
- bool ctrlPressed, altPressed;
- DownMap::iterator iter;
-
- ctrlPressed = false;
- altPressed = false;
- for (iter = downKeySym.begin();iter != downKeySym.end();++iter) {
- if (iter->second == XK_Control_L)
- ctrlPressed = true;
- else if (iter->second == XK_Alt_R)
- altPressed = true;
- }
-
- if (ctrlPressed && altPressed) {
+ if (downKeySym.count(0x1d) && downKeySym.count(0xb8)) {
vlog.debug("Faking release of AltGr (Ctrl_L+Alt_R)");
try {
- cc->writer()->keyEvent(XK_Control_L, 0, false);
- cc->writer()->keyEvent(XK_Alt_R, 0, false);
+ cc->writer()->keyEvent(downKeySym[0x1d], 0x1d, false);
+ cc->writer()->keyEvent(downKeySym[0xb8], 0xb8, false);
} catch (rdr::Exception& e) {
vlog.error("%s", e.str());
exit_vncviewer(e.str());
@@ -732,7 +774,11 @@
#endif
try {
- cc->writer()->keyEvent(keySym, 0, true);
+ // Fake keycode?
+ if (keyCode > 0xff)
+ cc->writer()->keyEvent(keySym, 0, true);
+ else
+ cc->writer()->keyEvent(keySym, keyCode, true);
} catch (rdr::Exception& e) {
vlog.error("%s", e.str());
exit_vncviewer(e.str());
@@ -740,11 +786,11 @@
#ifdef WIN32
// Ugly hack continued...
- if (ctrlPressed && altPressed) {
+ if (downKeySym.count(0x1d) && downKeySym.count(0xb8)) {
vlog.debug("Restoring AltGr state");
try {
- cc->writer()->keyEvent(XK_Control_L, 0, true);
- cc->writer()->keyEvent(XK_Alt_R, 0, true);
+ cc->writer()->keyEvent(downKeySym[0x1d], 0x1d, true);
+ cc->writer()->keyEvent(downKeySym[0xb8], 0xb8, true);
} catch (rdr::Exception& e) {
vlog.error("%s", e.str());
exit_vncviewer(e.str());
@@ -777,7 +823,10 @@
#endif
try {
- cc->writer()->keyEvent(iter->second, 0, false);
+ if (keyCode > 0xff)
+ cc->writer()->keyEvent(iter->second, 0, false);
+ else
+ cc->writer()->keyEvent(iter->second, keyCode, false);
} catch (rdr::Exception& e) {
vlog.error("%s", e.str());
exit_vncviewer(e.str());
@@ -837,14 +886,19 @@
}
}
+ if (keyCode & ~0x7f) {
+ vlog.error(_("Invalid scan code 0x%02x"), (int)keyCode);
+ return 1;
+ }
+
if (isExtended)
- keyCode |= 0x100;
+ keyCode |= 0x80;
// VK_SNAPSHOT sends different scan codes depending on the state of
// Alt. This means that we can get different scan codes on press and
// release. Force it to be something standard.
if (vKey == VK_SNAPSHOT)
- keyCode = 0x137;
+ keyCode = 0x54;
keySym = win32_vkey_to_keysym(vKey, isExtended);
if (keySym == NoSymbol) {
@@ -852,9 +906,12 @@
vlog.error(_("No symbol for extended virtual key 0x%02x"), (int)vKey);
else
vlog.error(_("No symbol for virtual key 0x%02x"), (int)vKey);
- return 1;
}
+ // Fortunately RFB and Windows use the same scan code set,
+ // so there is no conversion needed
+ // (as long as we encode the extended keys with the high bit)
+
self->handleKeyPress(keyCode, keySym);
return 1;
@@ -876,9 +933,9 @@
if (keyCode == 0x00)
keyCode = MapVirtualKey(vKey, MAPVK_VK_TO_VSC);
if (isExtended)
- keyCode |= 0x100;
+ keyCode |= 0x80;
if (vKey == VK_SNAPSHOT)
- keyCode = 0x137;
+ keyCode = 0x54;
self->handleKeyRelease(keyCode);
@@ -889,6 +946,10 @@
int keyCode;
keyCode = cocoa_event_keycode(event);
+ if ((unsigned)keyCode >= code_map_osx_to_qnum_len)
+ keyCode = 0;
+ else
+ keyCode = code_map_osx_to_qnum[keyCode];
if (cocoa_is_key_press(event)) {
rdr::U32 keySym;
@@ -897,7 +958,6 @@
if (keySym == NoSymbol) {
vlog.error(_("No symbol for key code 0x%02x (in the current state)"),
(int)keyCode);
- return 1;
}
self->handleKeyPress(keyCode, keySym);
@@ -916,14 +976,21 @@
XEvent *xevent = (XEvent*)event;
if (xevent->type == KeyPress) {
+ int keycode;
char str;
KeySym keysym;
+ 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, NULL);
if (keysym == NoSymbol) {
vlog.error(_("No symbol for key code %d (in the current state)"),
(int)xevent->xkey.keycode);
- return 1;
}
switch (keysym) {
@@ -943,10 +1010,13 @@
break;
}
- self->handleKeyPress(xevent->xkey.keycode, keysym);
+ self->handleKeyPress(keycode, keysym);
return 1;
} else if (xevent->type == KeyRelease) {
- self->handleKeyRelease(xevent->xkey.keycode);
+ int keycode = code_map_keycode_to_qnum[xevent->xkey.keycode];
+ if (keycode == 0)
+ keycode = 0x100 | xevent->xkey.keycode;
+ self->handleKeyRelease(keycode);
return 1;
}
#endif
@@ -982,7 +1052,7 @@
char sendMenuKey[64];
snprintf(sendMenuKey, 64, p_("ContextMenu|", "Send %s"), (const char *)menuKey);
fltk_menu_add(contextMenu, sendMenuKey, 0, NULL, (void*)ID_MENUKEY, 0);
- fltk_menu_add(contextMenu, "Secret shortcut menu key", menuKeyCode, NULL,
+ fltk_menu_add(contextMenu, "Secret shortcut menu key", menuKeyFLTK, NULL,
(void*)ID_MENUKEY, FL_MENU_INVISIBLE);
}
@@ -1050,30 +1120,30 @@
break;
case ID_CTRL:
if (m->value())
- handleKeyPress(fakeKeyBase + 0, XK_Control_L);
+ handleKeyPress(0x1d, XK_Control_L);
else
- handleKeyRelease(fakeKeyBase + 0);
+ handleKeyRelease(0x1d);
menuCtrlKey = !menuCtrlKey;
break;
case ID_ALT:
if (m->value())
- handleKeyPress(fakeKeyBase + 1, XK_Alt_L);
+ handleKeyPress(0x38, XK_Alt_L);
else
- handleKeyRelease(fakeKeyBase + 1);
+ handleKeyRelease(0x38);
menuAltKey = !menuAltKey;
break;
case ID_MENUKEY:
- handleKeyPress(fakeKeyBase + 2, menuKeySym);
- handleKeyRelease(fakeKeyBase + 2);
+ handleKeyPress(menuKeyCode, menuKeySym);
+ handleKeyRelease(menuKeyCode);
break;
case ID_CTRLALTDEL:
- handleKeyPress(fakeKeyBase + 3, XK_Control_L);
- handleKeyPress(fakeKeyBase + 4, XK_Alt_L);
- handleKeyPress(fakeKeyBase + 5, XK_Delete);
+ handleKeyPress(0x1d, XK_Control_L);
+ handleKeyPress(0x38, XK_Alt_L);
+ handleKeyPress(0xd3, XK_Delete);
- handleKeyRelease(fakeKeyBase + 5);
- handleKeyRelease(fakeKeyBase + 4);
- handleKeyRelease(fakeKeyBase + 3);
+ handleKeyRelease(0xd3);
+ handleKeyRelease(0x38);
+ handleKeyRelease(0x1d);
break;
case ID_REFRESH:
cc->refreshFramebuffer();
@@ -1099,7 +1169,7 @@
void Viewport::setMenuKey()
{
- getMenuKey(&menuKeyCode, &menuKeySym);
+ getMenuKey(&menuKeyFLTK, &menuKeyCode, &menuKeySym);
}