Codebase list virt-viewer / fedaa5f
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> Stephen Thom authored 3 years ago Daniel Berrange committed 3 years ago
5 changed file(s) with 209 addition(s) and 7 deletion(s). Raw diff Collapse all Expand all
8080 hotkeys specified here are handled by the client, it is still possible to send
8181 these key combinations to the guest via a menu item.
8282
83 =item -K, --keymap
84
85 Remap and/or block supplied keypresses to the host. All key identifiers are
86 case-sensitive and follow the naming convention as defined in gdkkeysyms.h
87 without the GDK_KEY_ prefix.
88
89 Running the application with --debug will display keypress symbols in the
90 following way:
91 "Key pressed was keycode='0x63', gdk_keyname='c'"
92 "Key pressed was keycode='0xffeb', gdk_keyname='Super_L'"
93
94 The format for supplying a keymap is:
95 <srcKeySym1>=[<destKeySym1>][+<destKeySym2][,<srckeySym2>=[<destKeySym1]
96
97 To block a keypress simply assign an empty parameter to the srcKeySym.
98
99 Example:
100 --keymap=Super_L=,Alt_L=,1=Shift_L+F1,2=Shift_L+F2
101
102 This will block the Super_L (typically Windows Key) and ALT_L keypresses
103 and remap key 1 to Shift F1, 2 to Shift F2.
104
83105 =item -k, --kiosk
84106
85107 Start in kiosk mode. In this mode, the application will start in
100100 hotkeys specified here are handled by the client, it is still possible to send
101101 these key combinations to the guest via a menu item.
102102
103 =item -K, --keymap
104
105 Remap and/or block supplied keypresses to the host. All key identifiers are
106 case-sensitive and follow the naming convention as defined in gdkkeysyms.h
107 without the GDK_KEY_ prefix.
108
109 Running the application with --debug will display keypress symbols in the
110 following way:
111 "Key pressed was keycode='0x63', gdk_keyname='c'"
112 "Key pressed was keycode='0xffeb', gdk_keyname='Super_L'"
113
114 The format for supplying a keymap is:
115 <srcKeySym1>=[<destKeySym1>][+<destKeySym2][,<srckeySym2>=[<destKeySym1]
116
117 To block a keypress simply assign an empty parameter to the srcKeySym.
118
119 Example:
120 --keymap=Super_L=,Alt_L=,1=Shift_L+F1,2=Shift_L+F2
121
122 This will block the Super_L (typically Windows Key) and ALT_L keypresses
123 and remap key 1 to Shift F1, 2 to Shift F2.
124
103125 =item -k, --kiosk
104126
105127 Start in kiosk mode. In this mode, the application will start in
162162 GdkModifierType remove_smartcard_accel_mods;
163163 gboolean quit_on_disconnect;
164164 gboolean supports_share_clipboard;
165 VirtViewerKeyMapping *keyMappings;
165166 };
166167
167168
567568 virt_viewer_app_apply_monitor_mapping(self);
568569 }
569570
571 static
572 void virt_viewer_app_set_keymap(VirtViewerApp *self, const gchar *keymap_string)
573 {
574 gchar **key, **keymaps, **valkey, **valuekeys = NULL;
575 VirtViewerKeyMapping *keyMappingArray, *keyMappingPtr;
576 guint *mappedArray, *ptrMove;
577
578 if (keymap_string == NULL) {
579 g_debug("keymap string is empty - nothing to do");
580 self->priv->keyMappings = NULL;
581 return;
582 }
583
584 g_debug("keymap string set to %s", keymap_string);
585
586 g_return_if_fail(VIRT_VIEWER_IS_APP(self));
587
588 g_debug("keymap command-line set to %s", keymap_string);
589 if (keymap_string) {
590 keymaps = g_strsplit(keymap_string, ",", -1);
591 }
592
593 if (!keymaps || g_strv_length(keymaps) == 0) {
594 g_strfreev(keymaps);
595 return;
596 }
597
598 keyMappingPtr = keyMappingArray = g_new0(VirtViewerKeyMapping, g_strv_length(keymaps));
599
600 g_debug("Allocated %d number of mappings", g_strv_length(keymaps));
601
602 for (key = keymaps; *key != NULL; key++) {
603 gchar *srcKey = strstr(*key, "=");
604 const gchar *value = (srcKey == NULL) ? NULL : (*srcKey = '\0', srcKey + 1);
605 if (value == NULL) {
606 g_warning("Missing mapping value for key '%s'", srcKey);
607 continue;
608 }
609
610 // Key value must be resolved to GDK key code
611 // along with mapped key which can also be void (for no action)
612 guint kcode;
613 kcode = gdk_keyval_from_name(*key);
614 if (kcode == GDK_KEY_VoidSymbol) {
615 g_warning("Unable to lookup '%s' key", *key);
616 continue;
617 }
618 g_debug("Mapped source key '%s' to %x", *key, kcode);
619
620 valuekeys = g_strsplit(value, "+", -1);
621
622 keyMappingPtr->sourceKey = kcode;
623 keyMappingPtr->numMappedKeys = g_strv_length(valuekeys);
624 keyMappingPtr->isLast = FALSE;
625
626 if (!valuekeys || g_strv_length(valuekeys) == 0) {
627 g_debug("No value set for key '%s' it will be blocked", *key);
628 keyMappingPtr->mappedKeys = NULL;
629 keyMappingPtr++;
630 g_strfreev(valuekeys);
631 continue;
632 }
633
634 ptrMove = mappedArray = g_new0(guint, g_strv_length(valuekeys));
635
636 guint mcode;
637 for (valkey = valuekeys; *valkey != NULL; valkey++) {
638 g_debug("Value key to map '%s'", *valkey);
639 mcode = gdk_keyval_from_name(*valkey);
640 if (mcode == GDK_KEY_VoidSymbol) {
641 g_warning("Unable to lookup mapped key '%s' it will be ignored", *valkey);
642 }
643 g_debug("Mapped dest key '%s' to %x", *valkey, mcode);
644 *ptrMove++ = mcode;
645 }
646 keyMappingPtr->mappedKeys = mappedArray;
647 keyMappingPtr++;
648 g_strfreev(valuekeys);
649
650 }
651 keyMappingPtr--;
652 keyMappingPtr->isLast=TRUE;
653
654 self->priv->keyMappings = keyMappingArray;
655 g_strfreev(keymaps);
656 }
657
570658 void
571659 virt_viewer_app_maybe_quit(VirtViewerApp *self, VirtViewerWindow *window)
572660 {
9791067
9801068 g_signal_connect(w, "hide", G_CALLBACK(viewer_window_visible_cb), self);
9811069 g_signal_connect(w, "show", G_CALLBACK(viewer_window_visible_cb), self);
1070
1071 if (self->priv->keyMappings) {
1072 g_object_set(window, "keymap", self->priv->keyMappings, NULL);
1073 }
1074
9821075 return window;
9831076 }
9841077
18791972
18801973 static int opt_zoom = NORMAL_ZOOM_LEVEL;
18811974 static gchar *opt_hotkeys = NULL;
1975 static gchar *opt_keymap = NULL;
18821976 static gboolean opt_version = FALSE;
18831977 static gboolean opt_verbose = FALSE;
18841978 static gboolean opt_debug = FALSE;
20102104 virt_viewer_app_set_debug(opt_debug);
20112105 virt_viewer_app_set_fullscreen(self, opt_fullscreen);
20122106
2107 virt_viewer_app_set_keymap(self, opt_keymap);
2108
20132109 self->priv->verbose = opt_verbose;
20142110 self->priv->quit_on_disconnect = opt_kiosk ? opt_kiosk_quit : TRUE;
20152111
28432939 N_("Open in full screen mode (adjusts guest resolution to fit the client)"), NULL },
28442940 { "hotkeys", 'H', 0, G_OPTION_ARG_STRING, &opt_hotkeys,
28452941 N_("Customise hotkeys"), NULL },
2942 { "keymap", 'K', 0, G_OPTION_ARG_STRING, &opt_keymap,
2943 N_("Remap keys format key=keymod+key e.g. F1=SHIFT+CTRL+F1,1=SHIFT+F1,ALT_L=Void"), NULL },
28462944 { "kiosk", 'k', 0, G_OPTION_ARG_NONE, &opt_kiosk,
28472945 N_("Enable kiosk mode"), NULL },
28482946 { "kiosk-quit", '\0', 0, G_OPTION_ARG_CALLBACK, option_kiosk_quit,
4141 GtkApplication parent;
4242 VirtViewerAppPrivate *priv;
4343 } VirtViewerApp;
44
45 typedef struct {
46 guint sourceKey;
47 guint numMappedKeys;
48 guint *mappedKeys;
49 gboolean isLast;
50 } VirtViewerKeyMapping;
4451
4552 typedef struct {
4653 GtkApplicationClass parent_class;
8585 PROP_DISPLAY,
8686 PROP_SUBTITLE,
8787 PROP_APP,
88 PROP_KEYMAP,
8889 };
8990
9091 struct _VirtViewerWindowPrivate {
113114 gboolean fullscreen;
114115 gchar *subtitle;
115116 gboolean initial_zoom_set;
117 VirtViewerKeyMapping *keyMappings;
116118 };
117119
118120 G_DEFINE_TYPE_WITH_PRIVATE (VirtViewerWindow, virt_viewer_window, G_TYPE_OBJECT)
163165 g_return_if_fail(priv->app == NULL);
164166 priv->app = g_value_get_object(value);
165167 break;
168
169 case PROP_KEYMAP:
170 g_free(priv->keyMappings);
171 priv->keyMappings = (VirtViewerKeyMapping *)g_value_get_pointer(value);
172 break;
166173
167174 default:
168175 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
312319 G_PARAM_WRITABLE |
313320 G_PARAM_CONSTRUCT_ONLY |
314321 G_PARAM_STATIC_STRINGS));
322 g_object_class_install_property(object_class,
323 PROP_KEYMAP,
324 g_param_spec_pointer("keymap",
325 "keymap",
326 "Remapped keys",
327 G_PARAM_WRITABLE |
328 G_PARAM_STATIC_STRINGS));
329
315330 }
316331
317332 static gboolean
14831498 }
14841499 static gboolean
14851500 window_key_pressed (GtkWidget *widget G_GNUC_UNUSED,
1486 GdkEvent *event,
1487 GtkWidget *display)
1488 {
1489 gtk_widget_grab_focus(display);
1490 return gtk_widget_event(display, event);
1491 }
1501 GdkEvent *ev,
1502 VirtViewerWindow *self)
1503 {
1504 GdkEventKey *event;
1505 VirtViewerWindowPrivate *priv;
1506 VirtViewerDisplay *display;
1507 priv = self->priv;
1508 display = priv->display;
1509 event = (GdkEventKey *)ev;
1510
1511 gtk_widget_grab_focus(GTK_WIDGET(display));
1512
1513 // Look through keymaps - if set for mappings and intercept
1514 if (priv->keyMappings) {
1515 VirtViewerKeyMapping *ptr, *matched;
1516 ptr = priv->keyMappings;
1517 matched = NULL;
1518 do {
1519 if (event->keyval == ptr->sourceKey) {
1520 matched = ptr;
1521 }
1522 if (ptr->isLast) {
1523 break;
1524 }
1525 ptr++;
1526 } while (matched == NULL);
1527
1528 if (matched) {
1529 if (matched->mappedKeys == NULL) {
1530 // Key to be ignored and not pass through to VM
1531 g_debug("Blocking keypress '%s'", gdk_keyval_name(matched->sourceKey));
1532 } else {
1533 g_debug("Sending through mapped keys");
1534 virt_viewer_display_send_keys(display,
1535 matched->mappedKeys, matched->numMappedKeys);
1536 }
1537 return TRUE;
1538 }
1539
1540 }
1541 g_debug("Key pressed was keycode='0x%x', gdk_keyname='%s'", event->keyval, gdk_keyval_name(event->keyval));
1542 return gtk_widget_event(GTK_WIDGET(display), ev);
1543 }
1544
14921545
14931546 void
14941547 virt_viewer_window_set_display(VirtViewerWindow *self, VirtViewerDisplay *display)
15161569 gtk_widget_realize(GTK_WIDGET(display));
15171570
15181571 virt_viewer_signal_connect_object(priv->window, "key-press-event",
1519 G_CALLBACK(window_key_pressed), display, 0);
1572 G_CALLBACK(window_key_pressed), self, 0);
15201573
15211574 /* switch back to non-display if not ready */
15221575 if (!(virt_viewer_display_get_show_hint(display) &