mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/virt-viewer
synced 2026-01-09 22:25:30 +00:00
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>
This commit is contained in:
parent
27ea968c70
commit
fedaa5f89a
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -43,6 +43,13 @@ typedef struct {
|
||||
VirtViewerAppPrivate *priv;
|
||||
} VirtViewerApp;
|
||||
|
||||
typedef struct {
|
||||
guint sourceKey;
|
||||
guint numMappedKeys;
|
||||
guint *mappedKeys;
|
||||
gboolean isLast;
|
||||
} VirtViewerKeyMapping;
|
||||
|
||||
typedef struct {
|
||||
GtkApplicationClass parent_class;
|
||||
|
||||
|
||||
@ -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) &
|
||||
|
||||
Loading…
Reference in New Issue
Block a user