qapi: convert sendkey
Convert 'sendkey' to use QAPI.
QAPI passes key's index of mapping table to qmp_send_key(),
not keycode. So we use help functions to convert key/code to
index of key_defs, and 'index' will be converted to 'keycode'
inside qmp_send_key().
For qmp, QAPI would check invalid key and raise error.
For hmp, invalid key is checked in hmp_send_key().
'send-key' of QMP doesn't support key in hexadecimal format.
Signed-off-by: Amos Kong <akong@redhat.com>
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
diff --git a/console.h b/console.h
index 7934b11..c702b23 100644
--- a/console.h
+++ b/console.h
@@ -399,7 +399,6 @@
void curses_display_init(DisplayState *ds, int full_screen);
/* input.c */
-extern const int key_defs[];
int index_from_key(const char *key);
int index_from_keycode(int code);
diff --git a/hmp-commands.hx b/hmp-commands.hx
index bd0c6c9..5cee131 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -505,7 +505,7 @@
.args_type = "keys:s,hold-time:i?",
.params = "keys [hold_ms]",
.help = "send keys to the VM (e.g. 'sendkey ctrl-alt-f1', default hold time=100 ms)",
- .mhandler.cmd = do_sendkey,
+ .mhandler.cmd = hmp_send_key,
},
STEXI
diff --git a/hmp.c b/hmp.c
index 81c8acb..ba99b2f 100644
--- a/hmp.c
+++ b/hmp.c
@@ -19,6 +19,7 @@
#include "qemu-timer.h"
#include "qmp-commands.h"
#include "monitor.h"
+#include "console.h"
static void hmp_handle_error(Monitor *mon, Error **errp)
{
@@ -1102,3 +1103,57 @@
qmp_closefd(fdname, &errp);
hmp_handle_error(mon, &errp);
}
+
+void hmp_send_key(Monitor *mon, const QDict *qdict)
+{
+ const char *keys = qdict_get_str(qdict, "keys");
+ QKeyCodeList *keylist, *head = NULL, *tmp = NULL;
+ int has_hold_time = qdict_haskey(qdict, "hold-time");
+ int hold_time = qdict_get_try_int(qdict, "hold-time", -1);
+ Error *err = NULL;
+ char keyname_buf[16];
+ char *separator;
+ int keyname_len, idx;
+
+ while (1) {
+ separator = strchr(keys, '-');
+ keyname_len = separator ? separator - keys : strlen(keys);
+ pstrcpy(keyname_buf, sizeof(keyname_buf), keys);
+
+ /* Be compatible with old interface, convert user inputted "<" */
+ if (!strncmp(keyname_buf, "<", 1) && keyname_len == 1) {
+ pstrcpy(keyname_buf, sizeof(keyname_buf), "less");
+ keyname_len = 4;
+ }
+ keyname_buf[keyname_len] = 0;
+
+ idx = index_from_key(keyname_buf);
+ if (idx == Q_KEY_CODE_MAX) {
+ monitor_printf(mon, "invalid parameter: %s\n", keyname_buf);
+ break;
+ }
+
+ keylist = g_malloc0(sizeof(*keylist));
+ keylist->value = idx;
+ keylist->next = NULL;
+
+ if (!head) {
+ head = keylist;
+ }
+ if (tmp) {
+ tmp->next = keylist;
+ }
+ tmp = keylist;
+
+ if (!separator) {
+ break;
+ }
+ keys = separator + 1;
+ }
+
+ if (idx != Q_KEY_CODE_MAX) {
+ qmp_send_key(head, has_hold_time, hold_time, &err);
+ }
+ hmp_handle_error(mon, &err);
+ qapi_free_QKeyCodeList(head);
+}
diff --git a/hmp.h b/hmp.h
index 7dd93bf..8e7838c 100644
--- a/hmp.h
+++ b/hmp.h
@@ -71,5 +71,6 @@
void hmp_netdev_del(Monitor *mon, const QDict *qdict);
void hmp_getfd(Monitor *mon, const QDict *qdict);
void hmp_closefd(Monitor *mon, const QDict *qdict);
+void hmp_send_key(Monitor *mon, const QDict *qdict);
#endif
diff --git a/input.c b/input.c
index 5630cb1..c4b0619 100644
--- a/input.c
+++ b/input.c
@@ -28,6 +28,7 @@
#include "console.h"
#include "error.h"
#include "qmp-commands.h"
+#include "qapi-types.h"
static QEMUPutKBDEvent *qemu_put_kbd_event;
static void *qemu_put_kbd_event_opaque;
@@ -37,7 +38,7 @@
static NotifierList mouse_mode_notifiers =
NOTIFIER_LIST_INITIALIZER(mouse_mode_notifiers);
-const int key_defs[] = {
+static const int key_defs[] = {
[Q_KEY_CODE_SHIFT] = 0x2a,
[Q_KEY_CODE_SHIFT_R] = 0x36,
@@ -223,6 +224,70 @@
return i;
}
+static QKeyCodeList *keycodes;
+static QEMUTimer *key_timer;
+
+static void release_keys(void *opaque)
+{
+ int keycode;
+ QKeyCodeList *p;
+
+ for (p = keycodes; p != NULL; p = p->next) {
+ keycode = key_defs[p->value];
+ if (keycode & 0x80) {
+ kbd_put_keycode(0xe0);
+ }
+ kbd_put_keycode(keycode | 0x80);
+ }
+ qapi_free_QKeyCodeList(keycodes);
+ keycodes = NULL;
+}
+
+void qmp_send_key(QKeyCodeList *keys, bool has_hold_time, int64_t hold_time,
+ Error **errp)
+{
+ int keycode;
+ QKeyCodeList *p, *keylist, *head = NULL, *tmp = NULL;
+
+ if (!key_timer) {
+ key_timer = qemu_new_timer_ns(vm_clock, release_keys, NULL);
+ }
+
+ if (keycodes != NULL) {
+ qemu_del_timer(key_timer);
+ release_keys(NULL);
+ }
+ if (!has_hold_time) {
+ hold_time = 100;
+ }
+
+ for (p = keys; p != NULL; p = p->next) {
+ keylist = g_malloc0(sizeof(*keylist));
+ keylist->value = p->value;
+ keylist->next = NULL;
+
+ if (!head) {
+ head = keylist;
+ }
+ if (tmp) {
+ tmp->next = keylist;
+ }
+ tmp = keylist;
+
+ /* key down events */
+ keycode = key_defs[p->value];
+ if (keycode & 0x80) {
+ kbd_put_keycode(0xe0);
+ }
+ kbd_put_keycode(keycode & 0x7f);
+ }
+ keycodes = head;
+
+ /* delayed key up events */
+ qemu_mod_timer(key_timer, qemu_get_clock_ns(vm_clock) +
+ muldiv64(get_ticks_per_sec(), hold_time, 1000));
+}
+
void qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque)
{
qemu_put_kbd_event_opaque = opaque;
diff --git a/monitor.c b/monitor.c
index 0c4f86f..d73bad8 100644
--- a/monitor.c
+++ b/monitor.c
@@ -1290,92 +1290,6 @@
monitor_printf(mon, "%05d\n", sum);
}
-#define MAX_KEYCODES 16
-static uint8_t keycodes[MAX_KEYCODES];
-static int nb_pending_keycodes;
-static QEMUTimer *key_timer;
-
-static void release_keys(void *opaque)
-{
- int keycode;
-
- while (nb_pending_keycodes > 0) {
- nb_pending_keycodes--;
- keycode = keycodes[nb_pending_keycodes];
- if (keycode & 0x80)
- kbd_put_keycode(0xe0);
- kbd_put_keycode(keycode | 0x80);
- }
-}
-
-static void do_sendkey(Monitor *mon, const QDict *qdict)
-{
- char keyname_buf[16];
- char *separator;
- int keyname_len, keycode, i, idx;
- const char *keys = qdict_get_str(qdict, "keys");
- int has_hold_time = qdict_haskey(qdict, "hold-time");
- int hold_time = qdict_get_try_int(qdict, "hold-time", -1);
-
- if (nb_pending_keycodes > 0) {
- qemu_del_timer(key_timer);
- release_keys(NULL);
- }
- if (!has_hold_time)
- hold_time = 100;
- i = 0;
- while (1) {
- separator = strchr(keys, '-');
- keyname_len = separator ? separator - keys : strlen(keys);
- if (keyname_len > 0) {
- pstrcpy(keyname_buf, sizeof(keyname_buf), keys);
- if (keyname_len > sizeof(keyname_buf) - 1) {
- monitor_printf(mon, "invalid key: '%s...'\n", keyname_buf);
- return;
- }
- if (i == MAX_KEYCODES) {
- monitor_printf(mon, "too many keys\n");
- return;
- }
-
- /* Be compatible with old interface, convert user inputted "<" */
- if (!strncmp(keyname_buf, "<", 1) && keyname_len == 1) {
- pstrcpy(keyname_buf, sizeof(keyname_buf), "less");
- keyname_len = 4;
- }
-
- keyname_buf[keyname_len] = 0;
-
- idx = index_from_key(keyname_buf);
- if (idx == Q_KEY_CODE_MAX) {
- monitor_printf(mon, "invalid parameter: %s\n", keyname_buf);
- return;
- }
-
- keycode = key_defs[idx];
- if (keycode < 0) {
- monitor_printf(mon, "unknown key: '%s'\n", keyname_buf);
- return;
- }
- keycodes[i++] = keycode;
- }
- if (!separator)
- break;
- keys = separator + 1;
- }
- nb_pending_keycodes = i;
- /* key down events */
- for (i = 0; i < nb_pending_keycodes; i++) {
- keycode = keycodes[i];
- if (keycode & 0x80)
- kbd_put_keycode(0xe0);
- kbd_put_keycode(keycode & 0x7f);
- }
- /* delayed key up events */
- qemu_mod_timer(key_timer, qemu_get_clock_ns(vm_clock) +
- muldiv64(get_ticks_per_sec(), hold_time, 1000));
-}
-
static int mouse_button_state;
static void do_mouse_move(Monitor *mon, const QDict *qdict)
@@ -4772,7 +4686,6 @@
Monitor *mon;
if (is_first_init) {
- key_timer = qemu_new_timer_ns(vm_clock, release_keys, NULL);
monitor_protocol_event_init();
is_first_init = 0;
}
diff --git a/qapi-schema.json b/qapi-schema.json
index 856e11a..5421382 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2519,3 +2519,23 @@
'left', 'up', 'down', 'right', 'insert', 'delete', 'stop', 'again',
'props', 'undo', 'front', 'copy', 'open', 'paste', 'find', 'cut',
'lf', 'help', 'meta_l', 'meta_r', 'compose' ] }
+
+##
+# @send-key:
+#
+# Send keys to guest.
+#
+# @keys: key sequence. 'keys' is the name of the key. Use a JSON array to
+# press several keys simultaneously.
+#
+# @hold-time: #optional time to delay key up events, milliseconds. Defaults
+# to 100
+#
+# Returns: Nothing on success
+# If key is unknown or redundant, InvalidParameter
+#
+# Since: 1.3.0
+#
+##
+{ 'command': 'send-key',
+ 'data': { 'keys': ['QKeyCode'], '*hold-time': 'int' } }
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 3745a21..470f08e 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -335,6 +335,34 @@
EQMP
{
+ .name = "send-key",
+ .args_type = "keys:O,hold-time:i?",
+ .mhandler.cmd_new = qmp_marshal_input_send_key,
+ },
+
+SQMP
+send-key
+----------
+
+Send keys to VM.
+
+Arguments:
+
+keys array:
+ - "key": key sequence (a json-array of key enum values)
+
+- hold-time: time to delay key up events, milliseconds. Defaults to 100
+ (json-int, optional)
+
+Example:
+
+-> { "execute": "send-key",
+ "arguments": { 'keys': [ 'ctrl', 'alt', 'delete' ] } }
+<- { "return": {} }
+
+EQMP
+
+ {
.name = "cpu",
.args_type = "index:i",
.mhandler.cmd_new = qmp_marshal_input_cpu,