aboutsummaryrefslogtreecommitdiffstats
path: root/vncviewer/Viewport.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'vncviewer/Viewport.cxx')
-rw-r--r--vncviewer/Viewport.cxx219
1 files changed, 180 insertions, 39 deletions
diff --git a/vncviewer/Viewport.cxx b/vncviewer/Viewport.cxx
index 2cffc7be..03e6fb09 100644
--- a/vncviewer/Viewport.cxx
+++ b/vncviewer/Viewport.cxx
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2011-2021 Pierre Ossman for Cendio AB
+ * Copyright 2011-2025 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -32,14 +32,20 @@
#include <rfb/CMsgWriter.h>
#include <rfb/Cursor.h>
+#include <rfb/KeysymStr.h>
#include <rfb/ledStates.h>
// FLTK can pull in the X11 headers on some systems
#ifndef XK_VoidSymbol
+#define XK_LATIN1
#define XK_MISCELLANY
#include <rfb/keysymdef.h>
#endif
+#ifndef NoSymbol
+#define NoSymbol 0
+#endif
+
#include "fltk/layout.h"
#include "fltk/util.h"
#include "Viewport.h"
@@ -48,7 +54,6 @@
#include "DesktopWindow.h"
#include "i18n.h"
#include "parameters.h"
-#include "menukey.h"
#include "vncviewer.h"
#include "PlatformPixelBuffer.h"
@@ -77,7 +82,7 @@ static core::LogWriter vlog("Viewport");
// Menu constants
enum { ID_DISCONNECT, ID_FULLSCREEN, ID_MINIMIZE, ID_RESIZE,
- ID_CTRL, ID_ALT, ID_MENUKEY, ID_CTRLALTDEL,
+ ID_CTRL, ID_ALT, ID_CTRLALTDEL,
ID_REFRESH, ID_OPTIONS, ID_INFO, ID_ABOUT };
// Used for fake key presses from the menu
@@ -91,7 +96,7 @@ static const int FAKE_KEY_CODE = 0xffff;
Viewport::Viewport(int w, int h, CConn* cc_)
: Fl_Widget(0, 0, w, h), cc(cc_), frameBuffer(nullptr),
lastPointerPos(0, 0), lastButtonMask(0),
- keyboard(nullptr),
+ keyboard(nullptr), shortcutBypass(false), shortcutActive(false),
firstLEDState(true), pendingClientClipboard(false),
menuCtrlKey(false), menuAltKey(false), cursor(nullptr),
cursorIsBlank(false)
@@ -130,7 +135,11 @@ Viewport::Viewport(int w, int h, CConn* cc_)
// reparenting to the current window works for most cases.
window()->add(contextMenu);
- setMenuKey();
+ unsigned modifierMask = 0;
+ for (core::EnumListEntry key : shortcutModifiers)
+ modifierMask |= ShortcutHandler::parseModifier(key.getValueStr().c_str());
+
+ shortcutHandler.setModifiers(modifierMask);
OptionsDialog::addCallback(handleOptions, this);
@@ -673,23 +682,122 @@ void Viewport::resetKeyboard()
}
keyboard->reset();
+
+ shortcutHandler.reset();
+ shortcutBypass = false;
+ shortcutActive = false;
+ pressedKeys.clear();
}
void Viewport::handleKeyPress(int systemKeyCode,
uint32_t keyCode, uint32_t keySym)
{
- static bool menuRecursion = false;
-
- // Prevent recursion if the menu wants to send its own
- // activation key.
- if (menuKeySym && (keySym == menuKeySym) && !menuRecursion) {
- menuRecursion = true;
- popupContextMenu();
- menuRecursion = false;
- return;
+ pressedKeys.insert(systemKeyCode);
+
+ // Possible keyboard shortcut?
+
+ if (!shortcutBypass) {
+ ShortcutHandler::KeyAction action;
+
+ action = shortcutHandler.handleKeyPress(systemKeyCode, keySym);
+
+ if (action == ShortcutHandler::KeyIgnore) {
+ vlog.debug("Ignoring key press %d => 0x%02x / XK_%s (0x%04x)",
+ systemKeyCode, keyCode, KeySymName(keySym), keySym);
+ return;
+ }
+
+ if (action == ShortcutHandler::KeyShortcut) {
+ std::list<uint32_t> keySyms;
+ std::list<uint32_t>::const_iterator iter;
+
+ // Modifiers can change the KeySym that's been resolved, so we
+ // need to check all possible KeySyms for this physical key, not
+ // just the current one
+ keySyms = keyboard->translateToKeySyms(systemKeyCode);
+
+ // Then we pick the one that matches first
+ keySym = NoSymbol;
+ for (iter = keySyms.begin(); iter != keySyms.end(); iter++) {
+ bool found;
+
+ switch (*iter) {
+ case XK_space:
+ case XK_G:
+ case XK_g:
+ case XK_M:
+ case XK_m:
+ case XK_KP_Enter:
+ case XK_Return:
+ keySym = *iter;
+ found = true;
+ break;
+ default:
+ found = false;
+ break;
+ }
+
+ if (found)
+ break;
+ }
+
+ vlog.debug("Detected shortcut %d => 0x%02x / XK_%s (0x%04x)",
+ systemKeyCode, keyCode, KeySymName(keySym), keySym);
+
+ // Special case which we need to handle first
+ if (keySym == XK_space) {
+ // If another shortcut has already fired, then we're too late as
+ // we've already released the modifier keys
+ if (!shortcutActive) {
+ shortcutBypass = true;
+ shortcutHandler.reset();
+ }
+ return;
+ }
+
+ shortcutActive = true;
+
+ // The remote session won't see any more keys, so release the ones
+ // currently down
+ try {
+ cc->releaseAllKeys();
+ } catch (std::exception& e) {
+ vlog.error("%s", e.what());
+ abort_connection(_("An unexpected error occurred when communicating "
+ "with the server:\n\n%s"), e.what());
+ }
+
+ switch (keySym) {
+ case XK_G:
+ case XK_g:
+ ((DesktopWindow*)window())->grabKeyboard();
+ break;
+ case XK_M:
+ case XK_m:
+ popupContextMenu();
+ break;
+ case XK_KP_Enter:
+ case XK_Return:
+ if (window()->fullscreen_active()) {
+ fullScreen.setParam(false);
+ window()->fullscreen_off();
+ } else {
+ fullScreen.setParam(true);
+ ((DesktopWindow*)window())->fullscreen_on();
+ }
+ break;
+ default:
+ // Unknown/Unused keyboard shortcut
+ break;
+ }
+
+ return;
+ }
}
+ // Normal key, so send to server...
+
if (viewOnly)
return;
@@ -704,6 +812,54 @@ void Viewport::handleKeyPress(int systemKeyCode,
void Viewport::handleKeyRelease(int systemKeyCode)
{
+ pressedKeys.erase(systemKeyCode);
+
+ if (pressedKeys.empty())
+ shortcutActive = false;
+
+ // Possible keyboard shortcut?
+
+ if (!shortcutBypass) {
+ ShortcutHandler::KeyAction action;
+
+ action = shortcutHandler.handleKeyRelease(systemKeyCode);
+
+ if (action == ShortcutHandler::KeyIgnore) {
+ vlog.debug("Ignoring key release %d", systemKeyCode);
+ return;
+ }
+
+ if (action == ShortcutHandler::KeyShortcut) {
+ vlog.debug("Shortcut release %d", systemKeyCode);
+ return;
+ }
+
+ if (action == ShortcutHandler::KeyUnarm) {
+ DesktopWindow *win;
+
+ vlog.debug("Detected shortcut to release grab");
+
+ try {
+ cc->releaseAllKeys();
+ } catch (std::exception& e) {
+ vlog.error("%s", e.what());
+ abort_connection(_("An unexpected error occurred when communicating "
+ "with the server:\n\n%s"), e.what());
+ }
+
+ win = dynamic_cast<DesktopWindow*>(window());
+ assert(win);
+ win->ungrabKeyboard();
+
+ return;
+ }
+ }
+
+ if (pressedKeys.empty())
+ shortcutBypass = false;
+
+ // Normal key, so send to server...
+
if (viewOnly)
return;
@@ -766,16 +922,6 @@ void Viewport::initContextMenu()
0, nullptr, (void*)ID_ALT,
FL_MENU_TOGGLE | (menuAltKey?FL_MENU_VALUE:0));
- if (menuKeySym) {
- char sendMenuKey[64];
- snprintf(sendMenuKey, 64, p_("ContextMenu|", "Send %s"),
- menuKey.getValueStr().c_str());
- fltk_menu_add(contextMenu, sendMenuKey, 0, nullptr, (void*)ID_MENUKEY, 0);
- fltk_menu_add(contextMenu, "Secret shortcut menu key",
- menuKeyFLTK, nullptr,
- (void*)ID_MENUKEY, FL_MENU_INVISIBLE);
- }
-
fltk_menu_add(contextMenu, p_("ContextMenu|", "Send Ctrl-Alt-&Del"),
0, nullptr, (void*)ID_CTRLALTDEL, FL_MENU_DIVIDER);
@@ -786,7 +932,7 @@ void Viewport::initContextMenu()
0, nullptr, (void*)ID_OPTIONS, 0);
fltk_menu_add(contextMenu, p_("ContextMenu|", "Connection &info..."),
0, nullptr, (void*)ID_INFO, 0);
- fltk_menu_add(contextMenu, p_("ContextMenu|", "About &TigerVNC viewer..."),
+ fltk_menu_add(contextMenu, p_("ContextMenu|", "About &TigerVNC..."),
0, nullptr, (void*)ID_ABOUT, 0);
}
#pragma GCC diagnostic pop
@@ -809,11 +955,11 @@ void Viewport::popupContextMenu()
window()->cursor(FL_CURSOR_DEFAULT);
// FLTK also doesn't switch focus properly for menus
- handle(FL_UNFOCUS);
+ Fl::handle(FL_UNFOCUS, window());
m = contextMenu->popup();
- handle(FL_FOCUS);
+ Fl::handle(FL_FOCUS, window());
// Back to our proper mouse pointer.
if (Fl::belowmouse() == this)
@@ -860,10 +1006,6 @@ void Viewport::popupContextMenu()
handleKeyRelease(FAKE_ALT_KEY_CODE);
menuAltKey = !menuAltKey;
break;
- case ID_MENUKEY:
- handleKeyPress(FAKE_KEY_CODE, menuKeyCode, menuKeySym);
- handleKeyRelease(FAKE_KEY_CODE);
- break;
case ID_CTRLALTDEL:
handleKeyPress(FAKE_CTRL_KEY_CODE, 0x1d, XK_Control_L);
handleKeyPress(FAKE_ALT_KEY_CODE, 0x38, XK_Alt_L);
@@ -892,18 +1034,17 @@ void Viewport::popupContextMenu()
}
}
-
-void Viewport::setMenuKey()
-{
- getMenuKey(&menuKeyFLTK, &menuKeyCode, &menuKeySym);
-}
-
-
void Viewport::handleOptions(void *data)
{
Viewport *self = (Viewport*)data;
+ unsigned modifierMask;
+
+ modifierMask = 0;
+ for (core::EnumListEntry key : shortcutModifiers)
+ modifierMask |= ShortcutHandler::parseModifier(key.getValueStr().c_str());
+
+ self->shortcutHandler.setModifiers(modifierMask);
- self->setMenuKey();
if (Fl::belowmouse() == self)
self->showCursor();
}