diff mbox

[v2,6/6] qapi: convert sendkey

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

Commit Message

Amos Kong June 1, 2012, 10:54 p.m. UTC
Convert 'sendkey' to use QAPI. do_sendkey() depends on some
variables/functions in monitor.c, so reserve qmp_sendkey()
to monitor.c

key_defs[] in monitor.c is the mapping of key name to keycode,
Keys' order in the enmu and key_defs[] is same.

Signed-off-by: Amos Kong <akong@redhat.com>
---
 hmp-commands.hx  |    2 +-
 hmp.c            |   56 +++++++++++++++++++++++++++++++++++++++++++
 hmp.h            |    1 +
 monitor.c        |   70 +++++++++++++----------------------------------------
 qapi-schema.json |   47 ++++++++++++++++++++++++++++++++++++
 qmp-commands.hx  |   27 ++++++++++++++++++++
 6 files changed, 149 insertions(+), 54 deletions(-)

Comments

Eric Blake June 4, 2012, 5:09 p.m. UTC | #1
On 06/01/2012 04:54 PM, Amos Kong wrote:
> Convert 'sendkey' to use QAPI. do_sendkey() depends on some
> variables/functions in monitor.c, so reserve qmp_sendkey()
> to monitor.c
> 
> key_defs[] in monitor.c is the mapping of key name to keycode,
> Keys' order in the enmu and key_defs[] is same.
> 
> Signed-off-by: Amos Kong <akong@redhat.com>
> ---

> +++ b/qapi-schema.json
> @@ -1755,3 +1755,50 @@
>  # Since: 0.14.0
>  ##
>  { 'command': 'device_del', 'data': {'id': 'str'} }
> +
> +##
> +# @KeyCodes:
> +#
> +# An enumeration of key name.
> +#
> +# This is used by the sendkey command.
> +#
> +# Since: 0.14.0

Really?  Or is this enum since 1.2?

> +
> +##
> +# @sendkey:
> +#
> +# Send keys to VM.
> +#
> +# @keys: key sequence
> +# @hold-time: time to delay key up events, milliseconds
> +#
> +# Returns: Nothing on success
> +#          If key is unknown or redundant, QERR_INVALID_PARAMETER
> +#          If keys number is over the limitation, QERR_OVERFLOW
> +#
> +# Notes: Send keys to the emulator. Keys could be the name of the
> +#        key or the raw value in either decimal or hexadecimal  format. Use
> +#        "-" to press several keys simultaneously.

These notes don't really correspond to the QMP interface of passing in a
JSON array of simultaneous keys to press.

> +#
> +# Since: 0.14.0

Again, shouldn't this be 1.2?


> +SQMP
> +sendkey
> +----------
> +
> +Send keys to VM.
> +
> +Arguments:
> +
> +keys array:
> +    - "key": key sequence (json-string)
> +
> +- hold-time: time to delay key up events, miliseconds (josn-int, optional)

s/miliseconds/milliseconds/
Amos Kong June 5, 2012, 2:55 p.m. UTC | #2
Hello Eric,

Thanks for your comments.

----- Original Message -----
> On 06/01/2012 04:54 PM, Amos Kong wrote:
> > Convert 'sendkey' to use QAPI. do_sendkey() depends on some
> > variables/functions in monitor.c, so reserve qmp_sendkey()
> > to monitor.c
> > 
> > key_defs[] in monitor.c is the mapping of key name to keycode,
> > Keys' order in the enmu and key_defs[] is same.
> > 
> > Signed-off-by: Amos Kong <akong@redhat.com>
> > ---
> 
> > +++ b/qapi-schema.json
> > @@ -1755,3 +1755,50 @@
> >  # Since: 0.14.0
> >  ##
> >  { 'command': 'device_del', 'data': {'id': 'str'} }
> > +
> > +##
> > +# @KeyCodes:
> > +#
> > +# An enumeration of key name.
> > +#
> > +# This is used by the sendkey command.
> > +#
> > +# Since: 0.14.0
> 
> Really?  Or is this enum since 1.2?

Yeah, it should be 1.2

> 
> > +
> > +##
> > +# @sendkey:
> > +#
> > +# Send keys to VM.
> > +#
> > +# @keys: key sequence
> > +# @hold-time: time to delay key up events, milliseconds
> > +#
> > +# Returns: Nothing on success
> > +#          If key is unknown or redundant, QERR_INVALID_PARAMETER
> > +#          If keys number is over the limitation, QERR_OVERFLOW
> > +#
> > +# Notes: Send keys to the emulator. Keys could be the name of the
> > +#        key or the raw value in either decimal or hexadecimal format. Use
> > +#        "-" to press several keys simultaneously.
> 
> These notes don't really correspond to the QMP interface of passing
> in a JSON array of simultaneous keys to press.


# Notes: Send keys to the emulator. Keys could be the name of the
#        key or the raw value in either decimal or hexadecimal format. Use
#        a JSON array to press several keys simultaneously.



Ho, I found another bug in my code, key in decimal or hexadecimal
format is not supported. I will fix it.

 
> > +#
> > +# Since: 0.14.0
> 
> Again, shouldn't this be 1.2?

yeah, 1.2
 
> 
> > +SQMP
> > +sendkey
> > +----------
> > +
> > +Send keys to VM.
> > +
> > +Arguments:
> > +
> > +keys array:
> > +    - "key": key sequence (json-string)
> > +
> > +- hold-time: time to delay key up events, miliseconds (josn-int,
> > optional)
> 
> s/miliseconds/milliseconds/
> 
> --
> Eric Blake   eblake@redhat.com    +1-919-301-3266
> Libvirt virtualization library http://libvirt.org
> 
>
Eric Blake June 5, 2012, 3:05 p.m. UTC | #3
On 06/05/2012 08:55 AM, Amos Kong wrote:

>>> +# @sendkey:
>>> +#
>>> +# Send keys to VM.
>>> +#
>>> +# @keys: key sequence
>>> +# @hold-time: time to delay key up events, milliseconds
>>> +#
>>> +# Returns: Nothing on success
>>> +#          If key is unknown or redundant, QERR_INVALID_PARAMETER
>>> +#          If keys number is over the limitation, QERR_OVERFLOW
>>> +#
>>> +# Notes: Send keys to the emulator. Keys could be the name of the
>>> +#        key or the raw value in either decimal or hexadecimal format. Use
>>> +#        "-" to press several keys simultaneously.
>>
>> These notes don't really correspond to the QMP interface of passing
>> in a JSON array of simultaneous keys to press.
> 
> 
> # Notes: Send keys to the emulator. Keys could be the name of the
> #        key or the raw value in either decimal or hexadecimal format. Use
> #        a JSON array to press several keys simultaneously.
> 
> 
> 
> Ho, I found another bug in my code, key in decimal or hexadecimal
> format is not supported. I will fix it.

How do you plan to support decimal in QMP?  I don't think it's possible,
at least not without still keeping a JSON array of keys to press.  On
the other hand, I'm not sure if we need to worry about that.  Let me
explain:

Let's suppose the QMP interface is symbolic only.

In the HMP interface, _you_ can use the lookup list to convert decimal
to key name, so that the HMP interface is a wrapper around the QMP
interface, but everything is symbolic by the time we get that far.

In the libvirt case, where libvirt talks directly to QMP, libvirt also
has the same table for looking up keysyms vs. values (in fact, libvirt
already uses that table to convert between different keysets, so for an
example, the libvirt user can specify a command using xt_kbd or usb or
... mappings, which libvirt then converts into the mapping desired by
qemu).  Since libvirt already consults a table, libvirt can ensure that
the table has the proper QMP symbolic names in that table.

Finally, I know there was a patch proposed by Dan Berrange a while back
to let both libvirt and qemu share a common database of keyset names and
corresponding integer mappings.  I don't remember if it was applied to
qemu; if not, it should be revived and used at this time.
Amos Kong June 6, 2012, 7:13 a.m. UTC | #4
On 05/06/12 23:05, Eric Blake wrote:
> On 06/05/2012 08:55 AM, Amos Kong wrote:
>
>>>> +# @sendkey:
>>>> +#
>>>> +# Send keys to VM.
>>>> +#
>>>> +# @keys: key sequence
>>>> +# @hold-time: time to delay key up events, milliseconds
>>>> +#
>>>> +# Returns: Nothing on success
>>>> +#          If key is unknown or redundant, QERR_INVALID_PARAMETER
>>>> +#          If keys number is over the limitation, QERR_OVERFLOW
>>>> +#
>>>> +# Notes: Send keys to the emulator. Keys could be the name of the
>>>> +#        key or the raw value in either decimal or hexadecimal format. Use
>>>> +#        "-" to press several keys simultaneously.
>>>
>>> These notes don't really correspond to the QMP interface of passing
>>> in a JSON array of simultaneous keys to press.
>>
>>
>> # Notes: Send keys to the emulator. Keys could be the name of the
>> #        key or the raw value in either decimal or hexadecimal format. Use
>> #        a JSON array to press several keys simultaneously.
>>
>>
>>
>> Ho, I found another bug in my code, key in decimal or hexadecimal
>> format is not supported. I will fix it.
>
> How do you plan to support decimal in QMP?

In old do_sendkey(), only key-name and hexadecimal were supported,
the description needs to be fixed.


>   I don't think it's possible,
> at least not without still keeping a JSON array of keys to press.  On
> the other hand, I'm not sure if we need to worry about that.  Let me
> explain:
>
> Let's suppose the QMP interface is symbolic only.
>
> In the HMP interface, _you_ can use the lookup list to convert decimal
> to key name, so that the HMP interface is a wrapper around the QMP
> interface, but everything is symbolic by the time we get that far.
>
> In the libvirt case, where libvirt talks directly to QMP,

It seems we can only support key-name for QMP, and support 
key-name/hexadecimal
for HMP. Is it acceptable?


>  libvirt also
> has the same table for looking up keysyms vs. values (in fact, libvirt
> already uses that table to convert between different keysets, so for an
> example, the libvirt user can specify a command using xt_kbd or usb or
> ... mappings, which libvirt then converts into the mapping desired by
> qemu).  Since libvirt already consults a table, libvirt can ensure that
> the table has the proper QMP symbolic names in that table.


> Finally, I know there was a patch proposed by Dan Berrange a while back
> to let both libvirt and qemu share a common database of keyset names and
> corresponding integer mappings.  I don't remember if it was applied to
> qemu; if not, it should be revived and used at this time.
>
Eric Blake June 6, 2012, 11:58 a.m. UTC | #5
On 06/06/2012 01:13 AM, Amos Kong wrote:

>>> Ho, I found another bug in my code, key in decimal or hexadecimal
>>> format is not supported. I will fix it.
>>
>> How do you plan to support decimal in QMP?
> 
> In old do_sendkey(), only key-name and hexadecimal were supported,
> the description needs to be fixed.
> 

> 
> It seems we can only support key-name for QMP, and support
> key-name/hexadecimal
> for HMP. Is it acceptable?

Yes, that was my argument for bare minimum support - anyone using QMP
(like libvirt) will have to be smart enough to use key-name only, while
anyone using HMP will be able to use hex because HMP will convert hex to
key-name at the appropriate time before calling into QMP.

Of course, being able to support hex from QMP would be a nice feature,
but I'm not sure how to add it in.
Anthony Liguori June 7, 2012, 4:51 a.m. UTC | #6
On 06/06/2012 07:58 PM, Eric Blake wrote:
> On 06/06/2012 01:13 AM, Amos Kong wrote:
>
>>>> Ho, I found another bug in my code, key in decimal or hexadecimal
>>>> format is not supported. I will fix it.
>>>
>>> How do you plan to support decimal in QMP?
>>
>> In old do_sendkey(), only key-name and hexadecimal were supported,
>> the description needs to be fixed.
>>
>
>>
>> It seems we can only support key-name for QMP, and support
>> key-name/hexadecimal
>> for HMP. Is it acceptable?
>
> Yes, that was my argument for bare minimum support - anyone using QMP
> (like libvirt) will have to be smart enough to use key-name only, while
> anyone using HMP will be able to use hex because HMP will convert hex to
> key-name at the appropriate time before calling into QMP.
>
> Of course, being able to support hex from QMP would be a nice feature,
> but I'm not sure how to add it in.

Why is it a nice feature?

I don't quite understand why anyone would want to use hex actually...  You would 
have to convert from whatever symbolic code you're using to QEMU's hex value so 
why not just stay in symbolic representation.

Regards,

Anthony Liguori

>
Eric Blake June 7, 2012, 1:08 p.m. UTC | #7
On 06/06/2012 10:51 PM, Anthony Liguori wrote:

>> Of course, being able to support hex from QMP would be a nice feature,
>> but I'm not sure how to add it in.
> 
> Why is it a nice feature?

Because libvirt already allows users to pass in integers instead of
symbolic names.  Providing integer support in QMP would let the path be
passthrough, instead of libvirt doing a reverse lookup to convert
integer to symbol just to call QMP for qemu to do a lookup from symbol
back to integer.

> 
> I don't quite understand why anyone would want to use hex actually... 
> You would have to convert from whatever symbolic code you're using to
> QEMU's hex value so why not just stay in symbolic representation.

Staying just symbolic is fine - remember, I already stated that libvirt
already has a lookup table to convert between several different keycode
sets already, and Dan Berrange has already proposed sharing that table
with qemu.  And settling on symbolic as the only interface, even if it
is less efficient than integer passthrough, is at least easier to maintain.
diff mbox

Patch

diff --git a/hmp-commands.hx b/hmp-commands.hx
index 18b8e31..afbfa61 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_sendkey,
     },
 
 STEXI
diff --git a/hmp.c b/hmp.c
index bb0952e..06a6686 100644
--- a/hmp.c
+++ b/hmp.c
@@ -16,6 +16,7 @@ 
 #include "hmp.h"
 #include "qemu-timer.h"
 #include "qmp-commands.h"
+#include "qapi-types.h"
 
 static void hmp_handle_error(Monitor *mon, Error **errp)
 {
@@ -947,3 +948,58 @@  void hmp_device_del(Monitor *mon, const QDict *qdict)
     qmp_device_del(id, &err);
     hmp_handle_error(mon, &err);
 }
+
+static int get_index(const char *key)
+{
+    int i;
+
+    for (i = 0; KeyCodes_lookup[i] != NULL; i++) {
+        if (!strcmp(key, KeyCodes_lookup[i]))
+            return i;
+    }
+    return -1;
+}
+
+void hmp_sendkey(Monitor *mon, const QDict *qdict)
+{
+    const char *keys = qdict_get_str(qdict, "keys");
+    KeyCodesList *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;
+
+    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 (!strcmp(keyname_buf, "<")) {
+            pstrcpy(keyname_buf, sizeof(keyname_buf), "less");
+            keyname_len = 4;
+        }
+        keyname_buf[keyname_len] = 0;
+
+        keylist = g_malloc0(sizeof(*keylist));
+        keylist->value = get_index(keyname_buf);
+        keylist->next = NULL;
+
+        if (tmp)
+            tmp->next = keylist;
+        tmp = keylist;
+        if (!head)
+            head = keylist;
+
+        if (!separator)
+            break;
+        keys = separator + 1;
+    }
+
+    qmp_sendkey(head, has_hold_time, hold_time, &err);
+    qapi_free_KeyCodesList(keylist);
+    hmp_handle_error(mon, &err);
+}
diff --git a/hmp.h b/hmp.h
index 443b812..40de72c 100644
--- a/hmp.h
+++ b/hmp.h
@@ -61,5 +61,6 @@  void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict);
 void hmp_block_job_cancel(Monitor *mon, const QDict *qdict);
 void hmp_migrate(Monitor *mon, const QDict *qdict);
 void hmp_device_del(Monitor *mon, const QDict *qdict);
+void hmp_sendkey(Monitor *mon, const QDict *qdict);
 
 #endif
diff --git a/monitor.c b/monitor.c
index 536d9dd..2858ac0 100644
--- a/monitor.c
+++ b/monitor.c
@@ -1341,24 +1341,6 @@  static const KeyDef key_defs[] = {
     { 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;
@@ -1377,14 +1359,12 @@  static void release_keys(void *opaque)
     }
 }
 
-static void do_sendkey(Monitor *mon, const QDict *qdict)
+void qmp_sendkey(KeyCodesList *keys, bool has_hold_time, int64_t hold_time,
+                 Error **errp)
 {
     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);
+    int keycode, i;
+    KeyCodesList *entry = keys;
 
     if (nb_pending_keycodes > 0) {
         qemu_del_timer(key_timer);
@@ -1392,39 +1372,23 @@  static void do_sendkey(Monitor *mon, const QDict *qdict)
     }
     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 (!strcmp(keyname_buf, "<")) {
-                pstrcpy(keyname_buf, sizeof(keyname_buf), "less");
-                keyname_len = 4;
-            }
+    i = 0;
+    while (entry != NULL) {
+        if (entry->value > sizeof(key_defs) / sizeof(*(key_defs))) {
+            error_set(errp, QERR_INVALID_PARAMETER, keyname_buf);
+            return;
+        }
 
-            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 (i == MAX_KEYCODES) {
+            error_set(errp, QERR_OVERFLOW);
+            return;
         }
-        if (!separator)
-            break;
-        keys = separator + 1;
+
+        keycodes[i++] = key_defs[entry->value].keycode;
+        entry = entry->next;
     }
+
     nb_pending_keycodes = i;
     /* key down events */
     for (i = 0; i < nb_pending_keycodes; i++) {
diff --git a/qapi-schema.json b/qapi-schema.json
index 2ca7195..8fa0cb7 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1755,3 +1755,50 @@ 
 # Since: 0.14.0
 ##
 { 'command': 'device_del', 'data': {'id': 'str'} }
+
+##
+# @KeyCodes:
+#
+# An enumeration of key name.
+#
+# This is used by the sendkey command.
+#
+# Since: 0.14.0
+##
+{ 'enum': 'KeyCodes',
+  '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' ] }
+
+##
+# @sendkey:
+#
+# Send keys to VM.
+#
+# @keys: key sequence
+# @hold-time: time to delay key up events, milliseconds
+#
+# Returns: Nothing on success
+#          If key is unknown or redundant, QERR_INVALID_PARAMETER
+#          If keys number is over the limitation, QERR_OVERFLOW
+#
+# Notes: Send keys to the emulator. Keys could be the name of the
+#        key or the raw value in either decimal or hexadecimal  format. Use
+#        "-" to press several keys simultaneously.
+#
+# Since: 0.14.0
+##
+{ 'command': 'sendkey',
+  'data': { 'keys': ['KeyCodes'], '*hold-time': 'int' } }
diff --git a/qmp-commands.hx b/qmp-commands.hx
index db980fa..3692805 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -335,6 +335,33 @@  Example:
 EQMP
 
     {
+        .name       = "sendkey",
+        .args_type  = "keys:O,hold-time:i?",
+        .mhandler.cmd_new = qmp_marshal_input_sendkey,
+    },
+
+SQMP
+sendkey
+----------
+
+Send keys to VM.
+
+Arguments:
+
+keys array:
+    - "key": key sequence (json-string)
+
+- hold-time: time to delay key up events, miliseconds (josn-int, optional)
+
+Example:
+
+-> { "execute": "sendkey",
+     "arguments": { 'keys': [ 'ctrl', 'alt', 'delete' ] } }
+<- { "return": {} }
+
+EQMP
+
+    {
         .name       = "cpu",
         .args_type  = "index:i",
         .mhandler.cmd_new = qmp_marshal_input_cpu,