summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephen Thom <sthom@williamhill.co.uk>2020-06-05 14:12:19 +0000
committerDaniel Berrange <berrange@redhat.com>2020-06-05 14:12:19 +0000
commitfedaa5f89a758aacd5353feb5695314302c3ab35 (patch)
tree6577d0ed9de8200647f0d6bf03ea955a4d2e94d2
parent27ea968c70dc91ecef9fc8d97815aa15607f990e (diff)
Added -K --keymap commandline option that allows user to block certain keypresses or to remap keypresses being sent to the underlying spice or vnc widget
Signed-off-by: Stephen Thom <sthom@williamhill.co.uk>
-rw-r--r--man/remote-viewer.pod22
-rw-r--r--man/virt-viewer.pod22
-rw-r--r--src/virt-viewer-app.c98
-rw-r--r--src/virt-viewer-app.h7
-rw-r--r--src/virt-viewer-window.c63
5 files changed, 207 insertions, 5 deletions
diff --git a/man/remote-viewer.pod b/man/remote-viewer.pod
index 9762233..614fa5e 100644
--- a/man/remote-viewer.pod
+++ b/man/remote-viewer.pod
@@ -81,6 +81,28 @@ Note that hotkeys for which no binding is given are disabled. Although the
hotkeys specified here are handled by the client, it is still possible to send
these key combinations to the guest via a menu item.
+=item -K, --keymap
+
+Remap and/or block supplied keypresses to the host. All key identifiers are
+case-sensitive and follow the naming convention as defined in gdkkeysyms.h
+without the GDK_KEY_ prefix.
+
+Running the application with --debug will display keypress symbols in the
+following way:
+ "Key pressed was keycode='0x63', gdk_keyname='c'"
+ "Key pressed was keycode='0xffeb', gdk_keyname='Super_L'"
+
+The format for supplying a keymap is:
+<srcKeySym1>=[<destKeySym1>][+<destKeySym2][,<srckeySym2>=[<destKeySym1]
+
+To block a keypress simply assign an empty parameter to the srcKeySym.
+
+Example:
+ --keymap=Super_L=,Alt_L=,1=Shift_L+F1,2=Shift_L+F2
+
+This will block the Super_L (typically Windows Key) and ALT_L keypresses
+and remap key 1 to Shift F1, 2 to Shift F2.
+
=item -k, --kiosk
Start in kiosk mode. In this mode, the application will start in
diff --git a/man/virt-viewer.pod b/man/virt-viewer.pod
index 010a920..5ef3aa7 100644
--- a/man/virt-viewer.pod
+++ b/man/virt-viewer.pod
@@ -101,6 +101,28 @@ Note that hotkeys for which no binding is given are disabled. Although the
hotkeys specified here are handled by the client, it is still possible to send
these key combinations to the guest via a menu item.
+=item -K, --keymap
+
+Remap and/or block supplied keypresses to the host. All key identifiers are
+case-sensitive and follow the naming convention as defined in gdkkeysyms.h
+without the GDK_KEY_ prefix.
+
+Running the application with --debug will display keypress symbols in the
+following way:
+ "Key pressed was keycode='0x63', gdk_keyname='c'"
+ "Key pressed was keycode='0xffeb', gdk_keyname='Super_L'"
+
+The format for supplying a keymap is:
+<srcKeySym1>=[<destKeySym1>][+<destKeySym2][,<srckeySym2>=[<destKeySym1]
+
+To block a keypress simply assign an empty parameter to the srcKeySym.
+
+Example:
+ --keymap=Super_L=,Alt_L=,1=Shift_L+F1,2=Shift_L+F2
+
+This will block the Super_L (typically Windows Key) and ALT_L keypresses
+and remap key 1 to Shift F1, 2 to Shift F2.
+
=item -k, --kiosk
Start in kiosk mode. In this mode, the application will start in
diff --git a/src/virt-viewer-app.c b/src/virt-viewer-app.c
index 12ee09d..9801d79 100644
--- a/src/virt-viewer-app.c
+++ b/src/virt-viewer-app.c
@@ -163,6 +163,7 @@ struct _VirtViewerAppPrivate {
GdkModifierType remove_smartcard_accel_mods;
gboolean quit_on_disconnect;
gboolean supports_share_clipboard;
+ VirtViewerKeyMapping *keyMappings;
};
@@ -568,6 +569,93 @@ void virt_viewer_app_set_uuid_string(VirtViewerApp *self, const gchar *uuid_stri
virt_viewer_app_apply_monitor_mapping(self);
}
+static
+void virt_viewer_app_set_keymap(VirtViewerApp *self, const gchar *keymap_string)
+{
+ gchar **key, **keymaps, **valkey, **valuekeys = NULL;
+ VirtViewerKeyMapping *keyMappingArray, *keyMappingPtr;
+ guint *mappedArray, *ptrMove;
+
+ if (keymap_string == NULL) {
+ g_debug("keymap string is empty - nothing to do");
+ self->priv->keyMappings = NULL;
+ return;
+ }
+
+ g_debug("keymap string set to %s", keymap_string);
+
+ g_return_if_fail(VIRT_VIEWER_IS_APP(self));
+
+ g_debug("keymap command-line set to %s", keymap_string);
+ if (keymap_string) {
+ keymaps = g_strsplit(keymap_string, ",", -1);
+ }
+
+ if (!keymaps || g_strv_length(keymaps) == 0) {
+ g_strfreev(keymaps);
+ return;
+ }
+
+ keyMappingPtr = keyMappingArray = g_new0(VirtViewerKeyMapping, g_strv_length(keymaps));
+
+ g_debug("Allocated %d number of mappings", g_strv_length(keymaps));
+
+ for (key = keymaps; *key != NULL; key++) {
+ gchar *srcKey = strstr(*key, "=");
+ const gchar *value = (srcKey == NULL) ? NULL : (*srcKey = '\0', srcKey + 1);
+ if (value == NULL) {
+ g_warning("Missing mapping value for key '%s'", srcKey);
+ continue;
+ }
+
+ // Key value must be resolved to GDK key code
+ // along with mapped key which can also be void (for no action)
+ guint kcode;
+ kcode = gdk_keyval_from_name(*key);
+ if (kcode == GDK_KEY_VoidSymbol) {
+ g_warning("Unable to lookup '%s' key", *key);
+ continue;
+ }
+ g_debug("Mapped source key '%s' to %x", *key, kcode);
+
+ valuekeys = g_strsplit(value, "+", -1);
+
+ keyMappingPtr->sourceKey = kcode;
+ keyMappingPtr->numMappedKeys = g_strv_length(valuekeys);
+ keyMappingPtr->isLast = FALSE;
+
+ if (!valuekeys || g_strv_length(valuekeys) == 0) {
+ g_debug("No value set for key '%s' it will be blocked", *key);
+ keyMappingPtr->mappedKeys = NULL;
+ keyMappingPtr++;
+ g_strfreev(valuekeys);
+ continue;
+ }
+
+ ptrMove = mappedArray = g_new0(guint, g_strv_length(valuekeys));
+
+ guint mcode;
+ for (valkey = valuekeys; *valkey != NULL; valkey++) {
+ g_debug("Value key to map '%s'", *valkey);
+ mcode = gdk_keyval_from_name(*valkey);
+ if (mcode == GDK_KEY_VoidSymbol) {
+ g_warning("Unable to lookup mapped key '%s' it will be ignored", *valkey);
+ }
+ g_debug("Mapped dest key '%s' to %x", *valkey, mcode);
+ *ptrMove++ = mcode;
+ }
+ keyMappingPtr->mappedKeys = mappedArray;
+ keyMappingPtr++;
+ g_strfreev(valuekeys);
+
+ }
+ keyMappingPtr--;
+ keyMappingPtr->isLast=TRUE;
+
+ self->priv->keyMappings = keyMappingArray;
+ g_strfreev(keymaps);
+}
+
void
virt_viewer_app_maybe_quit(VirtViewerApp *self, VirtViewerWindow *window)
{
@@ -980,6 +1068,11 @@ virt_viewer_app_window_new(VirtViewerApp *self, gint nth)
g_signal_connect(w, "hide", G_CALLBACK(viewer_window_visible_cb), self);
g_signal_connect(w, "show", G_CALLBACK(viewer_window_visible_cb), self);
+
+ if (self->priv->keyMappings) {
+ g_object_set(window, "keymap", self->priv->keyMappings, NULL);
+ }
+
return window;
}
@@ -1880,6 +1973,7 @@ gboolean virt_viewer_app_start(VirtViewerApp *self, GError **error)
static int opt_zoom = NORMAL_ZOOM_LEVEL;
static gchar *opt_hotkeys = NULL;
+static gchar *opt_keymap = NULL;
static gboolean opt_version = FALSE;
static gboolean opt_verbose = FALSE;
static gboolean opt_debug = FALSE;
@@ -2011,6 +2105,8 @@ virt_viewer_app_on_application_startup(GApplication *app)
virt_viewer_app_set_debug(opt_debug);
virt_viewer_app_set_fullscreen(self, opt_fullscreen);
+ virt_viewer_app_set_keymap(self, opt_keymap);
+
self->priv->verbose = opt_verbose;
self->priv->quit_on_disconnect = opt_kiosk ? opt_kiosk_quit : TRUE;
@@ -2844,6 +2940,8 @@ virt_viewer_app_add_option_entries(G_GNUC_UNUSED VirtViewerApp *self,
N_("Open in full screen mode (adjusts guest resolution to fit the client)"), NULL },
{ "hotkeys", 'H', 0, G_OPTION_ARG_STRING, &opt_hotkeys,
N_("Customise hotkeys"), NULL },
+ { "keymap", 'K', 0, G_OPTION_ARG_STRING, &opt_keymap,
+ N_("Remap keys format key=keymod+key e.g. F1=SHIFT+CTRL+F1,1=SHIFT+F1,ALT_L=Void"), NULL },
{ "kiosk", 'k', 0, G_OPTION_ARG_NONE, &opt_kiosk,
N_("Enable kiosk mode"), NULL },
{ "kiosk-quit", '\0', 0, G_OPTION_ARG_CALLBACK, option_kiosk_quit,
diff --git a/src/virt-viewer-app.h b/src/virt-viewer-app.h
index a39395f..a69101a 100644
--- a/src/virt-viewer-app.h
+++ b/src/virt-viewer-app.h
@@ -44,6 +44,13 @@ typedef struct {
} VirtViewerApp;
typedef struct {
+ guint sourceKey;
+ guint numMappedKeys;
+ guint *mappedKeys;
+ gboolean isLast;
+} VirtViewerKeyMapping;
+
+typedef struct {
GtkApplicationClass parent_class;
/*< private >*/
diff --git a/src/virt-viewer-window.c b/src/virt-viewer-window.c
index 80017d1..928658f 100644
--- a/src/virt-viewer-window.c
+++ b/src/virt-viewer-window.c
@@ -86,6 +86,7 @@ enum {
PROP_DISPLAY,
PROP_SUBTITLE,
PROP_APP,
+ PROP_KEYMAP,
};
struct _VirtViewerWindowPrivate {
@@ -114,6 +115,7 @@ struct _VirtViewerWindowPrivate {
gboolean fullscreen;
gchar *subtitle;
gboolean initial_zoom_set;
+ VirtViewerKeyMapping *keyMappings;
};
G_DEFINE_TYPE_WITH_PRIVATE (VirtViewerWindow, virt_viewer_window, G_TYPE_OBJECT)
@@ -165,6 +167,11 @@ virt_viewer_window_set_property (GObject *object, guint property_id,
priv->app = g_value_get_object(value);
break;
+ case PROP_KEYMAP:
+ g_free(priv->keyMappings);
+ priv->keyMappings = (VirtViewerKeyMapping *)g_value_get_pointer(value);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
@@ -313,6 +320,14 @@ virt_viewer_window_class_init (VirtViewerWindowClass *klass)
G_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property(object_class,
+ PROP_KEYMAP,
+ g_param_spec_pointer("keymap",
+ "keymap",
+ "Remapped keys",
+ G_PARAM_WRITABLE |
+ G_PARAM_STATIC_STRINGS));
+
}
static gboolean
@@ -1484,13 +1499,51 @@ display_show_hint(VirtViewerDisplay *display,
}
static gboolean
window_key_pressed (GtkWidget *widget G_GNUC_UNUSED,
- GdkEvent *event,
- GtkWidget *display)
+ GdkEvent *ev,
+ VirtViewerWindow *self)
{
- gtk_widget_grab_focus(display);
- return gtk_widget_event(display, event);
+ GdkEventKey *event;
+ VirtViewerWindowPrivate *priv;
+ VirtViewerDisplay *display;
+ priv = self->priv;
+ display = priv->display;
+ event = (GdkEventKey *)ev;
+
+ gtk_widget_grab_focus(GTK_WIDGET(display));
+
+ // Look through keymaps - if set for mappings and intercept
+ if (priv->keyMappings) {
+ VirtViewerKeyMapping *ptr, *matched;
+ ptr = priv->keyMappings;
+ matched = NULL;
+ do {
+ if (event->keyval == ptr->sourceKey) {
+ matched = ptr;
+ }
+ if (ptr->isLast) {
+ break;
+ }
+ ptr++;
+ } while (matched == NULL);
+
+ if (matched) {
+ if (matched->mappedKeys == NULL) {
+ // Key to be ignored and not pass through to VM
+ g_debug("Blocking keypress '%s'", gdk_keyval_name(matched->sourceKey));
+ } else {
+ g_debug("Sending through mapped keys");
+ virt_viewer_display_send_keys(display,
+ matched->mappedKeys, matched->numMappedKeys);
+ }
+ return TRUE;
+ }
+
+ }
+ g_debug("Key pressed was keycode='0x%x', gdk_keyname='%s'", event->keyval, gdk_keyval_name(event->keyval));
+ return gtk_widget_event(GTK_WIDGET(display), ev);
}
+
void
virt_viewer_window_set_display(VirtViewerWindow *self, VirtViewerDisplay *display)
{
@@ -1517,7 +1570,7 @@ virt_viewer_window_set_display(VirtViewerWindow *self, VirtViewerDisplay *displa
gtk_widget_realize(GTK_WIDGET(display));
virt_viewer_signal_connect_object(priv->window, "key-press-event",
- G_CALLBACK(window_key_pressed), display, 0);
+ G_CALLBACK(window_key_pressed), self, 0);
/* switch back to non-display if not ready */
if (!(virt_viewer_display_get_show_hint(display) &