[virt-tools-list] [PATCH 2/2] Handle release-cursor hotkey in VNC/Spice instead of GTK

Paul Donohue virt-tools at PaulSD.com
Fri Aug 3 23:59:13 UTC 2018


This avoids GTK accelerator conflicts, which allows the use of some
hotkey sequences that are rejected by GTK.

This also works around the fact that keyboard grabs are not released
by spice_display_mouse_ungrab().
(See https://gitlab.freedesktop.org/spice/spice-gtk/issues/73 )

Signed-off-by: Paul Donohue <virt-tools at PaulSD.com>
---
 src/virt-viewer-app.c           |  37 +++++++++-
 src/virt-viewer-app.h           |   2 +
 src/virt-viewer-display-spice.c |  16 ++--
 src/virt-viewer-display-vnc.c   |  16 ++--
 src/virt-viewer-file.c          |   7 +-
 src/virt-viewer-util.c          | 127 ++++++++++++++++++++++++++++++++
 src/virt-viewer-util.h          |   1 +
 src/virt-viewer-window.c        |  17 +----
 8 files changed, 192 insertions(+), 31 deletions(-)

diff --git a/src/virt-viewer-app.c b/src/virt-viewer-app.c
index 3a31bbb..69ac331 100644
--- a/src/virt-viewer-app.c
+++ b/src/virt-viewer-app.c
@@ -149,6 +149,7 @@ struct _VirtViewerAppPrivate {
     GKeyFile *config;
     gchar *config_file;
 
+    gchar *release_cursor_hotkey;
     guint insert_smartcard_accel_key;
     GdkModifierType insert_smartcard_accel_mods;
     guint remove_smartcard_accel_key;
@@ -1687,6 +1688,8 @@ virt_viewer_app_dispose (GObject *object)
     g_clear_pointer(&priv->config, g_key_file_free);
     g_clear_pointer(&priv->initial_display_map, g_hash_table_unref);
 
+    virt_viewer_app_set_release_cursor_hotkey(self, NULL);
+
     virt_viewer_app_free_connect_info(self);
 
     G_OBJECT_CLASS (virt_viewer_app_parent_class)->dispose (object);
@@ -1842,10 +1845,11 @@ virt_viewer_app_on_application_startup(GApplication *app)
 
     virt_viewer_window_set_zoom_level(self->priv->main_window, opt_zoom);
 
+    if (!virt_viewer_app_get_enable_accel(self))
+        virt_viewer_app_set_release_cursor_hotkey(self, "Shift+F12");
     virt_viewer_set_insert_smartcard_accel(self, GDK_KEY_F8, GDK_SHIFT_MASK);
     virt_viewer_set_remove_smartcard_accel(self, GDK_KEY_F9, GDK_SHIFT_MASK);
     gtk_accel_map_add_entry("<virt-viewer>/view/toggle-fullscreen", GDK_KEY_F11, 0);
-    gtk_accel_map_add_entry("<virt-viewer>/view/release-cursor", GDK_KEY_F12, GDK_SHIFT_MASK);
     gtk_accel_map_add_entry("<virt-viewer>/view/zoom-reset", GDK_KEY_0, GDK_CONTROL_MASK);
     gtk_accel_map_add_entry("<virt-viewer>/view/zoom-out", GDK_KEY_minus, GDK_CONTROL_MASK);
     gtk_accel_map_add_entry("<virt-viewer>/view/zoom-in", GDK_KEY_plus, GDK_CONTROL_MASK);
@@ -2060,8 +2064,8 @@ void
 virt_viewer_app_clear_hotkeys(VirtViewerApp *self)
 {
     /* Disable default bindings and replace them with our own */
+    virt_viewer_app_set_release_cursor_hotkey(self, NULL);
     gtk_accel_map_change_entry("<virt-viewer>/view/toggle-fullscreen", 0, 0, TRUE);
-    gtk_accel_map_change_entry("<virt-viewer>/view/release-cursor", 0, 0, TRUE);
     gtk_accel_map_change_entry("<virt-viewer>/view/zoom-reset", 0, 0, TRUE);
     gtk_accel_map_change_entry("<virt-viewer>/view/zoom-in", 0, 0, TRUE);
     gtk_accel_map_change_entry("<virt-viewer>/view/zoom-out", 0, 0, TRUE);
@@ -2077,6 +2081,20 @@ virt_viewer_app_set_enable_accel(VirtViewerApp *self, gboolean enable)
     g_object_notify(G_OBJECT(self), "enable-accel");
 }
 
+void
+virt_viewer_app_set_release_cursor_hotkey(VirtViewerApp *self, const gchar *hotkey_str)
+{
+    if (self->priv->release_cursor_hotkey)
+        g_free(self->priv->release_cursor_hotkey);
+    if (!hotkey_str) {
+      self->priv->release_cursor_hotkey = NULL;
+      return;
+    }
+    gchar *display_hotkey = spice_hotkey_to_display_hotkey(hotkey_str);
+    self->priv->release_cursor_hotkey = g_strdup(display_hotkey);
+    g_free(display_hotkey);
+}
+
 void
 virt_viewer_app_set_hotkeys(VirtViewerApp *self, const gchar *hotkeys_str)
 {
@@ -2103,6 +2121,11 @@ virt_viewer_app_set_hotkeys(VirtViewerApp *self, const gchar *hotkeys_str)
             continue;
         }
 
+        if (g_str_equal(*hotkey, "release-cursor")) {
+            virt_viewer_app_set_release_cursor_hotkey(self, value);
+            continue;
+        }
+
         gchar *accel = spice_hotkey_to_gtk_accelerator(value);
         guint accel_key;
         GdkModifierType accel_mods;
@@ -2117,8 +2140,6 @@ virt_viewer_app_set_hotkeys(VirtViewerApp *self, const gchar *hotkeys_str)
         gboolean status = TRUE;
         if (g_str_equal(*hotkey, "toggle-fullscreen")) {
             status = gtk_accel_map_change_entry("<virt-viewer>/view/toggle-fullscreen", accel_key, accel_mods, TRUE);
-        } else if (g_str_equal(*hotkey, "release-cursor")) {
-            status = gtk_accel_map_change_entry("<virt-viewer>/view/release-cursor", accel_key, accel_mods, TRUE);
         } else if (g_str_equal(*hotkey, "secure-attention")) {
             status = gtk_accel_map_change_entry("<virt-viewer>/send/secure-attention", accel_key, accel_mods, TRUE);
         } else if (g_str_equal(*hotkey, "smartcard-insert")) {
@@ -2470,6 +2491,14 @@ virt_viewer_app_get_enable_accel(VirtViewerApp *self)
     return self->priv->enable_accel;
 }
 
+gchar*
+virt_viewer_app_get_release_cursor_hotkey(VirtViewerApp *self)
+{
+    g_return_val_if_fail(VIRT_VIEWER_IS_APP(self), FALSE);
+
+    return self->priv->release_cursor_hotkey;
+}
+
 VirtViewerSession*
 virt_viewer_app_get_session(VirtViewerApp *self)
 {
diff --git a/src/virt-viewer-app.h b/src/virt-viewer-app.h
index 16b1c8c..e4828d0 100644
--- a/src/virt-viewer-app.h
+++ b/src/virt-viewer-app.h
@@ -89,12 +89,14 @@ void virt_viewer_app_show_status(VirtViewerApp *self, const gchar *fmt, ...);
 void virt_viewer_app_show_display(VirtViewerApp *self);
 GList* virt_viewer_app_get_windows(VirtViewerApp *self);
 gboolean virt_viewer_app_get_enable_accel(VirtViewerApp *self);
+gchar* virt_viewer_app_get_release_cursor_hotkey(VirtViewerApp *self);
 VirtViewerSession* virt_viewer_app_get_session(VirtViewerApp *self);
 gboolean virt_viewer_app_get_fullscreen(VirtViewerApp *app);
 void virt_viewer_app_clear_hotkeys(VirtViewerApp *app);
 GList* virt_viewer_app_get_initial_displays(VirtViewerApp* self);
 gint virt_viewer_app_get_initial_monitor_for_display(VirtViewerApp* self, gint display);
 void virt_viewer_app_set_enable_accel(VirtViewerApp *app, gboolean enable);
+void virt_viewer_app_set_release_cursor_hotkey(VirtViewerApp *app, const gchar *hotkey_str);
 void virt_viewer_app_show_preferences(VirtViewerApp *app, GtkWidget *parent);
 void virt_viewer_app_set_menus_sensitive(VirtViewerApp *self, gboolean sensitive);
 gboolean virt_viewer_app_get_session_cancelled(VirtViewerApp *self);
diff --git a/src/virt-viewer-display-spice.c b/src/virt-viewer-display-spice.c
index f9b383b..075acb2 100644
--- a/src/virt-viewer-display-spice.c
+++ b/src/virt-viewer-display-spice.c
@@ -243,17 +243,21 @@ enable_accel_changed(VirtViewerApp *app,
                      GParamSpec *pspec G_GNUC_UNUSED,
                      VirtViewerDisplaySpice *self)
 {
-    GtkAccelKey key = {0, 0, 0};
-    if (virt_viewer_app_get_enable_accel(app))
-        gtk_accel_map_lookup_entry("<virt-viewer>/view/release-cursor", &key);
+    if (!virt_viewer_app_get_enable_accel(app)) {
+        spice_display_set_grab_keys(self->priv->display, NULL);
+        return;
+    }
 
-    if (key.accel_key || key.accel_mods) {
+    gchar *seq_str = virt_viewer_app_get_release_cursor_hotkey(app);
+    if (seq_str) {
+        SpiceGrabSequence *seq = spice_grab_sequence_new_from_string(seq_str);
+        spice_display_set_grab_keys(self->priv->display, seq);
+        spice_grab_sequence_free(seq);
+    } else {
         SpiceGrabSequence *seq = spice_grab_sequence_new(0, NULL);
         /* disable default grab sequence */
         spice_display_set_grab_keys(self->priv->display, seq);
         spice_grab_sequence_free(seq);
-    } else {
-        spice_display_set_grab_keys(self->priv->display, NULL);
     }
 }
 
diff --git a/src/virt-viewer-display-vnc.c b/src/virt-viewer-display-vnc.c
index c200ac2..12a17e7 100644
--- a/src/virt-viewer-display-vnc.c
+++ b/src/virt-viewer-display-vnc.c
@@ -191,17 +191,21 @@ enable_accel_changed(VirtViewerApp *app,
                      GParamSpec *pspec G_GNUC_UNUSED,
                      VncDisplay *vnc)
 {
-    GtkAccelKey key = {0, 0, 0};
-    if (virt_viewer_app_get_enable_accel(app))
-        gtk_accel_map_lookup_entry("<virt-viewer>/view/release-cursor", &key);
+    if (!virt_viewer_app_get_enable_accel(app)) {
+        vnc_display_set_grab_keys(vnc, NULL);
+        return;
+    }
 
-    if (key.accel_key || key.accel_mods) {
+    gchar *seq_str = virt_viewer_app_get_release_cursor_hotkey(app);
+    if (seq_str) {
+        VncGrabSequence *seq = vnc_grab_sequence_new_from_string(seq_str);
+        vnc_display_set_grab_keys(vnc, seq);
+        vnc_grab_sequence_free(seq);
+    } else {
         VncGrabSequence *seq = vnc_grab_sequence_new(0, NULL);
         /* disable default grab sequence */
         vnc_display_set_grab_keys(vnc, seq);
         vnc_grab_sequence_free(seq);
-    } else {
-        vnc_display_set_grab_keys(vnc, NULL);
     }
 }
 
diff --git a/src/virt-viewer-file.c b/src/virt-viewer-file.c
index 1b0c310..7e7bfe0 100644
--- a/src/virt-viewer-file.c
+++ b/src/virt-viewer-file.c
@@ -902,7 +902,6 @@ virt_viewer_file_fill_app(VirtViewerFile* self, VirtViewerApp *app, GError **err
             const char *prop;
             const char *accel;
         } accels[] = {
-            { "release-cursor", "<virt-viewer>/view/release-cursor" },
             { "toggle-fullscreen", "<virt-viewer>/view/toggle-fullscreen" },
             { "smartcard-insert", "<virt-viewer>/file/smartcard-insert" },
             { "smartcard-remove", "<virt-viewer>/file/smartcard-remove" },
@@ -917,6 +916,12 @@ virt_viewer_file_fill_app(VirtViewerFile* self, VirtViewerApp *app, GError **err
             spice_hotkey_set_accel(accels[i].accel, val);
             g_free(val);
         }
+
+        if (virt_viewer_file_is_set(self, "release-cursor")) {
+            g_object_get(self, "release-cursor", &val, NULL);
+            virt_viewer_app_set_release_cursor_hotkey(app, val);
+            g_free(val);
+        }
     }
 
     virt_viewer_app_set_enable_accel(app, TRUE);
diff --git a/src/virt-viewer-util.c b/src/virt-viewer-util.c
index c0ccece..1450020 100644
--- a/src/virt-viewer-util.c
+++ b/src/virt-viewer-util.c
@@ -434,6 +434,133 @@ spice_hotkey_to_gtk_accelerator(const gchar *key)
     return accel;
 }
 
+static gchar *
+spice_key_to_gdk_key(const gchar *spice_key)
+{
+    guint i;
+    gchar *key = g_strdup(spice_key);
+
+    static const struct {
+        const char *spice;
+        const char *gdk;
+    } keys[] = {
+
+        { "alt", "Alt_L" },
+        { "lalt", "Alt_L" },
+        { "leftalt", "Alt_L" },
+        { "left-alt", "Alt_L" },
+        { "ralt", "Alt_R" },
+        { "rightalt", "Alt_R" },
+        { "right-alt", "Alt_R" },
+
+        { "ctrl", "Control_L" },
+        { "ctl", "Control_L" },
+        { "control", "Control_L" },
+        { "lctrl", "Control_L" },
+        { "leftctrl", "Control_L" },
+        { "left-ctrl", "Control_L" },
+        { "rctrl", "Control_R" },
+        { "rightctrl", "Control_R" },
+        { "right-ctrl", "Control_R" },
+
+        { "cmd", "Control_L" },
+        { "lcmd", "Control_L" },
+        { "leftcmd", "Control_L" },
+        { "left-cmd", "Control_L" },
+        { "rcmd", "Control_R" },
+        { "rightcmd", "Control_R" },
+        { "right-cmd", "Control_R" },
+
+        { "shift", "Shift_L" },
+        { "shft", "Shift_L" },
+        { "lshift", "Shift_L" },
+        { "leftshift", "Shift_L" },
+        { "left-shift", "Shift_L" },
+        { "rshift", "Shift_R" },
+        { "rightshift", "Shift_R" },
+        { "right-shift", "Shift_R" },
+
+        { "win", "Super_L" },
+        { "lwin", "Super_L" },
+        { "leftwin", "Super_L" },
+        { "left-win", "Super_L" },
+        { "rwin", "Super_R" },
+        { "rightwin", "Super_R" },
+        { "right-win", "Super_R" },
+
+        { "super", "Super_L" },
+        { "hyper", "Hyper_L" },
+        { "meta", "Meta_L" },
+
+        { "esc", "Escape" },
+        { "escape", "Escape" },
+        { "ins", "Insert" },
+        { "insert", "Insert" },
+        { "del", "Delete" },
+        { "delete", "Delete" },
+
+        { "pgup", "Page_Up" },
+        { "pageup", "Page_Up" },
+        { "pgdn", "Page_Down" },
+        { "pagedown", "Page_Down" },
+
+        { "home", "Home" },
+        { "end", "End" },
+        { "space", "space" },
+
+        { "enter", "Return" },
+
+        { "tab", "Tab" },
+        { "f1", "F1" },
+        { "f2", "F2" },
+        { "f3", "F3" },
+        { "f4", "F4" },
+        { "f5", "F5" },
+        { "f6", "F6" },
+        { "f7", "F7" },
+        { "f8", "F8" },
+        { "f9", "F9" },
+        { "f10", "F10" },
+        { "f11", "F11" },
+        { "f12", "F12" },
+    };
+
+    if (key[0] == '<' && key[strlen(key)-1] == '>') {
+        gchar *tmp = key;
+        key = g_strndup(key+1, strlen(key)-2);
+        g_free(tmp);
+    }
+
+    for (i = 0; i < G_N_ELEMENTS(keys); ++i) {
+        if (g_ascii_strcasecmp(keys[i].spice, key) == 0) {
+            g_free(key);
+            return g_strdup(keys[i].gdk);
+        }
+    }
+
+    return key;
+}
+
+gchar*
+spice_hotkey_to_display_hotkey(const gchar *key)
+{
+    gchar *new_key, **k, **keyv;
+
+    keyv = g_strsplit(key, "+", -1);
+    g_return_val_if_fail(keyv != NULL, NULL);
+
+    for (k = keyv; *k != NULL; k++) {
+        gchar *tmp = *k;
+        *k = spice_key_to_gdk_key(tmp);
+        g_free(tmp);
+    }
+
+    new_key = g_strjoinv(NULL, keyv);
+    g_strfreev(keyv);
+
+    return new_key;
+}
+
 static gboolean str_is_empty(const gchar *str)
 {
   return ((str == NULL) || (str[0] == '\0'));
diff --git a/src/virt-viewer-util.h b/src/virt-viewer-util.h
index 5515f66..2cc452f 100644
--- a/src/virt-viewer-util.h
+++ b/src/virt-viewer-util.h
@@ -55,6 +55,7 @@ gulong virt_viewer_signal_connect_object(gpointer instance,
                                          GConnectFlags connect_flags);
 
 gchar* spice_hotkey_to_gtk_accelerator(const gchar *key);
+gchar* spice_hotkey_to_display_hotkey(const gchar *key);
 gint virt_viewer_compare_buildid(const gchar *s1, const gchar *s2);
 
 /* monitor alignment */
diff --git a/src/virt-viewer-window.c b/src/virt-viewer-window.c
index af3441f..938a466 100644
--- a/src/virt-viewer-window.c
+++ b/src/virt-viewer-window.c
@@ -1266,21 +1266,10 @@ virt_viewer_window_update_title(VirtViewerWindow *self)
     gchar *ungrab = NULL;
 
     if (priv->grabbed) {
-        gchar *label;
-        GtkAccelKey key = {0, 0, 0};
-
-        if (virt_viewer_app_get_enable_accel(priv->app))
-            gtk_accel_map_lookup_entry("<virt-viewer>/view/release-cursor", &key);
-
-        if (key.accel_key || key.accel_mods) {
-            g_debug("release-cursor accel key: key=%u, mods=%x, flags=%u", key.accel_key, key.accel_mods, key.accel_flags);
-            label = gtk_accelerator_get_label(key.accel_key, key.accel_mods);
-        } else {
-            label = g_strdup(_("Ctrl+Alt"));
-        }
-
+        gchar *label = virt_viewer_app_get_release_cursor_hotkey(priv->app);
+        if (!label)
+            label = _("Ctrl+Alt");
         ungrab = g_strdup_printf(_("(Press %s to release pointer)"), label);
-        g_free(label);
     }
 
     if (!ungrab && !priv->subtitle)
-- 
2.17.1




More information about the virt-tools-list mailing list