diff options
author | Stephen Thom <sthom@williamhill.co.uk> | 2020-06-05 14:12:19 +0000 |
---|---|---|
committer | Daniel Berrange <berrange@redhat.com> | 2020-06-05 14:12:19 +0000 |
commit | fedaa5f89a758aacd5353feb5695314302c3ab35 (patch) | |
tree | 6577d0ed9de8200647f0d6bf03ea955a4d2e94d2 | |
parent | 27ea968c70dc91ecef9fc8d97815aa15607f990e (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.pod | 22 | ||||
-rw-r--r-- | man/virt-viewer.pod | 22 | ||||
-rw-r--r-- | src/virt-viewer-app.c | 98 | ||||
-rw-r--r-- | src/virt-viewer-app.h | 7 | ||||
-rw-r--r-- | src/virt-viewer-window.c | 63 |
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) & |