diff --git a/vncviewer/Viewport.cxx b/vncviewer/Viewport.cxx index 66b61cfac..ac2cf264e 100644 --- a/vncviewer/Viewport.cxx +++ b/vncviewer/Viewport.cxx @@ -21,6 +21,8 @@ #include #endif +#include + #include #include #include @@ -113,7 +115,7 @@ static const WORD SCAN_FAKE = 0xaa; Viewport::Viewport(int w, int h, const rfb::PixelFormat& serverPF, CConn* cc_) : Fl_Widget(0, 0, w, h), cc(cc_), frameBuffer(NULL), - lastPointerPos(0, 0), lastButtonMask(0), + lastPointerPos(0, 0), lastButtonMask(0), hotKeyWedged(false), #ifdef WIN32 altGrArmed(false), #endif @@ -823,52 +825,14 @@ void Viewport::handlePointerTimeout(void *data) void Viewport::handleKeyPress(int keyCode, rdr::U32 keySym) { - std::set hotkeys; - - hotkeys = parseHotKeyCombo(hotKeyCombo); - - if ((hotkeys.count(hotkeyCtrl) > 0) && - ((keySym == XK_Control_L) || (keySym == XK_Control_R))) - downHotkeys.insert(hotkeyCtrl); - else if ((hotkeys.count(hotkeyShift) > 0) && - ((keySym == XK_Shift_L) || (keySym == XK_Shift_R))) - downHotkeys.insert(hotkeyShift); - else if ((hotkeys.count(hotkeyAlt) > 0) && - ((keySym == XK_Alt_L) || (keySym == XK_Alt_R))) - downHotkeys.insert(hotkeyAlt); - else if ((hotkeys.count(hotkeySuper) > 0) && - ((keySym == XK_Super_L) || (keySym == XK_Super_R))) - downHotkeys.insert(hotkeySuper); - - 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 - // remote VNC server. However that means we lose the ability to use - // Alt as a shortcut modifier. Do what RealVNC does and hijack the - // left command key as an Alt replacement. - switch (keySym) { - case XK_Super_L: - keySym = XK_Alt_L; - break; - case XK_Super_R: - keySym = XK_Super_L; - break; - case XK_Alt_L: - keySym = XK_Mode_switch; - break; - case XK_Alt_R: - keySym = XK_ISO_Level3_Shift; - break; - } -#endif + // Possible hot key combo? + if (handleHotKeyPress(keyCode, keySym)) + return; // Because of the way keyboards work, we cannot expect to have the same // symbol on release as when pressed. This breaks the VNC protocol however, @@ -876,6 +840,12 @@ void Viewport::handleKeyPress(int keyCode, rdr::U32 keySym) // and send the same on release. downKeySym[keyCode] = keySym; + if (viewOnly) + return; + + // Platform specific adjustments + keySym = adjustKeySym(keyCode, keySym); + #if defined(WIN32) || defined(__APPLE__) vlog.debug("Key pressed: 0x%04x => 0x%04x", keyCode, keySym); #else @@ -900,9 +870,7 @@ void Viewport::handleKeyPress(int keyCode, rdr::U32 keySym) void Viewport::handleKeyRelease(int keyCode) { DownMap::iterator iter; - - if (viewOnly) - return; + rdr::U32 keySym; iter = downKeySym.find(keyCode); if (iter == downKeySym.end()) { @@ -912,25 +880,178 @@ void Viewport::handleKeyRelease(int keyCode) return; } + keySym = iter->second; + downKeySym.erase(iter); + + if (downKeySym.empty()) + hotKeyWedged = false; + + if (viewOnly) + return; + + // Platform specific adjustments + keySym = adjustKeySym(keyCode, keySym); + #if defined(WIN32) || defined(__APPLE__) - vlog.debug("Key released: 0x%04x => 0x%04x", keyCode, iter->second); + vlog.debug("Key released: 0x%04x => 0x%04x", keyCode, keySym); #else vlog.debug("Key released: 0x%04x => XK_%s (0x%04x)", - keyCode, XKeysymToString(iter->second), iter->second); + keyCode, XKeysymToString(keySym), keySym); #endif try { if (keyCode > 0xff) - cc->writer()->writeKeyEvent(iter->second, 0, false); + cc->writer()->writeKeyEvent(keySym, 0, false); else - cc->writer()->writeKeyEvent(iter->second, keyCode, false); + cc->writer()->writeKeyEvent(keySym, keyCode, false); } catch (rdr::Exception& e) { vlog.error("%s", e.str()); exit_vncviewer(_("An unexpected error occurred when communicating " "with the server:\n\n%s"), e.str()); } +} - downKeySym.erase(iter); +rdr::U32 Viewport::adjustKeySym(int keyCode, rdr::U32 keySym) +{ +#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 + // remote VNC server. However that means we lose the ability to use + // Alt as a shortcut modifier. Do what RealVNC does and hijack the + // left command key as an Alt replacement. + switch (keySym) { + case XK_Super_L: + keySym = XK_Alt_L; + break; + case XK_Super_R: + keySym = XK_Super_L; + break; + case XK_Alt_L: + keySym = XK_Mode_switch; + break; + case XK_Alt_R: + keySym = XK_ISO_Level3_Shift; + break; + } +#endif + + return keySym; +} + +bool Viewport::handleHotKeyPress(int keyCode, rdr::U32 keySym) +{ + // Already determined this isn't a hot key combo? + if (hotKeyWedged) + return false; + + // Are we still collecting modifiers? + if (!hotKeyReady) { + std::set hotkeys; + std::set downHotkeys; + DownMap::iterator iter; + + hotkeys = parseHotKeyCombo(hotKeyCombo); + + // No hot key combo configured? + if (hotkeys.empty()) { + hotKeyWedged = true; + return false; + } + + // Which modifiers are pressed? + for (iter = downKeySym.begin(); iter != downKeySym.end(); ++iter) { + switch (iter->second) { + case XK_Super_L: + case XK_Super_R: + downHotkeys.insert(hotkeySuper); + break; + case XK_Alt_L: + case XK_Alt_R: + downHotkeys.insert(hotkeyAlt); + break; + case XK_Shift_L: + case XK_Shift_R: + downHotkeys.insert(hotkeyShift); + break; + case XK_Control_L: + case XK_Control_R: + downHotkeys.insert(hotkeyCtrl); + break; + default: + // Something else snuck in + hotKeyWedged = true; + return false; + } + } + + // Not enough? + if (hotkeys != downHotkeys) { + // Extra modifiers? + if (!std::includes(hotkeys.begin(), hotkeys.end(), + downHotkeys.begin(), downHotkeys.end())) { + // Yes, so this is likely some other key combo we should ignore + hotKeyWedged = true; + } + + return false; + } + + // Is this new key also a modifier? + switch (keySym) { + case XK_Super_L: + case XK_Super_R: + case XK_Alt_L: + case XK_Alt_R: + case XK_Shift_L: + case XK_Shift_R: + case XK_Control_L: + case XK_Control_R: + // Yes, so this is likely some other key combo we should ignore + hotKeyWedged = true; + return false; + } + + if (keySym == XK_space) { + vlog.debug("Detected hot key escape sequence"); + // We're not really wedged, but we want the same effect, + // i.e. that all following keys are passed through + hotKeyWedged = true; + // This space is consumed though + return true; + } + + vlog.debug("Detected hot key 0x%04x", keySym); + + // The remote session won't see any more keys, so release the ones + // currently down + while (!downKeySym.empty()) + handleKeyRelease(downKeySym.begin()->first); + + hotKeyReady = true; + } + + assert(hotKeyReady); + + switch (keySym) { + case XK_m: + popupContextMenu(); + break; + case XK_KP_Enter: + case XK_Return: + if (window()->fullscreen_active()) + window()->fullscreen_off(); + else + ((DesktopWindow*)window())->fullscreen_on(); + break; + default: + // Unknown/Unused hot key combo + break; + } + + // FIXME: Should be able to press multiple hot keys + hotKeyReady = false; + + return true; } diff --git a/vncviewer/Viewport.h b/vncviewer/Viewport.h index d70786579..649e8d4ca 100644 --- a/vncviewer/Viewport.h +++ b/vncviewer/Viewport.h @@ -21,13 +21,11 @@ #define __VIEWPORT_H__ #include -#include #include #include -#include "parameters.h" #include "EmulateMB.h" class Fl_Menu_Button; @@ -89,6 +87,11 @@ class Viewport : public Fl_Widget, public EmulateMB { void handleKeyPress(int keyCode, rdr::U32 keySym); void handleKeyRelease(int keyCode); + rdr::U32 adjustKeySym(int keyCode, rdr::U32 keySym); + + bool handleHotKeyPress(int keyCode, rdr::U32 keySym); + bool handleHotKeyRelease(int keyCode, rdr::U32 keySym); + static int handleSystemEvent(void *event, void *data); #ifdef WIN32 @@ -112,7 +115,8 @@ class Viewport : public Fl_Widget, public EmulateMB { typedef std::map DownMap; DownMap downKeySym; - std::set downHotkeys; + bool hotKeyReady; + bool hotKeyWedged; #ifdef WIN32 bool altGrArmed;