diff mbox

[v5,6/6] qapi: convert sendkey

Message ID 1343278141-10843-7-git-send-email-akong@redhat.com
State New
Headers show

Commit Message

Amos Kong July 26, 2012, 4:49 a.m. UTC
Convert 'sendkey' to use QAPI.

Keys' indexes in the enmu are same as keycodes' indexes in the
key_defs[], index_from_code() and index_from_key() will return
Q_KEY_CODE_MAX if the code/key is invalid.

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>
---
 console.h        |    5 +
 hmp-commands.hx  |    2 +-
 hmp.c            |   55 ++++++++++++
 hmp.h            |    1 +
 input.c          |  249 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 monitor.c        |  258 ++----------------------------------------------------
 qapi-schema.json |   46 ++++++++++
 qmp-commands.hx  |   28 ++++++
 8 files changed, 393 insertions(+), 251 deletions(-)

Comments

Eric Blake July 26, 2012, 9:22 p.m. UTC | #1
On 07/25/2012 10:49 PM, Amos Kong wrote:
> Convert 'sendkey' to use QAPI.
> 
> Keys' indexes in the enmu are same as keycodes' indexes in the

s/enmu/enum/

> key_defs[], index_from_code() and index_from_key() will return
> Q_KEY_CODE_MAX if the code/key is invalid.
> 
> 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>

I glanced through the series, and didn't see anything else suspicious
from my point of view.  The QMP interface which libvirt will be using
appears to be sane.
Luiz Capitulino Aug. 2, 2012, 7:57 p.m. UTC | #2
On Thu, 26 Jul 2012 12:49:01 +0800
Amos Kong <akong@redhat.com> wrote:

> Convert 'sendkey' to use QAPI.
> 
> Keys' indexes in the enmu are same as keycodes' indexes in the
> key_defs[], index_from_code() and index_from_key() will return
> Q_KEY_CODE_MAX if the code/key is invalid.
> 
> 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>

I've some review comments below, besides I'd like to ask you to split
the addition of the QKeyCode enum and the key_defs table moving to a
different patch.

Other than that this looks quite better, hopefully v6 will be the last one.

> ---
>  console.h        |    5 +
>  hmp-commands.hx  |    2 +-
>  hmp.c            |   55 ++++++++++++
>  hmp.h            |    1 +
>  input.c          |  249 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  monitor.c        |  258 ++----------------------------------------------------
>  qapi-schema.json |   46 ++++++++++
>  qmp-commands.hx  |   28 ++++++
>  8 files changed, 393 insertions(+), 251 deletions(-)
> 
> diff --git a/console.h b/console.h
> index 4334db5..b2d7af6 100644
> --- a/console.h
> +++ b/console.h
> @@ -6,6 +6,7 @@
>  #include "notify.h"
>  #include "monitor.h"
>  #include "trace.h"
> +#include "qapi-types.h"
>  
>  /* keyboard/mouse support */
>  
> @@ -397,4 +398,8 @@ static inline int vnc_display_pw_expire(DisplayState *ds, time_t expires)
>  /* curses.c */
>  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);
>  #endif
> diff --git a/hmp-commands.hx b/hmp-commands.hx
> index 2891d48..8c2be24 100644
> --- a/hmp-commands.hx
> +++ b/hmp-commands.hx
> @@ -505,7 +505,7 @@ ETEXI
>          .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 6b72a64..041555a 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)
>  {
> @@ -1020,3 +1021,57 @@ void hmp_closefd(Monitor *mon, const QDict *qdict)
>      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) {
> +            error_set(&err, QERR_INVALID_PARAMETER, keyname_buf);

No need to use error_set(), you can call monitor_printf() directly.

> +            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 8d2b0d7..56d67a3 100644
> --- a/hmp.h
> +++ b/hmp.h
> @@ -66,5 +66,6 @@ void hmp_netdev_add(Monitor *mon, const QDict *qdict);
>  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 6968b31..2286581 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,6 +38,254 @@ static QTAILQ_HEAD(, QEMUPutMouseEntry) mouse_handlers =
>  static NotifierList mouse_mode_notifiers = 
>      NOTIFIER_LIST_INITIALIZER(mouse_mode_notifiers);
>  
> +const int key_defs[] = {

Please, make key_defs static.

> +    [Q_KEY_CODE_SHIFT] = 0x2a,
> +    [Q_KEY_CODE_SHIFT_R] = 0x36,
> +
> +    [Q_KEY_CODE_ALT] = 0x38,
> +    [Q_KEY_CODE_ALT_R] = 0xb8,
> +    [Q_KEY_CODE_ALTGR] = 0x64,
> +    [Q_KEY_CODE_ALTGR_R] = 0xe4,
> +    [Q_KEY_CODE_CTRL] = 0x1d,
> +    [Q_KEY_CODE_CTRL_R] = 0x9d,
> +
> +    [Q_KEY_CODE_MENU] = 0xdd,
> +
> +    [Q_KEY_CODE_ESC] = 0x01,
> +
> +    [Q_KEY_CODE_1] = 0x02,
> +    [Q_KEY_CODE_2] = 0x03,
> +    [Q_KEY_CODE_3] = 0x04,
> +    [Q_KEY_CODE_4] = 0x05,
> +    [Q_KEY_CODE_5] = 0x06,
> +    [Q_KEY_CODE_6] = 0x07,
> +    [Q_KEY_CODE_7] = 0x08,
> +    [Q_KEY_CODE_8] = 0x09,
> +    [Q_KEY_CODE_9] = 0x0a,
> +    [Q_KEY_CODE_0] = 0x0b,
> +    [Q_KEY_CODE_MINUS] = 0x0c,
> +    [Q_KEY_CODE_EQUAL] = 0x0d,
> +    [Q_KEY_CODE_BACKSPACE] = 0x0e,
> +
> +    [Q_KEY_CODE_TAB] = 0x0f,
> +    [Q_KEY_CODE_Q] = 0x10,
> +    [Q_KEY_CODE_W] = 0x11,
> +    [Q_KEY_CODE_E] = 0x12,
> +    [Q_KEY_CODE_R] = 0x13,
> +    [Q_KEY_CODE_T] = 0x14,
> +    [Q_KEY_CODE_Y] = 0x15,
> +    [Q_KEY_CODE_U] = 0x16,
> +    [Q_KEY_CODE_I] = 0x17,
> +    [Q_KEY_CODE_O] = 0x18,
> +    [Q_KEY_CODE_P] = 0x19,
> +    [Q_KEY_CODE_BRACKET_LEFT] = 0x1a,
> +    [Q_KEY_CODE_BRACKET_RIGHT] = 0x1b,
> +    [Q_KEY_CODE_RET] = 0x1c,
> +
> +    [Q_KEY_CODE_A] = 0x1e,
> +    [Q_KEY_CODE_S] = 0x1f,
> +    [Q_KEY_CODE_D] = 0x20,
> +    [Q_KEY_CODE_F] = 0x21,
> +    [Q_KEY_CODE_G] = 0x22,
> +    [Q_KEY_CODE_H] = 0x23,
> +    [Q_KEY_CODE_J] = 0x24,
> +    [Q_KEY_CODE_K] = 0x25,
> +    [Q_KEY_CODE_L] = 0x26,
> +    [Q_KEY_CODE_SEMICOLON] = 0x27,
> +    [Q_KEY_CODE_APOSTROPHE] = 0x28,
> +    [Q_KEY_CODE_GRAVE_ACCENT] = 0x29,
> +
> +    [Q_KEY_CODE_BACKSLASH] = 0x2b,
> +    [Q_KEY_CODE_Z] = 0x2c,
> +    [Q_KEY_CODE_X] = 0x2d,
> +    [Q_KEY_CODE_C] = 0x2e,
> +    [Q_KEY_CODE_V] = 0x2f,
> +    [Q_KEY_CODE_B] = 0x30,
> +    [Q_KEY_CODE_N] = 0x31,
> +    [Q_KEY_CODE_M] = 0x32,
> +    [Q_KEY_CODE_COMMA] = 0x33,
> +    [Q_KEY_CODE_DOT] = 0x34,
> +    [Q_KEY_CODE_SLASH] = 0x35,
> +
> +    [Q_KEY_CODE_ASTERISK] = 0x37,
> +
> +    [Q_KEY_CODE_SPC] = 0x39,
> +    [Q_KEY_CODE_CAPS_LOCK] = 0x3a,
> +    [Q_KEY_CODE_F1] = 0x3b,
> +    [Q_KEY_CODE_F2] = 0x3c,
> +    [Q_KEY_CODE_F3] = 0x3d,
> +    [Q_KEY_CODE_F4] = 0x3e,
> +    [Q_KEY_CODE_F5] = 0x3f,
> +    [Q_KEY_CODE_F6] = 0x40,
> +    [Q_KEY_CODE_F7] = 0x41,
> +    [Q_KEY_CODE_F8] = 0x42,
> +    [Q_KEY_CODE_F9] = 0x43,
> +    [Q_KEY_CODE_F10] = 0x44,
> +    [Q_KEY_CODE_NUM_LOCK] = 0x45,
> +    [Q_KEY_CODE_SCROLL_LOCK] = 0x46,
> +
> +    [Q_KEY_CODE_KP_DIVIDE] = 0xb5,
> +    [Q_KEY_CODE_KP_MULTIPLY] = 0x37,
> +    [Q_KEY_CODE_KP_SUBTRACT] = 0x4a,
> +    [Q_KEY_CODE_KP_ADD] = 0x4e,
> +    [Q_KEY_CODE_KP_ENTER] = 0x9c,
> +    [Q_KEY_CODE_KP_DECIMAL] = 0x53,
> +    [Q_KEY_CODE_SYSRQ] = 0x54,
> +
> +    [Q_KEY_CODE_KP_0] = 0x52,
> +    [Q_KEY_CODE_KP_1] = 0x4f,
> +    [Q_KEY_CODE_KP_2] = 0x50,
> +    [Q_KEY_CODE_KP_3] = 0x51,
> +    [Q_KEY_CODE_KP_4] = 0x4b,
> +    [Q_KEY_CODE_KP_5] = 0x4c,
> +    [Q_KEY_CODE_KP_6] = 0x4d,
> +    [Q_KEY_CODE_KP_7] = 0x47,
> +    [Q_KEY_CODE_KP_8] = 0x48,
> +    [Q_KEY_CODE_KP_9] = 0x49,
> +
> +    [Q_KEY_CODE_LESS] = 0x56,
> +
> +    [Q_KEY_CODE_F11] = 0x57,
> +    [Q_KEY_CODE_F12] = 0x58,
> +
> +    [Q_KEY_CODE_PRINT] = 0xb7,
> +
> +    [Q_KEY_CODE_HOME] = 0xc7,
> +    [Q_KEY_CODE_PGUP] = 0xc9,
> +    [Q_KEY_CODE_PGDN] = 0xd1,
> +    [Q_KEY_CODE_END] = 0xcf,
> +
> +    [Q_KEY_CODE_LEFT] = 0xcb,
> +    [Q_KEY_CODE_UP] = 0xc8,
> +    [Q_KEY_CODE_DOWN] = 0xd0,
> +    [Q_KEY_CODE_RIGHT] = 0xcd,
> +
> +    [Q_KEY_CODE_INSERT] = 0xd2,
> +    [Q_KEY_CODE_DELETE] = 0xd3,
> +#ifdef NEED_CPU_H
> +#if defined(TARGET_SPARC) && !defined(TARGET_SPARC64)
> +    [Q_KEY_CODE_STOP] = 0xf0,
> +    [Q_KEY_CODE_AGAIN] = 0xf1,
> +    [Q_KEY_CODE_PROPS] = 0xf2,
> +    [Q_KEY_CODE_UNDO] = 0xf3,
> +    [Q_KEY_CODE_FRONT] = 0xf4,
> +    [Q_KEY_CODE_COPY] = 0xf5,
> +    [Q_KEY_CODE_OPEN] = 0xf6,
> +    [Q_KEY_CODE_PASTE] = 0xf7,
> +    [Q_KEY_CODE_FIND] = 0xf8,
> +    [Q_KEY_CODE_CUT] = 0xf9,
> +    [Q_KEY_CODE_LF] = 0xfa,
> +    [Q_KEY_CODE_HELP] = 0xfb,
> +    [Q_KEY_CODE_META_L] = 0xfc,
> +    [Q_KEY_CODE_META_R] = 0xfd,
> +    [Q_KEY_CODE_COMPOSE] = 0xfe,
> +#endif
> +#endif
> +    [Q_KEY_CODE_MAX] = 0,
> +};
> +
> +int index_from_key(const char *key)

keycode_from_key() is better.

> +{
> +    int i, keycode;
> +    char *endp;
> +
> +    for (i = 0; QKeyCode_lookup[i] != NULL; i++) {
> +        if (!strcmp(key, QKeyCode_lookup[i])) {
> +            break;
> +        }
> +    }
> +
> +    if (strstart(key, "0x", NULL)) {
> +        keycode = strtoul(key, &endp, 0);
> +        if (*endp == '\0' && keycode >= 0x01 && keycode <= 0xff) {
> +            for (i = 0; i < Q_KEY_CODE_MAX; i++) {
> +                if (keycode == key_defs[i]) {
> +                    break;
> +                }
> +            }
> +        }
> +    }
> +
> +    /* Return Q_KEY_CODE_MAX if the key is invalid */
> +    return i;
> +}
> +
> +int index_from_keycode(int code)

keycode_from_code()

> +{
> +    int i;
> +    for (i = 0; i < Q_KEY_CODE_MAX; i++) {
> +        if (key_defs[i] == code) {
> +            break;
> +        }
> +    }
> +    /* Return Q_KEY_CODE_MAX if the code is invalid */
> +    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 4db0c1e..e3a2023 100644
> --- a/monitor.c
> +++ b/monitor.c
> @@ -1293,252 +1293,6 @@ static void do_sum(Monitor *mon, const QDict *qdict)
>      monitor_printf(mon, "%05d\n", sum);
>  }
>  
> -typedef struct {
> -    int keycode;
> -    const char *name;
> -} KeyDef;
> -
> -static const KeyDef key_defs[] = {
> -    { 0x2a, "shift" },
> -    { 0x36, "shift_r" },
> -
> -    { 0x38, "alt" },
> -    { 0xb8, "alt_r" },
> -    { 0x64, "altgr" },
> -    { 0xe4, "altgr_r" },
> -    { 0x1d, "ctrl" },
> -    { 0x9d, "ctrl_r" },
> -
> -    { 0xdd, "menu" },
> -
> -    { 0x01, "esc" },
> -
> -    { 0x02, "1" },
> -    { 0x03, "2" },
> -    { 0x04, "3" },
> -    { 0x05, "4" },
> -    { 0x06, "5" },
> -    { 0x07, "6" },
> -    { 0x08, "7" },
> -    { 0x09, "8" },
> -    { 0x0a, "9" },
> -    { 0x0b, "0" },
> -    { 0x0c, "minus" },
> -    { 0x0d, "equal" },
> -    { 0x0e, "backspace" },
> -
> -    { 0x0f, "tab" },
> -    { 0x10, "q" },
> -    { 0x11, "w" },
> -    { 0x12, "e" },
> -    { 0x13, "r" },
> -    { 0x14, "t" },
> -    { 0x15, "y" },
> -    { 0x16, "u" },
> -    { 0x17, "i" },
> -    { 0x18, "o" },
> -    { 0x19, "p" },
> -    { 0x1a, "bracket_left" },
> -    { 0x1b, "bracket_right" },
> -    { 0x1c, "ret" },
> -
> -    { 0x1e, "a" },
> -    { 0x1f, "s" },
> -    { 0x20, "d" },
> -    { 0x21, "f" },
> -    { 0x22, "g" },
> -    { 0x23, "h" },
> -    { 0x24, "j" },
> -    { 0x25, "k" },
> -    { 0x26, "l" },
> -    { 0x27, "semicolon" },
> -    { 0x28, "apostrophe" },
> -    { 0x29, "grave_accent" },
> -
> -    { 0x2b, "backslash" },
> -    { 0x2c, "z" },
> -    { 0x2d, "x" },
> -    { 0x2e, "c" },
> -    { 0x2f, "v" },
> -    { 0x30, "b" },
> -    { 0x31, "n" },
> -    { 0x32, "m" },
> -    { 0x33, "comma" },
> -    { 0x34, "dot" },
> -    { 0x35, "slash" },
> -
> -    { 0x37, "asterisk" },
> -
> -    { 0x39, "spc" },
> -    { 0x3a, "caps_lock" },
> -    { 0x3b, "f1" },
> -    { 0x3c, "f2" },
> -    { 0x3d, "f3" },
> -    { 0x3e, "f4" },
> -    { 0x3f, "f5" },
> -    { 0x40, "f6" },
> -    { 0x41, "f7" },
> -    { 0x42, "f8" },
> -    { 0x43, "f9" },
> -    { 0x44, "f10" },
> -    { 0x45, "num_lock" },
> -    { 0x46, "scroll_lock" },
> -
> -    { 0xb5, "kp_divide" },
> -    { 0x37, "kp_multiply" },
> -    { 0x4a, "kp_subtract" },
> -    { 0x4e, "kp_add" },
> -    { 0x9c, "kp_enter" },
> -    { 0x53, "kp_decimal" },
> -    { 0x54, "sysrq" },
> -
> -    { 0x52, "kp_0" },
> -    { 0x4f, "kp_1" },
> -    { 0x50, "kp_2" },
> -    { 0x51, "kp_3" },
> -    { 0x4b, "kp_4" },
> -    { 0x4c, "kp_5" },
> -    { 0x4d, "kp_6" },
> -    { 0x47, "kp_7" },
> -    { 0x48, "kp_8" },
> -    { 0x49, "kp_9" },
> -
> -    { 0x56, "less" },
> -
> -    { 0x57, "f11" },
> -    { 0x58, "f12" },
> -
> -    { 0xb7, "print" },
> -
> -    { 0xc7, "home" },
> -    { 0xc9, "pgup" },
> -    { 0xd1, "pgdn" },
> -    { 0xcf, "end" },
> -
> -    { 0xcb, "left" },
> -    { 0xc8, "up" },
> -    { 0xd0, "down" },
> -    { 0xcd, "right" },
> -
> -    { 0xd2, "insert" },
> -    { 0xd3, "delete" },
> -#if defined(TARGET_SPARC) && !defined(TARGET_SPARC64)
> -    { 0xf0, "stop" },
> -    { 0xf1, "again" },
> -    { 0xf2, "props" },
> -    { 0xf3, "undo" },
> -    { 0xf4, "front" },
> -    { 0xf5, "copy" },
> -    { 0xf6, "open" },
> -    { 0xf7, "paste" },
> -    { 0xf8, "find" },
> -    { 0xf9, "cut" },
> -    { 0xfa, "lf" },
> -    { 0xfb, "help" },
> -    { 0xfc, "meta_l" },
> -    { 0xfd, "meta_r" },
> -    { 0xfe, "compose" },
> -#endif
> -    { 0, NULL },
> -};
> -
> -static int get_keycode(const char *key)
> -{
> -    const KeyDef *p;
> -    char *endp;
> -    int ret;
> -
> -    for(p = key_defs; p->name != NULL; p++) {
> -        if (!strcmp(key, p->name))
> -            return p->keycode;
> -    }
> -    if (strstart(key, "0x", NULL)) {
> -        ret = strtoul(key, &endp, 0);
> -        if (*endp == '\0' && ret >= 0x01 && ret <= 0xff)
> -            return ret;
> -    }
> -    return -1;
> -}
> -
> -#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;
> -    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;
> -            keycode = get_keycode(keyname_buf);
> -            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)
> @@ -4077,7 +3831,7 @@ static void monitor_find_completion(const char *cmdline)
>      int nb_args, i, len;
>      const char *ptype, *str;
>      const mon_cmd_t *cmd;
> -    const KeyDef *key;
> +    const int *code;
>  
>      parse_cmdline(cmdline, &nb_args, args);
>  #ifdef DEBUG_COMPLETION
> @@ -4151,9 +3905,14 @@ static void monitor_find_completion(const char *cmdline)
>                  if (sep)
>                      str = sep + 1;
>                  readline_set_completion_index(cur_mon->rs, strlen(str));
> -                for(key = key_defs; key->name != NULL; key++) {
> -                    cmd_completion(str, key->name);

You can do:

cmd_completion(str, KeyCodes_lookup[i]);

The loop below is not needed.

> +                for (code = key_defs; code != NULL; code++) {
> +                    int idx;
> +                    idx = index_from_keycode(*code);
> +                    if (idx != Q_KEY_CODE_MAX) {
> +                        cmd_completion(str, QKeyCode_lookup[idx]);
> +                    }
>                  }
> +
>              } else if (!strcmp(cmd->name, "help|?")) {
>                  readline_set_completion_index(cur_mon->rs, strlen(str));
>                  for (cmd = mon_cmds; cmd->name != NULL; cmd++) {
> @@ -4681,7 +4440,6 @@ void monitor_init(CharDriverState *chr, int flags)
>      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 bc55ed2..3a562d8 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -2183,3 +2183,49 @@
>  # Since: 0.14.0
>  ##
>  { 'command': 'closefd', 'data': {'fdname': 'str'} }
> +
> +##
> +# @QKeyCode:
> +#
> +# An enumeration of key name.
> +#
> +# This is used by the send-key command.
> +#
> +# Since: 1.2
> +##
> +{ 'enum': 'QKeyCode',
> +  'data': [ 'shift', 'shift_r', 'alt', 'alt_r', 'altgr', 'altgr_r', 'ctrl',
> +            'ctrl_r', 'menu', 'esc', '1', '2', '3', '4', '5', '6', '7', '8',
> +            '9', '0', 'minus', 'equal', 'backspace', 'tab', 'q', 'w', 'e',
> +            'r', 't', 'y', 'u', 'i', 'o', 'p', 'bracket_left', 'bracket_right',
> +            'ret', 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'semicolon',
> +            'apostrophe', 'grave_accent', 'backslash', 'z', 'x', 'c', 'v', 'b',
> +            'n', 'm', 'comma', 'dot', 'slash', 'asterisk', 'spc', 'caps_lock',
> +            'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'f10',
> +            'num_lock', 'scroll_lock', 'kp_divide', 'kp_multiply',
> +            'kp_subtract', 'kp_add', 'kp_enter', 'kp_decimal', 'sysrq', 'kp_0',
> +            'kp_1', 'kp_2', 'kp_3', 'kp_4', 'kp_5', 'kp_6', 'kp_7', 'kp_8',
> +            'kp_9', 'less', 'f11', 'f12', 'print', 'home', 'pgup', 'pgdn', 'end',
> +            '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.2
> +#
> +##
> +{ 'command': 'send-key',
> +  'data': { 'keys': ['QKeyCode'], '*hold-time': 'int' } }
> diff --git a/qmp-commands.hx b/qmp-commands.hx
> index e3cf3c5..3c1e646 100644
> --- a/qmp-commands.hx
> +++ b/qmp-commands.hx
> @@ -335,6 +335,34 @@ Example:
>  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,
Amos Kong Aug. 3, 2012, 1:38 a.m. UTC | #3
----- Original Message -----
> On Thu, 26 Jul 2012 12:49:01 +0800
> Amos Kong <akong@redhat.com> wrote:
> 
> > Convert 'sendkey' to use QAPI.
> > 
> > Keys' indexes in the enmu are same as keycodes' indexes in the
> > key_defs[], index_from_code() and index_from_key() will return
> > Q_KEY_CODE_MAX if the code/key is invalid.
> > 
> > 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>
> 
> I've some review comments below, besides I'd like to ask you to split
> the addition of the QKeyCode enum and the key_defs table moving to a
> different patch.

Ok.

> Other than that this looks quite better, hopefully v6 will be the
> last one.

Thanks for your time :)

> > ---
> >  console.h        |    5 +
> >  hmp-commands.hx  |    2 +-
> >  hmp.c            |   55 ++++++++++++
> >  hmp.h            |    1 +
> >  input.c          |  249
> >  ++++++++++++++++++++++++++++++++++++++++++++++++++++
> >  monitor.c        |  258
> >  ++----------------------------------------------------
> >  qapi-schema.json |   46 ++++++++++
> >  qmp-commands.hx  |   28 ++++++
> >  8 files changed, 393 insertions(+), 251 deletions(-)
> > 
> > diff --git a/console.h b/console.h
> > index 4334db5..b2d7af6 100644
> > --- a/console.h
> > +++ b/console.h
> > @@ -6,6 +6,7 @@
> >  #include "notify.h"
> >  #include "monitor.h"
> >  #include "trace.h"
> > +#include "qapi-types.h"
> >  
> >  /* keyboard/mouse support */
> >  
> > @@ -397,4 +398,8 @@ static inline int
> > vnc_display_pw_expire(DisplayState *ds, time_t expires)
> >  /* curses.c */
> >  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);
> >  #endif
> > diff --git a/hmp-commands.hx b/hmp-commands.hx
> > index 2891d48..8c2be24 100644
> > --- a/hmp-commands.hx
> > +++ b/hmp-commands.hx
> > @@ -505,7 +505,7 @@ ETEXI
> >          .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 6b72a64..041555a 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)
> >  {
> > @@ -1020,3 +1021,57 @@ void hmp_closefd(Monitor *mon, const QDict
> > *qdict)
> >      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) {
> > +            error_set(&err, QERR_INVALID_PARAMETER, keyname_buf);
> 
> No need to use error_set(), you can call monitor_printf() directly.

Nod.

> > +            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 8d2b0d7..56d67a3 100644
> > --- a/hmp.h
> > +++ b/hmp.h
> > @@ -66,5 +66,6 @@ void hmp_netdev_add(Monitor *mon, const QDict
> > *qdict);
> >  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 6968b31..2286581 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,6 +38,254 @@ static QTAILQ_HEAD(, QEMUPutMouseEntry)
> > mouse_handlers =
> >  static NotifierList mouse_mode_notifiers =
> >      NOTIFIER_LIST_INITIALIZER(mouse_mode_notifiers);
> >  
> > +const int key_defs[] = {
> 
> Please, make key_defs static.



> > +    [Q_KEY_CODE_SHIFT] = 0x2a,
> > +    [Q_KEY_CODE_SHIFT_R] = 0x36,
> > +
> > +    [Q_KEY_CODE_ALT] = 0x38,
> > +    [Q_KEY_CODE_ALT_R] = 0xb8,
> > +    [Q_KEY_CODE_ALTGR] = 0x64,
> > +    [Q_KEY_CODE_ALTGR_R] = 0xe4,
> > +    [Q_KEY_CODE_CTRL] = 0x1d,
> > +    [Q_KEY_CODE_CTRL_R] = 0x9d,
> > +
> > +    [Q_KEY_CODE_MENU] = 0xdd,
> > +
> > +    [Q_KEY_CODE_ESC] = 0x01,
> > +
> > +    [Q_KEY_CODE_1] = 0x02,
> > +    [Q_KEY_CODE_2] = 0x03,
> > +    [Q_KEY_CODE_3] = 0x04,
> > +    [Q_KEY_CODE_4] = 0x05,
> > +    [Q_KEY_CODE_5] = 0x06,
> > +    [Q_KEY_CODE_6] = 0x07,
> > +    [Q_KEY_CODE_7] = 0x08,
> > +    [Q_KEY_CODE_8] = 0x09,
> > +    [Q_KEY_CODE_9] = 0x0a,
> > +    [Q_KEY_CODE_0] = 0x0b,
> > +    [Q_KEY_CODE_MINUS] = 0x0c,
> > +    [Q_KEY_CODE_EQUAL] = 0x0d,
> > +    [Q_KEY_CODE_BACKSPACE] = 0x0e,
> > +
> > +    [Q_KEY_CODE_TAB] = 0x0f,
> > +    [Q_KEY_CODE_Q] = 0x10,
> > +    [Q_KEY_CODE_W] = 0x11,
> > +    [Q_KEY_CODE_E] = 0x12,
> > +    [Q_KEY_CODE_R] = 0x13,
> > +    [Q_KEY_CODE_T] = 0x14,
> > +    [Q_KEY_CODE_Y] = 0x15,
> > +    [Q_KEY_CODE_U] = 0x16,
> > +    [Q_KEY_CODE_I] = 0x17,
> > +    [Q_KEY_CODE_O] = 0x18,
> > +    [Q_KEY_CODE_P] = 0x19,
> > +    [Q_KEY_CODE_BRACKET_LEFT] = 0x1a,
> > +    [Q_KEY_CODE_BRACKET_RIGHT] = 0x1b,
> > +    [Q_KEY_CODE_RET] = 0x1c,
> > +
> > +    [Q_KEY_CODE_A] = 0x1e,
> > +    [Q_KEY_CODE_S] = 0x1f,
> > +    [Q_KEY_CODE_D] = 0x20,
> > +    [Q_KEY_CODE_F] = 0x21,
> > +    [Q_KEY_CODE_G] = 0x22,
> > +    [Q_KEY_CODE_H] = 0x23,
> > +    [Q_KEY_CODE_J] = 0x24,
> > +    [Q_KEY_CODE_K] = 0x25,
> > +    [Q_KEY_CODE_L] = 0x26,
> > +    [Q_KEY_CODE_SEMICOLON] = 0x27,
> > +    [Q_KEY_CODE_APOSTROPHE] = 0x28,
> > +    [Q_KEY_CODE_GRAVE_ACCENT] = 0x29,
> > +
> > +    [Q_KEY_CODE_BACKSLASH] = 0x2b,
> > +    [Q_KEY_CODE_Z] = 0x2c,
> > +    [Q_KEY_CODE_X] = 0x2d,
> > +    [Q_KEY_CODE_C] = 0x2e,
> > +    [Q_KEY_CODE_V] = 0x2f,
> > +    [Q_KEY_CODE_B] = 0x30,
> > +    [Q_KEY_CODE_N] = 0x31,
> > +    [Q_KEY_CODE_M] = 0x32,
> > +    [Q_KEY_CODE_COMMA] = 0x33,
> > +    [Q_KEY_CODE_DOT] = 0x34,
> > +    [Q_KEY_CODE_SLASH] = 0x35,
> > +
> > +    [Q_KEY_CODE_ASTERISK] = 0x37,
> > +
> > +    [Q_KEY_CODE_SPC] = 0x39,
> > +    [Q_KEY_CODE_CAPS_LOCK] = 0x3a,
> > +    [Q_KEY_CODE_F1] = 0x3b,
> > +    [Q_KEY_CODE_F2] = 0x3c,
> > +    [Q_KEY_CODE_F3] = 0x3d,
> > +    [Q_KEY_CODE_F4] = 0x3e,
> > +    [Q_KEY_CODE_F5] = 0x3f,
> > +    [Q_KEY_CODE_F6] = 0x40,
> > +    [Q_KEY_CODE_F7] = 0x41,
> > +    [Q_KEY_CODE_F8] = 0x42,
> > +    [Q_KEY_CODE_F9] = 0x43,
> > +    [Q_KEY_CODE_F10] = 0x44,
> > +    [Q_KEY_CODE_NUM_LOCK] = 0x45,
> > +    [Q_KEY_CODE_SCROLL_LOCK] = 0x46,
> > +
> > +    [Q_KEY_CODE_KP_DIVIDE] = 0xb5,
> > +    [Q_KEY_CODE_KP_MULTIPLY] = 0x37,
> > +    [Q_KEY_CODE_KP_SUBTRACT] = 0x4a,
> > +    [Q_KEY_CODE_KP_ADD] = 0x4e,
> > +    [Q_KEY_CODE_KP_ENTER] = 0x9c,
> > +    [Q_KEY_CODE_KP_DECIMAL] = 0x53,
> > +    [Q_KEY_CODE_SYSRQ] = 0x54,
> > +
> > +    [Q_KEY_CODE_KP_0] = 0x52,
> > +    [Q_KEY_CODE_KP_1] = 0x4f,
> > +    [Q_KEY_CODE_KP_2] = 0x50,
> > +    [Q_KEY_CODE_KP_3] = 0x51,
> > +    [Q_KEY_CODE_KP_4] = 0x4b,
> > +    [Q_KEY_CODE_KP_5] = 0x4c,
> > +    [Q_KEY_CODE_KP_6] = 0x4d,
> > +    [Q_KEY_CODE_KP_7] = 0x47,
> > +    [Q_KEY_CODE_KP_8] = 0x48,
> > +    [Q_KEY_CODE_KP_9] = 0x49,
> > +
> > +    [Q_KEY_CODE_LESS] = 0x56,
> > +
> > +    [Q_KEY_CODE_F11] = 0x57,
> > +    [Q_KEY_CODE_F12] = 0x58,
> > +
> > +    [Q_KEY_CODE_PRINT] = 0xb7,
> > +
> > +    [Q_KEY_CODE_HOME] = 0xc7,
> > +    [Q_KEY_CODE_PGUP] = 0xc9,
> > +    [Q_KEY_CODE_PGDN] = 0xd1,
> > +    [Q_KEY_CODE_END] = 0xcf,
> > +
> > +    [Q_KEY_CODE_LEFT] = 0xcb,
> > +    [Q_KEY_CODE_UP] = 0xc8,
> > +    [Q_KEY_CODE_DOWN] = 0xd0,
> > +    [Q_KEY_CODE_RIGHT] = 0xcd,
> > +
> > +    [Q_KEY_CODE_INSERT] = 0xd2,
> > +    [Q_KEY_CODE_DELETE] = 0xd3,
> > +#ifdef NEED_CPU_H
> > +#if defined(TARGET_SPARC) && !defined(TARGET_SPARC64)
> > +    [Q_KEY_CODE_STOP] = 0xf0,
> > +    [Q_KEY_CODE_AGAIN] = 0xf1,
> > +    [Q_KEY_CODE_PROPS] = 0xf2,
> > +    [Q_KEY_CODE_UNDO] = 0xf3,
> > +    [Q_KEY_CODE_FRONT] = 0xf4,
> > +    [Q_KEY_CODE_COPY] = 0xf5,
> > +    [Q_KEY_CODE_OPEN] = 0xf6,
> > +    [Q_KEY_CODE_PASTE] = 0xf7,
> > +    [Q_KEY_CODE_FIND] = 0xf8,
> > +    [Q_KEY_CODE_CUT] = 0xf9,
> > +    [Q_KEY_CODE_LF] = 0xfa,
> > +    [Q_KEY_CODE_HELP] = 0xfb,
> > +    [Q_KEY_CODE_META_L] = 0xfc,
> > +    [Q_KEY_CODE_META_R] = 0xfd,
> > +    [Q_KEY_CODE_COMPOSE] = 0xfe,
> > +#endif
> > +#endif
> > +    [Q_KEY_CODE_MAX] = 0,
> > +};
> > +
> > +int index_from_key(const char *key)
> 
> keycode_from_key() is better.

QAPI passes 'index' to qmp_send_key(), not value (keycode)
So what those two functions return should be the 'index' of key_defs,
not the value (keycode).

'Index' will be converted to 'keycode' insider qmp_send_key()
 
> > +{
> > +    int i, keycode;
> > +    char *endp;
> > +
> > +    for (i = 0; QKeyCode_lookup[i] != NULL; i++) {
> > +        if (!strcmp(key, QKeyCode_lookup[i])) {
> > +            break;
> > +        }
> > +    }
> > +
> > +    if (strstart(key, "0x", NULL)) {
> > +        keycode = strtoul(key, &endp, 0);
> > +        if (*endp == '\0' && keycode >= 0x01 && keycode <= 0xff) {
> > +            for (i = 0; i < Q_KEY_CODE_MAX; i++) {
> > +                if (keycode == key_defs[i]) {
> > +                    break;
> > +                }
> > +            }
> > +        }
> > +    }
> > +
> > +    /* Return Q_KEY_CODE_MAX if the key is invalid */
> > +    return i;
> > +}
> > +
> > +int index_from_keycode(int code)
> 
> keycode_from_code()
> 
> > +{
> > +    int i;
> > +    for (i = 0; i < Q_KEY_CODE_MAX; i++) {
> > +        if (key_defs[i] == code) {
> > +            break;
> > +        }
> > +    }
> > +    /* Return Q_KEY_CODE_MAX if the code is invalid */
> > +    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 4db0c1e..e3a2023 100644
> > --- a/monitor.c
> > +++ b/monitor.c
> > @@ -1293,252 +1293,6 @@ static void do_sum(Monitor *mon, const
> > QDict *qdict)
> >      monitor_printf(mon, "%05d\n", sum);
> >  }
> >  
> > -typedef struct {
> > -    int keycode;
> > -    const char *name;
> > -} KeyDef;
> > -
> > -static const KeyDef key_defs[] = {
> > -    { 0x2a, "shift" },
> > -    { 0x36, "shift_r" },
> > -
> > -    { 0x38, "alt" },
> > -    { 0xb8, "alt_r" },
> > -    { 0x64, "altgr" },
> > -    { 0xe4, "altgr_r" },
> > -    { 0x1d, "ctrl" },
> > -    { 0x9d, "ctrl_r" },
> > -
> > -    { 0xdd, "menu" },
> > -
> > -    { 0x01, "esc" },
> > -
> > -    { 0x02, "1" },
> > -    { 0x03, "2" },
> > -    { 0x04, "3" },
> > -    { 0x05, "4" },
> > -    { 0x06, "5" },
> > -    { 0x07, "6" },
> > -    { 0x08, "7" },
> > -    { 0x09, "8" },
> > -    { 0x0a, "9" },
> > -    { 0x0b, "0" },
> > -    { 0x0c, "minus" },
> > -    { 0x0d, "equal" },
> > -    { 0x0e, "backspace" },
> > -
> > -    { 0x0f, "tab" },
> > -    { 0x10, "q" },
> > -    { 0x11, "w" },
> > -    { 0x12, "e" },
> > -    { 0x13, "r" },
> > -    { 0x14, "t" },
> > -    { 0x15, "y" },
> > -    { 0x16, "u" },
> > -    { 0x17, "i" },
> > -    { 0x18, "o" },
> > -    { 0x19, "p" },
> > -    { 0x1a, "bracket_left" },
> > -    { 0x1b, "bracket_right" },
> > -    { 0x1c, "ret" },
> > -
> > -    { 0x1e, "a" },
> > -    { 0x1f, "s" },
> > -    { 0x20, "d" },
> > -    { 0x21, "f" },
> > -    { 0x22, "g" },
> > -    { 0x23, "h" },
> > -    { 0x24, "j" },
> > -    { 0x25, "k" },
> > -    { 0x26, "l" },
> > -    { 0x27, "semicolon" },
> > -    { 0x28, "apostrophe" },
> > -    { 0x29, "grave_accent" },
> > -
> > -    { 0x2b, "backslash" },
> > -    { 0x2c, "z" },
> > -    { 0x2d, "x" },
> > -    { 0x2e, "c" },
> > -    { 0x2f, "v" },
> > -    { 0x30, "b" },
> > -    { 0x31, "n" },
> > -    { 0x32, "m" },
> > -    { 0x33, "comma" },
> > -    { 0x34, "dot" },
> > -    { 0x35, "slash" },
> > -
> > -    { 0x37, "asterisk" },
> > -
> > -    { 0x39, "spc" },
> > -    { 0x3a, "caps_lock" },
> > -    { 0x3b, "f1" },
> > -    { 0x3c, "f2" },
> > -    { 0x3d, "f3" },
> > -    { 0x3e, "f4" },
> > -    { 0x3f, "f5" },
> > -    { 0x40, "f6" },
> > -    { 0x41, "f7" },
> > -    { 0x42, "f8" },
> > -    { 0x43, "f9" },
> > -    { 0x44, "f10" },
> > -    { 0x45, "num_lock" },
> > -    { 0x46, "scroll_lock" },
> > -
> > -    { 0xb5, "kp_divide" },
> > -    { 0x37, "kp_multiply" },
> > -    { 0x4a, "kp_subtract" },
> > -    { 0x4e, "kp_add" },
> > -    { 0x9c, "kp_enter" },
> > -    { 0x53, "kp_decimal" },
> > -    { 0x54, "sysrq" },
> > -
> > -    { 0x52, "kp_0" },
> > -    { 0x4f, "kp_1" },
> > -    { 0x50, "kp_2" },
> > -    { 0x51, "kp_3" },
> > -    { 0x4b, "kp_4" },
> > -    { 0x4c, "kp_5" },
> > -    { 0x4d, "kp_6" },
> > -    { 0x47, "kp_7" },
> > -    { 0x48, "kp_8" },
> > -    { 0x49, "kp_9" },
> > -
> > -    { 0x56, "less" },
> > -
> > -    { 0x57, "f11" },
> > -    { 0x58, "f12" },
> > -
> > -    { 0xb7, "print" },
> > -
> > -    { 0xc7, "home" },
> > -    { 0xc9, "pgup" },
> > -    { 0xd1, "pgdn" },
> > -    { 0xcf, "end" },
> > -
> > -    { 0xcb, "left" },
> > -    { 0xc8, "up" },
> > -    { 0xd0, "down" },
> > -    { 0xcd, "right" },
> > -
> > -    { 0xd2, "insert" },
> > -    { 0xd3, "delete" },
> > -#if defined(TARGET_SPARC) && !defined(TARGET_SPARC64)
> > -    { 0xf0, "stop" },
> > -    { 0xf1, "again" },
> > -    { 0xf2, "props" },
> > -    { 0xf3, "undo" },
> > -    { 0xf4, "front" },
> > -    { 0xf5, "copy" },
> > -    { 0xf6, "open" },
> > -    { 0xf7, "paste" },
> > -    { 0xf8, "find" },
> > -    { 0xf9, "cut" },
> > -    { 0xfa, "lf" },
> > -    { 0xfb, "help" },
> > -    { 0xfc, "meta_l" },
> > -    { 0xfd, "meta_r" },
> > -    { 0xfe, "compose" },
> > -#endif
> > -    { 0, NULL },
> > -};
> > -
> > -static int get_keycode(const char *key)
> > -{
> > -    const KeyDef *p;
> > -    char *endp;
> > -    int ret;
> > -
> > -    for(p = key_defs; p->name != NULL; p++) {
> > -        if (!strcmp(key, p->name))
> > -            return p->keycode;
> > -    }
> > -    if (strstart(key, "0x", NULL)) {
> > -        ret = strtoul(key, &endp, 0);
> > -        if (*endp == '\0' && ret >= 0x01 && ret <= 0xff)
> > -            return ret;
> > -    }
> > -    return -1;
> > -}
> > -
> > -#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;
> > -    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;
> > -            keycode = get_keycode(keyname_buf);
> > -            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)
> > @@ -4077,7 +3831,7 @@ static void monitor_find_completion(const
> > char *cmdline)
> >      int nb_args, i, len;
> >      const char *ptype, *str;
> >      const mon_cmd_t *cmd;
> > -    const KeyDef *key;
> > +    const int *code;
> >  
> >      parse_cmdline(cmdline, &nb_args, args);
> >  #ifdef DEBUG_COMPLETION
> > @@ -4151,9 +3905,14 @@ static void monitor_find_completion(const
> > char *cmdline)
> >                  if (sep)
> >                      str = sep + 1;
> >                  readline_set_completion_index(cur_mon->rs,
> >                  strlen(str));
> > -                for(key = key_defs; key->name != NULL; key++) {
> > -                    cmd_completion(str, key->name);
> 
> You can do:
> 
> cmd_completion(str, KeyCodes_lookup[i]);
> 
> The loop below is not needed.

This is enough, then we can make key_defs static.

                for (i = 0; i < Q_KEY_CODE_MAX; i++) {
                    cmd_completion(str, QKeyCode_lookup[i]);
                }

 
> > +                for (code = key_defs; code != NULL; code++) {
> > +                    int idx;
> > +                    idx = index_from_keycode(*code);
> > +                    if (idx != Q_KEY_CODE_MAX) {
> > +                        cmd_completion(str, QKeyCode_lookup[idx]);
> > +                    }
> >                  }
> > +
> >              } else if (!strcmp(cmd->name, "help|?")) {
> >                  readline_set_completion_index(cur_mon->rs,
> >                  strlen(str));
> >                  for (cmd = mon_cmds; cmd->name != NULL; cmd++) {
> > @@ -4681,7 +4440,6 @@ void monitor_init(CharDriverState *chr, int
> > flags)
> >      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 bc55ed2..3a562d8 100644
> > --- a/qapi-schema.json
> > +++ b/qapi-schema.json
> > @@ -2183,3 +2183,49 @@
> >  # Since: 0.14.0
> >  ##
> >  { 'command': 'closefd', 'data': {'fdname': 'str'} }
> > +
> > +##
> > +# @QKeyCode:
> > +#
> > +# An enumeration of key name.
> > +#
> > +# This is used by the send-key command.
> > +#
> > +# Since: 1.2
> > +##
> > +{ 'enum': 'QKeyCode',
> > +  'data': [ 'shift', 'shift_r', 'alt', 'alt_r', 'altgr',
> > 'altgr_r', 'ctrl',
> > +            'ctrl_r', 'menu', 'esc', '1', '2', '3', '4', '5', '6',
> > '7', '8',
> > +            '9', '0', 'minus', 'equal', 'backspace', 'tab', 'q',
> > 'w', 'e',
> > +            'r', 't', 'y', 'u', 'i', 'o', 'p', 'bracket_left',
> > 'bracket_right',
> > +            'ret', 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l',
> > 'semicolon',
> > +            'apostrophe', 'grave_accent', 'backslash', 'z', 'x',
> > 'c', 'v', 'b',
> > +            'n', 'm', 'comma', 'dot', 'slash', 'asterisk', 'spc',
> > 'caps_lock',
> > +            'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9',
> > 'f10',
> > +            'num_lock', 'scroll_lock', 'kp_divide', 'kp_multiply',
> > +            'kp_subtract', 'kp_add', 'kp_enter', 'kp_decimal',
> > 'sysrq', 'kp_0',
> > +            'kp_1', 'kp_2', 'kp_3', 'kp_4', 'kp_5', 'kp_6',
> > 'kp_7', 'kp_8',
> > +            'kp_9', 'less', 'f11', 'f12', 'print', 'home', 'pgup',
> > 'pgdn', 'end',
> > +            '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.2
> > +#
> > +##
> > +{ 'command': 'send-key',
> > +  'data': { 'keys': ['QKeyCode'], '*hold-time': 'int' } }
> > diff --git a/qmp-commands.hx b/qmp-commands.hx
> > index e3cf3c5..3c1e646 100644
> > --- a/qmp-commands.hx
> > +++ b/qmp-commands.hx
> > @@ -335,6 +335,34 @@ Example:
> >  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,
> 
> 
>
Luiz Capitulino Aug. 3, 2012, 1:23 p.m. UTC | #4
On Thu, 2 Aug 2012 21:38:00 -0400 (EDT)
Amos Kong <akong@redhat.com> wrote:

> ----- Original Message -----
> > On Thu, 26 Jul 2012 12:49:01 +0800
> > Amos Kong <akong@redhat.com> wrote:
> > 
> > > Convert 'sendkey' to use QAPI.
> > > 
> > > Keys' indexes in the enmu are same as keycodes' indexes in the
> > > key_defs[], index_from_code() and index_from_key() will return
> > > Q_KEY_CODE_MAX if the code/key is invalid.
> > > 
> > > 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>
> > 
> > I've some review comments below, besides I'd like to ask you to split
> > the addition of the QKeyCode enum and the key_defs table moving to a
> > different patch.
> 
> Ok.
> 
> > Other than that this looks quite better, hopefully v6 will be the
> > last one.
> 
> Thanks for your time :)
> 
> > > ---
> > >  console.h        |    5 +
> > >  hmp-commands.hx  |    2 +-
> > >  hmp.c            |   55 ++++++++++++
> > >  hmp.h            |    1 +
> > >  input.c          |  249
> > >  ++++++++++++++++++++++++++++++++++++++++++++++++++++
> > >  monitor.c        |  258
> > >  ++----------------------------------------------------
> > >  qapi-schema.json |   46 ++++++++++
> > >  qmp-commands.hx  |   28 ++++++
> > >  8 files changed, 393 insertions(+), 251 deletions(-)
> > > 
> > > diff --git a/console.h b/console.h
> > > index 4334db5..b2d7af6 100644
> > > --- a/console.h
> > > +++ b/console.h
> > > @@ -6,6 +6,7 @@
> > >  #include "notify.h"
> > >  #include "monitor.h"
> > >  #include "trace.h"
> > > +#include "qapi-types.h"
> > >  
> > >  /* keyboard/mouse support */
> > >  
> > > @@ -397,4 +398,8 @@ static inline int
> > > vnc_display_pw_expire(DisplayState *ds, time_t expires)
> > >  /* curses.c */
> > >  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);
> > >  #endif
> > > diff --git a/hmp-commands.hx b/hmp-commands.hx
> > > index 2891d48..8c2be24 100644
> > > --- a/hmp-commands.hx
> > > +++ b/hmp-commands.hx
> > > @@ -505,7 +505,7 @@ ETEXI
> > >          .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 6b72a64..041555a 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)
> > >  {
> > > @@ -1020,3 +1021,57 @@ void hmp_closefd(Monitor *mon, const QDict
> > > *qdict)
> > >      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) {
> > > +            error_set(&err, QERR_INVALID_PARAMETER, keyname_buf);
> > 
> > No need to use error_set(), you can call monitor_printf() directly.
> 
> Nod.
> 
> > > +            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 8d2b0d7..56d67a3 100644
> > > --- a/hmp.h
> > > +++ b/hmp.h
> > > @@ -66,5 +66,6 @@ void hmp_netdev_add(Monitor *mon, const QDict
> > > *qdict);
> > >  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 6968b31..2286581 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,6 +38,254 @@ static QTAILQ_HEAD(, QEMUPutMouseEntry)
> > > mouse_handlers =
> > >  static NotifierList mouse_mode_notifiers =
> > >      NOTIFIER_LIST_INITIALIZER(mouse_mode_notifiers);
> > >  
> > > +const int key_defs[] = {
> > 
> > Please, make key_defs static.
> 
> 
> 
> > > +    [Q_KEY_CODE_SHIFT] = 0x2a,
> > > +    [Q_KEY_CODE_SHIFT_R] = 0x36,
> > > +
> > > +    [Q_KEY_CODE_ALT] = 0x38,
> > > +    [Q_KEY_CODE_ALT_R] = 0xb8,
> > > +    [Q_KEY_CODE_ALTGR] = 0x64,
> > > +    [Q_KEY_CODE_ALTGR_R] = 0xe4,
> > > +    [Q_KEY_CODE_CTRL] = 0x1d,
> > > +    [Q_KEY_CODE_CTRL_R] = 0x9d,
> > > +
> > > +    [Q_KEY_CODE_MENU] = 0xdd,
> > > +
> > > +    [Q_KEY_CODE_ESC] = 0x01,
> > > +
> > > +    [Q_KEY_CODE_1] = 0x02,
> > > +    [Q_KEY_CODE_2] = 0x03,
> > > +    [Q_KEY_CODE_3] = 0x04,
> > > +    [Q_KEY_CODE_4] = 0x05,
> > > +    [Q_KEY_CODE_5] = 0x06,
> > > +    [Q_KEY_CODE_6] = 0x07,
> > > +    [Q_KEY_CODE_7] = 0x08,
> > > +    [Q_KEY_CODE_8] = 0x09,
> > > +    [Q_KEY_CODE_9] = 0x0a,
> > > +    [Q_KEY_CODE_0] = 0x0b,
> > > +    [Q_KEY_CODE_MINUS] = 0x0c,
> > > +    [Q_KEY_CODE_EQUAL] = 0x0d,
> > > +    [Q_KEY_CODE_BACKSPACE] = 0x0e,
> > > +
> > > +    [Q_KEY_CODE_TAB] = 0x0f,
> > > +    [Q_KEY_CODE_Q] = 0x10,
> > > +    [Q_KEY_CODE_W] = 0x11,
> > > +    [Q_KEY_CODE_E] = 0x12,
> > > +    [Q_KEY_CODE_R] = 0x13,
> > > +    [Q_KEY_CODE_T] = 0x14,
> > > +    [Q_KEY_CODE_Y] = 0x15,
> > > +    [Q_KEY_CODE_U] = 0x16,
> > > +    [Q_KEY_CODE_I] = 0x17,
> > > +    [Q_KEY_CODE_O] = 0x18,
> > > +    [Q_KEY_CODE_P] = 0x19,
> > > +    [Q_KEY_CODE_BRACKET_LEFT] = 0x1a,
> > > +    [Q_KEY_CODE_BRACKET_RIGHT] = 0x1b,
> > > +    [Q_KEY_CODE_RET] = 0x1c,
> > > +
> > > +    [Q_KEY_CODE_A] = 0x1e,
> > > +    [Q_KEY_CODE_S] = 0x1f,
> > > +    [Q_KEY_CODE_D] = 0x20,
> > > +    [Q_KEY_CODE_F] = 0x21,
> > > +    [Q_KEY_CODE_G] = 0x22,
> > > +    [Q_KEY_CODE_H] = 0x23,
> > > +    [Q_KEY_CODE_J] = 0x24,
> > > +    [Q_KEY_CODE_K] = 0x25,
> > > +    [Q_KEY_CODE_L] = 0x26,
> > > +    [Q_KEY_CODE_SEMICOLON] = 0x27,
> > > +    [Q_KEY_CODE_APOSTROPHE] = 0x28,
> > > +    [Q_KEY_CODE_GRAVE_ACCENT] = 0x29,
> > > +
> > > +    [Q_KEY_CODE_BACKSLASH] = 0x2b,
> > > +    [Q_KEY_CODE_Z] = 0x2c,
> > > +    [Q_KEY_CODE_X] = 0x2d,
> > > +    [Q_KEY_CODE_C] = 0x2e,
> > > +    [Q_KEY_CODE_V] = 0x2f,
> > > +    [Q_KEY_CODE_B] = 0x30,
> > > +    [Q_KEY_CODE_N] = 0x31,
> > > +    [Q_KEY_CODE_M] = 0x32,
> > > +    [Q_KEY_CODE_COMMA] = 0x33,
> > > +    [Q_KEY_CODE_DOT] = 0x34,
> > > +    [Q_KEY_CODE_SLASH] = 0x35,
> > > +
> > > +    [Q_KEY_CODE_ASTERISK] = 0x37,
> > > +
> > > +    [Q_KEY_CODE_SPC] = 0x39,
> > > +    [Q_KEY_CODE_CAPS_LOCK] = 0x3a,
> > > +    [Q_KEY_CODE_F1] = 0x3b,
> > > +    [Q_KEY_CODE_F2] = 0x3c,
> > > +    [Q_KEY_CODE_F3] = 0x3d,
> > > +    [Q_KEY_CODE_F4] = 0x3e,
> > > +    [Q_KEY_CODE_F5] = 0x3f,
> > > +    [Q_KEY_CODE_F6] = 0x40,
> > > +    [Q_KEY_CODE_F7] = 0x41,
> > > +    [Q_KEY_CODE_F8] = 0x42,
> > > +    [Q_KEY_CODE_F9] = 0x43,
> > > +    [Q_KEY_CODE_F10] = 0x44,
> > > +    [Q_KEY_CODE_NUM_LOCK] = 0x45,
> > > +    [Q_KEY_CODE_SCROLL_LOCK] = 0x46,
> > > +
> > > +    [Q_KEY_CODE_KP_DIVIDE] = 0xb5,
> > > +    [Q_KEY_CODE_KP_MULTIPLY] = 0x37,
> > > +    [Q_KEY_CODE_KP_SUBTRACT] = 0x4a,
> > > +    [Q_KEY_CODE_KP_ADD] = 0x4e,
> > > +    [Q_KEY_CODE_KP_ENTER] = 0x9c,
> > > +    [Q_KEY_CODE_KP_DECIMAL] = 0x53,
> > > +    [Q_KEY_CODE_SYSRQ] = 0x54,
> > > +
> > > +    [Q_KEY_CODE_KP_0] = 0x52,
> > > +    [Q_KEY_CODE_KP_1] = 0x4f,
> > > +    [Q_KEY_CODE_KP_2] = 0x50,
> > > +    [Q_KEY_CODE_KP_3] = 0x51,
> > > +    [Q_KEY_CODE_KP_4] = 0x4b,
> > > +    [Q_KEY_CODE_KP_5] = 0x4c,
> > > +    [Q_KEY_CODE_KP_6] = 0x4d,
> > > +    [Q_KEY_CODE_KP_7] = 0x47,
> > > +    [Q_KEY_CODE_KP_8] = 0x48,
> > > +    [Q_KEY_CODE_KP_9] = 0x49,
> > > +
> > > +    [Q_KEY_CODE_LESS] = 0x56,
> > > +
> > > +    [Q_KEY_CODE_F11] = 0x57,
> > > +    [Q_KEY_CODE_F12] = 0x58,
> > > +
> > > +    [Q_KEY_CODE_PRINT] = 0xb7,
> > > +
> > > +    [Q_KEY_CODE_HOME] = 0xc7,
> > > +    [Q_KEY_CODE_PGUP] = 0xc9,
> > > +    [Q_KEY_CODE_PGDN] = 0xd1,
> > > +    [Q_KEY_CODE_END] = 0xcf,
> > > +
> > > +    [Q_KEY_CODE_LEFT] = 0xcb,
> > > +    [Q_KEY_CODE_UP] = 0xc8,
> > > +    [Q_KEY_CODE_DOWN] = 0xd0,
> > > +    [Q_KEY_CODE_RIGHT] = 0xcd,
> > > +
> > > +    [Q_KEY_CODE_INSERT] = 0xd2,
> > > +    [Q_KEY_CODE_DELETE] = 0xd3,
> > > +#ifdef NEED_CPU_H
> > > +#if defined(TARGET_SPARC) && !defined(TARGET_SPARC64)
> > > +    [Q_KEY_CODE_STOP] = 0xf0,
> > > +    [Q_KEY_CODE_AGAIN] = 0xf1,
> > > +    [Q_KEY_CODE_PROPS] = 0xf2,
> > > +    [Q_KEY_CODE_UNDO] = 0xf3,
> > > +    [Q_KEY_CODE_FRONT] = 0xf4,
> > > +    [Q_KEY_CODE_COPY] = 0xf5,
> > > +    [Q_KEY_CODE_OPEN] = 0xf6,
> > > +    [Q_KEY_CODE_PASTE] = 0xf7,
> > > +    [Q_KEY_CODE_FIND] = 0xf8,
> > > +    [Q_KEY_CODE_CUT] = 0xf9,
> > > +    [Q_KEY_CODE_LF] = 0xfa,
> > > +    [Q_KEY_CODE_HELP] = 0xfb,
> > > +    [Q_KEY_CODE_META_L] = 0xfc,
> > > +    [Q_KEY_CODE_META_R] = 0xfd,
> > > +    [Q_KEY_CODE_COMPOSE] = 0xfe,
> > > +#endif
> > > +#endif
> > > +    [Q_KEY_CODE_MAX] = 0,
> > > +};
> > > +
> > > +int index_from_key(const char *key)
> > 
> > keycode_from_key() is better.
> 
> QAPI passes 'index' to qmp_send_key(), not value (keycode)
> So what those two functions return should be the 'index' of key_defs,
> not the value (keycode).

You're right, although the problem is that we're calling two things
a keycode: the hexdecimal value that is defined in key_defs and is passed
to the guest and the QKeyCode enum, which actually maps keycodes in string
format to an enum.

I honestly don't know what to suggest here, the problem is that
index_from_key() is too generic for a public function.

> 
> 'Index' will be converted to 'keycode' insider qmp_send_key()
>  
> > > +{
> > > +    int i, keycode;
> > > +    char *endp;
> > > +
> > > +    for (i = 0; QKeyCode_lookup[i] != NULL; i++) {
> > > +        if (!strcmp(key, QKeyCode_lookup[i])) {
> > > +            break;
> > > +        }
> > > +    }
> > > +
> > > +    if (strstart(key, "0x", NULL)) {
> > > +        keycode = strtoul(key, &endp, 0);
> > > +        if (*endp == '\0' && keycode >= 0x01 && keycode <= 0xff) {
> > > +            for (i = 0; i < Q_KEY_CODE_MAX; i++) {
> > > +                if (keycode == key_defs[i]) {
> > > +                    break;
> > > +                }
> > > +            }
> > > +        }
> > > +    }
> > > +
> > > +    /* Return Q_KEY_CODE_MAX if the key is invalid */
> > > +    return i;
> > > +}
> > > +
> > > +int index_from_keycode(int code)
> > 
> > keycode_from_code()
> > 
> > > +{
> > > +    int i;
> > > +    for (i = 0; i < Q_KEY_CODE_MAX; i++) {
> > > +        if (key_defs[i] == code) {
> > > +            break;
> > > +        }
> > > +    }
> > > +    /* Return Q_KEY_CODE_MAX if the code is invalid */
> > > +    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 4db0c1e..e3a2023 100644
> > > --- a/monitor.c
> > > +++ b/monitor.c
> > > @@ -1293,252 +1293,6 @@ static void do_sum(Monitor *mon, const
> > > QDict *qdict)
> > >      monitor_printf(mon, "%05d\n", sum);
> > >  }
> > >  
> > > -typedef struct {
> > > -    int keycode;
> > > -    const char *name;
> > > -} KeyDef;
> > > -
> > > -static const KeyDef key_defs[] = {
> > > -    { 0x2a, "shift" },
> > > -    { 0x36, "shift_r" },
> > > -
> > > -    { 0x38, "alt" },
> > > -    { 0xb8, "alt_r" },
> > > -    { 0x64, "altgr" },
> > > -    { 0xe4, "altgr_r" },
> > > -    { 0x1d, "ctrl" },
> > > -    { 0x9d, "ctrl_r" },
> > > -
> > > -    { 0xdd, "menu" },
> > > -
> > > -    { 0x01, "esc" },
> > > -
> > > -    { 0x02, "1" },
> > > -    { 0x03, "2" },
> > > -    { 0x04, "3" },
> > > -    { 0x05, "4" },
> > > -    { 0x06, "5" },
> > > -    { 0x07, "6" },
> > > -    { 0x08, "7" },
> > > -    { 0x09, "8" },
> > > -    { 0x0a, "9" },
> > > -    { 0x0b, "0" },
> > > -    { 0x0c, "minus" },
> > > -    { 0x0d, "equal" },
> > > -    { 0x0e, "backspace" },
> > > -
> > > -    { 0x0f, "tab" },
> > > -    { 0x10, "q" },
> > > -    { 0x11, "w" },
> > > -    { 0x12, "e" },
> > > -    { 0x13, "r" },
> > > -    { 0x14, "t" },
> > > -    { 0x15, "y" },
> > > -    { 0x16, "u" },
> > > -    { 0x17, "i" },
> > > -    { 0x18, "o" },
> > > -    { 0x19, "p" },
> > > -    { 0x1a, "bracket_left" },
> > > -    { 0x1b, "bracket_right" },
> > > -    { 0x1c, "ret" },
> > > -
> > > -    { 0x1e, "a" },
> > > -    { 0x1f, "s" },
> > > -    { 0x20, "d" },
> > > -    { 0x21, "f" },
> > > -    { 0x22, "g" },
> > > -    { 0x23, "h" },
> > > -    { 0x24, "j" },
> > > -    { 0x25, "k" },
> > > -    { 0x26, "l" },
> > > -    { 0x27, "semicolon" },
> > > -    { 0x28, "apostrophe" },
> > > -    { 0x29, "grave_accent" },
> > > -
> > > -    { 0x2b, "backslash" },
> > > -    { 0x2c, "z" },
> > > -    { 0x2d, "x" },
> > > -    { 0x2e, "c" },
> > > -    { 0x2f, "v" },
> > > -    { 0x30, "b" },
> > > -    { 0x31, "n" },
> > > -    { 0x32, "m" },
> > > -    { 0x33, "comma" },
> > > -    { 0x34, "dot" },
> > > -    { 0x35, "slash" },
> > > -
> > > -    { 0x37, "asterisk" },
> > > -
> > > -    { 0x39, "spc" },
> > > -    { 0x3a, "caps_lock" },
> > > -    { 0x3b, "f1" },
> > > -    { 0x3c, "f2" },
> > > -    { 0x3d, "f3" },
> > > -    { 0x3e, "f4" },
> > > -    { 0x3f, "f5" },
> > > -    { 0x40, "f6" },
> > > -    { 0x41, "f7" },
> > > -    { 0x42, "f8" },
> > > -    { 0x43, "f9" },
> > > -    { 0x44, "f10" },
> > > -    { 0x45, "num_lock" },
> > > -    { 0x46, "scroll_lock" },
> > > -
> > > -    { 0xb5, "kp_divide" },
> > > -    { 0x37, "kp_multiply" },
> > > -    { 0x4a, "kp_subtract" },
> > > -    { 0x4e, "kp_add" },
> > > -    { 0x9c, "kp_enter" },
> > > -    { 0x53, "kp_decimal" },
> > > -    { 0x54, "sysrq" },
> > > -
> > > -    { 0x52, "kp_0" },
> > > -    { 0x4f, "kp_1" },
> > > -    { 0x50, "kp_2" },
> > > -    { 0x51, "kp_3" },
> > > -    { 0x4b, "kp_4" },
> > > -    { 0x4c, "kp_5" },
> > > -    { 0x4d, "kp_6" },
> > > -    { 0x47, "kp_7" },
> > > -    { 0x48, "kp_8" },
> > > -    { 0x49, "kp_9" },
> > > -
> > > -    { 0x56, "less" },
> > > -
> > > -    { 0x57, "f11" },
> > > -    { 0x58, "f12" },
> > > -
> > > -    { 0xb7, "print" },
> > > -
> > > -    { 0xc7, "home" },
> > > -    { 0xc9, "pgup" },
> > > -    { 0xd1, "pgdn" },
> > > -    { 0xcf, "end" },
> > > -
> > > -    { 0xcb, "left" },
> > > -    { 0xc8, "up" },
> > > -    { 0xd0, "down" },
> > > -    { 0xcd, "right" },
> > > -
> > > -    { 0xd2, "insert" },
> > > -    { 0xd3, "delete" },
> > > -#if defined(TARGET_SPARC) && !defined(TARGET_SPARC64)
> > > -    { 0xf0, "stop" },
> > > -    { 0xf1, "again" },
> > > -    { 0xf2, "props" },
> > > -    { 0xf3, "undo" },
> > > -    { 0xf4, "front" },
> > > -    { 0xf5, "copy" },
> > > -    { 0xf6, "open" },
> > > -    { 0xf7, "paste" },
> > > -    { 0xf8, "find" },
> > > -    { 0xf9, "cut" },
> > > -    { 0xfa, "lf" },
> > > -    { 0xfb, "help" },
> > > -    { 0xfc, "meta_l" },
> > > -    { 0xfd, "meta_r" },
> > > -    { 0xfe, "compose" },
> > > -#endif
> > > -    { 0, NULL },
> > > -};
> > > -
> > > -static int get_keycode(const char *key)
> > > -{
> > > -    const KeyDef *p;
> > > -    char *endp;
> > > -    int ret;
> > > -
> > > -    for(p = key_defs; p->name != NULL; p++) {
> > > -        if (!strcmp(key, p->name))
> > > -            return p->keycode;
> > > -    }
> > > -    if (strstart(key, "0x", NULL)) {
> > > -        ret = strtoul(key, &endp, 0);
> > > -        if (*endp == '\0' && ret >= 0x01 && ret <= 0xff)
> > > -            return ret;
> > > -    }
> > > -    return -1;
> > > -}
> > > -
> > > -#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;
> > > -    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;
> > > -            keycode = get_keycode(keyname_buf);
> > > -            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)
> > > @@ -4077,7 +3831,7 @@ static void monitor_find_completion(const
> > > char *cmdline)
> > >      int nb_args, i, len;
> > >      const char *ptype, *str;
> > >      const mon_cmd_t *cmd;
> > > -    const KeyDef *key;
> > > +    const int *code;
> > >  
> > >      parse_cmdline(cmdline, &nb_args, args);
> > >  #ifdef DEBUG_COMPLETION
> > > @@ -4151,9 +3905,14 @@ static void monitor_find_completion(const
> > > char *cmdline)
> > >                  if (sep)
> > >                      str = sep + 1;
> > >                  readline_set_completion_index(cur_mon->rs,
> > >                  strlen(str));
> > > -                for(key = key_defs; key->name != NULL; key++) {
> > > -                    cmd_completion(str, key->name);
> > 
> > You can do:
> > 
> > cmd_completion(str, KeyCodes_lookup[i]);
> > 
> > The loop below is not needed.
> 
> This is enough, then we can make key_defs static.
> 
>                 for (i = 0; i < Q_KEY_CODE_MAX; i++) {
>                     cmd_completion(str, QKeyCode_lookup[i]);
>                 }
> 
>  
> > > +                for (code = key_defs; code != NULL; code++) {
> > > +                    int idx;
> > > +                    idx = index_from_keycode(*code);
> > > +                    if (idx != Q_KEY_CODE_MAX) {
> > > +                        cmd_completion(str, QKeyCode_lookup[idx]);
> > > +                    }
> > >                  }
> > > +
> > >              } else if (!strcmp(cmd->name, "help|?")) {
> > >                  readline_set_completion_index(cur_mon->rs,
> > >                  strlen(str));
> > >                  for (cmd = mon_cmds; cmd->name != NULL; cmd++) {
> > > @@ -4681,7 +4440,6 @@ void monitor_init(CharDriverState *chr, int
> > > flags)
> > >      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 bc55ed2..3a562d8 100644
> > > --- a/qapi-schema.json
> > > +++ b/qapi-schema.json
> > > @@ -2183,3 +2183,49 @@
> > >  # Since: 0.14.0
> > >  ##
> > >  { 'command': 'closefd', 'data': {'fdname': 'str'} }
> > > +
> > > +##
> > > +# @QKeyCode:
> > > +#
> > > +# An enumeration of key name.
> > > +#
> > > +# This is used by the send-key command.
> > > +#
> > > +# Since: 1.2
> > > +##
> > > +{ 'enum': 'QKeyCode',
> > > +  'data': [ 'shift', 'shift_r', 'alt', 'alt_r', 'altgr',
> > > 'altgr_r', 'ctrl',
> > > +            'ctrl_r', 'menu', 'esc', '1', '2', '3', '4', '5', '6',
> > > '7', '8',
> > > +            '9', '0', 'minus', 'equal', 'backspace', 'tab', 'q',
> > > 'w', 'e',
> > > +            'r', 't', 'y', 'u', 'i', 'o', 'p', 'bracket_left',
> > > 'bracket_right',
> > > +            'ret', 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l',
> > > 'semicolon',
> > > +            'apostrophe', 'grave_accent', 'backslash', 'z', 'x',
> > > 'c', 'v', 'b',
> > > +            'n', 'm', 'comma', 'dot', 'slash', 'asterisk', 'spc',
> > > 'caps_lock',
> > > +            'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9',
> > > 'f10',
> > > +            'num_lock', 'scroll_lock', 'kp_divide', 'kp_multiply',
> > > +            'kp_subtract', 'kp_add', 'kp_enter', 'kp_decimal',
> > > 'sysrq', 'kp_0',
> > > +            'kp_1', 'kp_2', 'kp_3', 'kp_4', 'kp_5', 'kp_6',
> > > 'kp_7', 'kp_8',
> > > +            'kp_9', 'less', 'f11', 'f12', 'print', 'home', 'pgup',
> > > 'pgdn', 'end',
> > > +            '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.2
> > > +#
> > > +##
> > > +{ 'command': 'send-key',
> > > +  'data': { 'keys': ['QKeyCode'], '*hold-time': 'int' } }
> > > diff --git a/qmp-commands.hx b/qmp-commands.hx
> > > index e3cf3c5..3c1e646 100644
> > > --- a/qmp-commands.hx
> > > +++ b/qmp-commands.hx
> > > @@ -335,6 +335,34 @@ Example:
> > >  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,
> > 
> > 
> > 
>
diff mbox

Patch

diff --git a/console.h b/console.h
index 4334db5..b2d7af6 100644
--- a/console.h
+++ b/console.h
@@ -6,6 +6,7 @@ 
 #include "notify.h"
 #include "monitor.h"
 #include "trace.h"
+#include "qapi-types.h"
 
 /* keyboard/mouse support */
 
@@ -397,4 +398,8 @@  static inline int vnc_display_pw_expire(DisplayState *ds, time_t expires)
 /* curses.c */
 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);
 #endif
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 2891d48..8c2be24 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -505,7 +505,7 @@  ETEXI
         .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 6b72a64..041555a 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)
 {
@@ -1020,3 +1021,57 @@  void hmp_closefd(Monitor *mon, const QDict *qdict)
     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) {
+            error_set(&err, QERR_INVALID_PARAMETER, 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 8d2b0d7..56d67a3 100644
--- a/hmp.h
+++ b/hmp.h
@@ -66,5 +66,6 @@  void hmp_netdev_add(Monitor *mon, const QDict *qdict);
 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 6968b31..2286581 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,6 +38,254 @@  static QTAILQ_HEAD(, QEMUPutMouseEntry) mouse_handlers =
 static NotifierList mouse_mode_notifiers = 
     NOTIFIER_LIST_INITIALIZER(mouse_mode_notifiers);
 
+const int key_defs[] = {
+    [Q_KEY_CODE_SHIFT] = 0x2a,
+    [Q_KEY_CODE_SHIFT_R] = 0x36,
+
+    [Q_KEY_CODE_ALT] = 0x38,
+    [Q_KEY_CODE_ALT_R] = 0xb8,
+    [Q_KEY_CODE_ALTGR] = 0x64,
+    [Q_KEY_CODE_ALTGR_R] = 0xe4,
+    [Q_KEY_CODE_CTRL] = 0x1d,
+    [Q_KEY_CODE_CTRL_R] = 0x9d,
+
+    [Q_KEY_CODE_MENU] = 0xdd,
+
+    [Q_KEY_CODE_ESC] = 0x01,
+
+    [Q_KEY_CODE_1] = 0x02,
+    [Q_KEY_CODE_2] = 0x03,
+    [Q_KEY_CODE_3] = 0x04,
+    [Q_KEY_CODE_4] = 0x05,
+    [Q_KEY_CODE_5] = 0x06,
+    [Q_KEY_CODE_6] = 0x07,
+    [Q_KEY_CODE_7] = 0x08,
+    [Q_KEY_CODE_8] = 0x09,
+    [Q_KEY_CODE_9] = 0x0a,
+    [Q_KEY_CODE_0] = 0x0b,
+    [Q_KEY_CODE_MINUS] = 0x0c,
+    [Q_KEY_CODE_EQUAL] = 0x0d,
+    [Q_KEY_CODE_BACKSPACE] = 0x0e,
+
+    [Q_KEY_CODE_TAB] = 0x0f,
+    [Q_KEY_CODE_Q] = 0x10,
+    [Q_KEY_CODE_W] = 0x11,
+    [Q_KEY_CODE_E] = 0x12,
+    [Q_KEY_CODE_R] = 0x13,
+    [Q_KEY_CODE_T] = 0x14,
+    [Q_KEY_CODE_Y] = 0x15,
+    [Q_KEY_CODE_U] = 0x16,
+    [Q_KEY_CODE_I] = 0x17,
+    [Q_KEY_CODE_O] = 0x18,
+    [Q_KEY_CODE_P] = 0x19,
+    [Q_KEY_CODE_BRACKET_LEFT] = 0x1a,
+    [Q_KEY_CODE_BRACKET_RIGHT] = 0x1b,
+    [Q_KEY_CODE_RET] = 0x1c,
+
+    [Q_KEY_CODE_A] = 0x1e,
+    [Q_KEY_CODE_S] = 0x1f,
+    [Q_KEY_CODE_D] = 0x20,
+    [Q_KEY_CODE_F] = 0x21,
+    [Q_KEY_CODE_G] = 0x22,
+    [Q_KEY_CODE_H] = 0x23,
+    [Q_KEY_CODE_J] = 0x24,
+    [Q_KEY_CODE_K] = 0x25,
+    [Q_KEY_CODE_L] = 0x26,
+    [Q_KEY_CODE_SEMICOLON] = 0x27,
+    [Q_KEY_CODE_APOSTROPHE] = 0x28,
+    [Q_KEY_CODE_GRAVE_ACCENT] = 0x29,
+
+    [Q_KEY_CODE_BACKSLASH] = 0x2b,
+    [Q_KEY_CODE_Z] = 0x2c,
+    [Q_KEY_CODE_X] = 0x2d,
+    [Q_KEY_CODE_C] = 0x2e,
+    [Q_KEY_CODE_V] = 0x2f,
+    [Q_KEY_CODE_B] = 0x30,
+    [Q_KEY_CODE_N] = 0x31,
+    [Q_KEY_CODE_M] = 0x32,
+    [Q_KEY_CODE_COMMA] = 0x33,
+    [Q_KEY_CODE_DOT] = 0x34,
+    [Q_KEY_CODE_SLASH] = 0x35,
+
+    [Q_KEY_CODE_ASTERISK] = 0x37,
+
+    [Q_KEY_CODE_SPC] = 0x39,
+    [Q_KEY_CODE_CAPS_LOCK] = 0x3a,
+    [Q_KEY_CODE_F1] = 0x3b,
+    [Q_KEY_CODE_F2] = 0x3c,
+    [Q_KEY_CODE_F3] = 0x3d,
+    [Q_KEY_CODE_F4] = 0x3e,
+    [Q_KEY_CODE_F5] = 0x3f,
+    [Q_KEY_CODE_F6] = 0x40,
+    [Q_KEY_CODE_F7] = 0x41,
+    [Q_KEY_CODE_F8] = 0x42,
+    [Q_KEY_CODE_F9] = 0x43,
+    [Q_KEY_CODE_F10] = 0x44,
+    [Q_KEY_CODE_NUM_LOCK] = 0x45,
+    [Q_KEY_CODE_SCROLL_LOCK] = 0x46,
+
+    [Q_KEY_CODE_KP_DIVIDE] = 0xb5,
+    [Q_KEY_CODE_KP_MULTIPLY] = 0x37,
+    [Q_KEY_CODE_KP_SUBTRACT] = 0x4a,
+    [Q_KEY_CODE_KP_ADD] = 0x4e,
+    [Q_KEY_CODE_KP_ENTER] = 0x9c,
+    [Q_KEY_CODE_KP_DECIMAL] = 0x53,
+    [Q_KEY_CODE_SYSRQ] = 0x54,
+
+    [Q_KEY_CODE_KP_0] = 0x52,
+    [Q_KEY_CODE_KP_1] = 0x4f,
+    [Q_KEY_CODE_KP_2] = 0x50,
+    [Q_KEY_CODE_KP_3] = 0x51,
+    [Q_KEY_CODE_KP_4] = 0x4b,
+    [Q_KEY_CODE_KP_5] = 0x4c,
+    [Q_KEY_CODE_KP_6] = 0x4d,
+    [Q_KEY_CODE_KP_7] = 0x47,
+    [Q_KEY_CODE_KP_8] = 0x48,
+    [Q_KEY_CODE_KP_9] = 0x49,
+
+    [Q_KEY_CODE_LESS] = 0x56,
+
+    [Q_KEY_CODE_F11] = 0x57,
+    [Q_KEY_CODE_F12] = 0x58,
+
+    [Q_KEY_CODE_PRINT] = 0xb7,
+
+    [Q_KEY_CODE_HOME] = 0xc7,
+    [Q_KEY_CODE_PGUP] = 0xc9,
+    [Q_KEY_CODE_PGDN] = 0xd1,
+    [Q_KEY_CODE_END] = 0xcf,
+
+    [Q_KEY_CODE_LEFT] = 0xcb,
+    [Q_KEY_CODE_UP] = 0xc8,
+    [Q_KEY_CODE_DOWN] = 0xd0,
+    [Q_KEY_CODE_RIGHT] = 0xcd,
+
+    [Q_KEY_CODE_INSERT] = 0xd2,
+    [Q_KEY_CODE_DELETE] = 0xd3,
+#ifdef NEED_CPU_H
+#if defined(TARGET_SPARC) && !defined(TARGET_SPARC64)
+    [Q_KEY_CODE_STOP] = 0xf0,
+    [Q_KEY_CODE_AGAIN] = 0xf1,
+    [Q_KEY_CODE_PROPS] = 0xf2,
+    [Q_KEY_CODE_UNDO] = 0xf3,
+    [Q_KEY_CODE_FRONT] = 0xf4,
+    [Q_KEY_CODE_COPY] = 0xf5,
+    [Q_KEY_CODE_OPEN] = 0xf6,
+    [Q_KEY_CODE_PASTE] = 0xf7,
+    [Q_KEY_CODE_FIND] = 0xf8,
+    [Q_KEY_CODE_CUT] = 0xf9,
+    [Q_KEY_CODE_LF] = 0xfa,
+    [Q_KEY_CODE_HELP] = 0xfb,
+    [Q_KEY_CODE_META_L] = 0xfc,
+    [Q_KEY_CODE_META_R] = 0xfd,
+    [Q_KEY_CODE_COMPOSE] = 0xfe,
+#endif
+#endif
+    [Q_KEY_CODE_MAX] = 0,
+};
+
+int index_from_key(const char *key)
+{
+    int i, keycode;
+    char *endp;
+
+    for (i = 0; QKeyCode_lookup[i] != NULL; i++) {
+        if (!strcmp(key, QKeyCode_lookup[i])) {
+            break;
+        }
+    }
+
+    if (strstart(key, "0x", NULL)) {
+        keycode = strtoul(key, &endp, 0);
+        if (*endp == '\0' && keycode >= 0x01 && keycode <= 0xff) {
+            for (i = 0; i < Q_KEY_CODE_MAX; i++) {
+                if (keycode == key_defs[i]) {
+                    break;
+                }
+            }
+        }
+    }
+
+    /* Return Q_KEY_CODE_MAX if the key is invalid */
+    return i;
+}
+
+int index_from_keycode(int code)
+{
+    int i;
+    for (i = 0; i < Q_KEY_CODE_MAX; i++) {
+        if (key_defs[i] == code) {
+            break;
+        }
+    }
+    /* Return Q_KEY_CODE_MAX if the code is invalid */
+    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 4db0c1e..e3a2023 100644
--- a/monitor.c
+++ b/monitor.c
@@ -1293,252 +1293,6 @@  static void do_sum(Monitor *mon, const QDict *qdict)
     monitor_printf(mon, "%05d\n", sum);
 }
 
-typedef struct {
-    int keycode;
-    const char *name;
-} KeyDef;
-
-static const KeyDef key_defs[] = {
-    { 0x2a, "shift" },
-    { 0x36, "shift_r" },
-
-    { 0x38, "alt" },
-    { 0xb8, "alt_r" },
-    { 0x64, "altgr" },
-    { 0xe4, "altgr_r" },
-    { 0x1d, "ctrl" },
-    { 0x9d, "ctrl_r" },
-
-    { 0xdd, "menu" },
-
-    { 0x01, "esc" },
-
-    { 0x02, "1" },
-    { 0x03, "2" },
-    { 0x04, "3" },
-    { 0x05, "4" },
-    { 0x06, "5" },
-    { 0x07, "6" },
-    { 0x08, "7" },
-    { 0x09, "8" },
-    { 0x0a, "9" },
-    { 0x0b, "0" },
-    { 0x0c, "minus" },
-    { 0x0d, "equal" },
-    { 0x0e, "backspace" },
-
-    { 0x0f, "tab" },
-    { 0x10, "q" },
-    { 0x11, "w" },
-    { 0x12, "e" },
-    { 0x13, "r" },
-    { 0x14, "t" },
-    { 0x15, "y" },
-    { 0x16, "u" },
-    { 0x17, "i" },
-    { 0x18, "o" },
-    { 0x19, "p" },
-    { 0x1a, "bracket_left" },
-    { 0x1b, "bracket_right" },
-    { 0x1c, "ret" },
-
-    { 0x1e, "a" },
-    { 0x1f, "s" },
-    { 0x20, "d" },
-    { 0x21, "f" },
-    { 0x22, "g" },
-    { 0x23, "h" },
-    { 0x24, "j" },
-    { 0x25, "k" },
-    { 0x26, "l" },
-    { 0x27, "semicolon" },
-    { 0x28, "apostrophe" },
-    { 0x29, "grave_accent" },
-
-    { 0x2b, "backslash" },
-    { 0x2c, "z" },
-    { 0x2d, "x" },
-    { 0x2e, "c" },
-    { 0x2f, "v" },
-    { 0x30, "b" },
-    { 0x31, "n" },
-    { 0x32, "m" },
-    { 0x33, "comma" },
-    { 0x34, "dot" },
-    { 0x35, "slash" },
-
-    { 0x37, "asterisk" },
-
-    { 0x39, "spc" },
-    { 0x3a, "caps_lock" },
-    { 0x3b, "f1" },
-    { 0x3c, "f2" },
-    { 0x3d, "f3" },
-    { 0x3e, "f4" },
-    { 0x3f, "f5" },
-    { 0x40, "f6" },
-    { 0x41, "f7" },
-    { 0x42, "f8" },
-    { 0x43, "f9" },
-    { 0x44, "f10" },
-    { 0x45, "num_lock" },
-    { 0x46, "scroll_lock" },
-
-    { 0xb5, "kp_divide" },
-    { 0x37, "kp_multiply" },
-    { 0x4a, "kp_subtract" },
-    { 0x4e, "kp_add" },
-    { 0x9c, "kp_enter" },
-    { 0x53, "kp_decimal" },
-    { 0x54, "sysrq" },
-
-    { 0x52, "kp_0" },
-    { 0x4f, "kp_1" },
-    { 0x50, "kp_2" },
-    { 0x51, "kp_3" },
-    { 0x4b, "kp_4" },
-    { 0x4c, "kp_5" },
-    { 0x4d, "kp_6" },
-    { 0x47, "kp_7" },
-    { 0x48, "kp_8" },
-    { 0x49, "kp_9" },
-
-    { 0x56, "less" },
-
-    { 0x57, "f11" },
-    { 0x58, "f12" },
-
-    { 0xb7, "print" },
-
-    { 0xc7, "home" },
-    { 0xc9, "pgup" },
-    { 0xd1, "pgdn" },
-    { 0xcf, "end" },
-
-    { 0xcb, "left" },
-    { 0xc8, "up" },
-    { 0xd0, "down" },
-    { 0xcd, "right" },
-
-    { 0xd2, "insert" },
-    { 0xd3, "delete" },
-#if defined(TARGET_SPARC) && !defined(TARGET_SPARC64)
-    { 0xf0, "stop" },
-    { 0xf1, "again" },
-    { 0xf2, "props" },
-    { 0xf3, "undo" },
-    { 0xf4, "front" },
-    { 0xf5, "copy" },
-    { 0xf6, "open" },
-    { 0xf7, "paste" },
-    { 0xf8, "find" },
-    { 0xf9, "cut" },
-    { 0xfa, "lf" },
-    { 0xfb, "help" },
-    { 0xfc, "meta_l" },
-    { 0xfd, "meta_r" },
-    { 0xfe, "compose" },
-#endif
-    { 0, NULL },
-};
-
-static int get_keycode(const char *key)
-{
-    const KeyDef *p;
-    char *endp;
-    int ret;
-
-    for(p = key_defs; p->name != NULL; p++) {
-        if (!strcmp(key, p->name))
-            return p->keycode;
-    }
-    if (strstart(key, "0x", NULL)) {
-        ret = strtoul(key, &endp, 0);
-        if (*endp == '\0' && ret >= 0x01 && ret <= 0xff)
-            return ret;
-    }
-    return -1;
-}
-
-#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;
-    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;
-            keycode = get_keycode(keyname_buf);
-            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)
@@ -4077,7 +3831,7 @@  static void monitor_find_completion(const char *cmdline)
     int nb_args, i, len;
     const char *ptype, *str;
     const mon_cmd_t *cmd;
-    const KeyDef *key;
+    const int *code;
 
     parse_cmdline(cmdline, &nb_args, args);
 #ifdef DEBUG_COMPLETION
@@ -4151,9 +3905,14 @@  static void monitor_find_completion(const char *cmdline)
                 if (sep)
                     str = sep + 1;
                 readline_set_completion_index(cur_mon->rs, strlen(str));
-                for(key = key_defs; key->name != NULL; key++) {
-                    cmd_completion(str, key->name);
+                for (code = key_defs; code != NULL; code++) {
+                    int idx;
+                    idx = index_from_keycode(*code);
+                    if (idx != Q_KEY_CODE_MAX) {
+                        cmd_completion(str, QKeyCode_lookup[idx]);
+                    }
                 }
+
             } else if (!strcmp(cmd->name, "help|?")) {
                 readline_set_completion_index(cur_mon->rs, strlen(str));
                 for (cmd = mon_cmds; cmd->name != NULL; cmd++) {
@@ -4681,7 +4440,6 @@  void monitor_init(CharDriverState *chr, int flags)
     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 bc55ed2..3a562d8 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2183,3 +2183,49 @@ 
 # Since: 0.14.0
 ##
 { 'command': 'closefd', 'data': {'fdname': 'str'} }
+
+##
+# @QKeyCode:
+#
+# An enumeration of key name.
+#
+# This is used by the send-key command.
+#
+# Since: 1.2
+##
+{ 'enum': 'QKeyCode',
+  'data': [ 'shift', 'shift_r', 'alt', 'alt_r', 'altgr', 'altgr_r', 'ctrl',
+            'ctrl_r', 'menu', 'esc', '1', '2', '3', '4', '5', '6', '7', '8',
+            '9', '0', 'minus', 'equal', 'backspace', 'tab', 'q', 'w', 'e',
+            'r', 't', 'y', 'u', 'i', 'o', 'p', 'bracket_left', 'bracket_right',
+            'ret', 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'semicolon',
+            'apostrophe', 'grave_accent', 'backslash', 'z', 'x', 'c', 'v', 'b',
+            'n', 'm', 'comma', 'dot', 'slash', 'asterisk', 'spc', 'caps_lock',
+            'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'f10',
+            'num_lock', 'scroll_lock', 'kp_divide', 'kp_multiply',
+            'kp_subtract', 'kp_add', 'kp_enter', 'kp_decimal', 'sysrq', 'kp_0',
+            'kp_1', 'kp_2', 'kp_3', 'kp_4', 'kp_5', 'kp_6', 'kp_7', 'kp_8',
+            'kp_9', 'less', 'f11', 'f12', 'print', 'home', 'pgup', 'pgdn', 'end',
+            '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.2
+#
+##
+{ 'command': 'send-key',
+  'data': { 'keys': ['QKeyCode'], '*hold-time': 'int' } }
diff --git a/qmp-commands.hx b/qmp-commands.hx
index e3cf3c5..3c1e646 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -335,6 +335,34 @@  Example:
 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,