[libvirt] [PATCH] add sendevent command and related APIs

Enable libvirt send some events to the guest. This command currently only supports NMI and key events. Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- daemon/remote.c | 52 +++++++++++++++++++++ daemon/remote_dispatch_args.h | 2 daemon/remote_dispatch_prototypes.h | 16 ++++++ daemon/remote_dispatch_table.h | 10 ++++ include/libvirt/libvirt.h.in | 3 + src/driver.h | 7 ++ src/esx/esx_driver.c | 2 src/libvirt.c | 88 ++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 2 src/libxl/libxl_driver.c | 2 src/lxc/lxc_driver.c | 2 src/openvz/openvz_driver.c | 2 src/phyp/phyp_driver.c | 4 + src/qemu/qemu_driver.c | 86 +++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 27 +++++++++++ src/qemu/qemu_monitor.h | 3 + src/qemu/qemu_monitor_json.c | 68 +++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 3 + src/qemu/qemu_monitor_text.c | 56 ++++++++++++++++++++++ src/qemu/qemu_monitor_text.h | 2 src/remote/remote_driver.c | 50 ++++++++++++++++++++ src/remote/remote_protocol.c | 22 +++++++++ src/remote/remote_protocol.h | 19 +++++++ src/remote/remote_protocol.x | 14 +++++ src/test/test_driver.c | 2 src/uml/uml_driver.c | 2 src/vbox/vbox_tmpl.c | 2 src/vmware/vmware_driver.c | 2 src/xen/xen_driver.c | 2 src/xenapi/xenapi_driver.c | 2 tools/virsh.c | 56 ++++++++++++++++++++++ 31 files changed, 608 insertions(+), 2 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 1700c2d..5f9e78a 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -2836,6 +2836,58 @@ remoteDispatchDomainGetBlkioParameters(struct qemud_server *server } static int +remoteDispatchDomainSendEventNMI(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error *rerr, + remote_domain_send_event_nmi_args *args, + void *ret ATTRIBUTE_UNUSED) +{ + virDomainPtr dom; + + dom = get_nonnull_domain(conn, args->dom); + if (dom == NULL) { + remoteDispatchConnError(rerr, conn); + return -1; + } + + if (virDomainSendEventNMI(dom, args->vcpu) == -1) { + virDomainFree(dom); + remoteDispatchConnError(rerr, conn); + return -1; + } + virDomainFree(dom); + return 0; +} + +static int +remoteDispatchDomainSendEventKey(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error *rerr, + remote_domain_send_event_key_args *args, + void *ret ATTRIBUTE_UNUSED) +{ + virDomainPtr dom; + + dom = get_nonnull_domain(conn, args->dom); + if (dom == NULL) { + remoteDispatchConnError(rerr, conn); + return -1; + } + + if (virDomainSendEventKey(dom, args->key) == -1) { + virDomainFree(dom); + remoteDispatchConnError(rerr, conn); + return -1; + } + virDomainFree(dom); + return 0; +} + +static int remoteDispatchDomainSetVcpus (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, diff --git a/daemon/remote_dispatch_args.h b/daemon/remote_dispatch_args.h index f9537d7..289a42e 100644 --- a/daemon/remote_dispatch_args.h +++ b/daemon/remote_dispatch_args.h @@ -178,3 +178,5 @@ remote_domain_migrate_set_max_speed_args val_remote_domain_migrate_set_max_speed_args; remote_storage_vol_upload_args val_remote_storage_vol_upload_args; remote_storage_vol_download_args val_remote_storage_vol_download_args; + remote_domain_send_event_nmi_args val_remote_domain_send_event_nmi_args; + remote_domain_send_event_key_args val_remote_domain_send_event_key_args; diff --git a/daemon/remote_dispatch_prototypes.h b/daemon/remote_dispatch_prototypes.h index 18bf41d..f920193 100644 --- a/daemon/remote_dispatch_prototypes.h +++ b/daemon/remote_dispatch_prototypes.h @@ -1618,3 +1618,19 @@ static int remoteDispatchSupportsFeature( remote_error *err, remote_supports_feature_args *args, remote_supports_feature_ret *ret); +static int remoteDispatchDomainSendEventNMI( + struct qemud_server *server, + struct qemud_client *client, + virConnectPtr conn, + remote_message_header *hdr, + remote_error *err, + remote_domain_send_event_nmi_args *args, + void *ret); +static int remoteDispatchDomainSendEventKey( + struct qemud_server *server, + struct qemud_client *client, + virConnectPtr conn, + remote_message_header *hdr, + remote_error *err, + remote_domain_send_event_key_args *args, + void *ret); diff --git a/daemon/remote_dispatch_table.h b/daemon/remote_dispatch_table.h index b39f7c2..a706b19 100644 --- a/daemon/remote_dispatch_table.h +++ b/daemon/remote_dispatch_table.h @@ -1052,3 +1052,13 @@ .args_filter = (xdrproc_t) xdr_remote_storage_vol_download_args, .ret_filter = (xdrproc_t) xdr_void, }, +{ /* DomainSendEventNmi => 210 */ + .fn = (dispatch_fn) remoteDispatchDomainSendEventNMI, + .args_filter = (xdrproc_t) xdr_remote_domain_send_event_nmi_args, + .ret_filter = (xdrproc_t) xdr_void, +}, +{ /* DomainSendEventKey => 211 */ + .fn = (dispatch_fn) remoteDispatchDomainSendEventKey, + .args_filter = (xdrproc_t) xdr_remote_domain_send_event_key_args, + .ret_filter = (xdrproc_t) xdr_void, +}, diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index bd36015..adbe482 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -2517,6 +2517,9 @@ int virDomainOpenConsole(virDomainPtr dom, virStreamPtr st, unsigned int flags); +int virDomainSendEventNMI(virDomainPtr domain, unsigned int vcpu); +int virDomainSendEventKey(virDomainPtr domain, const char *key); + #ifdef __cplusplus } #endif diff --git a/src/driver.h b/src/driver.h index e5f91ca..6caf13f 100644 --- a/src/driver.h +++ b/src/driver.h @@ -515,6 +515,11 @@ typedef int virStreamPtr st, unsigned int flags); +typedef int + (*virDrvDomainSendEventNMI)(virDomainPtr dom, unsigned int vcpu); + +typedef int + (*virDrvDomainSendEventKey)(virDomainPtr dom, const char *key); /** * _virDriver: @@ -639,6 +644,8 @@ struct _virDriver { virDrvDomainSnapshotDelete domainSnapshotDelete; virDrvQemuDomainMonitorCommand qemuDomainMonitorCommand; virDrvDomainOpenConsole domainOpenConsole; + virDrvDomainSendEventNMI domainSendEventNMI; + virDrvDomainSendEventKey domainSendEventKey; }; typedef int diff --git a/src/esx/esx_driver.c b/src/esx/esx_driver.c index deda372..7167712 100644 --- a/src/esx/esx_driver.c +++ b/src/esx/esx_driver.c @@ -4675,6 +4675,8 @@ static virDriver esxDriver = { esxDomainSnapshotDelete, /* domainSnapshotDelete */ NULL, /* qemuDomainMonitorCommand */ NULL, /* domainOpenConsole */ + NULL, /* domainSendEventNMI */ + NULL, /* domainSendEventKey */ }; diff --git a/src/libvirt.c b/src/libvirt.c index 9bdb4c8..245247f 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -5245,6 +5245,94 @@ error: } /** + * virDomainSendEvnetNMI: + * @domain: pointer to domain object, or NULL for Domain0 + * @vcpu: the virtual CPU id to send NMI to + * + * Send NMI to a special vcpu of the guest + * + * Returns 0 in case of success, -1 in case of failure. + */ + +int virDomainSendEventNMI(virDomainPtr domain, unsigned int vcpu) +{ + virConnectPtr conn; + VIR_DOMAIN_DEBUG(domain, "vcpu=%u", vcpu); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return (-1); + } + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + conn = domain->conn; + + if (conn->driver->domainSendEventNMI) { + int ret; + ret = conn->driver->domainSendEventNMI(domain, vcpu); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(domain->conn); + return -1; +} + +/** + * virDomainSendEventKey: + * @domain: pointer to domain object, or NULL for Domain0 + * @key: the string of key or key sequence + * + * Send a special key or key sequence to the guest + * + * Returns 0 in case of success, -1 in case of failure. + */ + +int virDomainSendEventKey(virDomainPtr domain, const char *key) +{ + virConnectPtr conn; + VIR_DOMAIN_DEBUG(domain, "key=%s", key); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return (-1); + } + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + conn = domain->conn; + + if (conn->driver->domainSendEventKey) { + int ret; + ret = conn->driver->domainSendEventKey(domain, key); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(domain->conn); + return -1; +} + +/** * virDomainSetVcpus: * @domain: pointer to domain object, or NULL for Domain0 * @nvcpus: the new number of virtual CPUs for this domain diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index b4aed41..cd0f474 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -434,6 +434,8 @@ LIBVIRT_0.9.0 { virEventRunDefaultImpl; virStorageVolDownload; virStorageVolUpload; + virDomainSendEventNMI; + virDomainSendEventKey; } LIBVIRT_0.8.8; # .... define new API here using predicted next version number .... diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c index e996ff6..040fc16 100644 --- a/src/libxl/libxl_driver.c +++ b/src/libxl/libxl_driver.c @@ -2353,6 +2353,8 @@ static virDriver libxlDriver = { NULL, /* domainSnapshotDelete */ NULL, /* qemuDomainMonitorCommand */ NULL, /* domainOpenConsole */ + NULL, /* domainSendEventNMI */ + NULL, /* domainSendEventKey */ }; static virStateDriver libxlStateDriver = { diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index e905302..1284ab1 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -2906,6 +2906,8 @@ static virDriver lxcDriver = { NULL, /* domainSnapshotDelete */ NULL, /* qemuDomainMonitorCommand */ lxcDomainOpenConsole, /* domainOpenConsole */ + NULL, /* domainSendEventNMI */ + NULL, /* domainSendEventKey */ }; static virStateDriver lxcStateDriver = { diff --git a/src/openvz/openvz_driver.c b/src/openvz/openvz_driver.c index fb30c37..26ba0a5 100644 --- a/src/openvz/openvz_driver.c +++ b/src/openvz/openvz_driver.c @@ -1654,6 +1654,8 @@ static virDriver openvzDriver = { NULL, /* domainSnapshotDelete */ NULL, /* qemuDomainMonitorCommand */ NULL, /* domainOpenConsole */ + NULL, /* domainSendEventNMI */ + NULL, /* domainSendEventKey */ }; int openvzRegister(void) { diff --git a/src/phyp/phyp_driver.c b/src/phyp/phyp_driver.c index 51f9ff6..d5d0ea6 100644 --- a/src/phyp/phyp_driver.c +++ b/src/phyp/phyp_driver.c @@ -4054,7 +4054,9 @@ static virDriver phypDriver = { NULL, /* domainRevertToSnapshot */ NULL, /* domainSnapshotDelete */ NULL, /* qemuMonitorCommand */ - NULL, /* domainOpenConsole */ + NULL, /* domainOpenConsole */ + NULL, /* domainSendEventNMI */ + NULL, /* domainSendEventKey */ }; static virStorageDriver phypStorageDriver = { diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index dd12dc8..02af591 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -1659,6 +1659,90 @@ static int qemudDomainSetMemory(virDomainPtr dom, unsigned long newmem) { return qemudDomainSetMemoryFlags(dom, newmem, VIR_DOMAIN_MEM_LIVE); } +static int qemuDomainSendEventNMI(virDomainPtr domain, unsigned int vcpu) +{ + struct qemud_driver *driver = domain->conn->privateData; + virDomainObjPtr vm = NULL; + int ret = -1; + qemuDomainObjPrivatePtr priv; + + qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, domain->uuid); + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(domain->uuid, uuidstr); + qemuReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + if (!virDomainObjIsActive(vm)) { + qemuReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto cleanup; + } + + priv = vm->privateData; + + if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) + goto cleanup; + qemuDomainObjEnterMonitorWithDriver(driver, vm); + ret = qemuMonitorSendEventNMI(priv->mon, vcpu); + qemuDomainObjExitMonitorWithDriver(driver, vm); + if (qemuDomainObjEndJob(vm) == 0) { + vm = NULL; + goto cleanup; + } + +cleanup: + if (vm) + virDomainObjUnlock(vm); + qemuDriverUnlock(driver); + return ret; +} + +static int qemuDomainSendEventKey(virDomainPtr domain, const char *key) +{ + struct qemud_driver *driver = domain->conn->privateData; + virDomainObjPtr vm = NULL; + int ret = -1; + qemuDomainObjPrivatePtr priv; + + qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, domain->uuid); + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(domain->uuid, uuidstr); + qemuReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + if (!virDomainObjIsActive(vm)) { + qemuReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto cleanup; + } + + priv = vm->privateData; + + if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) + goto cleanup; + qemuDomainObjEnterMonitorWithDriver(driver, vm); + ret = qemuMonitorSendEventKey(priv->mon, key); + qemuDomainObjExitMonitorWithDriver(driver, vm); + if (qemuDomainObjEndJob(vm) == 0) { + vm = NULL; + goto cleanup; + } + +cleanup: + if (vm) + virDomainObjUnlock(vm); + qemuDriverUnlock(driver); + return ret; +} + static int qemudDomainGetInfo(virDomainPtr dom, virDomainInfoPtr info) { struct qemud_driver *driver = dom->conn->privateData; @@ -6923,6 +7007,8 @@ static virDriver qemuDriver = { qemuDomainSnapshotDelete, /* domainSnapshotDelete */ qemuDomainMonitorCommand, /* qemuDomainMonitorCommand */ qemuDomainOpenConsole, /* domainOpenConsole */ + qemuDomainSendEventNMI, /* domainSendEventNMI */ + qemuDomainSendEventKey, /* domainSendEventKey */ }; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 2d28f8d..bc2e269 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2228,3 +2228,30 @@ int qemuMonitorArbitraryCommand(qemuMonitorPtr mon, ret = qemuMonitorTextArbitraryCommand(mon, cmd, reply); return ret; } + + +int qemuMonitorSendEventNMI(qemuMonitorPtr mon, unsigned int vcpu) +{ + int ret; + + VIR_DEBUG("mon=%p, vcpu=%u", mon, vcpu); + + if (mon->json) + ret = qemuMonitorJSONSendEventNMI(mon, vcpu); + else + ret = qemuMonitorTextSendEventNMI(mon, vcpu); + return ret; +} + +int qemuMonitorSendEventKey(qemuMonitorPtr mon, const char *key) +{ + int ret; + + VIR_DEBUG("mon=%p, key sequence=%s", mon, key); + + if (mon->json) + ret = qemuMonitorJSONSendEventKey(mon, key); + else + ret = qemuMonitorTextSendEventKey(mon, key); + return ret; +} diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index c90219b..fdc9859 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -423,6 +423,9 @@ int qemuMonitorArbitraryCommand(qemuMonitorPtr mon, char **reply, bool hmp); +int qemuMonitorSendEventNMI(qemuMonitorPtr mon, unsigned int vcpu); +int qemuMonitorSendEventKey(qemuMonitorPtr mon, const char *key); + /** * When running two dd process and using <> redirection, we need a * shell that will not truncate files. These two strings serve that diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 20a78e1..5149d9e 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -2513,3 +2513,71 @@ cleanup: return ret; } + +int qemuMonitorJSONSendEventNMI(qemuMonitorPtr mon, unsigned int vcpu) +{ + int ret = -1; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + char *hmp_cmd; + + /* + * FIXME: qmp nmi is not supported until qemu-0.16.0, + * use human-monitor-command instead temporary. + */ + if (virAsprintf(&hmp_cmd, "nmi %u", vcpu) < 0) { + virReportOOMError(); + return -1; + } + + cmd = qemuMonitorJSONMakeCommand("human-monitor-command", + "s:command-line", hmp_cmd, + NULL); + if (!cmd) + goto out_free_hmp_cmd; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + virJSONValueFree(cmd); + virJSONValueFree(reply); +out_free_hmp_cmd: + VIR_FREE(hmp_cmd); + return ret; +} + +int qemuMonitorJSONSendEventKey(qemuMonitorPtr mon, const char *key_seq) +{ + int ret = -1; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + char *hmp_cmd; + + /* + * FIXME: qmp sendkey is not supported until qemu-0.16.0, + * use human-monitor-command instead temporary. + */ + if (virAsprintf(&hmp_cmd, "sendkey %s", key_seq) < 0) { + virReportOOMError(); + return -1; + } + + cmd = qemuMonitorJSONMakeCommand("human-monitor-command", + "s:command-line", hmp_cmd, + NULL); + if (!cmd) + goto out_free_hmp_cmd; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + virJSONValueFree(cmd); + virJSONValueFree(reply); +out_free_hmp_cmd: + VIR_FREE(hmp_cmd); + return ret; +} diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 086f0e1..dc206df 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -204,4 +204,7 @@ int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon, char **reply_str, bool hmp); +int qemuMonitorJSONSendEventNMI(qemuMonitorPtr mon, unsigned int vcpu); +int qemuMonitorJSONSendEventKey(qemuMonitorPtr mon, const char *key); + #endif /* QEMU_MONITOR_JSON_H */ diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index e0e3292..d3416a8 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -2627,3 +2627,59 @@ int qemuMonitorTextArbitraryCommand(qemuMonitorPtr mon, const char *cmd, return ret; } + +int qemuMonitorTextSendEventNMI(qemuMonitorPtr mon, unsigned int vcpu) +{ + char *cmd; + char *reply = NULL; + int ret = -1; + + if (virAsprintf(&cmd, "nmi %u", vcpu) < 0) { + virReportOOMError(); + return -1; + } + if (qemuMonitorTextArbitraryCommand(mon, cmd, &reply)) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("failed to send NMI using command '%s'"), + cmd); + goto cleanup; + } + + ret = 0; + +cleanup: + VIR_FREE(cmd); + VIR_FREE(reply); + return ret; +} + +int qemuMonitorTextSendEventKey(qemuMonitorPtr mon, const char *key) +{ + char *cmd; + char *reply = NULL; + int ret = -1; + + if (virAsprintf(&cmd, "sendkey %s", key) < 0) { + virReportOOMError(); + return -1; + } + if (qemuMonitorTextArbitraryCommand(mon, cmd, &reply)) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("failed to send key using command '%s'"), + cmd); + goto cleanup; + } + + if (strstr(reply, "unknown key") != NULL) { + qemuReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("sent unknown key")); + goto cleanup; + } + + ret = 0; + +cleanup: + VIR_FREE(cmd); + VIR_FREE(reply); + return ret; +} diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h index 0838a2b..4a03c40 100644 --- a/src/qemu/qemu_monitor_text.h +++ b/src/qemu/qemu_monitor_text.h @@ -198,4 +198,6 @@ int qemuMonitorTextDeleteSnapshot(qemuMonitorPtr mon, const char *name); int qemuMonitorTextArbitraryCommand(qemuMonitorPtr mon, const char *cmd, char **reply); +int qemuMonitorTextSendEventNMI(qemuMonitorPtr mon, unsigned int vcpu); +int qemuMonitorTextSendEventKey(qemuMonitorPtr mon, const char *key); #endif /* QEMU_MONITOR_TEXT_H */ diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index bf94e70..676f473 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -2929,6 +2929,54 @@ done: } static int +remoteDomainSendEventNMI(virDomainPtr domain, unsigned int vcpu) +{ + int rv = -1; + remote_domain_send_event_nmi_args args; + struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); + + make_nonnull_domain (&args.dom, domain); + args.vcpu = vcpu; + + if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_SEND_EVENT_NMI, + (xdrproc_t) xdr_remote_domain_send_event_nmi_args, (char *) &args, + (xdrproc_t) xdr_void, (char *) NULL) == -1) + goto done; + + rv = 0; + +done: + remoteDriverUnlock(priv); + return rv; +} + +static int +remoteDomainSendEventKey(virDomainPtr domain, const char *key) +{ + int rv = -1; + remote_domain_send_event_key_args args; + struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); + + make_nonnull_domain (&args.dom, domain); + args.key = (char *)key; + + if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_SEND_EVENT_KEY, + (xdrproc_t) xdr_remote_domain_send_event_key_args, (char *) &args, + (xdrproc_t) xdr_void, (char *) NULL) == -1) + goto done; + + rv = 0; + +done: + remoteDriverUnlock(priv); + return rv; +} + +static int remoteDomainSetVcpus (virDomainPtr domain, unsigned int nvcpus) { int rv = -1; @@ -11296,6 +11344,8 @@ static virDriver remote_driver = { remoteDomainSnapshotDelete, /* domainSnapshotDelete */ remoteQemuDomainMonitorCommand, /* qemuDomainMonitorCommand */ remoteDomainOpenConsole, /* domainOpenConsole */ + remoteDomainSendEventNMI, /* domainSendEventNMI */ + remoteDomainSendEventKey, /* domainSendEventKey */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.c b/src/remote/remote_protocol.c index 5604371..a829219 100644 --- a/src/remote/remote_protocol.c +++ b/src/remote/remote_protocol.c @@ -1463,6 +1463,28 @@ xdr_remote_domain_undefine_args (XDR *xdrs, remote_domain_undefine_args *objp) } bool_t +xdr_remote_domain_send_event_nmi_args (XDR *xdrs, remote_domain_send_event_nmi_args *objp) +{ + + if (!xdr_remote_nonnull_domain (xdrs, &objp->dom)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->vcpu)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_domain_send_event_key_args (XDR *xdrs, remote_domain_send_event_key_args *objp) +{ + + if (!xdr_remote_nonnull_domain (xdrs, &objp->dom)) + return FALSE; + if (!xdr_remote_nonnull_string (xdrs, &objp->key)) + return FALSE; + return TRUE; +} + +bool_t xdr_remote_domain_set_vcpus_args (XDR *xdrs, remote_domain_set_vcpus_args *objp) { diff --git a/src/remote/remote_protocol.h b/src/remote/remote_protocol.h index d9bf151..027ef88 100644 --- a/src/remote/remote_protocol.h +++ b/src/remote/remote_protocol.h @@ -2200,6 +2200,19 @@ struct remote_storage_vol_download_args { u_int flags; }; typedef struct remote_storage_vol_download_args remote_storage_vol_download_args; + +struct remote_domain_send_event_nmi_args { + remote_nonnull_domain dom; + u_int vcpu; +}; +typedef struct remote_domain_send_event_nmi_args remote_domain_send_event_nmi_args; + +struct remote_domain_send_event_key_args { + remote_nonnull_domain dom; + remote_nonnull_string key; +}; +typedef struct remote_domain_send_event_key_args remote_domain_send_event_key_args; + #define REMOTE_PROGRAM 0x20008086 #define REMOTE_PROTOCOL_VERSION 1 @@ -2413,6 +2426,8 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_MIGRATE_SET_MAX_SPEED = 207, REMOTE_PROC_STORAGE_VOL_UPLOAD = 208, REMOTE_PROC_STORAGE_VOL_DOWNLOAD = 209, + REMOTE_PROC_DOMAIN_SEND_EVENT_NMI = 210, + REMOTE_PROC_DOMAIN_SEND_EVENT_KEY = 211, }; typedef enum remote_procedure remote_procedure; @@ -2561,6 +2576,8 @@ extern bool_t xdr_remote_domain_create_with_flags_ret (XDR *, remote_domain_cre extern bool_t xdr_remote_domain_define_xml_args (XDR *, remote_domain_define_xml_args*); extern bool_t xdr_remote_domain_define_xml_ret (XDR *, remote_domain_define_xml_ret*); extern bool_t xdr_remote_domain_undefine_args (XDR *, remote_domain_undefine_args*); +extern bool_t xdr_remote_domain_send_event_nmi_args (XDR *, remote_domain_send_event_nmi_args*); +extern bool_t xdr_remote_domain_send_event_key_args (XDR *, remote_domain_send_event_key_args*); extern bool_t xdr_remote_domain_set_vcpus_args (XDR *, remote_domain_set_vcpus_args*); extern bool_t xdr_remote_domain_set_vcpus_flags_args (XDR *, remote_domain_set_vcpus_flags_args*); extern bool_t xdr_remote_domain_get_vcpus_flags_args (XDR *, remote_domain_get_vcpus_flags_args*); @@ -2918,6 +2935,8 @@ extern bool_t xdr_remote_domain_create_with_flags_ret (); extern bool_t xdr_remote_domain_define_xml_args (); extern bool_t xdr_remote_domain_define_xml_ret (); extern bool_t xdr_remote_domain_undefine_args (); +extern bool_t xdr_remote_domain_send_event_nmi_args (); +extern bool_t xdr_remote_domain_send_event_key_args (); extern bool_t xdr_remote_domain_set_vcpus_args (); extern bool_t xdr_remote_domain_set_vcpus_flags_args (); extern bool_t xdr_remote_domain_get_vcpus_flags_args (); diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 675eccd..34600d7 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -817,6 +817,16 @@ struct remote_domain_undefine_args { remote_nonnull_domain dom; }; +struct remote_domain_send_event_nmi_args { + remote_nonnull_domain dom; + unsigned int vcpu; +}; + +struct remote_domain_send_event_key_args { + remote_nonnull_domain dom; + remote_nonnull_string key; +}; + struct remote_domain_set_vcpus_args { remote_nonnull_domain dom; int nvcpus; @@ -2176,8 +2186,10 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_GET_BLKIO_PARAMETERS = 206, REMOTE_PROC_DOMAIN_MIGRATE_SET_MAX_SPEED = 207, REMOTE_PROC_STORAGE_VOL_UPLOAD = 208, - REMOTE_PROC_STORAGE_VOL_DOWNLOAD = 209 + REMOTE_PROC_STORAGE_VOL_DOWNLOAD = 209, + REMOTE_PROC_DOMAIN_SEND_EVENT_NMI = 210, + REMOTE_PROC_DOMAIN_SEND_EVENT_KEY = 211, /* * Notice how the entries are grouped in sets of 10 ? * Nice isn't it. Please keep it this way when adding more. diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 17f5ad9..2163850 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -5447,6 +5447,8 @@ static virDriver testDriver = { NULL, /* domainSnapshotDelete */ NULL, /* qemuDomainMonitorCommand */ NULL, /* domainOpenConsole */ + NULL, /* domainSendEventNMI */ + NULL, /* domainSendEventKey */ }; static virNetworkDriver testNetworkDriver = { diff --git a/src/uml/uml_driver.c b/src/uml/uml_driver.c index e2bd5f2..756877d 100644 --- a/src/uml/uml_driver.c +++ b/src/uml/uml_driver.c @@ -2249,6 +2249,8 @@ static virDriver umlDriver = { NULL, /* domainSnapshotDelete */ NULL, /* qemuDomainMonitorCommand */ umlDomainOpenConsole, /* domainOpenConsole */ + NULL, /* domainSendEventNMI */ + NULL, /* domainSendEventKey */ }; static int diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index 8bd27dd..73c5c87 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -8647,6 +8647,8 @@ virDriver NAME(Driver) = { vboxDomainSnapshotDelete, /* domainSnapshotDelete */ NULL, /* qemuDomainMonitorCommand */ NULL, /* domainOpenConsole */ + NULL, /* domainSendEventNMI */ + NULL, /* domainSendEventKey */ }; virNetworkDriver NAME(NetworkDriver) = { diff --git a/src/vmware/vmware_driver.c b/src/vmware/vmware_driver.c index b5e416b..eb64087 100644 --- a/src/vmware/vmware_driver.c +++ b/src/vmware/vmware_driver.c @@ -1007,6 +1007,8 @@ static virDriver vmwareDriver = { NULL, /* domainSnapshotDelete */ NULL, /* qemuDomainMonitorCommand */ NULL, /* domainOpenConsole */ + NULL, /* domainSendEventNMI */ + NULL, /* domainSendEventKey */ }; int diff --git a/src/xen/xen_driver.c b/src/xen/xen_driver.c index 9f47722..bd82001 100644 --- a/src/xen/xen_driver.c +++ b/src/xen/xen_driver.c @@ -2141,6 +2141,8 @@ static virDriver xenUnifiedDriver = { NULL, /* domainSnapshotDelete */ NULL, /* qemuDomainMonitorCommand */ xenUnifiedDomainOpenConsole, /* domainOpenConsole */ + NULL, /* domainSendEventNMI */ + NULL, /* domainSendEventKey */ }; /** diff --git a/src/xenapi/xenapi_driver.c b/src/xenapi/xenapi_driver.c index 27206a0..0f85ad8 100644 --- a/src/xenapi/xenapi_driver.c +++ b/src/xenapi/xenapi_driver.c @@ -1885,6 +1885,8 @@ static virDriver xenapiDriver = { NULL, /* domainSnapshotDelete */ NULL, /* qemuDomainMonitorCommand */ NULL, /* domainOpenConsole */ + NULL, /* domainSendEventNMI */ + NULL, /* domainSendEventKey */ }; /** diff --git a/tools/virsh.c b/tools/virsh.c index faeaf47..0b78c6d 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -2910,6 +2910,61 @@ cmdSetvcpus(vshControl *ctl, const vshCmd *cmd) } /* + * "sendevent" command + */ +static const vshCmdInfo info_sendevent[] = { + {"help", N_("send events to the guest")}, + {"desc", N_("Send events (NMI or Keys) to the guest domain.")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_sendevent[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, + {"eventtype", VSH_OT_DATA, VSH_OFLAG_REQ, N_("the type of event (nmi or key)")}, + {"eventcontent", VSH_OT_DATA, VSH_OFLAG_REQ, N_("content for the event.")}, + {NULL, 0, 0, NULL} +}; + + +static int +cmdSendEvent(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom; + const char *type; + int ret = TRUE; + + if (!vshConnectionUsability(ctl, ctl->conn)) + return FALSE; + + if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) + return FALSE; + + if (vshCommandOptString(cmd, "eventtype", &type) < 0) + return FALSE; + + if (STREQ(type, "nmi")) { + int cpu; + + if ((vshCommandOptInt(cmd, "eventcontent", &cpu) < 0) + || (virDomainSendEventNMI(dom, cpu) < 0)) + ret = FALSE; + } else if (STREQ(type, "key")) { + const char *key; + + if ((vshCommandOptString(cmd, "eventcontent", &key) < 0) + || (virDomainSendEventKey(dom, key) < 0)) + ret = FALSE; + } else { + virDomainFree(dom); + vshError(ctl, _("Invalid event type: %s, only \"nmi\" or \"key\" supported currently."), type); + return FALSE; + } + + virDomainFree(dom); + return ret; +} + +/* * "setmemory" command */ static const vshCmdInfo info_setmem[] = { @@ -10693,6 +10748,7 @@ static const vshCmdDef domManagementCmds[] = { {"setmaxmem", cmdSetmaxmem, opts_setmaxmem, info_setmaxmem}, {"setmem", cmdSetmem, opts_setmem, info_setmem}, {"setvcpus", cmdSetvcpus, opts_setvcpus, info_setvcpus}, + {"sendevent", cmdSendEvent, opts_sendevent, info_sendevent}, {"shutdown", cmdShutdown, opts_shutdown, info_shutdown}, {"start", cmdStart, opts_start, info_start}, {"suspend", cmdSuspend, opts_suspend, info_suspend},

On 04/01/2011 03:51 AM, Lai Jiangshan wrote:
Enable libvirt send some events to the guest. This command currently only supports NMI and key events.
Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com>
Would you mind resending this split into several patches? For example, see how https://www.redhat.com/archives/libvir-list/2011-April/msg00052.html divided a new API into 7 patches: Michal Privoznik (7): screenshot: Defining the public API screenshot: Defining the internal API screenshot: Implementing the public API screenshot: Implementing the remote protocol screenshot: Expose the new API in virsh qemu: Implement the driver methods vbox: Implement the driver methods By separating definition, public access, virsh use, and driver implementation into separate patches, it becomes easier to review. Each division should still cleanly compile. See also http://libvirt.org/api_extension.html (although it predates some of the recent changes such as adding libxl and dropping OpenNebula, it is still a good reference).
+++ b/src/libvirt.c @@ -5245,6 +5245,94 @@ error: }
/** + * virDomainSendEvnetNMI:
s/Evnet/Event/
+ * @domain: pointer to domain object, or NULL for Domain0 + * @vcpu: the virtual CPU id to send NMI to + * + * Send NMI to a special vcpu of the guest
s/special/specified/
+ * + * Returns 0 in case of success, -1 in case of failure. + */ + +int virDomainSendEventNMI(virDomainPtr domain, unsigned int vcpu)
Absolutely must have a flags argument for future extension (even if the only supported flags value is 0 for now).
+ +/** + * virDomainSendEventKey: + * @domain: pointer to domain object, or NULL for Domain0 + * @key: the string of key or key sequence + * + * Send a special key or key sequence to the guest + * + * Returns 0 in case of success, -1 in case of failure. + */ + +int virDomainSendEventKey(virDomainPtr domain, const char *key)
Needs a flags argument. Also, what is the format of a key sequence? There's several layers of key events, from what a keyboard sends, to what the OS driver encodes, to what X encodes, and so forth. If the format ever needs to include a NUL byte, then you need a length parameter. I'm worried that this is a bit underspecified. Do we know in advance which keys are acceptable (I'm assuming this is for SysRq sequences), in which case an enum value might be better than a raw char*.
+++ b/src/libvirt_public.syms @@ -434,6 +434,8 @@ LIBVIRT_0.9.0 { virEventRunDefaultImpl; virStorageVolDownload; virStorageVolUpload; + virDomainSendEventNMI; + virDomainSendEventKey; } LIBVIRT_0.8.8;
This is too late for the 0.9.0 feature freeze cutoff; when you split this into multiple patches, can you rebase it to be targetting 0.9.1? I haven't closely reviewed the rest of the patch yet, but think that the overall idea of being able to inject NMI and SysRq input to the virtual hardware of the guest is worth adding. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Fri, Apr 01, 2011 at 05:51:06PM +0800, Lai Jiangshan wrote:
Enable libvirt send some events to the guest. This command currently only supports NMI and key events.
Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- daemon/remote.c | 52 +++++++++++++++++++++ daemon/remote_dispatch_args.h | 2 daemon/remote_dispatch_prototypes.h | 16 ++++++ daemon/remote_dispatch_table.h | 10 ++++ include/libvirt/libvirt.h.in | 3 + src/driver.h | 7 ++ src/esx/esx_driver.c | 2 src/libvirt.c | 88 ++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 2 src/libxl/libxl_driver.c | 2 src/lxc/lxc_driver.c | 2 src/openvz/openvz_driver.c | 2 src/phyp/phyp_driver.c | 4 + src/qemu/qemu_driver.c | 86 +++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 27 +++++++++++ src/qemu/qemu_monitor.h | 3 + src/qemu/qemu_monitor_json.c | 68 +++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 3 + src/qemu/qemu_monitor_text.c | 56 ++++++++++++++++++++++ src/qemu/qemu_monitor_text.h | 2 src/remote/remote_driver.c | 50 ++++++++++++++++++++ src/remote/remote_protocol.c | 22 +++++++++ src/remote/remote_protocol.h | 19 +++++++ src/remote/remote_protocol.x | 14 +++++ src/test/test_driver.c | 2 src/uml/uml_driver.c | 2 src/vbox/vbox_tmpl.c | 2 src/vmware/vmware_driver.c | 2 src/xen/xen_driver.c | 2 src/xenapi/xenapi_driver.c | 2 tools/virsh.c | 56 ++++++++++++++++++++++ 31 files changed, 608 insertions(+), 2 deletions(-)
diff --git a/src/libvirt.c b/src/libvirt.c index 9bdb4c8..245247f 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -5245,6 +5245,94 @@ error: }
/** + * virDomainSendEvnetNMI: + * @domain: pointer to domain object, or NULL for Domain0 + * @vcpu: the virtual CPU id to send NMI to + * + * Send NMI to a special vcpu of the guest + * + * Returns 0 in case of success, -1 in case of failure. + */ + +int virDomainSendEventNMI(virDomainPtr domain, unsigned int vcpu)
Your proposal to qemu-devel to add inject-nmi for QMP does not include any CPU index parameter anymore. Instead it will automatically inject the NMI to all present CPUs. This libvirt API would appear to be incompatible with that QMP design. For Xen, it appears the API also does not allow a CPU index to be given - it just injects to the first CPU AFAICT. So do we really need to have a 'unsigned int vcpu' parameter in the libvirt API, or can we just leave it out and always inject to CPU==0 for HMP ? eg simplify to int virDomainSendNMI(virDomainPtr domain)
+{ + virConnectPtr conn; + VIR_DOMAIN_DEBUG(domain, "vcpu=%u", vcpu); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return (-1); + } + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + conn = domain->conn; + + if (conn->driver->domainSendEventNMI) { + int ret; + ret = conn->driver->domainSendEventNMI(domain, vcpu); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(domain->conn); + return -1; +} + +/** + * virDomainSendEventKey: + * @domain: pointer to domain object, or NULL for Domain0 + * @key: the string of key or key sequence + * + * Send a special key or key sequence to the guest + * + * Returns 0 in case of success, -1 in case of failure. + */ + +int virDomainSendEventKey(virDomainPtr domain, const char *key)
I don't entirely like that the 'const char *key' is just directly exposing QEMU key names and combining syntax eg "ctrl-alt-delete". The names are symbolic names for XT scancodes, which QEMU uses internally as its master scancode set (regardless of arch or whether the keyboard is actually XT based). The VirtualBox API, requires that the application send an array of integer scancodes. The QEMU 'sendkey' command also has another 'holdtime' parameter which controls how long QEMU waits between the 'press' and 'release' of the keys, which our API does not have. The core requirement is that our API be able to send any possible key, to any hypervisor. The problem with XT scancodes, is that while it can be made to represent any possible scancode, it gets somewhat confusing for apps because there are many different ways to represent extended scancodes in use across apps. One alternative would be to duplicate Linux's virtual scancode constants, which are a superset of all known possible scancode lists. Then we would do a conversion inside libvirt from Linux to hypervisor specific scancode format (probably XT values, or XT strings). The interesting thing about using Linux virtual scancode constants is that this includes mouse buttons. So the same API could be used to inject mouse clicks. We'd still need an explicit API for doing mouse events though, because we'd need to be able to press buttons, then generate motion events, then release buttons. So, my preference would be for an API virDomainSendKey(virDomainPtr dom, unsigned int nkeycodes, unsigned int keycodes, unsigned int holdtime); If "holdtime" is zero, then the hypervisor default behaviour would be used. This allows us to support VirtualBox where we'd have to reject any non-zero "holdtime" value Then I think I'd create a new header file include/libvirt/virtkeys.h which contained a copy of only the KEY_* constants from: /usr/include/linux/input.h We'd just have a static array with a mapping from keys to the QEMU keycode names.
diff --git a/tools/virsh.c b/tools/virsh.c index faeaf47..0b78c6d 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -2910,6 +2910,61 @@ cmdSetvcpus(vshControl *ctl, const vshCmd *cmd) }
/* + * "sendevent" command + */ +static const vshCmdInfo info_sendevent[] = { + {"help", N_("send events to the guest")}, + {"desc", N_("Send events (NMI or Keys) to the guest domain.")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_sendevent[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, + {"eventtype", VSH_OT_DATA, VSH_OFLAG_REQ, N_("the type of event (nmi or key)")}, + {"eventcontent", VSH_OT_DATA, VSH_OFLAG_REQ, N_("content for the event.")}, + {NULL, 0, 0, NULL} +}; + + +static int +cmdSendEvent(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom; + const char *type; + int ret = TRUE; + + if (!vshConnectionUsability(ctl, ctl->conn)) + return FALSE; + + if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) + return FALSE; + + if (vshCommandOptString(cmd, "eventtype", &type) < 0) + return FALSE; + + if (STREQ(type, "nmi")) { + int cpu; + + if ((vshCommandOptInt(cmd, "eventcontent", &cpu) < 0) + || (virDomainSendEventNMI(dom, cpu) < 0)) + ret = FALSE; + } else if (STREQ(type, "key")) { + const char *key; + + if ((vshCommandOptString(cmd, "eventcontent", &key) < 0) + || (virDomainSendEventKey(dom, key) < 0)) + ret = FALSE; + } else { + virDomainFree(dom); + vshError(ctl, _("Invalid event type: %s, only \"nmi\" or \"key\" supported currently."), type); + return FALSE; + } + + virDomainFree(dom); + return ret; +}
I don't like the way two APIs are combined into one virsh command. Particularly since I think we'll likely add another API 'virDomainSendPointer' which will take as many as 4/5 parameters, instead of just one. Thus I think it would be better to have virsh send-nmi <domain> virsh send-key [--holdtime millsec] <domain> <keycode1> [<keycode2> ...] virsh send-pointer <domain> <buttonstate> <dx> <dy> <dz>... virsh send-..... etc... Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Add API virDomainSendKey() and virsh send-key command. # virsh help send-key NAME send-key - Send keycodes to the guest SYNOPSIS send-key <domain> [--codeset <string>] [--holdtime <number>] <keycode>... DESCRIPTION Send keycodes to the guest, the keycodes must be integers or the qemu-style key strings for the "xt:keystring" codeset or the KEY_* strings listed below for the "linux" codeset. Available codeset: linux the keycodes specified in /usr/include/linux/input.h(default) default linux codeset will be used driver_default the hypervisor default codeset will be used xt XT(set1) scancode of standard AT keyboards and PS/2 keyboards atset1 set1 scancode of standard AT keyboards and PS/2 keyboards atset2 set2 scancode of standard AT keyboards and PS/2 keyboards atset3 set3 scancode of standard AT keyboards and PS/2 keyboards xt:keystring XT scancode, but <keycode>... must be the qemu-style key strings Examples: virsh # send-key <domain> 37 18 21 virsh # send-key <domain> --holdtime 1000 0x15 18 0xf virsh # send-key <domain> KEY_LEFTCTRL KEY_LEFTALT KEY_F1 virsh # send-key <domain> --codeset xt:keystring alt-sysrq h KEY_XXX strings for the "linux" codeset: . . . OPTIONS [--domain] <string> domain name, id or uuid --codeset <string> the codeset of keycodes, default:linux --holdtime <number> the time (in millsecond) how long the keys will be held <keycode> the key code PATCH 01~04 prepare PATCH 05~10 Add support for send keys to guest PATCH 11~13 improve usage of send keys. Python version of virDomainSendKey() has not been implemented yet, it will be done soon. Lai Jiangshan (13): allow name for VSH_OT_ARGV options improve the iteration of VSH_OT_ARGV options add VSH_OFLAG_REQ_OPT options remote_generator: support general dynamic array send-key: Defining the public API send-key: Defining the internal API send-key: Implementing the public API send-key: Implementing the remote protocol send-key: Expose the new API in virsh qemu:send-key: Implement the driver methods send-key: support KEY_XXX names for the linux keycode qemu,send-key: map linux keycode to xt keycode virsh,send-key: add --codeset xt:keystring support daemon/remote_generator.pl | 18 ++ include/libvirt/libvirt.h.in | 7 + include/libvirt/virtkeys.h | 276 ++++++++++++++++++++++++++++ python/generator.py | 1 + src/driver.h | 8 + src/libvirt.c | 54 ++++++ src/libvirt_public.syms | 1 + src/qemu/qemu_driver.c | 50 +++++ src/qemu/qemu_monitor.c | 75 ++++++++ src/qemu/qemu_monitor.h | 6 + src/qemu/qemu_monitor_json.c | 15 ++ src/qemu/qemu_monitor_json.h | 5 + src/qemu/qemu_monitor_text.c | 48 +++++ src/qemu/qemu_monitor_text.h | 5 + src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 19 ++- src/remote_protocol-structs | 11 + tools/virsh.c | 415 +++++++++++++++++++++++++++++++++++++++--- tools/virsh.pod | 4 + 19 files changed, 989 insertions(+), 30 deletions(-) create mode 100644 include/libvirt/virtkeys.h -- 1.7.4.4

A name will improve the usege, example # virsh help echo NAME echo - echo arguments SYNOPSIS echo [--shell] [--xml] [<string>]... DESCRIPTION Echo back arguments, possibly with quoting. OPTIONS --shell escape for shell use --xml escape for XML use <string> arguments to echo "[<string>]..." is added to SYNOPSIS. "<string> arguments to echo" is added to OPTIONS. Signed-off-by: Lai Jiangshan <laijs@fujitsu.com> --- tools/virsh.c | 19 +++++++++++++------ 1 files changed, 13 insertions(+), 6 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index de49489..c358580 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -126,7 +126,7 @@ typedef enum { VSH_OT_STRING, /* optional string option */ VSH_OT_INT, /* optional or mandatory int option */ VSH_OT_DATA, /* string data (as non-option) */ - VSH_OT_ARGV /* remaining arguments, opt->name should be "" */ + VSH_OT_ARGV /* remaining arguments */ } vshCmdOptType; /* @@ -10046,7 +10046,7 @@ static const vshCmdInfo info_echo[] = { static const vshCmdOptDef opts_echo[] = { {"shell", VSH_OT_BOOL, 0, N_("escape for shell use")}, {"xml", VSH_OT_BOOL, 0, N_("escape for XML use")}, - {"", VSH_OT_ARGV, 0, N_("arguments to echo")}, + {"string", VSH_OT_ARGV, 0, N_("arguments to echo")}, {NULL, 0, 0, NULL} }; @@ -11084,6 +11084,11 @@ vshCmddefGetOption(vshControl *ctl, const vshCmdDef *cmd, const char *name, vshError(ctl, _("option --%s already seen"), name); return NULL; } + if (opt->type == VSH_OT_ARGV) { + vshError(ctl, _("variable argument <%s> " + "should not be used with --<%s>"), name, name); + return NULL; + } *opts_seen |= 1 << i; return opt; } @@ -11132,7 +11137,7 @@ vshCommandCheckOpts(vshControl *ctl, const vshCmd *cmd, uint32_t opts_required, const vshCmdOptDef *opt = &def->opts[i]; vshError(ctl, - opt->type == VSH_OT_DATA ? + opt->type == VSH_OT_DATA || opt->type == VSH_OT_ARGV ? _("command '%s' requires <%s> option") : _("command '%s' requires --%s option"), def->name, opt->name); @@ -11240,7 +11245,8 @@ vshCmddefHelp(vshControl *ctl, const char *cmdname) break; case VSH_OT_ARGV: /* xgettext:c-format */ - fmt = _("[<string>]..."); + fmt = (opt->flag & VSH_OFLAG_REQ) ? _("<%s>...") + : _("[<%s>]..."); break; default: assert(0); @@ -11280,7 +11286,8 @@ vshCmddefHelp(vshControl *ctl, const char *cmdname) break; case VSH_OT_ARGV: /* Not really an option. */ - continue; + snprintf(buf, sizeof(buf), _("<%s>"), opt->name); + break; default: assert(0); } @@ -12693,7 +12700,7 @@ vshReadlineOptionsGenerator(const char *text, int state) list_index++; - if (opt->type == VSH_OT_DATA) + if (opt->type == VSH_OT_DATA || opt->type == VSH_OT_ARGV) /* ignore non --option */ continue; -- 1.7.4.4

On Wed, May 25, 2011 at 05:37:43PM +0800, Lai Jiangshan wrote:
A name will improve the usege, example
# virsh help echo NAME echo - echo arguments
SYNOPSIS echo [--shell] [--xml] [<string>]...
DESCRIPTION Echo back arguments, possibly with quoting.
OPTIONS --shell escape for shell use --xml escape for XML use <string> arguments to echo
"[<string>]..." is added to SYNOPSIS. "<string> arguments to echo" is added to OPTIONS.
Signed-off-by: Lai Jiangshan <laijs@fujitsu.com> --- tools/virsh.c | 19 +++++++++++++------ 1 files changed, 13 insertions(+), 6 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c index de49489..c358580 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -126,7 +126,7 @@ typedef enum { VSH_OT_STRING, /* optional string option */ VSH_OT_INT, /* optional or mandatory int option */ VSH_OT_DATA, /* string data (as non-option) */ - VSH_OT_ARGV /* remaining arguments, opt->name should be "" */ + VSH_OT_ARGV /* remaining arguments */ } vshCmdOptType;
/* @@ -10046,7 +10046,7 @@ static const vshCmdInfo info_echo[] = { static const vshCmdOptDef opts_echo[] = { {"shell", VSH_OT_BOOL, 0, N_("escape for shell use")}, {"xml", VSH_OT_BOOL, 0, N_("escape for XML use")}, - {"", VSH_OT_ARGV, 0, N_("arguments to echo")}, + {"string", VSH_OT_ARGV, 0, N_("arguments to echo")}, {NULL, 0, 0, NULL} };
@@ -11084,6 +11084,11 @@ vshCmddefGetOption(vshControl *ctl, const vshCmdDef *cmd, const char *name, vshError(ctl, _("option --%s already seen"), name); return NULL; } + if (opt->type == VSH_OT_ARGV) { + vshError(ctl, _("variable argument <%s> " + "should not be used with --<%s>"), name, name); + return NULL; + } *opts_seen |= 1 << i; return opt; } @@ -11132,7 +11137,7 @@ vshCommandCheckOpts(vshControl *ctl, const vshCmd *cmd, uint32_t opts_required, const vshCmdOptDef *opt = &def->opts[i];
vshError(ctl, - opt->type == VSH_OT_DATA ? + opt->type == VSH_OT_DATA || opt->type == VSH_OT_ARGV ? _("command '%s' requires <%s> option") : _("command '%s' requires --%s option"), def->name, opt->name); @@ -11240,7 +11245,8 @@ vshCmddefHelp(vshControl *ctl, const char *cmdname) break; case VSH_OT_ARGV: /* xgettext:c-format */ - fmt = _("[<string>]..."); + fmt = (opt->flag & VSH_OFLAG_REQ) ? _("<%s>...") + : _("[<%s>]..."); break; default: assert(0); @@ -11280,7 +11286,8 @@ vshCmddefHelp(vshControl *ctl, const char *cmdname) break; case VSH_OT_ARGV: /* Not really an option. */ - continue; + snprintf(buf, sizeof(buf), _("<%s>"), opt->name); + break; default: assert(0); } @@ -12693,7 +12700,7 @@ vshReadlineOptionsGenerator(const char *text, int state)
list_index++;
- if (opt->type == VSH_OT_DATA) + if (opt->type == VSH_OT_DATA || opt->type == VSH_OT_ARGV) /* ignore non --option */ continue;
ACK Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Signed-off-by: Lai Jiangshan <laijs@fujitsu.com> --- tools/virsh.c | 47 ++++++++++++++++++++++++----------------------- 1 files changed, 24 insertions(+), 23 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index c358580..2e27535 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -277,7 +277,27 @@ static int vshCommandOptULongLong(const vshCmd *cmd, const char *name, unsigned long long *value) ATTRIBUTE_NONNULL(3) ATTRIBUTE_RETURN_CHECK; static bool vshCommandOptBool(const vshCmd *cmd, const char *name); -static char *vshCommandOptArgv(const vshCmd *cmd, int count); + +/* + * Iterate all the argv arguments. + * + * Requires that a VSH_OT_ARGV option be last in the + * list of supported options in CMD->def->opts. + */ +static inline const vshCmdOpt *__variable_arg(const vshCmdOpt *opt) +{ + while (opt) { + if (opt->def && opt->def->type == VSH_OT_ARGV) + break; + opt = opt->next; + } + + return opt; +} + +#define for_each_variable_arg(cmd, opt) \ + for (opt = __variable_arg(cmd->opts); opt; opt = __variable_arg(opt->next)) + #define VSH_BYID (1 << 1) #define VSH_BYUUID (1 << 2) @@ -10059,6 +10079,7 @@ cmdEcho (vshControl *ctl ATTRIBUTE_UNUSED, const vshCmd *cmd) bool shell = false; bool xml = false; int count = 0; + const vshCmdOpt *opt; char *arg; virBuffer buf = VIR_BUFFER_INITIALIZER; @@ -10067,10 +10088,11 @@ cmdEcho (vshControl *ctl ATTRIBUTE_UNUSED, const vshCmd *cmd) if (vshCommandOptBool(cmd, "xml")) xml = true; - while ((arg = vshCommandOptArgv(cmd, count)) != NULL) { + for_each_variable_arg(cmd, opt) { bool close_quote = false; char *q; + arg = opt->data; if (count) virBufferAddChar(&buf, ' '); /* Add outer '' only if arg included shell metacharacters. */ @@ -11484,27 +11506,6 @@ vshCommandOptBool(const vshCmd *cmd, const char *name) return vshCommandOpt(cmd, name) != NULL; } -/* - * Returns the COUNT argv argument, or NULL after last argument. - * - * Requires that a VSH_OT_ARGV option with the name "" be last in the - * list of supported options in CMD->def->opts. - */ -static char * -vshCommandOptArgv(const vshCmd *cmd, int count) -{ - vshCmdOpt *opt = cmd->opts; - - while (opt) { - if (opt->def && opt->def->type == VSH_OT_ARGV) { - if (count-- == 0) - return opt->data; - } - opt = opt->next; - } - return NULL; -} - /* Determine whether CMD->opts includes an option with name OPTNAME. If not, give a diagnostic and return false. If so, return true. */ -- 1.7.4.4

On Wed, May 25, 2011 at 05:37:44PM +0800, Lai Jiangshan wrote:
Signed-off-by: Lai Jiangshan <laijs@fujitsu.com> --- tools/virsh.c | 47 ++++++++++++++++++++++++----------------------- 1 files changed, 24 insertions(+), 23 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c index c358580..2e27535 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -277,7 +277,27 @@ static int vshCommandOptULongLong(const vshCmd *cmd, const char *name, unsigned long long *value) ATTRIBUTE_NONNULL(3) ATTRIBUTE_RETURN_CHECK; static bool vshCommandOptBool(const vshCmd *cmd, const char *name); -static char *vshCommandOptArgv(const vshCmd *cmd, int count); + +/* + * Iterate all the argv arguments. + * + * Requires that a VSH_OT_ARGV option be last in the + * list of supported options in CMD->def->opts. + */ +static inline const vshCmdOpt *__variable_arg(const vshCmdOpt *opt) +{ + while (opt) { + if (opt->def && opt->def->type == VSH_OT_ARGV) + break; + opt = opt->next; + } + + return opt; +} + +#define for_each_variable_arg(cmd, opt) \ + for (opt = __variable_arg(cmd->opts); opt; opt = __variable_arg(opt->next)) +
#define VSH_BYID (1 << 1) #define VSH_BYUUID (1 << 2) @@ -10059,6 +10079,7 @@ cmdEcho (vshControl *ctl ATTRIBUTE_UNUSED, const vshCmd *cmd) bool shell = false; bool xml = false; int count = 0; + const vshCmdOpt *opt; char *arg; virBuffer buf = VIR_BUFFER_INITIALIZER;
@@ -10067,10 +10088,11 @@ cmdEcho (vshControl *ctl ATTRIBUTE_UNUSED, const vshCmd *cmd) if (vshCommandOptBool(cmd, "xml")) xml = true;
- while ((arg = vshCommandOptArgv(cmd, count)) != NULL) { + for_each_variable_arg(cmd, opt) { bool close_quote = false; char *q;
+ arg = opt->data; if (count) virBufferAddChar(&buf, ' '); /* Add outer '' only if arg included shell metacharacters. */ @@ -11484,27 +11506,6 @@ vshCommandOptBool(const vshCmd *cmd, const char *name) return vshCommandOpt(cmd, name) != NULL; }
-/* - * Returns the COUNT argv argument, or NULL after last argument. - * - * Requires that a VSH_OT_ARGV option with the name "" be last in the - * list of supported options in CMD->def->opts. - */ -static char * -vshCommandOptArgv(const vshCmd *cmd, int count) -{ - vshCmdOpt *opt = cmd->opts; - - while (opt) { - if (opt->def && opt->def->type == VSH_OT_ARGV) { - if (count-- == 0) - return opt->data; - } - opt = opt->next; - } - return NULL; -} - /* Determine whether CMD->opts includes an option with name OPTNAME. If not, give a diagnostic and return false. If so, return true. */
I'm not entirely sure I understand what the effect of this patch is. Can you explain what the change in semantics for the parser is with this patch applied Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 05/26/2011 12:38 AM, Daniel P. Berrange wrote:
On Wed, May 25, 2011 at 05:37:44PM +0800, Lai Jiangshan wrote:
Signed-off-by: Lai Jiangshan <laijs@fujitsu.com> --- tools/virsh.c | 47 ++++++++++++++++++++++++----------------------- 1 files changed, 24 insertions(+), 23 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c index c358580..2e27535 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -277,7 +277,27 @@ static int vshCommandOptULongLong(const vshCmd *cmd, const char *name, unsigned long long *value) ATTRIBUTE_NONNULL(3) ATTRIBUTE_RETURN_CHECK; static bool vshCommandOptBool(const vshCmd *cmd, const char *name); -static char *vshCommandOptArgv(const vshCmd *cmd, int count); + +/* + * Iterate all the argv arguments. + * + * Requires that a VSH_OT_ARGV option be last in the + * list of supported options in CMD->def->opts. + */ +static inline const vshCmdOpt *__variable_arg(const vshCmdOpt *opt) +{ + while (opt) { + if (opt->def && opt->def->type == VSH_OT_ARGV) + break; + opt = opt->next; + } + + return opt; +} + +#define for_each_variable_arg(cmd, opt) \ + for (opt = __variable_arg(cmd->opts); opt; opt = __variable_arg(opt->next)) +
#define VSH_BYID (1 << 1) #define VSH_BYUUID (1 << 2) @@ -10059,6 +10079,7 @@ cmdEcho (vshControl *ctl ATTRIBUTE_UNUSED, const vshCmd *cmd) bool shell = false; bool xml = false; int count = 0; + const vshCmdOpt *opt; char *arg; virBuffer buf = VIR_BUFFER_INITIALIZER;
@@ -10067,10 +10088,11 @@ cmdEcho (vshControl *ctl ATTRIBUTE_UNUSED, const vshCmd *cmd) if (vshCommandOptBool(cmd, "xml")) xml = true;
- while ((arg = vshCommandOptArgv(cmd, count)) != NULL) { + for_each_variable_arg(cmd, opt) { bool close_quote = false; char *q;
+ arg = opt->data; if (count) virBufferAddChar(&buf, ' '); /* Add outer '' only if arg included shell metacharacters. */ @@ -11484,27 +11506,6 @@ vshCommandOptBool(const vshCmd *cmd, const char *name) return vshCommandOpt(cmd, name) != NULL; }
-/* - * Returns the COUNT argv argument, or NULL after last argument. - * - * Requires that a VSH_OT_ARGV option with the name "" be last in the - * list of supported options in CMD->def->opts. - */ -static char * -vshCommandOptArgv(const vshCmd *cmd, int count) -{ - vshCmdOpt *opt = cmd->opts; - - while (opt) { - if (opt->def && opt->def->type == VSH_OT_ARGV) { - if (count-- == 0) - return opt->data; - } - opt = opt->next; - } - return NULL; -} - /* Determine whether CMD->opts includes an option with name OPTNAME. If not, give a diagnostic and return false. If so, return true. */
I'm not entirely sure I understand what the effect of this patch is. Can you explain what the change in semantics for the parser is with this patch applied
"for_each_XXXX" macro is more friendly/directly, it very common in linux kernel. "while ((arg = vshCommandOptArgv(cmd, count)) != NULL)" is O(N*N) time. "while ((arg = vshCommandOptArgv(cmd, count)) != NULL)" requires a "count" to control the iteration. Thanks, Lai

A VSH_OFLAG_REQ_OPT option means --optionname is required when used. It will kill any ambiguity even !VSH_OFLAG_REQ option listed before VSH_OFLAG_REQ option if the !VSH_OFLAG_REQ option is a VSH_OFLAG_REQ_OPT option. It will help us use optional arguement with VSH_OT_ARGV argument. Signed-off-by: Lai Jiangshan <laijs@fujitsu.com> --- tools/virsh.c | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 2e27535..80cffac 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -152,6 +152,7 @@ enum { VSH_OFLAG_NONE = 0, /* without flags */ VSH_OFLAG_REQ = (1 << 0), /* option required */ VSH_OFLAG_EMPTY_OK = (1 << 1), /* empty string option allowed */ + VSH_OFLAG_REQ_OPT = (1 << 2), /* --optionname required */ }; /* dummy */ @@ -11080,6 +11081,12 @@ vshCmddefOptParse(const vshCmdDef *cmd, uint32_t* opts_need_arg, return -1; /* bool options can't be mandatory */ continue; } + if (opt->flag & VSH_OFLAG_REQ_OPT) { + if (opt->flag & VSH_OFLAG_REQ) + *opts_required |= 1 << i; + continue; + } + *opts_need_arg |= 1 << i; if (opt->flag & VSH_OFLAG_REQ) { if (optional) -- 1.7.4.4

It will allow us use "dynamic_array_basic_type member_name<MAX>" for remote protocol and avoid so many manual coding. For avoiding ambiguity, dynamic_array_basic_type must have a "_DABT" suffix. Signed-off-by: Lai Jiangshan <laijs@fujitsu.com> --- daemon/remote_generator.pl | 18 ++++++++++++++++++ 1 files changed, 18 insertions(+), 0 deletions(-) diff --git a/daemon/remote_generator.pl b/daemon/remote_generator.pl index d21f959..6905ff3 100755 --- a/daemon/remote_generator.pl +++ b/daemon/remote_generator.pl @@ -343,6 +343,12 @@ elsif ($opt_b) { } push(@args_list, "args->$2.$2_len"); + } elsif ($args_member =~ m/^(\S+_DABT) (\S+)<\S+>;/) { # normal dynamic array + if (! @args_list) { + push(@args_list, "conn"); + } + push(@args_list, "args->$2.$2_len"); + push(@args_list, "($1 *)args->$2.$2_val"); } elsif ($args_member =~ m/<\S+>;/ or $args_member =~ m/\[\S+\];/) { # just make all other array types fail die "unhandled type for argument value: $args_member"; @@ -838,6 +844,18 @@ elsif ($opt_k) { push(@setters_list, "args.$arg_name.${arg_name}_val = (char *)$arg_name;"); push(@setters_list, "args.$arg_name.${arg_name}_len = ${arg_name}len;"); push(@args_check_list, { name => "\"$arg_name\"", arg => "${arg_name}len", limit => $limit }); + } elsif ($args_member =~ m/^(\S+_DABT) (\S+)<(\S+)>;/) { # normal dynamic array + my $type_name = $1; + my $arg_name = $2; + my $limit = $3; + + push(@args_list, "unsigned int n${arg_name}"); + push(@args_list, "${type_name} *$arg_name"); + + push(@setters_list, "args.$arg_name.${arg_name}_len = n${arg_name};"); + push(@setters_list, "args.$arg_name.${arg_name}_val = (${type_name} *)$arg_name;"); + + push(@args_check_list, { name => "\"$arg_name\"", arg => "n${arg_name}", limit => $limit }); } elsif ($args_member =~ m/^(unsigned )?(int|hyper) (\S+);/) { my $type_name; my $arg_name = $3; -- 1.7.4.4

On Wed, May 25, 2011 at 05:37:46PM +0800, Lai Jiangshan wrote:
It will allow us use "dynamic_array_basic_type member_name<MAX>" for remote protocol and avoid so many manual coding.
For avoiding ambiguity, dynamic_array_basic_type must have a "_DABT" suffix.
Signed-off-by: Lai Jiangshan <laijs@fujitsu.com>
A good idea to make the generator support more, but rather than requiring a magic name for the data type, add an annotation to the data type eg, kind of like Matthias has done to annotate 'hyper' http://www.redhat.com/archives/libvir-list/2011-May/msg01434.html Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

2011/5/25 Daniel P. Berrange <berrange@redhat.com>:
On Wed, May 25, 2011 at 05:37:46PM +0800, Lai Jiangshan wrote:
It will allow us use "dynamic_array_basic_type member_name<MAX>" for remote protocol and avoid so many manual coding.
For avoiding ambiguity, dynamic_array_basic_type must have a "_DABT" suffix.
Signed-off-by: Lai Jiangshan <laijs@fujitsu.com>
A good idea to make the generator support more, but rather than requiring a magic name for the data type, add an annotation to the data type
eg, kind of like Matthias has done to annotate 'hyper'
http://www.redhat.com/archives/libvir-list/2011-May/msg01434.html
Actually there is not need for this _DABT suffix nor for any other annotation. unsigned int keycodes<REMOTE_SEND_KEY_MAX> is just a common pattern and the generator already deals with such arrays. I propose the attached patch as a v2 of this patch. It doesn't try to cover the generic array case at that isn't useful, it just deals with [unsigned] int arrays. This patch assumes that keycodes and keycodeslen are reordered to the common pattern. Matthias

Add public virDomainSendKey() and enum libvirt_keycode_set for the @codeset. Python version of virDomainSendKey() has not been implemented yet, it will be done soon. Signed-off-by: Lai Jiangshan <laijs@fujitsu.com> --- include/libvirt/libvirt.h.in | 7 +++++++ include/libvirt/virtkeys.h | 23 +++++++++++++++++++++++ python/generator.py | 1 + src/libvirt_public.syms | 1 + 4 files changed, 32 insertions(+), 0 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 7cd6e13..9167dbc 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -2617,6 +2617,13 @@ int virDomainOpenConsole(virDomainPtr dom, int virDomainInjectNMI(virDomainPtr domain, unsigned int flags); +int virDomainSendKey(virDomainPtr domain, + unsigned int codeset, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes, + unsigned int flags); + #ifdef __cplusplus } #endif diff --git a/include/libvirt/virtkeys.h b/include/libvirt/virtkeys.h new file mode 100644 index 0000000..eb07129 --- /dev/null +++ b/include/libvirt/virtkeys.h @@ -0,0 +1,23 @@ +#ifndef _LIBVIRT_VIRTKEYS_H +#define _LIBVIRT_VIRTKEYS_H + +/* + * Copyright (c) 2011 Lai Jiangshan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +enum libvirt_keycode_set { + LIBVIRT_KEYCODE_LINUX = 0, + LIBVIRT_KEYCODE_DRIVER_DEFAULT = 1, + LIBVIRT_KEYCODE_XT = 2, + LIBVIRT_KEYCODE_ATSET1 = LIBVIRT_KEYCODE_XT, + LIBVIRT_KEYCODE_ATSET2 = 3, + LIBVIRT_KEYCODE_ATSET3 = 4, +}; + +#define MAX_SEND_KEY 16 + +#endif diff --git a/python/generator.py b/python/generator.py index 1741bba..3d57bf9 100755 --- a/python/generator.py +++ b/python/generator.py @@ -355,6 +355,7 @@ skip_impl = ( 'virNodeDeviceListCaps', 'virConnectBaselineCPU', 'virDomainRevertToSnapshot', + 'virDomainSendKey', ) diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 0590535..9e8a37c 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -442,6 +442,7 @@ LIBVIRT_0.9.2 { virDomainInjectNMI; virDomainScreenshot; virDomainSetSchedulerParametersFlags; + virDomainSendKey; } LIBVIRT_0.9.0; # .... define new API here using predicted next version number .... -- 1.7.4.4

On Wed, May 25, 2011 at 05:37:47PM +0800, Lai Jiangshan wrote:
Add public virDomainSendKey() and enum libvirt_keycode_set for the @codeset.
Python version of virDomainSendKey() has not been implemented yet, it will be done soon.
Signed-off-by: Lai Jiangshan <laijs@fujitsu.com> --- include/libvirt/libvirt.h.in | 7 +++++++ include/libvirt/virtkeys.h | 23 +++++++++++++++++++++++ python/generator.py | 1 + src/libvirt_public.syms | 1 + 4 files changed, 32 insertions(+), 0 deletions(-)
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 7cd6e13..9167dbc 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -2617,6 +2617,13 @@ int virDomainOpenConsole(virDomainPtr dom,
int virDomainInjectNMI(virDomainPtr domain, unsigned int flags);
+int virDomainSendKey(virDomainPtr domain, + unsigned int codeset, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes, + unsigned int flags); +
This looks fine. As mentioned earlier we might like to *also* have a variant which takes strings, to make life easier for virsh, or similar use cases +int virDomainSendKeyStr(virDomainPtr domain, + unsigned int codeset, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned char **keycodestrs, + unsigned int flags); +
#ifdef __cplusplus } #endif diff --git a/include/libvirt/virtkeys.h b/include/libvirt/virtkeys.h new file mode 100644 index 0000000..eb07129 --- /dev/null +++ b/include/libvirt/virtkeys.h @@ -0,0 +1,23 @@ +#ifndef _LIBVIRT_VIRTKEYS_H +#define _LIBVIRT_VIRTKEYS_H + +/* + * Copyright (c) 2011 Lai Jiangshan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +enum libvirt_keycode_set { + LIBVIRT_KEYCODE_LINUX = 0, + LIBVIRT_KEYCODE_DRIVER_DEFAULT = 1, + LIBVIRT_KEYCODE_XT = 2, + LIBVIRT_KEYCODE_ATSET1 = LIBVIRT_KEYCODE_XT, + LIBVIRT_KEYCODE_ATSET2 = 3, + LIBVIRT_KEYCODE_ATSET3 = 4, +};
If we're going to have constants for XT and ATSET1 then we probably want to have them separate values. If not then we should just kill one of them. As mentioned in the initial message, I think 'driver default' isn't useful, so I'd go with: +enum libvirt_keycode_set { + LIBVIRT_KEYCODE_LINUX = 0, + LIBVIRT_KEYCODE_XT = 1, + LIBVIRT_KEYCODE_ATSET1 = 2 + LIBVIRT_KEYCODE_ATSET2 = 3, + LIBVIRT_KEYCODE_ATSET3 = 4, +};
+ +#define MAX_SEND_KEY 16 + +#endif diff --git a/python/generator.py b/python/generator.py index 1741bba..3d57bf9 100755 --- a/python/generator.py +++ b/python/generator.py @@ -355,6 +355,7 @@ skip_impl = ( 'virNodeDeviceListCaps', 'virConnectBaselineCPU', 'virDomainRevertToSnapshot', + 'virDomainSendKey', )
diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 0590535..9e8a37c 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -442,6 +442,7 @@ LIBVIRT_0.9.2 { virDomainInjectNMI; virDomainScreenshot; virDomainSetSchedulerParametersFlags; + virDomainSendKey; } LIBVIRT_0.9.0;
# .... define new API here using predicted next version number ....
Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 05/26/2011 12:43 AM, Daniel P. Berrange wrote:
On Wed, May 25, 2011 at 05:37:47PM +0800, Lai Jiangshan wrote:
Add public virDomainSendKey() and enum libvirt_keycode_set for the @codeset.
Python version of virDomainSendKey() has not been implemented yet, it will be done soon.
Signed-off-by: Lai Jiangshan <laijs@fujitsu.com> --- include/libvirt/libvirt.h.in | 7 +++++++ include/libvirt/virtkeys.h | 23 +++++++++++++++++++++++ python/generator.py | 1 + src/libvirt_public.syms | 1 + 4 files changed, 32 insertions(+), 0 deletions(-)
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 7cd6e13..9167dbc 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -2617,6 +2617,13 @@ int virDomainOpenConsole(virDomainPtr dom,
int virDomainInjectNMI(virDomainPtr domain, unsigned int flags);
+int virDomainSendKey(virDomainPtr domain, + unsigned int codeset, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes, + unsigned int flags); +
This looks fine. As mentioned earlier we might like to *also* have a variant which takes strings, to make life easier for virsh, or similar use cases
+int virDomainSendKeyStr(virDomainPtr domain, + unsigned int codeset, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned char **keycodestrs, + unsigned int flags); +
#ifdef __cplusplus } #endif diff --git a/include/libvirt/virtkeys.h b/include/libvirt/virtkeys.h new file mode 100644 index 0000000..eb07129 --- /dev/null +++ b/include/libvirt/virtkeys.h @@ -0,0 +1,23 @@ +#ifndef _LIBVIRT_VIRTKEYS_H +#define _LIBVIRT_VIRTKEYS_H + +/* + * Copyright (c) 2011 Lai Jiangshan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +enum libvirt_keycode_set { + LIBVIRT_KEYCODE_LINUX = 0, + LIBVIRT_KEYCODE_DRIVER_DEFAULT = 1, + LIBVIRT_KEYCODE_XT = 2, + LIBVIRT_KEYCODE_ATSET1 = LIBVIRT_KEYCODE_XT, + LIBVIRT_KEYCODE_ATSET2 = 3, + LIBVIRT_KEYCODE_ATSET3 = 4, +};
If we're going to have constants for XT and ATSET1 then we probably want to have them separate values. If not then we should just kill one of them. As mentioned in the initial message, I think 'driver default' isn't useful, so I'd go with:
+enum libvirt_keycode_set { + LIBVIRT_KEYCODE_LINUX = 0, + LIBVIRT_KEYCODE_XT = 1, + LIBVIRT_KEYCODE_ATSET1 = 2 + LIBVIRT_KEYCODE_ATSET2 = 3, + LIBVIRT_KEYCODE_ATSET3 = 4, +};
Sorry, That's my fault, I thought xt and atset1 are the same codeset. Thank you for correcting me. Thanks, Lai.
+ +#define MAX_SEND_KEY 16 + +#endif diff --git a/python/generator.py b/python/generator.py index 1741bba..3d57bf9 100755 --- a/python/generator.py +++ b/python/generator.py @@ -355,6 +355,7 @@ skip_impl = ( 'virNodeDeviceListCaps', 'virConnectBaselineCPU', 'virDomainRevertToSnapshot', + 'virDomainSendKey', )
diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 0590535..9e8a37c 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -442,6 +442,7 @@ LIBVIRT_0.9.2 { virDomainInjectNMI; virDomainScreenshot; virDomainSetSchedulerParametersFlags; + virDomainSendKey; } LIBVIRT_0.9.0;
# .... define new API here using predicted next version number ....
Daniel

2011/5/25 Lai Jiangshan <laijs@cn.fujitsu.com>:
Add public virDomainSendKey() and enum libvirt_keycode_set for the @codeset.
Python version of virDomainSendKey() has not been implemented yet, it will be done soon.
Signed-off-by: Lai Jiangshan <laijs@fujitsu.com> --- include/libvirt/libvirt.h.in | 7 +++++++ include/libvirt/virtkeys.h | 23 +++++++++++++++++++++++ python/generator.py | 1 + src/libvirt_public.syms | 1 + 4 files changed, 32 insertions(+), 0 deletions(-)
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 7cd6e13..9167dbc 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -2617,6 +2617,13 @@ int virDomainOpenConsole(virDomainPtr dom,
int virDomainInjectNMI(virDomainPtr domain, unsigned int flags);
+int virDomainSendKey(virDomainPtr domain, + unsigned int codeset, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes, + unsigned int flags); +
This should have nkeycodes and keycodes renamed and reordered to match the common pattern: keycodes, keycodeslen Matthias

Signed-off-by: Lai Jiangshan <laijs@fujitsu.com> --- src/driver.h | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-) diff --git a/src/driver.h b/src/driver.h index 450dd53..70a30d9 100644 --- a/src/driver.h +++ b/src/driver.h @@ -535,6 +535,13 @@ typedef int typedef int (*virDrvDomainInjectNMI)(virDomainPtr dom, unsigned int flags); +typedef int + (*virDrvDomainSendKey)(virDomainPtr dom, unsigned int codeset, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes, + unsigned int flags); + typedef char * (*virDrvDomainMigrateBegin3) (virDomainPtr domain, @@ -738,6 +745,7 @@ struct _virDriver { virDrvDomainMigratePerform3 domainMigratePerform3; virDrvDomainMigrateFinish3 domainMigrateFinish3; virDrvDomainMigrateConfirm3 domainMigrateConfirm3; + virDrvDomainSendKey domainSendKey; }; typedef int -- 1.7.4.4

On Wed, May 25, 2011 at 05:37:48PM +0800, Lai Jiangshan wrote:
Signed-off-by: Lai Jiangshan <laijs@fujitsu.com> --- src/driver.h | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/src/driver.h b/src/driver.h index 450dd53..70a30d9 100644 --- a/src/driver.h +++ b/src/driver.h @@ -535,6 +535,13 @@ typedef int typedef int (*virDrvDomainInjectNMI)(virDomainPtr dom, unsigned int flags);
+typedef int + (*virDrvDomainSendKey)(virDomainPtr dom, unsigned int codeset, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes, + unsigned int flags); + typedef char * (*virDrvDomainMigrateBegin3) (virDomainPtr domain, @@ -738,6 +745,7 @@ struct _virDriver { virDrvDomainMigratePerform3 domainMigratePerform3; virDrvDomainMigrateFinish3 domainMigrateFinish3; virDrvDomainMigrateConfirm3 domainMigrateConfirm3; + virDrvDomainSendKey domainSendKey; };
ACK looks fine wrt to whatever the public API ends up being Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Signed-off-by: Lai Jiangshan <laijs@fujitsu.com> --- src/libvirt.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 54 insertions(+), 0 deletions(-) diff --git a/src/libvirt.c b/src/libvirt.c index ff16c48..8246975 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -6080,6 +6080,60 @@ error: } /** + * virDomainSendKey: + * @domain: pointer to domain object, or NULL for Domain0 + * @codeset: the code set of keycodes + * @holdtime: the time (in millsecond) how long the keys will be held + * @nkeycodes: number of keycodes + * @keycodes: array of keycodes + * @flags: the flags for controlling behavior, pass 0 for now + * + * Send key to the guest + * + * Returns 0 in case of success, -1 in case of failure. + */ + +int virDomainSendKey(virDomainPtr domain, + unsigned int codeset, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes, + unsigned int flags) +{ + virConnectPtr conn; + VIR_DOMAIN_DEBUG(domain, "flags=%u", flags); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + conn = domain->conn; + + if (conn->driver->domainSendKey) { + int ret; + ret = conn->driver->domainSendKey(domain, codeset, holdtime, + nkeycodes, keycodes, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(domain->conn); + return -1; +} + +/** * virDomainSetVcpus: * @domain: pointer to domain object, or NULL for Domain0 * @nvcpus: the new number of virtual CPUs for this domain -- 1.7.4.4

On Wed, May 25, 2011 at 05:37:49PM +0800, Lai Jiangshan wrote:
Signed-off-by: Lai Jiangshan <laijs@fujitsu.com> --- src/libvirt.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 54 insertions(+), 0 deletions(-)
diff --git a/src/libvirt.c b/src/libvirt.c index ff16c48..8246975 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -6080,6 +6080,60 @@ error: }
/** + * virDomainSendKey: + * @domain: pointer to domain object, or NULL for Domain0 + * @codeset: the code set of keycodes + * @holdtime: the time (in millsecond) how long the keys will be held + * @nkeycodes: number of keycodes + * @keycodes: array of keycodes + * @flags: the flags for controlling behavior, pass 0 for now + * + * Send key to the guest + * + * Returns 0 in case of success, -1 in case of failure. + */ + +int virDomainSendKey(virDomainPtr domain, + unsigned int codeset, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes, + unsigned int flags) +{ + virConnectPtr conn; + VIR_DOMAIN_DEBUG(domain, "flags=%u", flags);
Should include the other parameters in this debug message too
+ + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + conn = domain->conn; + + if (conn->driver->domainSendKey) { + int ret; + ret = conn->driver->domainSendKey(domain, codeset, holdtime, + nkeycodes, keycodes, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(domain->conn); + return -1; +} + +/** * virDomainSetVcpus: * @domain: pointer to domain object, or NULL for Domain0 * @nvcpus: the new number of virtual CPUs for this domain
ACK, pending the changes suggest to the public API signature Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

2011/5/25 Lai Jiangshan <laijs@cn.fujitsu.com>:
Signed-off-by: Lai Jiangshan <laijs@fujitsu.com> --- src/libvirt.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 54 insertions(+), 0 deletions(-)
diff --git a/src/libvirt.c b/src/libvirt.c index ff16c48..8246975 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -6080,6 +6080,60 @@ error: }
/** + * virDomainSendKey: + * @domain: pointer to domain object, or NULL for Domain0 + * @codeset: the code set of keycodes + * @holdtime: the time (in millsecond) how long the keys will be held + * @nkeycodes: number of keycodes + * @keycodes: array of keycodes + * @flags: the flags for controlling behavior, pass 0 for now + * + * Send key to the guest + * + * Returns 0 in case of success, -1 in case of failure. + */ + +int virDomainSendKey(virDomainPtr domain, + unsigned int codeset, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes, + unsigned int flags) +{ + virConnectPtr conn; + VIR_DOMAIN_DEBUG(domain, "flags=%u", flags); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } +
Invalid argument checks are missing, For example keycodes should not be NULL and nkeycodes should probably be larger than 0. Also the comment about renaming an reordering of nkeycodes and keycodes applies here too. Matthias

Signed-off-by: Lai Jiangshan <laijs@fujitsu.com> --- src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 19 ++++++++++++++++++- src/remote_protocol-structs | 11 +++++++++++ 3 files changed, 30 insertions(+), 1 deletions(-) diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 1691dab..6614250 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -6868,6 +6868,7 @@ static virDriver remote_driver = { .domainMigrateFinish3 = remoteDomainMigrateFinish3, /* 0.9.2 */ .domainMigrateConfirm3 = remoteDomainMigrateConfirm3, /* 0.9.2 */ .domainSetSchedulerParametersFlags = remoteDomainSetSchedulerParametersFlags, /* 0.9.2 */ + .domainSendKey = remoteDomainSendKey, /* 0.9.2 */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index f0da95d..61504c4 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -191,6 +191,14 @@ const REMOTE_SECRET_UUID_LIST_MAX = 16384; */ const REMOTE_CPU_BASELINE_MAX = 256; +/* + * Max number of sending keycodes. + */ +const REMOTE_SEND_KEY_MAX = 16; + +/* define dynamic array's base type for unsigned int */ +typedef unsigned int u_int_DABT; + /* UUID. VIR_UUID_BUFLEN definition comes from libvirt.h */ typedef opaque remote_uuid[VIR_UUID_BUFLEN]; @@ -838,6 +846,14 @@ struct remote_domain_inject_nmi_args { unsigned int flags; }; +struct remote_domain_send_key_args { + remote_nonnull_domain dom; + unsigned int codeset; + unsigned int holdtime; + u_int_DABT keycodes<REMOTE_SEND_KEY_MAX>; + unsigned int flags; +}; + struct remote_domain_set_vcpus_args { remote_nonnull_domain dom; int nvcpus; @@ -2291,7 +2307,8 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_MIGRATE_PERFORM3 = 216, /* skipgen skipgen */ REMOTE_PROC_DOMAIN_MIGRATE_FINISH3 = 217, /* skipgen skipgen */ REMOTE_PROC_DOMAIN_MIGRATE_CONFIRM3 = 218, /* skipgen skipgen */ - REMOTE_PROC_DOMAIN_SET_SCHEDULER_PARAMETERS_FLAGS = 219 /* skipgen skipgen */ + REMOTE_PROC_DOMAIN_SET_SCHEDULER_PARAMETERS_FLAGS = 219, /* skipgen skipgen */ + REMOTE_PROC_DOMAIN_SEND_KEY = 220 /* autogen autogen */ /* * Notice how the entries are grouped in sets of 10 ? diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 414b4d5..814821a 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -1553,3 +1553,14 @@ struct remote_message_header { u_int serial; remote_message_status status; }; + +struct remote_domain_send_key_args { + remote_nonnull_domain dom; + unsigned int codeset; + unsigned int holdtime; + struct { + unsigned int keycodes_len; + unsigned int * keycodes_val; + } keycodes; + unsigned int flags; +}; -- 1.7.4.4

2011/5/25 Lai Jiangshan <laijs@cn.fujitsu.com>:
Signed-off-by: Lai Jiangshan <laijs@fujitsu.com> --- src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 19 ++++++++++++++++++- src/remote_protocol-structs | 11 +++++++++++ 3 files changed, 30 insertions(+), 1 deletions(-)
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 1691dab..6614250 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -6868,6 +6868,7 @@ static virDriver remote_driver = { .domainMigrateFinish3 = remoteDomainMigrateFinish3, /* 0.9.2 */ .domainMigrateConfirm3 = remoteDomainMigrateConfirm3, /* 0.9.2 */ .domainSetSchedulerParametersFlags = remoteDomainSetSchedulerParametersFlags, /* 0.9.2 */ + .domainSendKey = remoteDomainSendKey, /* 0.9.2 */ };
static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index f0da95d..61504c4 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -191,6 +191,14 @@ const REMOTE_SECRET_UUID_LIST_MAX = 16384; */ const REMOTE_CPU_BASELINE_MAX = 256;
+/* + * Max number of sending keycodes. + */ +const REMOTE_SEND_KEY_MAX = 16; +
Why such a low limit?
+/* define dynamic array's base type for unsigned int */ +typedef unsigned int u_int_DABT; +
No need for this typedef
/* UUID. VIR_UUID_BUFLEN definition comes from libvirt.h */ typedef opaque remote_uuid[VIR_UUID_BUFLEN]; @@ -838,6 +846,14 @@ struct remote_domain_inject_nmi_args { unsigned int flags; };
+struct remote_domain_send_key_args { + remote_nonnull_domain dom; + unsigned int codeset; + unsigned int holdtime; + u_int_DABT keycodes<REMOTE_SEND_KEY_MAX>; + unsigned int flags; +}; +
As said in 4/13 there is no need for such an annotation. Here's a v2 that works in combination with my v2 for 4/13. Matthias

Signed-off-by: Lai Jiangshan <laijs@fujitsu.com> --- tools/virsh.c | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 4 ++ 2 files changed, 107 insertions(+), 0 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 80cffac..505a821 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -33,6 +33,8 @@ #include <signal.h> #include <poll.h> +#include <libvirt/virtkeys.h> + #include <libxml/parser.h> #include <libxml/tree.h> #include <libxml/xpath.h> @@ -3010,6 +3012,106 @@ cmdInjectNMI(vshControl *ctl, const vshCmd *cmd) } /* + * "send-key" command + */ +static const vshCmdInfo info_send_key[] = { + {"help", N_("Send keycodes to the guest")}, + {"desc", N_("Send keycodes to the guest, the keycodes must be integers\n" + " Examples:\n\n" + " virsh # send-key <domain> 37 18 21\n" + " virsh # send-key <domain> --holdtime 1000 0x15 18 0xf\n" + {NULL, NULL} +}; + +static const vshCmdOptDef opts_send_key[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, + {"codeset", VSH_OT_STRING, VSH_OFLAG_REQ_OPT, N_("the codeset of keycodes, default:linux")}, + {"holdtime", VSH_OT_INT, VSH_OFLAG_REQ_OPT, + N_("the time (in millsecond) how long the keys will be held")}, + {"keycode", VSH_OT_ARGV, VSH_OFLAG_REQ, N_("the key code")}, + {NULL, 0, 0, NULL} +}; + +static int get_integer_keycode(const char *key_name) +{ + long val; + char *endptr; + + val = strtol(key_name, &endptr, 0); + if (*endptr != '\0' || val > 255 || val <= 0) + return -1; + + return val; +} + +static bool +cmdSendKey(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom; + int ret = false; + const char *codeset_option; + int codeset; + int holdtime; + int count = 0; + const vshCmdOpt *opt; + int keycode; + unsigned int keycodes[MAX_SEND_KEY]; + + if (!vshConnectionUsability(ctl, ctl->conn)) + return false; + + if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) + return false; + + if (vshCommandOptString(cmd, "codeset", &codeset_option) <= 0) + codeset_option = "default"; + + if (vshCommandOptInt(cmd, "holdtime", &holdtime) <= 0) + holdtime = 0; + + if (STREQ(codeset_option, "default") || STREQ(codeset_option, "linux")) { + codeset = LIBVIRT_KEYCODE_LINUX; + } else if (STREQ(codeset_option, "diriver_default")) { + codeset = LIBVIRT_KEYCODE_DRIVER_DEFAULT; + } else if (STREQ(codeset_option, "xt")) { + codeset = LIBVIRT_KEYCODE_XT; + } else if (STREQ(codeset_option, "atset1")) { + codeset = LIBVIRT_KEYCODE_ATSET1; + } else if (STREQ(codeset_option, "atset2")) { + codeset = LIBVIRT_KEYCODE_ATSET2; + } else if (STREQ(codeset_option, "atset3")) { + codeset = LIBVIRT_KEYCODE_ATSET3; + } else { + vshError(ctl, _("unknown codeset: '%s'"), codeset_option); + goto free_domain; + } + + for_each_variable_arg(cmd, opt) { + if (count == MAX_SEND_KEY) { + vshError(ctl, _("too many keycode")); + goto free_domain; + } + + if ((keycode = get_integer_keycode(opt->data)) > 0) + goto get_keycode; + + vshError(ctl, _("invalid keycode: '%s'"), opt->data); + goto free_domain; + +get_keycode: + keycodes[count] = keycode; + count++; + } + + if (!(virDomainSendKey(dom, codeset, holdtime, count, keycodes, 0) < 0)) + ret = true; + +free_domain: + virDomainFree(dom); + return ret; +} + +/* * "setmemory" command */ static const vshCmdInfo info_setmem[] = { @@ -10807,6 +10909,7 @@ static const vshCmdDef domManagementCmds[] = { {"dumpxml", cmdDumpXML, opts_dumpxml, info_dumpxml, 0}, {"edit", cmdEdit, opts_edit, info_edit, 0}, {"inject-nmi", cmdInjectNMI, opts_inject_nmi, info_inject_nmi, 0}, + {"send-key", cmdSendKey, opts_send_key, info_send_key}, {"managedsave", cmdManagedSave, opts_managedsave, info_managedsave, 0}, {"managedsave-remove", cmdManagedSaveRemove, opts_managedsaveremove, info_managedsaveremove, 0}, diff --git a/tools/virsh.pod b/tools/virsh.pod index ef01f41..beef608 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -294,6 +294,10 @@ scheduling by the hypervisor. Inject NMI to the guest +=item B<send-key> I<domain-id> I<--codeset> B<codeset> I<--holdtime> B<holdtime> B<keycode>... + +Send keys to the guest + =item B<shutdown> The domain is in the process of shutting down, i.e. the guest operating system -- 1.7.4.4

On Wed, May 25, 2011 at 05:37:51PM +0800, Lai Jiangshan wrote:
Signed-off-by: Lai Jiangshan <laijs@fujitsu.com> --- tools/virsh.c | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 4 ++ 2 files changed, 107 insertions(+), 0 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c index 80cffac..505a821 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -33,6 +33,8 @@ #include <signal.h> #include <poll.h>
+#include <libvirt/virtkeys.h> + #include <libxml/parser.h> #include <libxml/tree.h> #include <libxml/xpath.h> @@ -3010,6 +3012,106 @@ cmdInjectNMI(vshControl *ctl, const vshCmd *cmd) }
/* + * "send-key" command + */ +static const vshCmdInfo info_send_key[] = { + {"help", N_("Send keycodes to the guest")}, + {"desc", N_("Send keycodes to the guest, the keycodes must be integers\n" + " Examples:\n\n" + " virsh # send-key <domain> 37 18 21\n" + " virsh # send-key <domain> --holdtime 1000 0x15 18 0xf\n" + {NULL, NULL} +};
As mentioned in my earlier message, it is probably desirable for this to use key strings by default, since that is more friendly for the admin. Either have an explicit flag to enable use of key values, or perhaps can auto-detect them.
+static const vshCmdOptDef opts_send_key[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, + {"codeset", VSH_OT_STRING, VSH_OFLAG_REQ_OPT, N_("the codeset of keycodes, default:linux")}, + {"holdtime", VSH_OT_INT, VSH_OFLAG_REQ_OPT, + N_("the time (in millsecond) how long the keys will be held")}, + {"keycode", VSH_OT_ARGV, VSH_OFLAG_REQ, N_("the key code")}, + {NULL, 0, 0, NULL} +}; + +static int get_integer_keycode(const char *key_name) +{ + long val; + char *endptr; + + val = strtol(key_name, &endptr, 0); + if (*endptr != '\0' || val > 255 || val <= 0) + return -1; + + return val; +} + +static bool +cmdSendKey(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom; + int ret = false; + const char *codeset_option; + int codeset; + int holdtime; + int count = 0; + const vshCmdOpt *opt; + int keycode; + unsigned int keycodes[MAX_SEND_KEY]; + + if (!vshConnectionUsability(ctl, ctl->conn)) + return false; + + if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) + return false; + + if (vshCommandOptString(cmd, "codeset", &codeset_option) <= 0) + codeset_option = "default"; + + if (vshCommandOptInt(cmd, "holdtime", &holdtime) <= 0) + holdtime = 0; + + if (STREQ(codeset_option, "default") || STREQ(codeset_option, "linux")) { + codeset = LIBVIRT_KEYCODE_LINUX; + } else if (STREQ(codeset_option, "diriver_default")) { + codeset = LIBVIRT_KEYCODE_DRIVER_DEFAULT;
As mentioned, I don't think we want 'driver_default' as a use case.
+ } else if (STREQ(codeset_option, "xt")) { + codeset = LIBVIRT_KEYCODE_XT; + } else if (STREQ(codeset_option, "atset1")) { + codeset = LIBVIRT_KEYCODE_ATSET1; + } else if (STREQ(codeset_option, "atset2")) { + codeset = LIBVIRT_KEYCODE_ATSET2; + } else if (STREQ(codeset_option, "atset3")) { + codeset = LIBVIRT_KEYCODE_ATSET3; + } else { + vshError(ctl, _("unknown codeset: '%s'"), codeset_option); + goto free_domain; + } + + for_each_variable_arg(cmd, opt) { + if (count == MAX_SEND_KEY) { + vshError(ctl, _("too many keycode")); + goto free_domain; + } + + if ((keycode = get_integer_keycode(opt->data)) > 0) + goto get_keycode; + + vshError(ctl, _("invalid keycode: '%s'"), opt->data); + goto free_domain; + +get_keycode: + keycodes[count] = keycode; + count++; + } + + if (!(virDomainSendKey(dom, codeset, holdtime, count, keycodes, 0) < 0)) + ret = true; + +free_domain: + virDomainFree(dom); + return ret; +} + +/* * "setmemory" command */ static const vshCmdInfo info_setmem[] = { @@ -10807,6 +10909,7 @@ static const vshCmdDef domManagementCmds[] = { {"dumpxml", cmdDumpXML, opts_dumpxml, info_dumpxml, 0}, {"edit", cmdEdit, opts_edit, info_edit, 0}, {"inject-nmi", cmdInjectNMI, opts_inject_nmi, info_inject_nmi, 0}, + {"send-key", cmdSendKey, opts_send_key, info_send_key}, {"managedsave", cmdManagedSave, opts_managedsave, info_managedsave, 0}, {"managedsave-remove", cmdManagedSaveRemove, opts_managedsaveremove, info_managedsaveremove, 0}, diff --git a/tools/virsh.pod b/tools/virsh.pod index ef01f41..beef608 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -294,6 +294,10 @@ scheduling by the hypervisor.
Inject NMI to the guest
+=item B<send-key> I<domain-id> I<--codeset> B<codeset> I<--holdtime> B<holdtime> B<keycode>... + +Send keys to the guest + =item B<shutdown>
The domain is in the process of shutting down, i.e. the guest operating system -- 1.7.4.4
-- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Signed-off-by: Lai Jiangshan <laijs@fujitsu.com> --- src/qemu/qemu_driver.c | 50 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 28 +++++++++++++++++++++++ src/qemu/qemu_monitor.h | 6 +++++ src/qemu/qemu_monitor_json.c | 15 ++++++++++++ src/qemu/qemu_monitor_json.h | 5 ++++ src/qemu/qemu_monitor_text.c | 48 ++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_text.h | 5 ++++ 7 files changed, 157 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 691965d..f7e21bf 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -1753,6 +1753,55 @@ cleanup: return ret; } +static int qemuDomainSendKey(virDomainPtr domain, + unsigned int codeset, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes, + unsigned int flags) +{ + struct qemud_driver *driver = domain->conn->privateData; + virDomainObjPtr vm = NULL; + int ret = -1; + qemuDomainObjPrivatePtr priv; + + virCheckFlags(0, -1); + + qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, domain->uuid); + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(domain->uuid, uuidstr); + qemuReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + if (!virDomainObjIsActive(vm)) { + qemuReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto cleanup; + } + + priv = vm->privateData; + + if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) + goto cleanup; + qemuDomainObjEnterMonitorWithDriver(driver, vm); + ret = qemuMonitorSendKey(priv->mon, codeset, holdtime, nkeycodes, keycodes); + qemuDomainObjExitMonitorWithDriver(driver, vm); + if (qemuDomainObjEndJob(vm) == 0) { + vm = NULL; + goto cleanup; + } + +cleanup: + if (vm) + virDomainObjUnlock(vm); + qemuDriverUnlock(driver); + return ret; +} + static int qemudDomainGetInfo(virDomainPtr dom, virDomainInfoPtr info) { @@ -7746,6 +7795,7 @@ static virDriver qemuDriver = { .domainMigrateFinish3 = qemuDomainMigrateFinish3, /* 0.9.2 */ .domainMigrateConfirm3 = qemuDomainMigrateConfirm3, /* 0.9.2 */ .domainSetSchedulerParametersFlags = qemuSetSchedulerParametersFlags, /* 0.9.2 */ + .domainSendKey = qemuDomainSendKey, /* 0.9.2 */ }; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 5186f99..c0688fd 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -38,6 +38,8 @@ #include "logging.h" #include "files.h" +#include <libvirt/virtkeys.h> + #define VIR_FROM_THIS VIR_FROM_QEMU #define DEBUG_IO 0 @@ -2294,6 +2296,32 @@ int qemuMonitorInjectNMI(qemuMonitorPtr mon) return ret; } +int qemuMonitorSendKey(qemuMonitorPtr mon, + unsigned int codeset, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes) +{ + int ret; + + VIR_DEBUG("mon=%p, codeset=%u, holdtime=%u, nkeycodes=%u", + mon, codeset, holdtime, nkeycodes); + + if (!(codeset == LIBVIRT_KEYCODE_DRIVER_DEFAULT + || codeset == LIBVIRT_KEYCODE_XT)) { + qemuReportError(VIR_ERR_NO_SUPPORT, + "qemu monitor can not support the codeset: %d", + codeset); + return -1; + } + + if (mon->json) + ret = qemuMonitorJSONSendKey(mon, holdtime, nkeycodes, keycodes); + else + ret = qemuMonitorTextSendKey(mon, holdtime, nkeycodes, keycodes); + return ret; +} + int qemuMonitorScreendump(qemuMonitorPtr mon, const char *file) { diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 05c3359..76a849a 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -435,6 +435,12 @@ int qemuMonitorInjectNMI(qemuMonitorPtr mon); int qemuMonitorScreendump(qemuMonitorPtr mon, const char *file); +int qemuMonitorSendKey(qemuMonitorPtr mon, + unsigned int codeset, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes); + /** * When running two dd process and using <> redirection, we need a * shell that will not truncate files. These two strings serve that diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 2d8a390..a547f1d 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -2615,6 +2615,21 @@ cleanup: return ret; } +int qemuMonitorJSONSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes) +{ + /* + * FIXME: qmp sendkey has not been implemented yet, + * and qmp API of it can not be anticipated, so we use hmp temporary. + */ + if (qemuMonitorCheckHMP(mon, "sendkey")) { + return qemuMonitorTextSendKey(mon, holdtime, nkeycodes, keycodes); + } else + return -1; +} + int qemuMonitorJSONScreendump(qemuMonitorPtr mon, const char *file) { diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index ec79b03..89d7515 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -214,6 +214,11 @@ int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon, int qemuMonitorJSONInjectNMI(qemuMonitorPtr mon); +int qemuMonitorJSONSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes); + int qemuMonitorJSONScreendump(qemuMonitorPtr mon, const char *file); diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index 106f2d3..433cde1 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -2717,6 +2717,54 @@ fail: return -1; } +int qemuMonitorTextSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes) +{ + int i; + int pos = strlen("sendkey "); + int cmd_len = pos + nkeycodes * strlen("0xFF-") + 12 + sizeof('\0'); + char *cmd, *reply = NULL; + + if (nkeycodes > 16 || nkeycodes == 0) + return -1; + + if (VIR_ALLOC_N(cmd, cmd_len) < 0) + return -1; + + memcpy(cmd, "sendkey ", pos); + for (i = 0; i < nkeycodes; i++) { + if (keycodes[i] > 255) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("the %dth keycode is invalid: 0x%02X"), + i, keycodes[i]); + VIR_FREE(cmd); + return -1; + } + + pos += sprintf(cmd + pos, "0x%02X-", keycodes[i]); + } + cmd[pos - 1] = ' '; // the last '-' --> ' ' + + if (holdtime) + sprintf(cmd + pos, "%u", holdtime); + + if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0) + goto fail; + + VIR_FREE(cmd); + VIR_FREE(reply); + return 0; + +fail: + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("failed to send key using command '%s'"), + cmd); + VIR_FREE(cmd); + return -1; +} + /* Returns -1 on error, -2 if not supported */ int qemuMonitorTextScreendump(qemuMonitorPtr mon, const char *file) { diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h index 8a69105..971de83 100644 --- a/src/qemu/qemu_monitor_text.h +++ b/src/qemu/qemu_monitor_text.h @@ -208,6 +208,11 @@ int qemuMonitorTextArbitraryCommand(qemuMonitorPtr mon, const char *cmd, int qemuMonitorTextInjectNMI(qemuMonitorPtr mon); +int qemuMonitorTextSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes); + int qemuMonitorTextScreendump(qemuMonitorPtr mon, const char *file); #endif /* QEMU_MONITOR_TEXT_H */ -- 1.7.4.4

On Wed, May 25, 2011 at 05:37:52PM +0800, Lai Jiangshan wrote:
Signed-off-by: Lai Jiangshan <laijs@fujitsu.com> --- src/qemu/qemu_driver.c | 50 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 28 +++++++++++++++++++++++ src/qemu/qemu_monitor.h | 6 +++++ src/qemu/qemu_monitor_json.c | 15 ++++++++++++ src/qemu/qemu_monitor_json.h | 5 ++++ src/qemu/qemu_monitor_text.c | 48 ++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_text.h | 5 ++++ 7 files changed, 157 insertions(+), 0 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 691965d..f7e21bf 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -1753,6 +1753,55 @@ cleanup: return ret; }
+static int qemuDomainSendKey(virDomainPtr domain, + unsigned int codeset, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes, + unsigned int flags) +{ + struct qemud_driver *driver = domain->conn->privateData; + virDomainObjPtr vm = NULL; + int ret = -1; + qemuDomainObjPrivatePtr priv; + + virCheckFlags(0, -1); + + qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, domain->uuid); + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(domain->uuid, uuidstr); + qemuReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + if (!virDomainObjIsActive(vm)) { + qemuReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto cleanup; + } + + priv = vm->privateData; + + if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) + goto cleanup; + qemuDomainObjEnterMonitorWithDriver(driver, vm); + ret = qemuMonitorSendKey(priv->mon, codeset, holdtime, nkeycodes, keycodes); + qemuDomainObjExitMonitorWithDriver(driver, vm); + if (qemuDomainObjEndJob(vm) == 0) { + vm = NULL; + goto cleanup; + } + +cleanup: + if (vm) + virDomainObjUnlock(vm); + qemuDriverUnlock(driver); + return ret; +} + static int qemudDomainGetInfo(virDomainPtr dom, virDomainInfoPtr info) { @@ -7746,6 +7795,7 @@ static virDriver qemuDriver = { .domainMigrateFinish3 = qemuDomainMigrateFinish3, /* 0.9.2 */ .domainMigrateConfirm3 = qemuDomainMigrateConfirm3, /* 0.9.2 */ .domainSetSchedulerParametersFlags = qemuSetSchedulerParametersFlags, /* 0.9.2 */ + .domainSendKey = qemuDomainSendKey, /* 0.9.2 */ };
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 5186f99..c0688fd 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -38,6 +38,8 @@ #include "logging.h" #include "files.h"
+#include <libvirt/virtkeys.h> + #define VIR_FROM_THIS VIR_FROM_QEMU
#define DEBUG_IO 0 @@ -2294,6 +2296,32 @@ int qemuMonitorInjectNMI(qemuMonitorPtr mon) return ret; }
+int qemuMonitorSendKey(qemuMonitorPtr mon, + unsigned int codeset, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes) +{ + int ret; + + VIR_DEBUG("mon=%p, codeset=%u, holdtime=%u, nkeycodes=%u", + mon, codeset, holdtime, nkeycodes); + + if (!(codeset == LIBVIRT_KEYCODE_DRIVER_DEFAULT + || codeset == LIBVIRT_KEYCODE_XT)) { + qemuReportError(VIR_ERR_NO_SUPPORT, + "qemu monitor can not support the codeset: %d", + codeset); + return -1; + } + + if (mon->json) + ret = qemuMonitorJSONSendKey(mon, holdtime, nkeycodes, keycodes); + else + ret = qemuMonitorTextSendKey(mon, holdtime, nkeycodes, keycodes); + return ret; +} + int qemuMonitorScreendump(qemuMonitorPtr mon, const char *file) { diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 05c3359..76a849a 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -435,6 +435,12 @@ int qemuMonitorInjectNMI(qemuMonitorPtr mon); int qemuMonitorScreendump(qemuMonitorPtr mon, const char *file);
+int qemuMonitorSendKey(qemuMonitorPtr mon, + unsigned int codeset, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes); + /** * When running two dd process and using <> redirection, we need a * shell that will not truncate files. These two strings serve that diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 2d8a390..a547f1d 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -2615,6 +2615,21 @@ cleanup: return ret; }
+int qemuMonitorJSONSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes) +{ + /* + * FIXME: qmp sendkey has not been implemented yet, + * and qmp API of it can not be anticipated, so we use hmp temporary. + */ + if (qemuMonitorCheckHMP(mon, "sendkey")) { + return qemuMonitorTextSendKey(mon, holdtime, nkeycodes, keycodes); + } else + return -1; +} + int qemuMonitorJSONScreendump(qemuMonitorPtr mon, const char *file) { diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index ec79b03..89d7515 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -214,6 +214,11 @@ int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon,
int qemuMonitorJSONInjectNMI(qemuMonitorPtr mon);
+int qemuMonitorJSONSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes); + int qemuMonitorJSONScreendump(qemuMonitorPtr mon, const char *file);
diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index 106f2d3..433cde1 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -2717,6 +2717,54 @@ fail: return -1; }
+int qemuMonitorTextSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes) +{ + int i; + int pos = strlen("sendkey "); + int cmd_len = pos + nkeycodes * strlen("0xFF-") + 12 + sizeof('\0'); + char *cmd, *reply = NULL; + + if (nkeycodes > 16 || nkeycodes == 0) + return -1; + + if (VIR_ALLOC_N(cmd, cmd_len) < 0) + return -1; + + memcpy(cmd, "sendkey ", pos); + for (i = 0; i < nkeycodes; i++) { + if (keycodes[i] > 255) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("the %dth keycode is invalid: 0x%02X"), + i, keycodes[i]); + VIR_FREE(cmd); + return -1; + } + + pos += sprintf(cmd + pos, "0x%02X-", keycodes[i]); + } + cmd[pos - 1] = ' '; // the last '-' --> ' ' + + if (holdtime) + sprintf(cmd + pos, "%u", holdtime);
Really don't want to use VIR_ALLOC_N/memcpy/sprintf here. Better to use virBuffer APIs to build up the command string.
+ + if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0) + goto fail; + + VIR_FREE(cmd); + VIR_FREE(reply); + return 0; + +fail: + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("failed to send key using command '%s'"), + cmd); + VIR_FREE(cmd); + return -1; +} + /* Returns -1 on error, -2 if not supported */ int qemuMonitorTextScreendump(qemuMonitorPtr mon, const char *file) { diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h index 8a69105..971de83 100644 --- a/src/qemu/qemu_monitor_text.h +++ b/src/qemu/qemu_monitor_text.h @@ -208,6 +208,11 @@ int qemuMonitorTextArbitraryCommand(qemuMonitorPtr mon, const char *cmd,
int qemuMonitorTextInjectNMI(qemuMonitorPtr mon);
+int qemuMonitorTextSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes); + int qemuMonitorTextScreendump(qemuMonitorPtr mon, const char *file);
Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

It make send-key command more friendly for user. Signed-off-by: Lai Jiangshan <laijs@fujitsu.com> --- include/libvirt/virtkeys.h | 253 ++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.c | 21 ++++ 2 files changed, 274 insertions(+), 0 deletions(-) diff --git a/include/libvirt/virtkeys.h b/include/libvirt/virtkeys.h index eb07129..7893450 100644 --- a/include/libvirt/virtkeys.h +++ b/include/libvirt/virtkeys.h @@ -20,4 +20,257 @@ enum libvirt_keycode_set { #define MAX_SEND_KEY 16 +#define ITEM_KEYCODE(KEY_XXX) VIRT##KEY_XXX +#define KEYCODES() \ + keycode(KEY_ESC, 1)\ + keycode(KEY_1, 2)\ + keycode(KEY_2, 3)\ + keycode(KEY_3, 4)\ + keycode(KEY_4, 5)\ + keycode(KEY_5, 6)\ + keycode(KEY_6, 7)\ + keycode(KEY_7, 8)\ + keycode(KEY_8, 9)\ + keycode(KEY_9, 10)\ + keycode(KEY_0, 11)\ + keycode(KEY_MINUS, 12)\ + keycode(KEY_EQUAL, 13)\ + keycode(KEY_BACKSPACE, 14)\ + keycode(KEY_TAB, 15)\ + keycode(KEY_Q, 16)\ + keycode(KEY_W, 17)\ + keycode(KEY_E, 18)\ + keycode(KEY_R, 19)\ + keycode(KEY_T, 20)\ + keycode(KEY_Y, 21)\ + keycode(KEY_U, 22)\ + keycode(KEY_I, 23)\ + keycode(KEY_O, 24)\ + keycode(KEY_P, 25)\ + keycode(KEY_LEFTBRACE, 26)\ + keycode(KEY_RIGHTBRACE, 27)\ + keycode(KEY_ENTER, 28)\ + keycode(KEY_LEFTCTRL, 29)\ + keycode(KEY_A, 30)\ + keycode(KEY_S, 31)\ + keycode(KEY_D, 32)\ + keycode(KEY_F, 33)\ + keycode(KEY_G, 34)\ + keycode(KEY_H, 35)\ + keycode(KEY_J, 36)\ + keycode(KEY_K, 37)\ + keycode(KEY_L, 38)\ + keycode(KEY_SEMICOLON, 39)\ + keycode(KEY_APOSTROPHE, 40)\ + keycode(KEY_GRAVE, 41)\ + keycode(KEY_LEFTSHIFT, 42)\ + keycode(KEY_BACKSLASH, 43)\ + keycode(KEY_Z, 44)\ + keycode(KEY_X, 45)\ + keycode(KEY_C, 46)\ + keycode(KEY_V, 47)\ + keycode(KEY_B, 48)\ + keycode(KEY_N, 49)\ + keycode(KEY_M, 50)\ + keycode(KEY_COMMA, 51)\ + keycode(KEY_DOT, 52)\ + keycode(KEY_SLASH, 53)\ + keycode(KEY_RIGHTSHIFT, 54)\ + keycode(KEY_KPASTERISK, 55)\ + keycode(KEY_LEFTALT, 56)\ + keycode(KEY_SPACE, 57)\ + keycode(KEY_CAPSLOCK, 58)\ + keycode(KEY_F1, 59)\ + keycode(KEY_F2, 60)\ + keycode(KEY_F3, 61)\ + keycode(KEY_F4, 62)\ + keycode(KEY_F5, 63)\ + keycode(KEY_F6, 64)\ + keycode(KEY_F7, 65)\ + keycode(KEY_F8, 66)\ + keycode(KEY_F9, 67)\ + keycode(KEY_F10, 68)\ + keycode(KEY_NUMLOCK, 69)\ + keycode(KEY_SCROLLLOCK, 70)\ + keycode(KEY_KP7, 71)\ + keycode(KEY_KP8, 72)\ + keycode(KEY_KP9, 73)\ + keycode(KEY_KPMINUS, 74)\ + keycode(KEY_KP4, 75)\ + keycode(KEY_KP5, 76)\ + keycode(KEY_KP6, 77)\ + keycode(KEY_KPPLUS, 78)\ + keycode(KEY_KP1, 79)\ + keycode(KEY_KP2, 80)\ + keycode(KEY_KP3, 81)\ + keycode(KEY_KP0, 82)\ + keycode(KEY_KPDOT, 83)\ + keycode(KEY_ZENKAKUHANKAKU, 85)\ + keycode(KEY_102ND, 86)\ + keycode(KEY_F11, 87)\ + keycode(KEY_F12, 88)\ + keycode(KEY_RO, 89)\ + keycode(KEY_KATAKANA, 90)\ + keycode(KEY_HIRAGANA, 91)\ + keycode(KEY_HENKAN, 92)\ + keycode(KEY_KATAKANAHIRAGANA, 93)\ + keycode(KEY_MUHENKAN, 94)\ + keycode(KEY_KPJPCOMMA, 95)\ + keycode(KEY_KPENTER, 96)\ + keycode(KEY_RIGHTCTRL, 97)\ + keycode(KEY_KPSLASH, 98)\ + keycode(KEY_SYSRQ, 99)\ + keycode(KEY_RIGHTALT, 100)\ + keycode(KEY_LINEFEED, 101)\ + keycode(KEY_HOME, 102)\ + keycode(KEY_UP, 103)\ + keycode(KEY_PAGEUP, 104)\ + keycode(KEY_LEFT, 105)\ + keycode(KEY_RIGHT, 106)\ + keycode(KEY_END, 107)\ + keycode(KEY_DOWN, 108)\ + keycode(KEY_PAGEDOWN, 109)\ + keycode(KEY_INSERT, 110)\ + keycode(KEY_DELETE, 111)\ + keycode(KEY_MACRO, 112)\ + keycode(KEY_MUTE, 113)\ + keycode(KEY_VOLUMEDOWN, 114)\ + keycode(KEY_VOLUMEUP, 115)\ + keycode(KEY_POWER, 116)\ + keycode(KEY_KPEQUAL, 117)\ + keycode(KEY_KPPLUSMINUS, 118)\ + keycode(KEY_PAUSE, 119)\ + keycode(KEY_SCALE, 120)\ + keycode(KEY_KPCOMMA, 121)\ + keycode(KEY_HANGEUL, 122)\ + keycode(KEY_HANGUEL, 122)\ + keycode(KEY_HANJA, 123)\ + keycode(KEY_YEN, 124)\ + keycode(KEY_LEFTMETA, 125)\ + keycode(KEY_RIGHTMETA, 126)\ + keycode(KEY_COMPOSE, 127)\ + keycode(KEY_STOP, 128)\ + keycode(KEY_AGAIN, 129)\ + keycode(KEY_PROPS, 130)\ + keycode(KEY_UNDO, 131)\ + keycode(KEY_FRONT, 132)\ + keycode(KEY_COPY, 133)\ + keycode(KEY_OPEN, 134)\ + keycode(KEY_PASTE, 135)\ + keycode(KEY_FIND, 136)\ + keycode(KEY_CUT, 137)\ + keycode(KEY_HELP, 138)\ + keycode(KEY_MENU, 139)\ + keycode(KEY_CALC, 140)\ + keycode(KEY_SETUP, 141)\ + keycode(KEY_SLEEP, 142)\ + keycode(KEY_WAKEUP, 143)\ + keycode(KEY_FILE, 144)\ + keycode(KEY_SENDFILE, 145)\ + keycode(KEY_DELETEFILE, 146)\ + keycode(KEY_XFER, 147)\ + keycode(KEY_PROG1, 148)\ + keycode(KEY_PROG2, 149)\ + keycode(KEY_WWW, 150)\ + keycode(KEY_MSDOS, 151)\ + keycode(KEY_COFFEE, 152)\ + keycode(KEY_SCREENLOCK, 152)\ + keycode(KEY_DIRECTION, 153)\ + keycode(KEY_CYCLEWINDOWS, 154)\ + keycode(KEY_MAIL, 155)\ + keycode(KEY_BOOKMARKS, 156)\ + keycode(KEY_COMPUTER, 157)\ + keycode(KEY_BACK, 158)\ + keycode(KEY_FORWARD, 159)\ + keycode(KEY_CLOSECD, 160)\ + keycode(KEY_EJECTCD, 161)\ + keycode(KEY_EJECTCLOSECD, 162)\ + keycode(KEY_NEXTSONG, 163)\ + keycode(KEY_PLAYPAUSE, 164)\ + keycode(KEY_PREVIOUSSONG, 165)\ + keycode(KEY_STOPCD, 166)\ + keycode(KEY_RECORD, 167)\ + keycode(KEY_REWIND, 168)\ + keycode(KEY_PHONE, 169)\ + keycode(KEY_ISO, 170)\ + keycode(KEY_CONFIG, 171)\ + keycode(KEY_HOMEPAGE, 172)\ + keycode(KEY_REFRESH, 173)\ + keycode(KEY_EXIT, 174)\ + keycode(KEY_MOVE, 175)\ + keycode(KEY_EDIT, 176)\ + keycode(KEY_SCROLLUP, 177)\ + keycode(KEY_SCROLLDOWN, 178)\ + keycode(KEY_KPLEFTPAREN, 179)\ + keycode(KEY_KPRIGHTPAREN, 180)\ + keycode(KEY_NEW, 181)\ + keycode(KEY_REDO, 182)\ + keycode(KEY_F13, 183)\ + keycode(KEY_F14, 184)\ + keycode(KEY_F15, 185)\ + keycode(KEY_F16, 186)\ + keycode(KEY_F17, 187)\ + keycode(KEY_F18, 188)\ + keycode(KEY_F19, 189)\ + keycode(KEY_F20, 190)\ + keycode(KEY_F21, 191)\ + keycode(KEY_F22, 192)\ + keycode(KEY_F23, 193)\ + keycode(KEY_F24, 194)\ + keycode(KEY_PLAYCD, 200)\ + keycode(KEY_PAUSECD, 201)\ + keycode(KEY_PROG3, 202)\ + keycode(KEY_PROG4, 203)\ + keycode(KEY_DASHBOARD, 204)\ + keycode(KEY_SUSPEND, 205)\ + keycode(KEY_CLOSE, 206)\ + keycode(KEY_PLAY, 207)\ + keycode(KEY_FASTFORWARD, 208)\ + keycode(KEY_BASSBOOST, 209)\ + keycode(KEY_PRINT, 210)\ + keycode(KEY_HP, 211)\ + keycode(KEY_CAMERA, 212)\ + keycode(KEY_SOUND, 213)\ + keycode(KEY_QUESTION, 214)\ + keycode(KEY_EMAIL, 215)\ + keycode(KEY_CHAT, 216)\ + keycode(KEY_SEARCH, 217)\ + keycode(KEY_CONNECT, 218)\ + keycode(KEY_FINANCE, 219)\ + keycode(KEY_SPORT, 220)\ + keycode(KEY_SHOP, 221)\ + keycode(KEY_ALTERASE, 222)\ + keycode(KEY_CANCEL, 223)\ + keycode(KEY_BRIGHTNESSDOWN, 224)\ + keycode(KEY_BRIGHTNESSUP, 225)\ + keycode(KEY_MEDIA, 226)\ + keycode(KEY_SWITCHVIDEOMODE, 227)\ + keycode(KEY_KBDILLUMTOGGLE, 228)\ + keycode(KEY_KBDILLUMDOWN, 229)\ + keycode(KEY_KBDILLUMUP, 230)\ + keycode(KEY_SEND, 231)\ + keycode(KEY_REPLY, 232)\ + keycode(KEY_FORWARDMAIL, 233)\ + keycode(KEY_SAVE, 234)\ + keycode(KEY_DOCUMENTS, 235)\ + keycode(KEY_BATTERY, 236)\ + keycode(KEY_BLUETOOTH, 237)\ + keycode(KEY_WLAN, 238)\ + keycode(KEY_UWB, 239)\ + keycode(KEY_UNKNOWN, 240)\ + keycode(KEY_VIDEO_NEXT, 241)\ + keycode(KEY_VIDEO_PREV, 242)\ + keycode(KEY_BRIGHTNESS_CYCLE, 243)\ + keycode(KEY_BRIGHTNESS_ZERO, 244)\ + keycode(KEY_DISPLAY_OFF, 245)\ + keycode(KEY_WIMAX, 246)\ + keycode(KEY_RFKILL, 247) + +/* define keycode constants */ +enum { +#define keycode(KEY_XXX, value) ITEM_KEYCODE(KEY_XXX) = value, + KEYCODES() +#undef keycode +}; + #endif diff --git a/tools/virsh.c b/tools/virsh.c index 505a821..3bccc08 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -3017,9 +3017,16 @@ cmdInjectNMI(vshControl *ctl, const vshCmd *cmd) static const vshCmdInfo info_send_key[] = { {"help", N_("Send keycodes to the guest")}, {"desc", N_("Send keycodes to the guest, the keycodes must be integers\n" + " or the KEY_* strings listed below for the \"linux\" codeset.\n\n" " Examples:\n\n" " virsh # send-key <domain> 37 18 21\n" " virsh # send-key <domain> --holdtime 1000 0x15 18 0xf\n" + " virsh # send-eky <domain> KEY_LEFTCTRL KEY_LEFTALT KEY_F1\n" + "\n" + " KEY_XXX strings for the \"linux\" codeset:\n" +#define keycode(var, value) " " #var " = " #value "\n" + KEYCODES())}, +#undef keycode {NULL, NULL} }; @@ -3032,6 +3039,17 @@ static const vshCmdOptDef opts_send_key[] = { {NULL, 0, 0, NULL} }; +static int get_linux_keycode(const char *key_name) +{ + if (key_name[0] == 'K' && key_name[1] == 'E' && key_name[2] == 'Y' && + key_name[3] == '_') { +#define keycode(var, value) if (STREQ(#var + 4, key_name + 4)) return value; + KEYCODES() +#undef keycode + } + return -1; +} + static int get_integer_keycode(const char *key_name) { long val; @@ -3092,6 +3110,9 @@ cmdSendKey(vshControl *ctl, const vshCmd *cmd) goto free_domain; } + if ((keycode = get_linux_keycode(opt->data)) > 0) + goto get_keycode; + if ((keycode = get_integer_keycode(opt->data)) > 0) goto get_keycode; -- 1.7.4.4

On Wed, May 25, 2011 at 05:37:53PM +0800, Lai Jiangshan wrote:
It make send-key command more friendly for user.
Signed-off-by: Lai Jiangshan <laijs@fujitsu.com> --- include/libvirt/virtkeys.h | 253 ++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.c | 21 ++++ 2 files changed, 274 insertions(+), 0 deletions(-)
diff --git a/include/libvirt/virtkeys.h b/include/libvirt/virtkeys.h index eb07129..7893450 100644 --- a/include/libvirt/virtkeys.h +++ b/include/libvirt/virtkeys.h @@ -20,4 +20,257 @@ enum libvirt_keycode_set {
#define MAX_SEND_KEY 16
+#define ITEM_KEYCODE(KEY_XXX) VIRT##KEY_XXX +#define KEYCODES() \ + keycode(KEY_ESC, 1)\ + keycode(KEY_1, 2)\ + keycode(KEY_2, 3)\ + keycode(KEY_3, 4)\ + keycode(KEY_4, 5)\ + keycode(KEY_5, 6)\ + keycode(KEY_6, 7)\ + keycode(KEY_7, 8)\ + keycode(KEY_8, 9)\ + keycode(KEY_9, 10)\ + keycode(KEY_0, 11)\ + keycode(KEY_MINUS, 12)\ + keycode(KEY_EQUAL, 13)\ + keycode(KEY_BACKSPACE, 14)\ + keycode(KEY_TAB, 15)\ + keycode(KEY_Q, 16)\ + keycode(KEY_W, 17)\ + keycode(KEY_E, 18)\ + keycode(KEY_R, 19)\ + keycode(KEY_T, 20)\ + keycode(KEY_Y, 21)\ + keycode(KEY_U, 22)\ + keycode(KEY_I, 23)\ + keycode(KEY_O, 24)\ + keycode(KEY_P, 25)\ + keycode(KEY_LEFTBRACE, 26)\ + keycode(KEY_RIGHTBRACE, 27)\ + keycode(KEY_ENTER, 28)\ + keycode(KEY_LEFTCTRL, 29)\ + keycode(KEY_A, 30)\ + keycode(KEY_S, 31)\ + keycode(KEY_D, 32)\ + keycode(KEY_F, 33)\ + keycode(KEY_G, 34)\ + keycode(KEY_H, 35)\ + keycode(KEY_J, 36)\ + keycode(KEY_K, 37)\ + keycode(KEY_L, 38)\ + keycode(KEY_SEMICOLON, 39)\ + keycode(KEY_APOSTROPHE, 40)\ + keycode(KEY_GRAVE, 41)\ + keycode(KEY_LEFTSHIFT, 42)\ + keycode(KEY_BACKSLASH, 43)\ + keycode(KEY_Z, 44)\ + keycode(KEY_X, 45)\ + keycode(KEY_C, 46)\ + keycode(KEY_V, 47)\ + keycode(KEY_B, 48)\ + keycode(KEY_N, 49)\ + keycode(KEY_M, 50)\ + keycode(KEY_COMMA, 51)\ + keycode(KEY_DOT, 52)\ + keycode(KEY_SLASH, 53)\ + keycode(KEY_RIGHTSHIFT, 54)\ + keycode(KEY_KPASTERISK, 55)\ + keycode(KEY_LEFTALT, 56)\ + keycode(KEY_SPACE, 57)\ + keycode(KEY_CAPSLOCK, 58)\ + keycode(KEY_F1, 59)\ + keycode(KEY_F2, 60)\ + keycode(KEY_F3, 61)\ + keycode(KEY_F4, 62)\ + keycode(KEY_F5, 63)\ + keycode(KEY_F6, 64)\ + keycode(KEY_F7, 65)\ + keycode(KEY_F8, 66)\ + keycode(KEY_F9, 67)\ + keycode(KEY_F10, 68)\ + keycode(KEY_NUMLOCK, 69)\ + keycode(KEY_SCROLLLOCK, 70)\ + keycode(KEY_KP7, 71)\ + keycode(KEY_KP8, 72)\ + keycode(KEY_KP9, 73)\ + keycode(KEY_KPMINUS, 74)\ + keycode(KEY_KP4, 75)\ + keycode(KEY_KP5, 76)\ + keycode(KEY_KP6, 77)\ + keycode(KEY_KPPLUS, 78)\ + keycode(KEY_KP1, 79)\ + keycode(KEY_KP2, 80)\ + keycode(KEY_KP3, 81)\ + keycode(KEY_KP0, 82)\ + keycode(KEY_KPDOT, 83)\ + keycode(KEY_ZENKAKUHANKAKU, 85)\ + keycode(KEY_102ND, 86)\ + keycode(KEY_F11, 87)\ + keycode(KEY_F12, 88)\ + keycode(KEY_RO, 89)\ + keycode(KEY_KATAKANA, 90)\ + keycode(KEY_HIRAGANA, 91)\ + keycode(KEY_HENKAN, 92)\ + keycode(KEY_KATAKANAHIRAGANA, 93)\ + keycode(KEY_MUHENKAN, 94)\ + keycode(KEY_KPJPCOMMA, 95)\ + keycode(KEY_KPENTER, 96)\ + keycode(KEY_RIGHTCTRL, 97)\ + keycode(KEY_KPSLASH, 98)\ + keycode(KEY_SYSRQ, 99)\ + keycode(KEY_RIGHTALT, 100)\ + keycode(KEY_LINEFEED, 101)\ + keycode(KEY_HOME, 102)\ + keycode(KEY_UP, 103)\ + keycode(KEY_PAGEUP, 104)\ + keycode(KEY_LEFT, 105)\ + keycode(KEY_RIGHT, 106)\ + keycode(KEY_END, 107)\ + keycode(KEY_DOWN, 108)\ + keycode(KEY_PAGEDOWN, 109)\ + keycode(KEY_INSERT, 110)\ + keycode(KEY_DELETE, 111)\ + keycode(KEY_MACRO, 112)\ + keycode(KEY_MUTE, 113)\ + keycode(KEY_VOLUMEDOWN, 114)\ + keycode(KEY_VOLUMEUP, 115)\ + keycode(KEY_POWER, 116)\ + keycode(KEY_KPEQUAL, 117)\ + keycode(KEY_KPPLUSMINUS, 118)\ + keycode(KEY_PAUSE, 119)\ + keycode(KEY_SCALE, 120)\ + keycode(KEY_KPCOMMA, 121)\ + keycode(KEY_HANGEUL, 122)\ + keycode(KEY_HANGUEL, 122)\ + keycode(KEY_HANJA, 123)\ + keycode(KEY_YEN, 124)\ + keycode(KEY_LEFTMETA, 125)\ + keycode(KEY_RIGHTMETA, 126)\ + keycode(KEY_COMPOSE, 127)\ + keycode(KEY_STOP, 128)\ + keycode(KEY_AGAIN, 129)\ + keycode(KEY_PROPS, 130)\ + keycode(KEY_UNDO, 131)\ + keycode(KEY_FRONT, 132)\ + keycode(KEY_COPY, 133)\ + keycode(KEY_OPEN, 134)\ + keycode(KEY_PASTE, 135)\ + keycode(KEY_FIND, 136)\ + keycode(KEY_CUT, 137)\ + keycode(KEY_HELP, 138)\ + keycode(KEY_MENU, 139)\ + keycode(KEY_CALC, 140)\ + keycode(KEY_SETUP, 141)\ + keycode(KEY_SLEEP, 142)\ + keycode(KEY_WAKEUP, 143)\ + keycode(KEY_FILE, 144)\ + keycode(KEY_SENDFILE, 145)\ + keycode(KEY_DELETEFILE, 146)\ + keycode(KEY_XFER, 147)\ + keycode(KEY_PROG1, 148)\ + keycode(KEY_PROG2, 149)\ + keycode(KEY_WWW, 150)\ + keycode(KEY_MSDOS, 151)\ + keycode(KEY_COFFEE, 152)\ + keycode(KEY_SCREENLOCK, 152)\ + keycode(KEY_DIRECTION, 153)\ + keycode(KEY_CYCLEWINDOWS, 154)\ + keycode(KEY_MAIL, 155)\ + keycode(KEY_BOOKMARKS, 156)\ + keycode(KEY_COMPUTER, 157)\ + keycode(KEY_BACK, 158)\ + keycode(KEY_FORWARD, 159)\ + keycode(KEY_CLOSECD, 160)\ + keycode(KEY_EJECTCD, 161)\ + keycode(KEY_EJECTCLOSECD, 162)\ + keycode(KEY_NEXTSONG, 163)\ + keycode(KEY_PLAYPAUSE, 164)\ + keycode(KEY_PREVIOUSSONG, 165)\ + keycode(KEY_STOPCD, 166)\ + keycode(KEY_RECORD, 167)\ + keycode(KEY_REWIND, 168)\ + keycode(KEY_PHONE, 169)\ + keycode(KEY_ISO, 170)\ + keycode(KEY_CONFIG, 171)\ + keycode(KEY_HOMEPAGE, 172)\ + keycode(KEY_REFRESH, 173)\ + keycode(KEY_EXIT, 174)\ + keycode(KEY_MOVE, 175)\ + keycode(KEY_EDIT, 176)\ + keycode(KEY_SCROLLUP, 177)\ + keycode(KEY_SCROLLDOWN, 178)\ + keycode(KEY_KPLEFTPAREN, 179)\ + keycode(KEY_KPRIGHTPAREN, 180)\ + keycode(KEY_NEW, 181)\ + keycode(KEY_REDO, 182)\ + keycode(KEY_F13, 183)\ + keycode(KEY_F14, 184)\ + keycode(KEY_F15, 185)\ + keycode(KEY_F16, 186)\ + keycode(KEY_F17, 187)\ + keycode(KEY_F18, 188)\ + keycode(KEY_F19, 189)\ + keycode(KEY_F20, 190)\ + keycode(KEY_F21, 191)\ + keycode(KEY_F22, 192)\ + keycode(KEY_F23, 193)\ + keycode(KEY_F24, 194)\ + keycode(KEY_PLAYCD, 200)\ + keycode(KEY_PAUSECD, 201)\ + keycode(KEY_PROG3, 202)\ + keycode(KEY_PROG4, 203)\ + keycode(KEY_DASHBOARD, 204)\ + keycode(KEY_SUSPEND, 205)\ + keycode(KEY_CLOSE, 206)\ + keycode(KEY_PLAY, 207)\ + keycode(KEY_FASTFORWARD, 208)\ + keycode(KEY_BASSBOOST, 209)\ + keycode(KEY_PRINT, 210)\ + keycode(KEY_HP, 211)\ + keycode(KEY_CAMERA, 212)\ + keycode(KEY_SOUND, 213)\ + keycode(KEY_QUESTION, 214)\ + keycode(KEY_EMAIL, 215)\ + keycode(KEY_CHAT, 216)\ + keycode(KEY_SEARCH, 217)\ + keycode(KEY_CONNECT, 218)\ + keycode(KEY_FINANCE, 219)\ + keycode(KEY_SPORT, 220)\ + keycode(KEY_SHOP, 221)\ + keycode(KEY_ALTERASE, 222)\ + keycode(KEY_CANCEL, 223)\ + keycode(KEY_BRIGHTNESSDOWN, 224)\ + keycode(KEY_BRIGHTNESSUP, 225)\ + keycode(KEY_MEDIA, 226)\ + keycode(KEY_SWITCHVIDEOMODE, 227)\ + keycode(KEY_KBDILLUMTOGGLE, 228)\ + keycode(KEY_KBDILLUMDOWN, 229)\ + keycode(KEY_KBDILLUMUP, 230)\ + keycode(KEY_SEND, 231)\ + keycode(KEY_REPLY, 232)\ + keycode(KEY_FORWARDMAIL, 233)\ + keycode(KEY_SAVE, 234)\ + keycode(KEY_DOCUMENTS, 235)\ + keycode(KEY_BATTERY, 236)\ + keycode(KEY_BLUETOOTH, 237)\ + keycode(KEY_WLAN, 238)\ + keycode(KEY_UWB, 239)\ + keycode(KEY_UNKNOWN, 240)\ + keycode(KEY_VIDEO_NEXT, 241)\ + keycode(KEY_VIDEO_PREV, 242)\ + keycode(KEY_BRIGHTNESS_CYCLE, 243)\ + keycode(KEY_BRIGHTNESS_ZERO, 244)\ + keycode(KEY_DISPLAY_OFF, 245)\ + keycode(KEY_WIMAX, 246)\ + keycode(KEY_RFKILL, 247) + +/* define keycode constants */ +enum { +#define keycode(KEY_XXX, value) ITEM_KEYCODE(KEY_XXX) = value, + KEYCODES() +#undef keycode +};
+ #endif diff --git a/tools/virsh.c b/tools/virsh.c index 505a821..3bccc08 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -3017,9 +3017,16 @@ cmdInjectNMI(vshControl *ctl, const vshCmd *cmd) static const vshCmdInfo info_send_key[] = { {"help", N_("Send keycodes to the guest")}, {"desc", N_("Send keycodes to the guest, the keycodes must be integers\n" + " or the KEY_* strings listed below for the \"linux\" codeset.\n\n" " Examples:\n\n" " virsh # send-key <domain> 37 18 21\n" " virsh # send-key <domain> --holdtime 1000 0x15 18 0xf\n" + " virsh # send-eky <domain> KEY_LEFTCTRL KEY_LEFTALT KEY_F1\n" + "\n" + " KEY_XXX strings for the \"linux\" codeset:\n" +#define keycode(var, value) " " #var " = " #value "\n" + KEYCODES())}, +#undef keycode {NULL, NULL} };
@@ -3032,6 +3039,17 @@ static const vshCmdOptDef opts_send_key[] = { {NULL, 0, 0, NULL} };
+static int get_linux_keycode(const char *key_name) +{ + if (key_name[0] == 'K' && key_name[1] == 'E' && key_name[2] == 'Y' && + key_name[3] == '_') { +#define keycode(var, value) if (STREQ(#var + 4, key_name + 4)) return value; + KEYCODES() +#undef keycode + } + return -1; +} + static int get_integer_keycode(const char *key_name) { long val; @@ -3092,6 +3110,9 @@ cmdSendKey(vshControl *ctl, const vshCmd *cmd) goto free_domain; }
+ if ((keycode = get_linux_keycode(opt->data)) > 0) + goto get_keycode; + if ((keycode = get_integer_keycode(opt->data)) > 0) goto get_keycode;
Rather than hardcode two big tables in the source code I would like to have all these keycode tables automatically generated, from a master data file in the source tree. For the GTK-VNC application, I constructed a giant CSV data file which has mappings for all common keycode sets. http://git.gnome.org/browse/gtk-vnc/tree/src/keymaps.csv Copy this csv file into the libvirt source tree. I also have a script which takes the CSV file and can generate big tables for mapping between 2 different keycode sets: http://git.gnome.org/browse/gtk-vnc/tree/src/keymap-gen.pl You can take that keymap-gen.pl script and extend the code, so that it can also generate the data for virtkeys.h Eventually, I'll move the keymaps.csv/keymap-gen.pl into a separate project, which can be packaged up & shared between libvirt, gtk-vnc and spice-gtk, so we only have one place to update keycode mappings Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

It allows us use linux keycode for qemu driver. Signed-off-by: Lai Jiangshan <laijs@fujitsu.com> --- src/qemu/qemu_monitor.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 48 insertions(+), 1 deletions(-) diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index c0688fd..f6cdff1 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2296,6 +2296,41 @@ int qemuMonitorInjectNMI(qemuMonitorPtr mon) return ret; } +unsigned int linux2xt_keycode[256] = { + /* 00: */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + /* 08: */ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + /* 10: */ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + /* 18: */ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + /* 20: */ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + /* 28: */ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + /* 30: */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + /* 38: */ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + /* 40: */ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + /* 48: */ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x00, 0x4f, + /* 50: */ 0x50, 0x51, 0x52, 0x00, 0x00, 0x00, 0x00, 0x57, + /* 58: */ 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 60: */ 0x00, 0x9d, 0xb5, 0x54, 0xb8, 0x00, 0xc7, 0xc8, + /* 68: */ 0xc9, 0xcb, 0xcd, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, + /* 70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 80: */ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + /* 88: */ 0xf8, 0xf9, 0xfb, 0xdd, 0x00, 0x00, 0x00, 0x00, + /* 90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* d0: */ 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, + /* d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + int qemuMonitorSendKey(qemuMonitorPtr mon, unsigned int codeset, unsigned int holdtime, @@ -2307,7 +2342,19 @@ int qemuMonitorSendKey(qemuMonitorPtr mon, VIR_DEBUG("mon=%p, codeset=%u, holdtime=%u, nkeycodes=%u", mon, codeset, holdtime, nkeycodes); - if (!(codeset == LIBVIRT_KEYCODE_DRIVER_DEFAULT + if (codeset == LIBVIRT_KEYCODE_LINUX) { + int i; + + for (i = 0; i < nkeycodes; i++) { + if (keycodes[i] >= 256 || !linux2xt_keycode[keycodes[i]]) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "can not map the linux keycode:%d to XT keycode", + keycodes[i]); + return -1; + } + keycodes[i] = linux2xt_keycode[keycodes[i]]; + } + } else if (!(codeset == LIBVIRT_KEYCODE_DRIVER_DEFAULT || codeset == LIBVIRT_KEYCODE_XT)) { qemuReportError(VIR_ERR_NO_SUPPORT, "qemu monitor can not support the codeset: %d", -- 1.7.4.4

On Wed, May 25, 2011 at 05:37:54PM +0800, Lai Jiangshan wrote:
It allows us use linux keycode for qemu driver.
Signed-off-by: Lai Jiangshan <laijs@fujitsu.com> --- src/qemu/qemu_monitor.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 48 insertions(+), 1 deletions(-)
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index c0688fd..f6cdff1 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2296,6 +2296,41 @@ int qemuMonitorInjectNMI(qemuMonitorPtr mon) return ret; }
+unsigned int linux2xt_keycode[256] = { + /* 00: */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + /* 08: */ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + /* 10: */ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + /* 18: */ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + /* 20: */ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + /* 28: */ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + /* 30: */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + /* 38: */ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + /* 40: */ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + /* 48: */ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x00, 0x4f, + /* 50: */ 0x50, 0x51, 0x52, 0x00, 0x00, 0x00, 0x00, 0x57, + /* 58: */ 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 60: */ 0x00, 0x9d, 0xb5, 0x54, 0xb8, 0x00, 0xc7, 0xc8, + /* 68: */ 0xc9, 0xcb, 0xcd, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, + /* 70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 80: */ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + /* 88: */ 0xf8, 0xf9, 0xfb, 0xdd, 0x00, 0x00, 0x00, 0x00, + /* 90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* d0: */ 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, + /* d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +};
See my comment in the previous patch. This table can be automatically generated by my keymap-gen.pl script during build, so we don't need to manually have this table of wierd magic numbers. Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

It make send-key command more friendly for user. Signed-off-by: Lai Jiangshan <laijs@fujitsu.com> --- tools/virsh.c | 224 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 221 insertions(+), 3 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 3bccc08..18ef4bb 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -3017,11 +3017,24 @@ cmdInjectNMI(vshControl *ctl, const vshCmd *cmd) static const vshCmdInfo info_send_key[] = { {"help", N_("Send keycodes to the guest")}, {"desc", N_("Send keycodes to the guest, the keycodes must be integers\n" + " or the qemu-style key strings for the \"xt:keystring\" codeset\n" " or the KEY_* strings listed below for the \"linux\" codeset.\n\n" + " Available codeset:\n" + " linux the keycodes specified in \n" + " /usr/include/linux/input.h(default)\n" + " default linux codeset will be used\n" + " driver_default the hypervisor default codeset will be used\n" + " xt XT(set1) scancode of standard AT keyboards and PS/2 keyboards\n" + " atset1 set1 scancode of standard AT keyboards and PS/2 keyboards\n" + " atset2 set2 scancode of standard AT keyboards and PS/2 keyboards\n" + " atset3 set3 scancode of standard AT keyboards and PS/2 keyboards\n" + " xt:keystring XT scancode, but <keycode>... must be the qemu-style key strings\n" + "\n" " Examples:\n\n" " virsh # send-key <domain> 37 18 21\n" " virsh # send-key <domain> --holdtime 1000 0x15 18 0xf\n" - " virsh # send-eky <domain> KEY_LEFTCTRL KEY_LEFTALT KEY_F1\n" + " virsh # send-key <domain> KEY_LEFTCTRL KEY_LEFTALT KEY_F1\n" + " virsh # send-key <domain> --codeset xt:keystring alt-sysrq h\n" "\n" " KEY_XXX strings for the \"linux\" codeset:\n" #define keycode(var, value) " " #var " = " #value "\n" @@ -3062,6 +3075,178 @@ static int get_integer_keycode(const char *key_name) return val; } + +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, "<" }, + + { 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" }, + + { 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" }, + + { 0, NULL }, +}; + +static int get_xtkeystr_keycode(const char *key_name, unsigned int keyname_len) +{ + const KeyDef *p; + char *endp; + int ret; + + if (keyname_len == 0) + return -1; + + for(p = key_defs; p->name != NULL; p++) { + if (!strncmp(key_name, p->name, keyname_len)) + return p->keycode; + } + if (key_name[0] == '0' && (key_name[1] == 'x' || key_name[1] == 'X')) { + ret = strtoul(key_name, &endp, 0); + if (endp - key_name == keyname_len && ret >= 0x01 && ret <= 0xff) + return ret; + } + return -1; +} + + static bool cmdSendKey(vshControl *ctl, const vshCmd *cmd) { @@ -3073,6 +3258,7 @@ cmdSendKey(vshControl *ctl, const vshCmd *cmd) int count = 0; const vshCmdOpt *opt; int keycode; + int xt_keystring = 0; unsigned int keycodes[MAX_SEND_KEY]; if (!vshConnectionUsability(ctl, ctl->conn)) @@ -3099,6 +3285,9 @@ cmdSendKey(vshControl *ctl, const vshCmd *cmd) codeset = LIBVIRT_KEYCODE_ATSET2; } else if (STREQ(codeset_option, "atset3")) { codeset = LIBVIRT_KEYCODE_ATSET3; + } else if (STREQ(codeset_option, "xt:keystring")) { + codeset = LIBVIRT_KEYCODE_XT; + xt_keystring = 1; } else { vshError(ctl, _("unknown codeset: '%s'"), codeset_option); goto free_domain; @@ -3106,11 +3295,40 @@ cmdSendKey(vshControl *ctl, const vshCmd *cmd) for_each_variable_arg(cmd, opt) { if (count == MAX_SEND_KEY) { - vshError(ctl, _("too many keycode")); + vshError(ctl, _("too many keycodes")); goto free_domain; } - if ((keycode = get_linux_keycode(opt->data)) > 0) + if (codeset == LIBVIRT_KEYCODE_XT && xt_keystring == 1) { + const char *sep, *key_string = opt->data; + unsigned int keyname_len; + + for (;;) { + if (count == MAX_SEND_KEY) { + vshError(ctl, _("too many keycodes")); + goto free_domain; + } + + sep = strchr(key_string, '-'); + keyname_len = sep ? sep - key_string : strlen(key_string); + + keycode = get_xtkeystr_keycode(key_string, keyname_len); + if (keycode < 0) { + vshError(ctl, _("invalid keycode: '%s'"), opt->data); + goto free_domain; + } + + keycodes[count] = keycode; + count++; + if (!sep) + break; + key_string += keyname_len + 1; + } + continue; + } + + if (codeset == LIBVIRT_KEYCODE_LINUX && + (keycode = get_linux_keycode(opt->data)) > 0) goto get_keycode; if ((keycode = get_integer_keycode(opt->data)) > 0) -- 1.7.4.4

On Wed, May 25, 2011 at 05:37:55PM +0800, Lai Jiangshan wrote:
It make send-key command more friendly for user.
Signed-off-by: Lai Jiangshan <laijs@fujitsu.com> --- tools/virsh.c | 224 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 221 insertions(+), 3 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c index 3bccc08..18ef4bb 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -3017,11 +3017,24 @@ cmdInjectNMI(vshControl *ctl, const vshCmd *cmd) static const vshCmdInfo info_send_key[] = { {"help", N_("Send keycodes to the guest")}, {"desc", N_("Send keycodes to the guest, the keycodes must be integers\n" + " or the qemu-style key strings for the \"xt:keystring\" codeset\n" " or the KEY_* strings listed below for the \"linux\" codeset.\n\n" + " Available codeset:\n" + " linux the keycodes specified in \n" + " /usr/include/linux/input.h(default)\n" + " default linux codeset will be used\n" + " driver_default the hypervisor default codeset will be used\n" + " xt XT(set1) scancode of standard AT keyboards and PS/2 keyboards\n" + " atset1 set1 scancode of standard AT keyboards and PS/2 keyboards\n" + " atset2 set2 scancode of standard AT keyboards and PS/2 keyboards\n" + " atset3 set3 scancode of standard AT keyboards and PS/2 keyboards\n" + " xt:keystring XT scancode, but <keycode>... must be the qemu-style key strings\n" + "\n" " Examples:\n\n" " virsh # send-key <domain> 37 18 21\n" " virsh # send-key <domain> --holdtime 1000 0x15 18 0xf\n" - " virsh # send-eky <domain> KEY_LEFTCTRL KEY_LEFTALT KEY_F1\n" + " virsh # send-key <domain> KEY_LEFTCTRL KEY_LEFTALT KEY_F1\n" + " virsh # send-key <domain> --codeset xt:keystring alt-sysrq h\n" "\n" " KEY_XXX strings for the \"linux\" codeset:\n" #define keycode(var, value) " " #var " = " #value "\n" @@ -3062,6 +3075,178 @@ static int get_integer_keycode(const char *key_name) return val; }
+ +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, "<" }, + + { 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" }, + + { 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" }, + + { 0, NULL }, +};
If we add a 2nd public API which takes a list of keycode strings, then this mapping table can be kept in the QEMU driver source, instead of in virsh. Again, we can use my keymap-gen.pl script to automatically generate the mapping table.
+ +static int get_xtkeystr_keycode(const char *key_name, unsigned int keyname_len) +{ + const KeyDef *p; + char *endp; + int ret; + + if (keyname_len == 0) + return -1; + + for(p = key_defs; p->name != NULL; p++) { + if (!strncmp(key_name, p->name, keyname_len)) + return p->keycode; + } + if (key_name[0] == '0' && (key_name[1] == 'x' || key_name[1] == 'X')) { + ret = strtoul(key_name, &endp, 0); + if (endp - key_name == keyname_len && ret >= 0x01 && ret <= 0xff) + return ret; + } + return -1; +} + + static bool cmdSendKey(vshControl *ctl, const vshCmd *cmd) { @@ -3073,6 +3258,7 @@ cmdSendKey(vshControl *ctl, const vshCmd *cmd) int count = 0; const vshCmdOpt *opt; int keycode; + int xt_keystring = 0; unsigned int keycodes[MAX_SEND_KEY];
if (!vshConnectionUsability(ctl, ctl->conn)) @@ -3099,6 +3285,9 @@ cmdSendKey(vshControl *ctl, const vshCmd *cmd) codeset = LIBVIRT_KEYCODE_ATSET2; } else if (STREQ(codeset_option, "atset3")) { codeset = LIBVIRT_KEYCODE_ATSET3; + } else if (STREQ(codeset_option, "xt:keystring")) { + codeset = LIBVIRT_KEYCODE_XT; + xt_keystring = 1; } else { vshError(ctl, _("unknown codeset: '%s'"), codeset_option); goto free_domain; @@ -3106,11 +3295,40 @@ cmdSendKey(vshControl *ctl, const vshCmd *cmd)
for_each_variable_arg(cmd, opt) { if (count == MAX_SEND_KEY) { - vshError(ctl, _("too many keycode")); + vshError(ctl, _("too many keycodes")); goto free_domain; }
- if ((keycode = get_linux_keycode(opt->data)) > 0) + if (codeset == LIBVIRT_KEYCODE_XT && xt_keystring == 1) { + const char *sep, *key_string = opt->data; + unsigned int keyname_len; + + for (;;) { + if (count == MAX_SEND_KEY) { + vshError(ctl, _("too many keycodes")); + goto free_domain; + } + + sep = strchr(key_string, '-'); + keyname_len = sep ? sep - key_string : strlen(key_string); + + keycode = get_xtkeystr_keycode(key_string, keyname_len); + if (keycode < 0) { + vshError(ctl, _("invalid keycode: '%s'"), opt->data); + goto free_domain; + } + + keycodes[count] = keycode; + count++; + if (!sep) + break; + key_string += keyname_len + 1; + } + continue; + } + + if (codeset == LIBVIRT_KEYCODE_LINUX && + (keycode = get_linux_keycode(opt->data)) > 0) goto get_keycode;
if ((keycode = get_integer_keycode(opt->data)) > 0)
Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On Wed, May 25, 2011 at 05:37:42PM +0800, Lai Jiangshan wrote:
Add API virDomainSendKey() and virsh send-key command.
# virsh help send-key NAME send-key - Send keycodes to the guest
SYNOPSIS send-key <domain> [--codeset <string>] [--holdtime <number>] <keycode>...
DESCRIPTION Send keycodes to the guest, the keycodes must be integers or the qemu-style key strings for the "xt:keystring" codeset or the KEY_* strings listed below for the "linux" codeset.
Available codeset: linux the keycodes specified in /usr/include/linux/input.h(default) default linux codeset will be used driver_default the hypervisor default codeset will be used xt XT(set1) scancode of standard AT keyboards and PS/2 keyboards atset1 set1 scancode of standard AT keyboards and PS/2 keyboards atset2 set2 scancode of standard AT keyboards and PS/2 keyboards atset3 set3 scancode of standard AT keyboards and PS/2 keyboards xt:keystring XT scancode, but <keycode>... must be the qemu-style key strings
I was thinking we'd just use the Linux keycode set in the API, but I guess if the client app already has things in XT codeset, it isn't too nice to force them to convert to Linux keycodes, only for libvirt to convert them straight back for QEMU. So I think it was a good idea to add different codesets. I don't think that 'driver_default' makes sense though. For that to be usable, the person invoking the API must somehow know what the driver default codeset is. If they know that, then they can trivially just specify that already, so 'driver_default' doesn't seem to add any benefit. As for 'xt:keystring', if you think it is worth being able to use key strings, then perhaps we should have 2 apis. One API that takes a list of keycodes as ints, and one API that takes a list of keycodes as strings. This would avoid the need for every client application to maintain a mapping table of strings <-> ints. eg, all the mapping tables would be contained within libvirt.
Examples:
virsh # send-key <domain> 37 18 21 virsh # send-key <domain> --holdtime 1000 0x15 18 0xf virsh # send-key <domain> KEY_LEFTCTRL KEY_LEFTALT KEY_F1 virsh # send-key <domain> --codeset xt:keystring alt-sysrq h
KEY_XXX strings for the "linux" codeset:
For virsh, perhaps it should default to always using the strings for the keys, and only use integers if given a special flag. I think most admins using virsh would just be using strings. The integer keycodes are mostly useful for apps using the API directly. eg, perhaps virsh # send-key <domain> KEY_LEFTCTRL KEY_LEFTALT KEY_F1 virsh # send-key <domain> --codeset linux KEY_LEFTCTRL KEY_LEFTALT KEY_F1 virsh # send-key <domain> --codeset xt alt-sysrq h virsh # send-key <domain> --num 37 18 21 virsh # send-key <domain> --num --holdtime 1000 0x15 18 0xf virsh # send-key <domain> --num --codeset linux --holdtime 1000 0x15 18 0xf
. . . OPTIONS [--domain] <string> domain name, id or uuid --codeset <string> the codeset of keycodes, default:linux --num the keys are specified as integer values, --holdtime <number> the time (in millsecond) how long the keys will be held <keycode> the key string (or value if --num is set)
Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 05/26/2011 12:36 AM, Daniel P. Berrange wrote:
On Wed, May 25, 2011 at 05:37:42PM +0800, Lai Jiangshan wrote:
Add API virDomainSendKey() and virsh send-key command.
# virsh help send-key NAME send-key - Send keycodes to the guest
SYNOPSIS send-key <domain> [--codeset <string>] [--holdtime <number>] <keycode>...
DESCRIPTION Send keycodes to the guest, the keycodes must be integers or the qemu-style key strings for the "xt:keystring" codeset or the KEY_* strings listed below for the "linux" codeset.
Available codeset: linux the keycodes specified in /usr/include/linux/input.h(default) default linux codeset will be used driver_default the hypervisor default codeset will be used xt XT(set1) scancode of standard AT keyboards and PS/2 keyboards atset1 set1 scancode of standard AT keyboards and PS/2 keyboards atset2 set2 scancode of standard AT keyboards and PS/2 keyboards atset3 set3 scancode of standard AT keyboards and PS/2 keyboards xt:keystring XT scancode, but <keycode>... must be the qemu-style key strings
I was thinking we'd just use the Linux keycode set in the API, but I guess if the client app already has things in XT codeset, it isn't too nice to force them to convert to Linux keycodes, only for libvirt to convert them straight back for QEMU. So I think it was a good idea to add different codesets.
I don't think that 'driver_default' makes sense though. For that to be usable, the person invoking the API must somehow know what the driver default codeset is. If they know that, then they can trivially just specify that already, so 'driver_default' doesn't seem to add any benefit.
OK, it will be removed.
As for 'xt:keystring', if you think it is worth being able to use key strings, then perhaps we should have 2 apis. One API that takes a list of keycodes as ints, and one API that takes a list of keycodes as strings.
I don't think it is a good idea to add a second API, virDomainSendKey() is not used directly by human, integer is enough. 2 apis will make the user of the lib confused. virsh send-key command is a human interface, I think it is good if it accepts strings, so I allow KEY_XXX strings. And this command should wrap all things(convert strings to integers). If you deny 'xt:keystring', I can remove the patch, but KEY_XXX strings will still stay.
This would avoid the need for every client application to maintain a mapping table of strings <-> ints. eg, all the mapping tables would be contained within libvirt.
qemu monitor don't require "ints-->names" conversions, it just needs very trivial conversions "ints-->0xNN", this conversion will not need when qmp send-key is implemented. I don't know other hypervisors, but I think virDomainSendKey() taking a list of keycode as ints is the best api for all hypervisors.
Examples:
virsh # send-key <domain> 37 18 21 virsh # send-key <domain> --holdtime 1000 0x15 18 0xf virsh # send-key <domain> KEY_LEFTCTRL KEY_LEFTALT KEY_F1 virsh # send-key <domain> --codeset xt:keystring alt-sysrq h
KEY_XXX strings for the "linux" codeset:
For virsh, perhaps it should default to always using the strings for the keys, and only use integers if given a special flag. I think most admins using virsh would just be using strings. The integer keycodes are mostly useful for apps using the API directly.
eg, perhaps
virsh # send-key <domain> KEY_LEFTCTRL KEY_LEFTALT KEY_F1 virsh # send-key <domain> --codeset linux KEY_LEFTCTRL KEY_LEFTALT KEY_F1 virsh # send-key <domain> --codeset xt alt-sysrq h virsh # send-key <domain> --num 37 18 21 virsh # send-key <domain> --num --holdtime 1000 0x15 18 0xf virsh # send-key <domain> --num --codeset linux --holdtime 1000 0x15 18 0xf
It is good for me, but I may want to wrap the strings->ints conversions in virsh instead of in hypervisors.
. . . OPTIONS [--domain] <string> domain name, id or uuid --codeset <string> the codeset of keycodes, default:linux --num the keys are specified as integer values, --holdtime <number> the time (in millsecond) how long the keys will be held <keycode> the key string (or value if --num is set)
Daniel

On Thu, May 26, 2011 at 06:00:08PM +0800, Lai Jiangshan wrote:
On 05/26/2011 12:36 AM, Daniel P. Berrange wrote:
On Wed, May 25, 2011 at 05:37:42PM +0800, Lai Jiangshan wrote:
Add API virDomainSendKey() and virsh send-key command.
# virsh help send-key NAME send-key - Send keycodes to the guest
SYNOPSIS send-key <domain> [--codeset <string>] [--holdtime <number>] <keycode>...
DESCRIPTION Send keycodes to the guest, the keycodes must be integers or the qemu-style key strings for the "xt:keystring" codeset or the KEY_* strings listed below for the "linux" codeset.
Available codeset: linux the keycodes specified in /usr/include/linux/input.h(default) default linux codeset will be used driver_default the hypervisor default codeset will be used xt XT(set1) scancode of standard AT keyboards and PS/2 keyboards atset1 set1 scancode of standard AT keyboards and PS/2 keyboards atset2 set2 scancode of standard AT keyboards and PS/2 keyboards atset3 set3 scancode of standard AT keyboards and PS/2 keyboards xt:keystring XT scancode, but <keycode>... must be the qemu-style key strings
I was thinking we'd just use the Linux keycode set in the API, but I guess if the client app already has things in XT codeset, it isn't too nice to force them to convert to Linux keycodes, only for libvirt to convert them straight back for QEMU. So I think it was a good idea to add different codesets.
I don't think that 'driver_default' makes sense though. For that to be usable, the person invoking the API must somehow know what the driver default codeset is. If they know that, then they can trivially just specify that already, so 'driver_default' doesn't seem to add any benefit.
OK, it will be removed.
As for 'xt:keystring', if you think it is worth being able to use key strings, then perhaps we should have 2 apis. One API that takes a list of keycodes as ints, and one API that takes a list of keycodes as strings.
I don't think it is a good idea to add a second API, virDomainSendKey() is not used directly by human, integer is enough. 2 apis will make the user of the lib confused.
Ok, we can always re-consider at a later date if we find it neccessary todo so. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Add API virDomainSendKey() and virsh send-key command. PATCH 01~04 prepare PATCH 05~10 Add support for send keys to guest Python version of virDomainSendKey() has not been implemented yet, it will be done soon. Some usage-improvment patches will be sent later(after these 10 are applied) these usage-improvment patches does not touch any APIs nor change the behaviors: support KEY_XXX names for the linux keycode for virsh command(auto detect), translate keycodes between different codesets, ...etc. Lai Jiangshan (10): allow name for VSH_OT_ARGV options improve the iteration of VSH_OT_ARGV options add VSH_OFLAG_REQ_OPT options remote generator: Handle (unsigned) int arrays send-key: Defining the public API send-key: Defining the internal API send-key: Implementing the public API send-key: Implementing the remote protocol send-key: Expose the new API in virsh qemu:send-key: Implement the driver methods daemon/remote_generator.pl | 17 ++++ include/libvirt/libvirt.h.in | 7 ++ include/libvirt/virtkeys.h | 22 +++++ python/generator.py | 1 + src/driver.h | 8 ++ src/libvirt.c | 63 +++++++++++++++ src/libvirt_public.syms | 5 + src/qemu/qemu_driver.c | 50 ++++++++++++ src/qemu/qemu_monitor.c | 27 +++++++ src/qemu/qemu_monitor.h | 6 ++ src/qemu/qemu_monitor_json.c | 15 ++++ src/qemu/qemu_monitor_json.h | 5 + src/qemu/qemu_monitor_text.c | 47 +++++++++++ src/qemu/qemu_monitor_text.h | 5 + src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 16 ++++- src/remote_protocol-structs | 11 +++ tools/virsh.c | 175 +++++++++++++++++++++++++++++++++++------- tools/virsh.pod | 4 + 19 files changed, 455 insertions(+), 30 deletions(-) create mode 100644 include/libvirt/virtkeys.h -- 1.7.4.4

A name will improve the usege, example # virsh help echo NAME echo - echo arguments SYNOPSIS echo [--shell] [--xml] [<string>]... DESCRIPTION Echo back arguments, possibly with quoting. OPTIONS --shell escape for shell use --xml escape for XML use <string> arguments to echo "[<string>]..." is added to SYNOPSIS. "<string> arguments to echo" is added to OPTIONS. Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- tools/virsh.c | 19 +++++++++++++------ 1 files changed, 13 insertions(+), 6 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index d98be1c..61eb11e 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -126,7 +126,7 @@ typedef enum { VSH_OT_STRING, /* optional string option */ VSH_OT_INT, /* optional or mandatory int option */ VSH_OT_DATA, /* string data (as non-option) */ - VSH_OT_ARGV /* remaining arguments, opt->name should be "" */ + VSH_OT_ARGV /* remaining arguments */ } vshCmdOptType; /* @@ -10328,7 +10328,7 @@ static const vshCmdInfo info_echo[] = { static const vshCmdOptDef opts_echo[] = { {"shell", VSH_OT_BOOL, 0, N_("escape for shell use")}, {"xml", VSH_OT_BOOL, 0, N_("escape for XML use")}, - {"", VSH_OT_ARGV, 0, N_("arguments to echo")}, + {"string", VSH_OT_ARGV, 0, N_("arguments to echo")}, {NULL, 0, 0, NULL} }; @@ -11379,6 +11379,11 @@ vshCmddefGetOption(vshControl *ctl, const vshCmdDef *cmd, const char *name, vshError(ctl, _("option --%s already seen"), name); return NULL; } + if (opt->type == VSH_OT_ARGV) { + vshError(ctl, _("variable argument <%s> " + "should not be used with --<%s>"), name, name); + return NULL; + } *opts_seen |= 1 << i; return opt; } @@ -11427,7 +11432,7 @@ vshCommandCheckOpts(vshControl *ctl, const vshCmd *cmd, uint32_t opts_required, const vshCmdOptDef *opt = &def->opts[i]; vshError(ctl, - opt->type == VSH_OT_DATA ? + opt->type == VSH_OT_DATA || opt->type == VSH_OT_ARGV ? _("command '%s' requires <%s> option") : _("command '%s' requires --%s option"), def->name, opt->name); @@ -11535,7 +11540,8 @@ vshCmddefHelp(vshControl *ctl, const char *cmdname) break; case VSH_OT_ARGV: /* xgettext:c-format */ - fmt = _("[<string>]..."); + fmt = (opt->flag & VSH_OFLAG_REQ) ? _("<%s>...") + : _("[<%s>]..."); break; default: assert(0); @@ -11575,7 +11581,8 @@ vshCmddefHelp(vshControl *ctl, const char *cmdname) break; case VSH_OT_ARGV: /* Not really an option. */ - continue; + snprintf(buf, sizeof(buf), _("<%s>"), opt->name); + break; default: assert(0); } @@ -13012,7 +13019,7 @@ vshReadlineOptionsGenerator(const char *text, int state) list_index++; - if (opt->type == VSH_OT_DATA) + if (opt->type == VSH_OT_DATA || opt->type == VSH_OT_ARGV) /* ignore non --option */ continue; -- 1.7.4.4

On Tue, Jun 07, 2011 at 05:11:08PM +0800, Lai Jiangshan wrote:
A name will improve the usege, example
# virsh help echo NAME echo - echo arguments
SYNOPSIS echo [--shell] [--xml] [<string>]...
DESCRIPTION Echo back arguments, possibly with quoting.
OPTIONS --shell escape for shell use --xml escape for XML use <string> arguments to echo
"[<string>]..." is added to SYNOPSIS. "<string> arguments to echo" is added to OPTIONS.
Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- tools/virsh.c | 19 +++++++++++++------ 1 files changed, 13 insertions(+), 6 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c index d98be1c..61eb11e 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -126,7 +126,7 @@ typedef enum { VSH_OT_STRING, /* optional string option */ VSH_OT_INT, /* optional or mandatory int option */ VSH_OT_DATA, /* string data (as non-option) */ - VSH_OT_ARGV /* remaining arguments, opt->name should be "" */ + VSH_OT_ARGV /* remaining arguments */ } vshCmdOptType;
/* @@ -10328,7 +10328,7 @@ static const vshCmdInfo info_echo[] = { static const vshCmdOptDef opts_echo[] = { {"shell", VSH_OT_BOOL, 0, N_("escape for shell use")}, {"xml", VSH_OT_BOOL, 0, N_("escape for XML use")}, - {"", VSH_OT_ARGV, 0, N_("arguments to echo")}, + {"string", VSH_OT_ARGV, 0, N_("arguments to echo")}, {NULL, 0, 0, NULL} };
@@ -11379,6 +11379,11 @@ vshCmddefGetOption(vshControl *ctl, const vshCmdDef *cmd, const char *name, vshError(ctl, _("option --%s already seen"), name); return NULL; } + if (opt->type == VSH_OT_ARGV) { + vshError(ctl, _("variable argument <%s> " + "should not be used with --<%s>"), name, name); + return NULL; + } *opts_seen |= 1 << i; return opt; } @@ -11427,7 +11432,7 @@ vshCommandCheckOpts(vshControl *ctl, const vshCmd *cmd, uint32_t opts_required, const vshCmdOptDef *opt = &def->opts[i];
vshError(ctl, - opt->type == VSH_OT_DATA ? + opt->type == VSH_OT_DATA || opt->type == VSH_OT_ARGV ? _("command '%s' requires <%s> option") : _("command '%s' requires --%s option"), def->name, opt->name); @@ -11535,7 +11540,8 @@ vshCmddefHelp(vshControl *ctl, const char *cmdname) break; case VSH_OT_ARGV: /* xgettext:c-format */ - fmt = _("[<string>]..."); + fmt = (opt->flag & VSH_OFLAG_REQ) ? _("<%s>...") + : _("[<%s>]..."); break; default: assert(0); @@ -11575,7 +11581,8 @@ vshCmddefHelp(vshControl *ctl, const char *cmdname) break; case VSH_OT_ARGV: /* Not really an option. */ - continue; + snprintf(buf, sizeof(buf), _("<%s>"), opt->name); + break; default: assert(0); } @@ -13012,7 +13019,7 @@ vshReadlineOptionsGenerator(const char *text, int state)
list_index++;
- if (opt->type == VSH_OT_DATA) + if (opt->type == VSH_OT_DATA || opt->type == VSH_OT_ARGV) /* ignore non --option */ continue;
ACK Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 06/07/2011 03:11 AM, Lai Jiangshan wrote:
A name will improve the usege, example
s/usege/usage/
# virsh help echo NAME echo - echo arguments
SYNOPSIS echo [--shell] [--xml] [<string>]...
DESCRIPTION Echo back arguments, possibly with quoting.
OPTIONS --shell escape for shell use --xml escape for XML use <string> arguments to echo
"[<string>]..." is added to SYNOPSIS.
Technically, that was already there, although it was hard-coded as [<string>]... rather than being flexible as [<name>]... for your choice of name.
"<string> arguments to echo" is added to OPTIONS.
Nice.
ACK
Pushed. (oops, I realized I pushed before making the typo fix in the commit message) -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- tools/virsh.c | 47 ++++++++++++++++++++++++----------------------- 1 files changed, 24 insertions(+), 23 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 61eb11e..638029c 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -280,7 +280,27 @@ static int vshCommandOptULongLong(const vshCmd *cmd, const char *name, unsigned long long *value) ATTRIBUTE_NONNULL(3) ATTRIBUTE_RETURN_CHECK; static bool vshCommandOptBool(const vshCmd *cmd, const char *name); -static char *vshCommandOptArgv(const vshCmd *cmd, int count); + +/* + * Iterate all the argv arguments. + * + * Requires that a VSH_OT_ARGV option be last in the + * list of supported options in CMD->def->opts. + */ +static inline const vshCmdOpt *__variable_arg(const vshCmdOpt *opt) +{ + while (opt) { + if (opt->def && opt->def->type == VSH_OT_ARGV) + break; + opt = opt->next; + } + + return opt; +} + +#define for_each_variable_arg(cmd, opt) \ + for (opt = __variable_arg(cmd->opts); opt; opt = __variable_arg(opt->next)) + #define VSH_BYID (1 << 1) #define VSH_BYUUID (1 << 2) @@ -10341,6 +10361,7 @@ cmdEcho (vshControl *ctl ATTRIBUTE_UNUSED, const vshCmd *cmd) bool shell = false; bool xml = false; int count = 0; + const vshCmdOpt *opt; char *arg; virBuffer buf = VIR_BUFFER_INITIALIZER; @@ -10349,10 +10370,11 @@ cmdEcho (vshControl *ctl ATTRIBUTE_UNUSED, const vshCmd *cmd) if (vshCommandOptBool(cmd, "xml")) xml = true; - while ((arg = vshCommandOptArgv(cmd, count)) != NULL) { + for_each_variable_arg(cmd, opt) { bool close_quote = false; char *q; + arg = opt->data; if (count) virBufferAddChar(&buf, ' '); /* Add outer '' only if arg included shell metacharacters. */ @@ -11803,27 +11825,6 @@ vshCommandOptBool(const vshCmd *cmd, const char *name) return vshCommandOpt(cmd, name) != NULL; } -/* - * Returns the COUNT argv argument, or NULL after last argument. - * - * Requires that a VSH_OT_ARGV option with the name "" be last in the - * list of supported options in CMD->def->opts. - */ -static char * -vshCommandOptArgv(const vshCmd *cmd, int count) -{ - vshCmdOpt *opt = cmd->opts; - - while (opt) { - if (opt->def && opt->def->type == VSH_OT_ARGV) { - if (count-- == 0) - return opt->data; - } - opt = opt->next; - } - return NULL; -} - /* Determine whether CMD->opts includes an option with name OPTNAME. If not, give a diagnostic and return false. If so, return true. */ -- 1.7.4.4

On Tue, Jun 07, 2011 at 05:11:09PM +0800, Lai Jiangshan wrote:
Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- tools/virsh.c | 47 ++++++++++++++++++++++++----------------------- 1 files changed, 24 insertions(+), 23 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c index 61eb11e..638029c 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -280,7 +280,27 @@ static int vshCommandOptULongLong(const vshCmd *cmd, const char *name, unsigned long long *value) ATTRIBUTE_NONNULL(3) ATTRIBUTE_RETURN_CHECK; static bool vshCommandOptBool(const vshCmd *cmd, const char *name); -static char *vshCommandOptArgv(const vshCmd *cmd, int count); + +/* + * Iterate all the argv arguments. + * + * Requires that a VSH_OT_ARGV option be last in the + * list of supported options in CMD->def->opts. + */ +static inline const vshCmdOpt *__variable_arg(const vshCmdOpt *opt) +{ + while (opt) { + if (opt->def && opt->def->type == VSH_OT_ARGV) + break; + opt = opt->next; + } + + return opt; +} + +#define for_each_variable_arg(cmd, opt) \ + for (opt = __variable_arg(cmd->opts); opt; opt = __variable_arg(opt->next)) +
#define VSH_BYID (1 << 1) #define VSH_BYUUID (1 << 2) @@ -10341,6 +10361,7 @@ cmdEcho (vshControl *ctl ATTRIBUTE_UNUSED, const vshCmd *cmd) bool shell = false; bool xml = false; int count = 0; + const vshCmdOpt *opt; char *arg; virBuffer buf = VIR_BUFFER_INITIALIZER;
@@ -10349,10 +10370,11 @@ cmdEcho (vshControl *ctl ATTRIBUTE_UNUSED, const vshCmd *cmd) if (vshCommandOptBool(cmd, "xml")) xml = true;
- while ((arg = vshCommandOptArgv(cmd, count)) != NULL) { + for_each_variable_arg(cmd, opt) {
Stylewise, I'm not really a fan of hiding for/while loops behind macros. I prefer to see the loop logic unobscured. Since 'for_each_variable_arg' is only used once and the code isn't really significantly simpler/ shorter, I'd prefer if we just removed the macro here. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

This reduces things from O(n^2) to O(n). * tools/virsh.c (vshCommandOptArgv): Change signature. (cmdEcho): Update caller. Based on a patch by Lai Jiangshan. --- v2: Drop the macro, avoid __ naming, reduce patch size tools/virsh.c | 21 ++++++++++++--------- 1 files changed, 12 insertions(+), 9 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index d2f4020..da975ba 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -280,7 +280,8 @@ static int vshCommandOptULongLong(const vshCmd *cmd, const char *name, unsigned long long *value) ATTRIBUTE_NONNULL(3) ATTRIBUTE_RETURN_CHECK; static bool vshCommandOptBool(const vshCmd *cmd, const char *name); -static char *vshCommandOptArgv(const vshCmd *cmd, int count); +static const vshCmdOpt *vshCommandOptArgv(const vshCmd *cmd, + const vshCmdOpt *opt); #define VSH_BYID (1 << 1) #define VSH_BYUUID (1 << 2) @@ -10439,6 +10440,7 @@ cmdEcho (vshControl *ctl ATTRIBUTE_UNUSED, const vshCmd *cmd) bool shell = false; bool xml = false; int count = 0; + const vshCmdOpt *opt = NULL; char *arg; virBuffer buf = VIR_BUFFER_INITIALIZER; @@ -10447,10 +10449,11 @@ cmdEcho (vshControl *ctl ATTRIBUTE_UNUSED, const vshCmd *cmd) if (vshCommandOptBool(cmd, "xml")) xml = true; - while ((arg = vshCommandOptArgv(cmd, count)) != NULL) { + while ((opt = vshCommandOptArgv(cmd, opt))) { bool close_quote = false; char *q; + arg = opt->data; if (count) virBufferAddChar(&buf, ' '); /* Add outer '' only if arg included shell metacharacters. */ @@ -11904,20 +11907,20 @@ vshCommandOptBool(const vshCmd *cmd, const char *name) } /* - * Returns the COUNT argv argument, or NULL after last argument. + * Returns the next argv argument after OPT (or the first one if OPT + * is NULL), or NULL if no more are present. * - * Requires that a VSH_OT_ARGV option with the name "" be last in the + * Requires that a VSH_OT_ARGV option be last in the * list of supported options in CMD->def->opts. */ -static char * -vshCommandOptArgv(const vshCmd *cmd, int count) +static const vshCmdOpt * +vshCommandOptArgv(const vshCmd *cmd, const vshCmdOpt *opt) { - vshCmdOpt *opt = cmd->opts; + opt = opt ? opt->next : cmd->opts; while (opt) { if (opt->def && opt->def->type == VSH_OT_ARGV) { - if (count-- == 0) - return opt->data; + return opt; } opt = opt->next; } -- 1.7.4.4

2011/6/14 Eric Blake <eblake@redhat.com>:
This reduces things from O(n^2) to O(n).
* tools/virsh.c (vshCommandOptArgv): Change signature. (cmdEcho): Update caller. Based on a patch by Lai Jiangshan. ---
v2: Drop the macro, avoid __ naming, reduce patch size
tools/virsh.c | 21 ++++++++++++--------- 1 files changed, 12 insertions(+), 9 deletions(-)
ACK. -- Matthias Bolte http://photron.blogspot.com

On 06/15/2011 12:36 AM, Matthias Bolte wrote:
2011/6/14 Eric Blake <eblake@redhat.com>:
This reduces things from O(n^2) to O(n).
* tools/virsh.c (vshCommandOptArgv): Change signature. (cmdEcho): Update caller. Based on a patch by Lai Jiangshan. ---
v2: Drop the macro, avoid __ naming, reduce patch size
tools/virsh.c | 21 ++++++++++++--------- 1 files changed, 12 insertions(+), 9 deletions(-)
ACK.
Thanks; pushed. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

A VSH_OFLAG_REQ_OPT option means --optionname is required when used. It will kill any ambiguity even !VSH_OFLAG_REQ option listed before VSH_OFLAG_REQ option if the !VSH_OFLAG_REQ option is a VSH_OFLAG_REQ_OPT option. It will help us use optional arguement with VSH_OT_ARGV argument. Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- tools/virsh.c | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 638029c..d13c12b 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -152,6 +152,7 @@ enum { VSH_OFLAG_NONE = 0, /* without flags */ VSH_OFLAG_REQ = (1 << 0), /* option required */ VSH_OFLAG_EMPTY_OK = (1 << 1), /* empty string option allowed */ + VSH_OFLAG_REQ_OPT = (1 << 2), /* --optionname required */ }; /* dummy */ @@ -11375,6 +11376,12 @@ vshCmddefOptParse(const vshCmdDef *cmd, uint32_t* opts_need_arg, return -1; /* bool options can't be mandatory */ continue; } + if (opt->flag & VSH_OFLAG_REQ_OPT) { + if (opt->flag & VSH_OFLAG_REQ) + *opts_required |= 1 << i; + continue; + } + *opts_need_arg |= 1 << i; if (opt->flag & VSH_OFLAG_REQ) { if (optional) -- 1.7.4.4

On Tue, Jun 07, 2011 at 05:11:10PM +0800, Lai Jiangshan wrote:
A VSH_OFLAG_REQ_OPT option means --optionname is required when used. It will kill any ambiguity even !VSH_OFLAG_REQ option listed before VSH_OFLAG_REQ option if the !VSH_OFLAG_REQ option is a VSH_OFLAG_REQ_OPT option.
It will help us use optional arguement with VSH_OT_ARGV argument.
Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- tools/virsh.c | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c index 638029c..d13c12b 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -152,6 +152,7 @@ enum { VSH_OFLAG_NONE = 0, /* without flags */ VSH_OFLAG_REQ = (1 << 0), /* option required */ VSH_OFLAG_EMPTY_OK = (1 << 1), /* empty string option allowed */ + VSH_OFLAG_REQ_OPT = (1 << 2), /* --optionname required */ };
/* dummy */ @@ -11375,6 +11376,12 @@ vshCmddefOptParse(const vshCmdDef *cmd, uint32_t* opts_need_arg, return -1; /* bool options can't be mandatory */ continue; } + if (opt->flag & VSH_OFLAG_REQ_OPT) { + if (opt->flag & VSH_OFLAG_REQ) + *opts_required |= 1 << i; + continue; + } + *opts_need_arg |= 1 << i; if (opt->flag & VSH_OFLAG_REQ) { if (optional)
ACK Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 06/13/2011 04:47 PM, Daniel P. Berrange wrote:
On Tue, Jun 07, 2011 at 05:11:10PM +0800, Lai Jiangshan wrote:
A VSH_OFLAG_REQ_OPT option means --optionname is required when used. It will kill any ambiguity even !VSH_OFLAG_REQ option listed before VSH_OFLAG_REQ option if the !VSH_OFLAG_REQ option is a VSH_OFLAG_REQ_OPT option.
It will help us use optional arguement with VSH_OT_ARGV argument.
s/arguement/argument/
Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- tools/virsh.c | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c index 638029c..d13c12b 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -152,6 +152,7 @@ enum { VSH_OFLAG_NONE = 0, /* without flags */ VSH_OFLAG_REQ = (1 << 0), /* option required */ VSH_OFLAG_EMPTY_OK = (1 << 1), /* empty string option allowed */ + VSH_OFLAG_REQ_OPT = (1 << 2), /* --optionname required */ };
/* dummy */ @@ -11375,6 +11376,12 @@ vshCmddefOptParse(const vshCmdDef *cmd, uint32_t* opts_need_arg, return -1; /* bool options can't be mandatory */ continue; } + if (opt->flag & VSH_OFLAG_REQ_OPT) { + if (opt->flag & VSH_OFLAG_REQ) + *opts_required |= 1 << i; + continue; + } +
I had to look ahead to patch 9/10 to see where this was used, but it makes sense now: virsh send-key domain linux 37 18 21 would be problematic (since the parser would try to associate linux as the first var-arg, but var-arg wants only keycodes), while: virsh send-key domain --codeset linux 37 18 21 makes it obvious that the --codeset was required for proper parsing. All because send-key, unlike echo, must have at least one keycode arg. If I'm not mistaken, this also means: virsh send-key domain 37 18 21 --codeset linux works (which is fine by me), whereas: virsh send-key domain -- 37 18 21 --codeset linux tries to parse "--codeset" and "linux" as keycodes.
*opts_need_arg |= 1 << i; if (opt->flag & VSH_OFLAG_REQ) { if (optional)
ACK
Pushed (note that 2/10 is still unpushed, while waiting for an ACK on my v2 variant to address review comments on v1). -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- daemon/remote_generator.pl | 17 +++++++++++++++++ 1 files changed, 17 insertions(+), 0 deletions(-) diff --git a/daemon/remote_generator.pl b/daemon/remote_generator.pl index 632972c..532fe63 100755 --- a/daemon/remote_generator.pl +++ b/daemon/remote_generator.pl @@ -407,6 +407,13 @@ elsif ($opt_b) { } push(@args_list, "args->$2.$2_len"); + } elsif ($args_member =~ m/^(?:unsigned )?int (\S+)<\S+>;/) { + if (! @args_list) { + push(@args_list, "conn"); + } + + push(@args_list, "args->$1.$1_val"); + push(@args_list, "args->$1.$1_len"); } elsif ($args_member =~ m/^remote_typed_param (\S+)<(\S+)>;/) { push(@vars_list, "virTypedParameterPtr $1 = NULL"); push(@vars_list, "int n$1"); @@ -985,6 +992,16 @@ elsif ($opt_k) { push(@setters_list, "args.$arg_name.${arg_name}_val = (char *)$arg_name;"); push(@setters_list, "args.$arg_name.${arg_name}_len = ${arg_name}len;"); push(@args_check_list, { name => "\"$arg_name\"", arg => "${arg_name}len", limit => $limit }); + } elsif ($args_member =~ m/^((?:unsigned )?int) (\S+)<(\S+)>;/) { + my $type_name = $1; + my $arg_name = $2; + my $limit = $3; + + push(@args_list, "${type_name} *$arg_name"); + push(@args_list, "unsigned int ${arg_name}len"); + push(@setters_list, "args.$arg_name.${arg_name}_val = $arg_name;"); + push(@setters_list, "args.$arg_name.${arg_name}_len = ${arg_name}len;"); + push(@args_check_list, { name => "\"$arg_name\"", arg => "${arg_name}len", limit => $limit }); } elsif ($args_member =~ m/^remote_typed_param (\S+)<(\S+)>;/) { push(@args_list, "virTypedParameterPtr $1"); push(@args_list, "int n$1"); -- 1.7.4.4

On Tue, Jun 07, 2011 at 05:11:11PM +0800, Lai Jiangshan wrote:
Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- daemon/remote_generator.pl | 17 +++++++++++++++++ 1 files changed, 17 insertions(+), 0 deletions(-)
diff --git a/daemon/remote_generator.pl b/daemon/remote_generator.pl index 632972c..532fe63 100755 --- a/daemon/remote_generator.pl +++ b/daemon/remote_generator.pl @@ -407,6 +407,13 @@ elsif ($opt_b) { }
push(@args_list, "args->$2.$2_len"); + } elsif ($args_member =~ m/^(?:unsigned )?int (\S+)<\S+>;/) { + if (! @args_list) { + push(@args_list, "conn"); + } + + push(@args_list, "args->$1.$1_val"); + push(@args_list, "args->$1.$1_len"); } elsif ($args_member =~ m/^remote_typed_param (\S+)<(\S+)>;/) { push(@vars_list, "virTypedParameterPtr $1 = NULL"); push(@vars_list, "int n$1"); @@ -985,6 +992,16 @@ elsif ($opt_k) { push(@setters_list, "args.$arg_name.${arg_name}_val = (char *)$arg_name;"); push(@setters_list, "args.$arg_name.${arg_name}_len = ${arg_name}len;"); push(@args_check_list, { name => "\"$arg_name\"", arg => "${arg_name}len", limit => $limit }); + } elsif ($args_member =~ m/^((?:unsigned )?int) (\S+)<(\S+)>;/) { + my $type_name = $1; + my $arg_name = $2; + my $limit = $3; + + push(@args_list, "${type_name} *$arg_name"); + push(@args_list, "unsigned int ${arg_name}len"); + push(@setters_list, "args.$arg_name.${arg_name}_val = $arg_name;"); + push(@setters_list, "args.$arg_name.${arg_name}_len = ${arg_name}len;"); + push(@args_check_list, { name => "\"$arg_name\"", arg => "${arg_name}len", limit => $limit }); } elsif ($args_member =~ m/^remote_typed_param (\S+)<(\S+)>;/) { push(@args_list, "virTypedParameterPtr $1"); push(@args_list, "int n$1");
ACK Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 06/13/2011 04:47 PM, Daniel P. Berrange wrote:
On Tue, Jun 07, 2011 at 05:11:11PM +0800, Lai Jiangshan wrote:
Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- daemon/remote_generator.pl | 17 +++++++++++++++++ 1 files changed, 17 insertions(+), 0 deletions(-)
ACK
Pushed. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

Add public virDomainSendKey() and enum libvirt_keycode_set for the @codeset. Python version of virDomainSendKey() has not been implemented yet, it will be done soon. Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- include/libvirt/libvirt.h.in | 7 +++++++ include/libvirt/virtkeys.h | 22 ++++++++++++++++++++++ python/generator.py | 1 + src/libvirt_public.syms | 5 +++++ 4 files changed, 35 insertions(+), 0 deletions(-) create mode 100644 include/libvirt/virtkeys.h diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index df213f1..94da205 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -2673,6 +2673,13 @@ typedef struct _virTypedParameter virMemoryParameter; */ typedef virMemoryParameter *virMemoryParameterPtr; +int virDomainSendKey(virDomainPtr domain, + unsigned int codeset, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes, + unsigned int flags); + #ifdef __cplusplus } #endif diff --git a/include/libvirt/virtkeys.h b/include/libvirt/virtkeys.h new file mode 100644 index 0000000..854594a --- /dev/null +++ b/include/libvirt/virtkeys.h @@ -0,0 +1,22 @@ +#ifndef _LIBVIRT_VIRTKEYS_H +#define _LIBVIRT_VIRTKEYS_H + +/* + * Copyright (c) 2011 Lai Jiangshan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +enum libvirt_keycode_set { + LIBVIRT_KEYCODE_LINUX = 0, + LIBVIRT_KEYCODE_XT = 1, + LIBVIRT_KEYCODE_ATSET1 = 2, + LIBVIRT_KEYCODE_ATSET2 = 3, + LIBVIRT_KEYCODE_ATSET3 = 4, +}; + +#define MAX_SEND_KEY 16 + +#endif diff --git a/python/generator.py b/python/generator.py index 7c38fdd..57373f0 100755 --- a/python/generator.py +++ b/python/generator.py @@ -356,6 +356,7 @@ skip_impl = ( 'virNodeDeviceListCaps', 'virConnectBaselineCPU', 'virDomainRevertToSnapshot', + 'virDomainSendKey', ) diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 4d4299a..ddfed44 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -450,4 +450,9 @@ LIBVIRT_0.9.2 { virInterfaceChangeRollback; } LIBVIRT_0.9.0; +LIBVIRT_0.9.3 { + global: + virDomainSendKey; +} LIBVIRT_0.9.2; + # .... define new API here using predicted next version number .... -- 1.7.4.4

On Tue, Jun 07, 2011 at 05:11:12PM +0800, Lai Jiangshan wrote:
Add public virDomainSendKey() and enum libvirt_keycode_set for the @codeset.
Python version of virDomainSendKey() has not been implemented yet, it will be done soon.
Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- include/libvirt/libvirt.h.in | 7 +++++++ include/libvirt/virtkeys.h | 22 ++++++++++++++++++++++ python/generator.py | 1 + src/libvirt_public.syms | 5 +++++ 4 files changed, 35 insertions(+), 0 deletions(-) create mode 100644 include/libvirt/virtkeys.h
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index df213f1..94da205 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -2673,6 +2673,13 @@ typedef struct _virTypedParameter virMemoryParameter; */ typedef virMemoryParameter *virMemoryParameterPtr;
+int virDomainSendKey(virDomainPtr domain, + unsigned int codeset, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes, + unsigned int flags); + #ifdef __cplusplus } #endif diff --git a/include/libvirt/virtkeys.h b/include/libvirt/virtkeys.h new file mode 100644 index 0000000..854594a --- /dev/null +++ b/include/libvirt/virtkeys.h @@ -0,0 +1,22 @@ +#ifndef _LIBVIRT_VIRTKEYS_H +#define _LIBVIRT_VIRTKEYS_H + +/* + * Copyright (c) 2011 Lai Jiangshan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +enum libvirt_keycode_set { + LIBVIRT_KEYCODE_LINUX = 0, + LIBVIRT_KEYCODE_XT = 1, + LIBVIRT_KEYCODE_ATSET1 = 2, + LIBVIRT_KEYCODE_ATSET2 = 3, + LIBVIRT_KEYCODE_ATSET3 = 4, +}; + +#define MAX_SEND_KEY 16 + +#endif
I think this enum + #define should be in the main libvirt.h header file, alongside the virDomainSendKey API definition. Keep the separate 'virtkeys.h' file to be used only for the large list of keycode names. Also, using 'libvirt_' or LIBVIRT_ isn't our normal namespace, and enums use capital letters for separation, rather than underscores. Any enum should have a typedef, and finally the constant should have a VIR_DOMAIN prefix So should be more like typedef enum { VIR_KEYCODE_LINUX = 0, ... } virKeycodeSet; #define VIR_DOMAIN_SEND_KEY_MAX_KEYS 16
diff --git a/python/generator.py b/python/generator.py index 7c38fdd..57373f0 100755 --- a/python/generator.py +++ b/python/generator.py @@ -356,6 +356,7 @@ skip_impl = ( 'virNodeDeviceListCaps', 'virConnectBaselineCPU', 'virDomainRevertToSnapshot', + 'virDomainSendKey', )
diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 4d4299a..ddfed44 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -450,4 +450,9 @@ LIBVIRT_0.9.2 { virInterfaceChangeRollback; } LIBVIRT_0.9.0;
+LIBVIRT_0.9.3 { + global: + virDomainSendKey; +} LIBVIRT_0.9.2; + # .... define new API here using predicted next version number ....
Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 06/13/2011 04:52 PM, Daniel P. Berrange wrote:
On Tue, Jun 07, 2011 at 05:11:12PM +0800, Lai Jiangshan wrote:
Add public virDomainSendKey() and enum libvirt_keycode_set for the @codeset.
Python version of virDomainSendKey() has not been implemented yet, it will be done soon.
Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- include/libvirt/libvirt.h.in | 7 +++++++ include/libvirt/virtkeys.h | 22 ++++++++++++++++++++++ python/generator.py | 1 + src/libvirt_public.syms | 5 +++++ 4 files changed, 35 insertions(+), 0 deletions(-) create mode 100644 include/libvirt/virtkeys.h
+++ b/include/libvirt/virtkeys.h @@ -0,0 +1,22 @@
I think this enum + #define should be in the main libvirt.h header file, alongside the virDomainSendKey API definition. Keep the separate 'virtkeys.h' file to be used only for the large list of keycode names.
Well, we don't have a large list of keycode names yet, so for now there is no reason to have virtkeys.h. Not to mention that you can't add a new header without also touching Makefile.am to make sure it gets into the tarball. So I just removed that file.
Also, using 'libvirt_' or LIBVIRT_ isn't our normal namespace, and enums use capital letters for separation, rather than underscores. Any enum should have a typedef, and finally the constant should have a VIR_DOMAIN prefix
So should be more like
typedef enum { VIR_KEYCODE_LINUX = 0, ... } virKeycodeSet;
#define VIR_DOMAIN_SEND_KEY_MAX_KEYS 16
ACK to Daniel's approach, and needs documentation. Also, this should be added prior to deprecated interfaces. Here's what I squashed in before pushing: diff --git i/include/libvirt/libvirt.h.in w/include/libvirt/libvirt.h.in index 63c0582..f0b6d9b 100644 --- i/include/libvirt/libvirt.h.in +++ w/include/libvirt/libvirt.h.in @@ -1566,6 +1566,33 @@ char * virStorageVolGetXMLDesc (virStorageVolPtr pool, char * virStorageVolGetPath (virStorageVolPtr vol); +/** + * virKeycodeSet: + * + * Enum to specify which keycode mapping is in use for virDomainSendKey(). + */ +typedef enum { + VIR_KEYCODE_SET_LINUX = 0, + VIR_KEYCODE_SET_XT = 1, + VIR_KEYCODE_SET_ATSET1 = 2, + VIR_KEYCODE_SET_ATSET2 = 3, + VIR_KEYCODE_SET_ATSET3 = 4, +} virKeycodeSet; + +/** + * VIR_DOMAIN_SEND_KEY_MAX_KEYS: + * + * Maximum number of keycodes that can be sent in one virDomainSendKey() call. + */ +#define VIR_DOMAIN_SEND_KEY_MAX_KEYS 16 + +int virDomainSendKey(virDomainPtr domain, + unsigned int codeset, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes, + unsigned int flags); + /* * Deprecated calls */ @@ -2691,13 +2718,6 @@ typedef struct _virTypedParameter virMemoryParameter; */ typedef virMemoryParameter *virMemoryParameterPtr; -int virDomainSendKey(virDomainPtr domain, - unsigned int codeset, - unsigned int holdtime, - unsigned int *keycodes, - unsigned int nkeycodes, - unsigned int flags); - #ifdef __cplusplus } #endif -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On 06/07/2011 03:11 AM, Lai Jiangshan wrote:
Add public virDomainSendKey() and enum libvirt_keycode_set for the @codeset.
+int virDomainSendKey(virDomainPtr domain, + unsigned int codeset, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes, + unsigned int flags);
Sorry for not noticing sooner, but ALL other public APIs that pass array/len parameter pairs use 'int nfoo' or 'int maxfoo' for the lengthh parameter name. This is the first API that passes unsigned int. While that is technically more correct from the ABI perspective (since arrays can never be negative length), it doesn't make much sense to be inconsistent. Either we should change ALL existing APIs to use 'unsigned int' in reference to array lengths (probably a safe change, even for C++ code, since our declarations are extern "C" and the two types are ABI compatible), or we should change just this API to use 'int' instead of 'unsigned int' for nkeycodes. Either way, now is the time to make the change, before we hit freeze for 0.9.3. Preferences? -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- src/driver.h | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-) diff --git a/src/driver.h b/src/driver.h index 5df798a..2e335a1 100644 --- a/src/driver.h +++ b/src/driver.h @@ -542,6 +542,13 @@ typedef int typedef int (*virDrvDomainInjectNMI)(virDomainPtr dom, unsigned int flags); +typedef int + (*virDrvDomainSendKey)(virDomainPtr dom, unsigned int codeset, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes, + unsigned int flags); + typedef char * (*virDrvDomainMigrateBegin3) (virDomainPtr domain, @@ -749,6 +756,7 @@ struct _virDriver { virDrvDomainMigratePerform3 domainMigratePerform3; virDrvDomainMigrateFinish3 domainMigrateFinish3; virDrvDomainMigrateConfirm3 domainMigrateConfirm3; + virDrvDomainSendKey domainSendKey; }; typedef int -- 1.7.4.4

On Tue, Jun 07, 2011 at 05:11:13PM +0800, Lai Jiangshan wrote:
Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- src/driver.h | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/src/driver.h b/src/driver.h index 5df798a..2e335a1 100644 --- a/src/driver.h +++ b/src/driver.h @@ -542,6 +542,13 @@ typedef int typedef int (*virDrvDomainInjectNMI)(virDomainPtr dom, unsigned int flags);
+typedef int + (*virDrvDomainSendKey)(virDomainPtr dom, unsigned int codeset, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes, + unsigned int flags); + typedef char * (*virDrvDomainMigrateBegin3) (virDomainPtr domain, @@ -749,6 +756,7 @@ struct _virDriver { virDrvDomainMigratePerform3 domainMigratePerform3; virDrvDomainMigrateFinish3 domainMigrateFinish3; virDrvDomainMigrateConfirm3 domainMigrateConfirm3; + virDrvDomainSendKey domainSendKey; };
typedef int
ACK Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 06/13/2011 04:53 PM, Daniel P. Berrange wrote:
On Tue, Jun 07, 2011 at 05:11:13PM +0800, Lai Jiangshan wrote:
Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- src/driver.h | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-)
ACK
Pushed. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- src/libvirt.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 63 insertions(+), 0 deletions(-) diff --git a/src/libvirt.c b/src/libvirt.c index cbe1926..112f690 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -26,6 +26,8 @@ #include <libxml/uri.h> #include "getpass.h" +#include <libvirt/virtkeys.h> + #ifdef HAVE_WINSOCK2_H # include <winsock2.h> #endif @@ -6511,6 +6513,67 @@ error: } /** + * virDomainSendKey: + * @domain: pointer to domain object, or NULL for Domain0 + * @codeset: the code set of keycodes + * @holdtime: the time (in millsecond) how long the keys will be held + * @nkeycodes: number of keycodes + * @keycodes: array of keycodes + * @flags: the flags for controlling behavior, pass 0 for now + * + * Send key to the guest + * + * Returns 0 in case of success, -1 in case of failure. + */ + +int virDomainSendKey(virDomainPtr domain, + unsigned int codeset, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes, + unsigned int flags) +{ + virConnectPtr conn; + VIR_DOMAIN_DEBUG(domain, "codeset=%u,holdtime=%u,nkeycodes=%u,flags=%u", + codeset, holdtime, nkeycodes, flags); + + virResetLastError(); + + if (nkeycodes == 0 || nkeycodes > MAX_SEND_KEY) { + virLibDomainError(VIR_ERR_OPERATION_INVALID, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + conn = domain->conn; + + if (conn->driver->domainSendKey) { + int ret; + ret = conn->driver->domainSendKey(domain, codeset, holdtime, + keycodes, nkeycodes, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(domain->conn); + return -1; +} + +/** * virDomainSetVcpus: * @domain: pointer to domain object, or NULL for Domain0 * @nvcpus: the new number of virtual CPUs for this domain -- 1.7.4.4

On Tue, Jun 07, 2011 at 05:11:14PM +0800, Lai Jiangshan wrote:
Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- src/libvirt.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 63 insertions(+), 0 deletions(-)
diff --git a/src/libvirt.c b/src/libvirt.c index cbe1926..112f690 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -26,6 +26,8 @@ #include <libxml/uri.h> #include "getpass.h"
+#include <libvirt/virtkeys.h> +
If we move the enum/#define into libvirt.h, this extra include won't be needed (at least not for this file)
#ifdef HAVE_WINSOCK2_H # include <winsock2.h> #endif @@ -6511,6 +6513,67 @@ error: }
/** + * virDomainSendKey: + * @domain: pointer to domain object, or NULL for Domain0 + * @codeset: the code set of keycodes + * @holdtime: the time (in millsecond) how long the keys will be held + * @nkeycodes: number of keycodes + * @keycodes: array of keycodes + * @flags: the flags for controlling behavior, pass 0 for now + * + * Send key to the guest + * + * Returns 0 in case of success, -1 in case of failure. + */ + +int virDomainSendKey(virDomainPtr domain, + unsigned int codeset, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes, + unsigned int flags) +{ + virConnectPtr conn; + VIR_DOMAIN_DEBUG(domain, "codeset=%u,holdtime=%u,nkeycodes=%u,flags=%u", + codeset, holdtime, nkeycodes, flags);
Normal formatting is to include whitespace after each comma in the debug messages, eg "codeset=%u, holdtime...."
+ + virResetLastError(); + + if (nkeycodes == 0 || nkeycodes > MAX_SEND_KEY) { + virLibDomainError(VIR_ERR_OPERATION_INVALID, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + conn = domain->conn; + + if (conn->driver->domainSendKey) { + int ret; + ret = conn->driver->domainSendKey(domain, codeset, holdtime, + keycodes, nkeycodes, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(domain->conn); + return -1; +} + +/** * virDomainSetVcpus: * @domain: pointer to domain object, or NULL for Domain0 * @nvcpus: the new number of virtual CPUs for this domain
ACK, if the whitespace is fixed in the debug message Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 06/13/2011 04:55 PM, Daniel P. Berrange wrote:
+++ b/src/libvirt.c @@ -26,6 +26,8 @@ #include <libxml/uri.h> #include "getpass.h"
+#include <libvirt/virtkeys.h> +
If we move the enum/#define into libvirt.h, this extra include won't be needed (at least not for this file)
/** + * virDomainSendKey: + * @domain: pointer to domain object, or NULL for Domain0 + * @codeset: the code set of keycodes + * @holdtime: the time (in millsecond) how long the keys will be held
s/millsecond/milliseconds/, plus some grammar improvements
+ * @nkeycodes: number of keycodes + * @keycodes: array of keycodes
Order doesn't match. Per Matthias' comment on 8/10, document the limit.
ACK, if the whitespace is fixed in the debug message
Here's what I squashed in before pushing: diff --git i/src/libvirt.c w/src/libvirt.c index 8389e06..a7b55b6 100644 --- i/src/libvirt.c +++ w/src/libvirt.c @@ -26,8 +26,6 @@ #include <libxml/uri.h> #include "getpass.h" -#include <libvirt/virtkeys.h> - #ifdef HAVE_WINSOCK2_H # include <winsock2.h> #endif @@ -6465,13 +6463,13 @@ error: /** * virDomainSendKey: * @domain: pointer to domain object, or NULL for Domain0 - * @codeset: the code set of keycodes - * @holdtime: the time (in millsecond) how long the keys will be held - * @nkeycodes: number of keycodes + * @codeset: the code set of keycodes, from virKeycodeSet + * @holdtime: the duration (in milliseconds) that the keys will be held * @keycodes: array of keycodes + * @nkeycodes: number of keycodes, up to VIR_DOMAIN_SEND_KEY_MAX_KEYS * @flags: the flags for controlling behavior, pass 0 for now * - * Send key to the guest + * Send key(s) to the guest. * * Returns 0 in case of success, -1 in case of failure. */ @@ -6484,12 +6482,13 @@ int virDomainSendKey(virDomainPtr domain, unsigned int flags) { virConnectPtr conn; - VIR_DOMAIN_DEBUG(domain, "codeset=%u,holdtime=%u,nkeycodes=%u,flags=%u", + VIR_DOMAIN_DEBUG(domain, "codeset=%u, holdtime=%u, nkeycodes=%u, flags=%u", codeset, holdtime, nkeycodes, flags); virResetLastError(); - if (nkeycodes == 0 || nkeycodes > MAX_SEND_KEY) { + if (keycodes == NULL || + nkeycodes == 0 || nkeycodes > VIR_DOMAIN_SEND_KEY_MAX_KEYS) { virLibDomainError(VIR_ERR_OPERATION_INVALID, __FUNCTION__); virDispatchError(NULL); return -1; -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 16 +++++++++++++++- src/remote_protocol-structs | 11 +++++++++++ 3 files changed, 27 insertions(+), 1 deletions(-) diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 8335a1a..f08a609 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -6337,6 +6337,7 @@ static virDriver remote_driver = { .domainMigratePerform3 = remoteDomainMigratePerform3, /* 0.9.2 */ .domainMigrateFinish3 = remoteDomainMigrateFinish3, /* 0.9.2 */ .domainMigrateConfirm3 = remoteDomainMigrateConfirm3, /* 0.9.2 */ + .domainSendKey = remoteDomainSendKey, /* 0.9.3 */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index c9b8cff..2126325 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -191,6 +191,11 @@ const REMOTE_SECRET_UUID_LIST_MAX = 16384; */ const REMOTE_CPU_BASELINE_MAX = 256; +/* + * Max number of sending keycodes. + */ +const REMOTE_SEND_KEY_MAX = 16; + /* UUID. VIR_UUID_BUFLEN definition comes from libvirt.h */ typedef opaque remote_uuid[VIR_UUID_BUFLEN]; @@ -811,6 +816,14 @@ struct remote_domain_inject_nmi_args { unsigned int flags; }; +struct remote_domain_send_key_args { + remote_nonnull_domain dom; + unsigned int codeset; + unsigned int holdtime; + unsigned int keycodes<REMOTE_SEND_KEY_MAX>; + unsigned int flags; +}; + struct remote_domain_set_vcpus_args { remote_nonnull_domain dom; unsigned int nvcpus; @@ -2297,7 +2310,8 @@ enum remote_procedure { REMOTE_PROC_INTERFACE_CHANGE_COMMIT = 221, /* autogen autogen */ REMOTE_PROC_INTERFACE_CHANGE_ROLLBACK = 222, /* autogen autogen */ REMOTE_PROC_DOMAIN_GET_SCHEDULER_PARAMETERS_FLAGS = 223, /* skipgen autogen */ - REMOTE_PROC_DOMAIN_EVENT_CONTROL_ERROR = 224 /* skipgen skipgen */ + REMOTE_PROC_DOMAIN_EVENT_CONTROL_ERROR = 224, /* skipgen skipgen */ + REMOTE_PROC_DOMAIN_SEND_KEY = 225 /* autogen autogen */ /* * Notice how the entries are grouped in sets of 10 ? diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 1d90dd5..dd70c24 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -1550,3 +1550,14 @@ struct remote_message_header { u_int serial; remote_message_status status; }; + +struct remote_domain_send_key_args { + remote_nonnull_domain dom; + unsigned int codeset; + unsigned int holdtime; + struct { + unsigned int keycodes_len; + unsigned int * keycodes_val; + } keycodes; + unsigned int flags; +}; -- 1.7.4.4

On Tue, Jun 07, 2011 at 05:11:15PM +0800, Lai Jiangshan wrote:
Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 16 +++++++++++++++- src/remote_protocol-structs | 11 +++++++++++ 3 files changed, 27 insertions(+), 1 deletions(-)
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 8335a1a..f08a609 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -6337,6 +6337,7 @@ static virDriver remote_driver = { .domainMigratePerform3 = remoteDomainMigratePerform3, /* 0.9.2 */ .domainMigrateFinish3 = remoteDomainMigrateFinish3, /* 0.9.2 */ .domainMigrateConfirm3 = remoteDomainMigrateConfirm3, /* 0.9.2 */ + .domainSendKey = remoteDomainSendKey, /* 0.9.3 */ };
static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index c9b8cff..2126325 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -191,6 +191,11 @@ const REMOTE_SECRET_UUID_LIST_MAX = 16384; */ const REMOTE_CPU_BASELINE_MAX = 256;
+/* + * Max number of sending keycodes. + */ +const REMOTE_SEND_KEY_MAX = 16;
Should call this REMOTE_DOMAIN_SEND_KEY_MAX really
+ /* UUID. VIR_UUID_BUFLEN definition comes from libvirt.h */ typedef opaque remote_uuid[VIR_UUID_BUFLEN];
@@ -811,6 +816,14 @@ struct remote_domain_inject_nmi_args { unsigned int flags; };
+struct remote_domain_send_key_args { + remote_nonnull_domain dom; + unsigned int codeset; + unsigned int holdtime; + unsigned int keycodes<REMOTE_SEND_KEY_MAX>; + unsigned int flags; +}; + struct remote_domain_set_vcpus_args { remote_nonnull_domain dom; unsigned int nvcpus; @@ -2297,7 +2310,8 @@ enum remote_procedure { REMOTE_PROC_INTERFACE_CHANGE_COMMIT = 221, /* autogen autogen */ REMOTE_PROC_INTERFACE_CHANGE_ROLLBACK = 222, /* autogen autogen */ REMOTE_PROC_DOMAIN_GET_SCHEDULER_PARAMETERS_FLAGS = 223, /* skipgen autogen */ - REMOTE_PROC_DOMAIN_EVENT_CONTROL_ERROR = 224 /* skipgen skipgen */ + REMOTE_PROC_DOMAIN_EVENT_CONTROL_ERROR = 224, /* skipgen skipgen */ + REMOTE_PROC_DOMAIN_SEND_KEY = 225 /* autogen autogen */
/* * Notice how the entries are grouped in sets of 10 ? diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 1d90dd5..dd70c24 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -1550,3 +1550,14 @@ struct remote_message_header { u_int serial; remote_message_status status; }; + +struct remote_domain_send_key_args { + remote_nonnull_domain dom; + unsigned int codeset; + unsigned int holdtime; + struct { + unsigned int keycodes_len; + unsigned int * keycodes_val; + } keycodes; + unsigned int flags; +};
ACK with the minor constant rename. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

2011/6/14 Daniel P. Berrange <berrange@redhat.com>:
On Tue, Jun 07, 2011 at 05:11:15PM +0800, Lai Jiangshan wrote:
Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 16 +++++++++++++++- src/remote_protocol-structs | 11 +++++++++++ 3 files changed, 27 insertions(+), 1 deletions(-)
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 8335a1a..f08a609 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -6337,6 +6337,7 @@ static virDriver remote_driver = { .domainMigratePerform3 = remoteDomainMigratePerform3, /* 0.9.2 */ .domainMigrateFinish3 = remoteDomainMigrateFinish3, /* 0.9.2 */ .domainMigrateConfirm3 = remoteDomainMigrateConfirm3, /* 0.9.2 */ + .domainSendKey = remoteDomainSendKey, /* 0.9.3 */ };
static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index c9b8cff..2126325 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -191,6 +191,11 @@ const REMOTE_SECRET_UUID_LIST_MAX = 16384; */ const REMOTE_CPU_BASELINE_MAX = 256;
+/* + * Max number of sending keycodes. + */ +const REMOTE_SEND_KEY_MAX = 16;
Should call this REMOTE_DOMAIN_SEND_KEY_MAX really
And I still wonder why we go with such a low limit here that is not documented anywhere. If an applications tries to send 17 key strokes in one call it'll just fail with an RPC error. -- Matthias Bolte http://photron.blogspot.com

On Tue, Jun 14, 2011 at 11:21:39AM +0200, Matthias Bolte wrote:
2011/6/14 Daniel P. Berrange <berrange@redhat.com>:
On Tue, Jun 07, 2011 at 05:11:15PM +0800, Lai Jiangshan wrote:
Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 16 +++++++++++++++- src/remote_protocol-structs | 11 +++++++++++ 3 files changed, 27 insertions(+), 1 deletions(-)
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 8335a1a..f08a609 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -6337,6 +6337,7 @@ static virDriver remote_driver = { .domainMigratePerform3 = remoteDomainMigratePerform3, /* 0.9.2 */ .domainMigrateFinish3 = remoteDomainMigrateFinish3, /* 0.9.2 */ .domainMigrateConfirm3 = remoteDomainMigrateConfirm3, /* 0.9.2 */ + .domainSendKey = remoteDomainSendKey, /* 0.9.3 */ };
static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index c9b8cff..2126325 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -191,6 +191,11 @@ const REMOTE_SECRET_UUID_LIST_MAX = 16384; */ const REMOTE_CPU_BASELINE_MAX = 256;
+/* + * Max number of sending keycodes. + */ +const REMOTE_SEND_KEY_MAX = 16;
Should call this REMOTE_DOMAIN_SEND_KEY_MAX really
And I still wonder why we go with such a low limit here that is not documented anywhere. If an applications tries to send 17 key strokes in one call it'll just fail with an RPC error.
Perhaps it can be raised, but really this API is only for doing things like sending magic key combinations like Ctrl-Alt-Delete, Ctrl+F12 etc. It isn't suitable for sending large volumes of text, for the same reason we don't want todo that via GTK-VNC: http://mail.gnome.org/archives/gtk-vnc-list/2010-December/msg00004.html "Another fairly serious problem is that if you paste a few kilobytes of data then you're pushing the keyboard input stack incredibly hard. These interfaces are designed for human typing speeds and even a few kilobytes will drive Thunderbird (say) to distraction. Tens of kilobytes will probably lock you out for longer than you're prepared to wait." Of course '16' is no where near kilobytes, so perhaps we can raise it a little, but I don't think we should give application developers the idea that this is useful for sending large data volumes. Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

2011/6/14 Daniel P. Berrange <berrange@redhat.com>:
On Tue, Jun 14, 2011 at 11:21:39AM +0200, Matthias Bolte wrote:
2011/6/14 Daniel P. Berrange <berrange@redhat.com>:
On Tue, Jun 07, 2011 at 05:11:15PM +0800, Lai Jiangshan wrote:
Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 16 +++++++++++++++- src/remote_protocol-structs | 11 +++++++++++ 3 files changed, 27 insertions(+), 1 deletions(-)
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 8335a1a..f08a609 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -6337,6 +6337,7 @@ static virDriver remote_driver = { .domainMigratePerform3 = remoteDomainMigratePerform3, /* 0.9.2 */ .domainMigrateFinish3 = remoteDomainMigrateFinish3, /* 0.9.2 */ .domainMigrateConfirm3 = remoteDomainMigrateConfirm3, /* 0.9.2 */ + .domainSendKey = remoteDomainSendKey, /* 0.9.3 */ };
static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index c9b8cff..2126325 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -191,6 +191,11 @@ const REMOTE_SECRET_UUID_LIST_MAX = 16384; */ const REMOTE_CPU_BASELINE_MAX = 256;
+/* + * Max number of sending keycodes. + */ +const REMOTE_SEND_KEY_MAX = 16;
Should call this REMOTE_DOMAIN_SEND_KEY_MAX really
And I still wonder why we go with such a low limit here that is not documented anywhere. If an applications tries to send 17 key strokes in one call it'll just fail with an RPC error.
Perhaps it can be raised, but really this API is only for doing things like sending magic key combinations like Ctrl-Alt-Delete, Ctrl+F12 etc.
It isn't suitable for sending large volumes of text, for the same reason we don't want todo that via GTK-VNC:
http://mail.gnome.org/archives/gtk-vnc-list/2010-December/msg00004.html
"Another fairly serious problem is that if you paste a few kilobytes of data then you're pushing the keyboard input stack incredibly hard. These interfaces are designed for human typing speeds and even a few kilobytes will drive Thunderbird (say) to distraction. Tens of kilobytes will probably lock you out for longer than you're prepared to wait."
Of course '16' is no where near kilobytes, so perhaps we can raise it a little, but I don't think we should give application developers the idea that this is useful for sending large data volumes.
Regards, Daniel
Okay, I'm fine with that. But then we should also state this in the documentation of virDomainSendKey (patch 7/10) that this is only meant for a few key stokes at a time. -- Matthias Bolte http://photron.blogspot.com

On 06/14/2011 03:32 AM, Matthias Bolte wrote:
+/* + * Max number of sending keycodes. + */ +const REMOTE_SEND_KEY_MAX = 16;
Should call this REMOTE_DOMAIN_SEND_KEY_MAX really
Of course '16' is no where near kilobytes, so perhaps we can raise it a little, but I don't think we should give application developers the idea that this is useful for sending large data volumes.
Okay, I'm fine with that. But then we should also state this in the documentation of virDomainSendKey (patch 7/10) that this is only meant for a few key stokes at a time.
At any rate, 16 is consistent between this patch and MAX_SEND_KEY (renamed VIR_DOMAIN_SEND_KEY_MAX_KEYS) of patch 05/10, and I'm okay leaving it at 16 for now. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On 06/13/2011 04:56 PM, Daniel P. Berrange wrote:
On Tue, Jun 07, 2011 at 05:11:15PM +0800, Lai Jiangshan wrote:
Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 16 +++++++++++++++- src/remote_protocol-structs | 11 +++++++++++ 3 files changed, 27 insertions(+), 1 deletions(-)
+/* + * Max number of sending keycodes. + */ +const REMOTE_SEND_KEY_MAX = 16;
Should call this REMOTE_DOMAIN_SEND_KEY_MAX really
ACK with the minor constant rename.
I rebased this and made that name change, fixed the fallout from 'make check' (remote_protocol-structs uses 'u_int', not 'unsigned int', due to the way pdwtags works, and your whitespace was off; but you only notice that if you have the dwarves package installed), and pushed. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- tools/virsh.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 4 ++ 2 files changed, 106 insertions(+), 0 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index d13c12b..7b5847f 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -33,6 +33,8 @@ #include <signal.h> #include <poll.h> +#include <libvirt/virtkeys.h> + #include <libxml/parser.h> #include <libxml/tree.h> #include <libxml/xpath.h> @@ -3182,6 +3184,105 @@ cmdInjectNMI(vshControl *ctl, const vshCmd *cmd) } /* + * "send-key" command + */ +static const vshCmdInfo info_send_key[] = { + {"help", N_("Send keycodes to the guest")}, + {"desc", N_("Send keycodes to the guest, the keycodes must be integers\n" + " Examples:\n\n" + " virsh # send-key <domain> 37 18 21\n" + " virsh # send-key <domain> --holdtime 1000 0x15 18 0xf\n" + )}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_send_key[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, + {"codeset", VSH_OT_STRING, VSH_OFLAG_REQ_OPT, N_("the codeset of keycodes, default:linux")}, + {"holdtime", VSH_OT_INT, VSH_OFLAG_REQ_OPT, + N_("the time (in millsecond) how long the keys will be held")}, + {"keycode", VSH_OT_ARGV, VSH_OFLAG_REQ, N_("the key code")}, + {NULL, 0, 0, NULL} +}; + +static int get_integer_keycode(const char *key_name) +{ + long val; + char *endptr; + + val = strtol(key_name, &endptr, 0); + if (*endptr != '\0' || val > 255 || val <= 0) + return -1; + + return val; +} + +static bool +cmdSendKey(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom; + int ret = false; + const char *codeset_option; + int codeset; + int holdtime; + int count = 0; + const vshCmdOpt *opt; + int keycode; + unsigned int keycodes[MAX_SEND_KEY]; + + if (!vshConnectionUsability(ctl, ctl->conn)) + return false; + + if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) + return false; + + if (vshCommandOptString(cmd, "codeset", &codeset_option) <= 0) + codeset_option = "default"; + + if (vshCommandOptInt(cmd, "holdtime", &holdtime) <= 0) + holdtime = 0; + + if (STREQ(codeset_option, "default") || STREQ(codeset_option, "linux")) { + codeset = LIBVIRT_KEYCODE_LINUX; + } else if (STREQ(codeset_option, "xt")) { + codeset = LIBVIRT_KEYCODE_XT; + } else if (STREQ(codeset_option, "atset1")) { + codeset = LIBVIRT_KEYCODE_ATSET1; + } else if (STREQ(codeset_option, "atset2")) { + codeset = LIBVIRT_KEYCODE_ATSET2; + } else if (STREQ(codeset_option, "atset3")) { + codeset = LIBVIRT_KEYCODE_ATSET3; + } else { + vshError(ctl, _("unknown codeset: '%s'"), codeset_option); + goto free_domain; + } + + for_each_variable_arg(cmd, opt) { + if (count == MAX_SEND_KEY) { + vshError(ctl, _("too many keycode")); + goto free_domain; + } + + if ((keycode = get_integer_keycode(opt->data)) > 0) + goto get_keycode; + + vshError(ctl, _("invalid keycode: '%s'"), opt->data); + goto free_domain; + +get_keycode: + keycodes[count] = keycode; + count++; + } + + if (!(virDomainSendKey(dom, codeset, holdtime, keycodes, count, 0) < 0)) + ret = true; + +free_domain: + virDomainFree(dom); + return ret; +} + +/* * "setmemory" command */ static const vshCmdInfo info_setmem[] = { @@ -11095,6 +11196,7 @@ static const vshCmdDef domManagementCmds[] = { {"dumpxml", cmdDumpXML, opts_dumpxml, info_dumpxml, 0}, {"edit", cmdEdit, opts_edit, info_edit, 0}, {"inject-nmi", cmdInjectNMI, opts_inject_nmi, info_inject_nmi, 0}, + {"send-key", cmdSendKey, opts_send_key, info_send_key}, {"managedsave", cmdManagedSave, opts_managedsave, info_managedsave, 0}, {"managedsave-remove", cmdManagedSaveRemove, opts_managedsaveremove, info_managedsaveremove, 0}, diff --git a/tools/virsh.pod b/tools/virsh.pod index 7ed3003..03b1418 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -296,6 +296,10 @@ scheduling by the hypervisor. Inject NMI to the guest +=item B<send-key> I<domain-id> I<--codeset> B<codeset> I<--holdtime> B<holdtime> B<keycode>... + +Send keys to the guest + =item B<shutdown> The domain is in the process of shutting down, i.e. the guest operating system -- 1.7.4.4

On Tue, Jun 07, 2011 at 05:11:16PM +0800, Lai Jiangshan wrote:
Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- tools/virsh.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 4 ++ 2 files changed, 106 insertions(+), 0 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c index d13c12b..7b5847f 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -33,6 +33,8 @@ #include <signal.h> #include <poll.h>
+#include <libvirt/virtkeys.h> + #include <libxml/parser.h> #include <libxml/tree.h> #include <libxml/xpath.h> @@ -3182,6 +3184,105 @@ cmdInjectNMI(vshControl *ctl, const vshCmd *cmd) }
/* + * "send-key" command + */ +static const vshCmdInfo info_send_key[] = { + {"help", N_("Send keycodes to the guest")}, + {"desc", N_("Send keycodes to the guest, the keycodes must be integers\n" + " Examples:\n\n" + " virsh # send-key <domain> 37 18 21\n" + " virsh # send-key <domain> --holdtime 1000 0x15 18 0xf\n" + )}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_send_key[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, + {"codeset", VSH_OT_STRING, VSH_OFLAG_REQ_OPT, N_("the codeset of keycodes, default:linux")}, + {"holdtime", VSH_OT_INT, VSH_OFLAG_REQ_OPT, + N_("the time (in millsecond) how long the keys will be held")}, + {"keycode", VSH_OT_ARGV, VSH_OFLAG_REQ, N_("the key code")}, + {NULL, 0, 0, NULL} +}; + +static int get_integer_keycode(const char *key_name) +{ + long val; + char *endptr; + + val = strtol(key_name, &endptr, 0); + if (*endptr != '\0' || val > 255 || val <= 0) + return -1; + + return val; +} + +static bool +cmdSendKey(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom; + int ret = false; + const char *codeset_option; + int codeset; + int holdtime; + int count = 0; + const vshCmdOpt *opt; + int keycode; + unsigned int keycodes[MAX_SEND_KEY]; + + if (!vshConnectionUsability(ctl, ctl->conn)) + return false; + + if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) + return false; + + if (vshCommandOptString(cmd, "codeset", &codeset_option) <= 0) + codeset_option = "default"; + + if (vshCommandOptInt(cmd, "holdtime", &holdtime) <= 0) + holdtime = 0; + + if (STREQ(codeset_option, "default") || STREQ(codeset_option, "linux")) { + codeset = LIBVIRT_KEYCODE_LINUX; + } else if (STREQ(codeset_option, "xt")) { + codeset = LIBVIRT_KEYCODE_XT; + } else if (STREQ(codeset_option, "atset1")) { + codeset = LIBVIRT_KEYCODE_ATSET1; + } else if (STREQ(codeset_option, "atset2")) { + codeset = LIBVIRT_KEYCODE_ATSET2; + } else if (STREQ(codeset_option, "atset3")) { + codeset = LIBVIRT_KEYCODE_ATSET3; + } else { + vshError(ctl, _("unknown codeset: '%s'"), codeset_option); + goto free_domain; + } + + for_each_variable_arg(cmd, opt) { + if (count == MAX_SEND_KEY) { + vshError(ctl, _("too many keycode")); + goto free_domain; + } + + if ((keycode = get_integer_keycode(opt->data)) > 0) + goto get_keycode; + + vshError(ctl, _("invalid keycode: '%s'"), opt->data); + goto free_domain; + +get_keycode: + keycodes[count] = keycode; + count++;
This control flow is a little odd to me - the extra label and goto seems overkill. Why not just do if ((keycode = get_integer_keycode(opt->data)) <= 0) vshError(ctl, _("invalid keycode: '%s'"), opt->data); goto free_domain; } keycodes[count] = keycode; count++;
+ } + + if (!(virDomainSendKey(dom, codeset, holdtime, keycodes, count, 0) < 0)) + ret = true;
Minor whitespace bug there.
+ +free_domain:
Normal naming convention for this label would be 'cleanup'
+ virDomainFree(dom); + return ret; +} + +/* * "setmemory" command */ static const vshCmdInfo info_setmem[] = { @@ -11095,6 +11196,7 @@ static const vshCmdDef domManagementCmds[] = { {"dumpxml", cmdDumpXML, opts_dumpxml, info_dumpxml, 0}, {"edit", cmdEdit, opts_edit, info_edit, 0}, {"inject-nmi", cmdInjectNMI, opts_inject_nmi, info_inject_nmi, 0}, + {"send-key", cmdSendKey, opts_send_key, info_send_key}, {"managedsave", cmdManagedSave, opts_managedsave, info_managedsave, 0}, {"managedsave-remove", cmdManagedSaveRemove, opts_managedsaveremove, info_managedsaveremove, 0}, diff --git a/tools/virsh.pod b/tools/virsh.pod index 7ed3003..03b1418 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -296,6 +296,10 @@ scheduling by the hypervisor.
Inject NMI to the guest
+=item B<send-key> I<domain-id> I<--codeset> B<codeset> I<--holdtime> B<holdtime> B<keycode>... + +Send keys to the guest +
It would be nice to list the values that are allowed for codeset here at least
=item B<shutdown>
The domain is in the process of shutting down, i.e. the guest operating system
Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 06/13/2011 05:01 PM, Daniel P. Berrange wrote:
On Tue, Jun 07, 2011 at 05:11:16PM +0800, Lai Jiangshan wrote:
Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- tools/virsh.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 4 ++ 2 files changed, 106 insertions(+), 0 deletions(-)
In addition to Daniel's comments:
+ + for_each_variable_arg(cmd, opt) {
This will need rebasing on top of my revised patch 2/10 where I proposed changing the signature of vshCommandOptArgv.
+ if (count == MAX_SEND_KEY) {
and to pick up the new define name
+ vshError(ctl, _("too many keycode"));
s/keycode/keycodes/
+++ b/tools/virsh.pod @@ -296,6 +296,10 @@ scheduling by the hypervisor.
Inject NMI to the guest
+=item B<send-key> I<domain-id> I<--codeset> B<codeset> I<--holdtime> B<holdtime> B<keycode>...
long line, and mark which items are optional: item B<send-key> I<domain-id> optional I<--codeset> B<codeset> optional I<--holdtime> B<holdtime> B<keycode>... [Hmm, we really ought to rewrite virsh.pod to use "[arg]" instead of "optional arg" everywhere, but that's a separate task] I'll wait for a v3 before pushing this patch. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On 06/14/2011 01:33 PM, Eric Blake wrote:
+++ b/tools/virsh.pod @@ -296,6 +296,10 @@ scheduling by the hypervisor.
Inject NMI to the guest
+=item B<send-key> I<domain-id> I<--codeset> B<codeset> I<--holdtime> B<holdtime> B<keycode>...
long line, and mark which items are optional:
item B<send-key> I<domain-id> optional I<--codeset> B<codeset> optional I<--holdtime> B<holdtime> B<keycode>...
[Hmm, we really ought to rewrite virsh.pod to use "[arg]" instead of "optional arg" everywhere, but that's a separate task]
All that advice, then I went ahead and changed virsh.pod to use [arg] everywhere in the meantime, so now send-key is the lone hold-out for using the old style. I'll be preparing a patch. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On 06/07/2011 03:11 AM, Lai Jiangshan wrote:
+ + if (STREQ(codeset_option, "default") || STREQ(codeset_option, "linux")) { + codeset = LIBVIRT_KEYCODE_LINUX; + } else if (STREQ(codeset_option, "xt")) { + codeset = LIBVIRT_KEYCODE_XT; + } else if (STREQ(codeset_option, "atset1")) { + codeset = LIBVIRT_KEYCODE_ATSET1; + } else if (STREQ(codeset_option, "atset2")) { + codeset = LIBVIRT_KEYCODE_ATSET2; + } else if (STREQ(codeset_option, "atset3")) { + codeset = LIBVIRT_KEYCODE_ATSET3;
This begs for a virEnum translation table which converts between strings and enum values via a few short macro calls, rather than open-coding the conversion. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- src/qemu/qemu_driver.c | 50 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 27 ++++++++++++++++++++++ src/qemu/qemu_monitor.h | 6 +++++ src/qemu/qemu_monitor_json.c | 15 ++++++++++++ src/qemu/qemu_monitor_json.h | 5 ++++ src/qemu/qemu_monitor_text.c | 47 +++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_text.h | 5 ++++ 7 files changed, 155 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 2957467..1678deb 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -1764,6 +1764,55 @@ cleanup: return ret; } +static int qemuDomainSendKey(virDomainPtr domain, + unsigned int codeset, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes, + unsigned int flags) +{ + struct qemud_driver *driver = domain->conn->privateData; + virDomainObjPtr vm = NULL; + int ret = -1; + qemuDomainObjPrivatePtr priv; + + virCheckFlags(0, -1); + + qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, domain->uuid); + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(domain->uuid, uuidstr); + qemuReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + if (!virDomainObjIsActive(vm)) { + qemuReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto cleanup; + } + + priv = vm->privateData; + + if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) + goto cleanup; + qemuDomainObjEnterMonitorWithDriver(driver, vm); + ret = qemuMonitorSendKey(priv->mon, codeset, holdtime, nkeycodes, keycodes); + qemuDomainObjExitMonitorWithDriver(driver, vm); + if (qemuDomainObjEndJob(vm) == 0) { + vm = NULL; + goto cleanup; + } + +cleanup: + if (vm) + virDomainObjUnlock(vm); + qemuDriverUnlock(driver); + return ret; +} + static int qemudDomainGetInfo(virDomainPtr dom, virDomainInfoPtr info) { @@ -8092,6 +8141,7 @@ static virDriver qemuDriver = { .domainMigratePerform3 = qemuDomainMigratePerform3, /* 0.9.2 */ .domainMigrateFinish3 = qemuDomainMigrateFinish3, /* 0.9.2 */ .domainMigrateConfirm3 = qemuDomainMigrateConfirm3, /* 0.9.2 */ + .domainSendKey = qemuDomainSendKey, /* 0.9.2 */ }; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 26bb814..faab819 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -38,6 +38,8 @@ #include "logging.h" #include "files.h" +#include <libvirt/virtkeys.h> + #define VIR_FROM_THIS VIR_FROM_QEMU #define DEBUG_IO 0 @@ -2357,6 +2359,31 @@ int qemuMonitorInjectNMI(qemuMonitorPtr mon) return ret; } +int qemuMonitorSendKey(qemuMonitorPtr mon, + unsigned int codeset, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes) +{ + int ret; + + VIR_DEBUG("mon=%p, codeset=%u, holdtime=%u, nkeycodes=%u", + mon, codeset, holdtime, nkeycodes); + + if (codeset != LIBVIRT_KEYCODE_XT) { + qemuReportError(VIR_ERR_NO_SUPPORT, + "qemu monitor can not support the codeset: %d", + codeset); + return -1; + } + + if (mon->json) + ret = qemuMonitorJSONSendKey(mon, holdtime, nkeycodes, keycodes); + else + ret = qemuMonitorTextSendKey(mon, holdtime, nkeycodes, keycodes); + return ret; +} + int qemuMonitorScreendump(qemuMonitorPtr mon, const char *file) { diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 910865b..64a6320 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -441,6 +441,12 @@ int qemuMonitorInjectNMI(qemuMonitorPtr mon); int qemuMonitorScreendump(qemuMonitorPtr mon, const char *file); +int qemuMonitorSendKey(qemuMonitorPtr mon, + unsigned int codeset, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes); + /** * When running two dd process and using <> redirection, we need a * shell that will not truncate files. These two strings serve that diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 75adf66..538e98f 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -2619,6 +2619,21 @@ cleanup: return ret; } +int qemuMonitorJSONSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes) +{ + /* + * FIXME: qmp sendkey has not been implemented yet, + * and qmp API of it can not be anticipated, so we use hmp temporary. + */ + if (qemuMonitorCheckHMP(mon, "sendkey")) { + return qemuMonitorTextSendKey(mon, holdtime, nkeycodes, keycodes); + } else + return -1; +} + int qemuMonitorJSONScreendump(qemuMonitorPtr mon, const char *file) { diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index ec79b03..89d7515 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -214,6 +214,11 @@ int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon, int qemuMonitorJSONInjectNMI(qemuMonitorPtr mon); +int qemuMonitorJSONSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes); + int qemuMonitorJSONScreendump(qemuMonitorPtr mon, const char *file); diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index 3b42e7a..1b301e4 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -2718,6 +2718,53 @@ fail: return -1; } +int qemuMonitorTextSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes) +{ + int i; + virBuffer buf = VIR_BUFFER_INITIALIZER; + char *cmd, *reply = NULL; + + if (nkeycodes > 16 || nkeycodes == 0) + return -1; + + virBufferAddLit(&buf, "sendkey "); + for (i = 0; i < nkeycodes; i++) { + if (keycodes[i] > 255) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("the %dth keycode is invalid: 0x%02X"), + i, keycodes[i]); + cmd = virBufferContentAndReset(&buf); + VIR_FREE(cmd); + return -1; + } + + if (i) + virBufferAddChar(&buf, '-'); + virBufferAsprintf(&buf, "0x%02X", keycodes[i]); + } + + if (holdtime) + virBufferAsprintf(&buf, " %u", holdtime); + + cmd = virBufferContentAndReset(&buf); + if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0) + goto fail; + + VIR_FREE(cmd); + VIR_FREE(reply); + return 0; + +fail: + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("failed to send key using command '%s'"), + cmd); + VIR_FREE(cmd); + return -1; +} + /* Returns -1 on error, -2 if not supported */ int qemuMonitorTextScreendump(qemuMonitorPtr mon, const char *file) { diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h index 8a69105..971de83 100644 --- a/src/qemu/qemu_monitor_text.h +++ b/src/qemu/qemu_monitor_text.h @@ -208,6 +208,11 @@ int qemuMonitorTextArbitraryCommand(qemuMonitorPtr mon, const char *cmd, int qemuMonitorTextInjectNMI(qemuMonitorPtr mon); +int qemuMonitorTextSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes); + int qemuMonitorTextScreendump(qemuMonitorPtr mon, const char *file); #endif /* QEMU_MONITOR_TEXT_H */ -- 1.7.4.4

On Tue, Jun 07, 2011 at 05:11:17PM +0800, Lai Jiangshan wrote:
Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- src/qemu/qemu_driver.c | 50 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 27 ++++++++++++++++++++++ src/qemu/qemu_monitor.h | 6 +++++ src/qemu/qemu_monitor_json.c | 15 ++++++++++++ src/qemu/qemu_monitor_json.h | 5 ++++ src/qemu/qemu_monitor_text.c | 47 +++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_text.h | 5 ++++ 7 files changed, 155 insertions(+), 0 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 2957467..1678deb 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -1764,6 +1764,55 @@ cleanup: return ret; }
+static int qemuDomainSendKey(virDomainPtr domain, + unsigned int codeset, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes, + unsigned int flags) +{ + struct qemud_driver *driver = domain->conn->privateData; + virDomainObjPtr vm = NULL; + int ret = -1; + qemuDomainObjPrivatePtr priv; + + virCheckFlags(0, -1); + + qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, domain->uuid); + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(domain->uuid, uuidstr); + qemuReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + if (!virDomainObjIsActive(vm)) { + qemuReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto cleanup; + } + + priv = vm->privateData; + + if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) + goto cleanup; + qemuDomainObjEnterMonitorWithDriver(driver, vm); + ret = qemuMonitorSendKey(priv->mon, codeset, holdtime, nkeycodes, keycodes); + qemuDomainObjExitMonitorWithDriver(driver, vm); + if (qemuDomainObjEndJob(vm) == 0) { + vm = NULL; + goto cleanup; + } + +cleanup: + if (vm) + virDomainObjUnlock(vm); + qemuDriverUnlock(driver); + return ret; +} + static int qemudDomainGetInfo(virDomainPtr dom, virDomainInfoPtr info) { @@ -8092,6 +8141,7 @@ static virDriver qemuDriver = { .domainMigratePerform3 = qemuDomainMigratePerform3, /* 0.9.2 */ .domainMigrateFinish3 = qemuDomainMigrateFinish3, /* 0.9.2 */ .domainMigrateConfirm3 = qemuDomainMigrateConfirm3, /* 0.9.2 */ + .domainSendKey = qemuDomainSendKey, /* 0.9.2 */
Needs updating to 0.9.3
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 26bb814..faab819 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -38,6 +38,8 @@ #include "logging.h" #include "files.h"
+#include <libvirt/virtkeys.h> + #define VIR_FROM_THIS VIR_FROM_QEMU
#define DEBUG_IO 0 @@ -2357,6 +2359,31 @@ int qemuMonitorInjectNMI(qemuMonitorPtr mon) return ret; }
+int qemuMonitorSendKey(qemuMonitorPtr mon, + unsigned int codeset, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes) +{ + int ret; + + VIR_DEBUG("mon=%p, codeset=%u, holdtime=%u, nkeycodes=%u", + mon, codeset, holdtime, nkeycodes); + + if (codeset != LIBVIRT_KEYCODE_XT) { + qemuReportError(VIR_ERR_NO_SUPPORT, + "qemu monitor can not support the codeset: %d", + codeset); + return -1; + } + + if (mon->json) + ret = qemuMonitorJSONSendKey(mon, holdtime, nkeycodes, keycodes); + else + ret = qemuMonitorTextSendKey(mon, holdtime, nkeycodes, keycodes); + return ret; +} + int qemuMonitorScreendump(qemuMonitorPtr mon, const char *file) { diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 910865b..64a6320 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -441,6 +441,12 @@ int qemuMonitorInjectNMI(qemuMonitorPtr mon); int qemuMonitorScreendump(qemuMonitorPtr mon, const char *file);
+int qemuMonitorSendKey(qemuMonitorPtr mon, + unsigned int codeset, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes); + /** * When running two dd process and using <> redirection, we need a * shell that will not truncate files. These two strings serve that diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 75adf66..538e98f 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -2619,6 +2619,21 @@ cleanup: return ret; }
+int qemuMonitorJSONSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes) +{ + /* + * FIXME: qmp sendkey has not been implemented yet, + * and qmp API of it can not be anticipated, so we use hmp temporary. + */ + if (qemuMonitorCheckHMP(mon, "sendkey")) { + return qemuMonitorTextSendKey(mon, holdtime, nkeycodes, keycodes); + } else + return -1; +} + int qemuMonitorJSONScreendump(qemuMonitorPtr mon, const char *file) { diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index ec79b03..89d7515 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -214,6 +214,11 @@ int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon,
int qemuMonitorJSONInjectNMI(qemuMonitorPtr mon);
+int qemuMonitorJSONSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes); + int qemuMonitorJSONScreendump(qemuMonitorPtr mon, const char *file);
diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index 3b42e7a..1b301e4 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -2718,6 +2718,53 @@ fail: return -1; }
+int qemuMonitorTextSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes) +{ + int i; + virBuffer buf = VIR_BUFFER_INITIALIZER; + char *cmd, *reply = NULL; + + if (nkeycodes > 16 || nkeycodes == 0) + return -1; + + virBufferAddLit(&buf, "sendkey "); + for (i = 0; i < nkeycodes; i++) { + if (keycodes[i] > 255) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("the %dth keycode is invalid: 0x%02X"), + i, keycodes[i]); + cmd = virBufferContentAndReset(&buf); + VIR_FREE(cmd);
You can just replace thse two lines with virBufferFreeAndReset(&buf) instead.
+ return -1; + } + + if (i) + virBufferAddChar(&buf, '-'); + virBufferAsprintf(&buf, "0x%02X", keycodes[i]); + } + + if (holdtime) + virBufferAsprintf(&buf, " %u", holdtime);
You want a check here before using 'cmd': if (virBufferError(&buf)) { virReportOOMError(); return -1; }
+ + cmd = virBufferContentAndReset(&buf); + if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0) + goto fail; + + VIR_FREE(cmd); + VIR_FREE(reply); + return 0; + +fail: + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("failed to send key using command '%s'"), + cmd); + VIR_FREE(cmd); + return -1;
There's only one place which ever jumps to 'fail', so I'd just move the code upto that place and eliminate the extra goto.
+} + /* Returns -1 on error, -2 if not supported */ int qemuMonitorTextScreendump(qemuMonitorPtr mon, const char *file) { diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h index 8a69105..971de83 100644 --- a/src/qemu/qemu_monitor_text.h +++ b/src/qemu/qemu_monitor_text.h @@ -208,6 +208,11 @@ int qemuMonitorTextArbitraryCommand(qemuMonitorPtr mon, const char *cmd,
int qemuMonitorTextInjectNMI(qemuMonitorPtr mon);
+int qemuMonitorTextSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes); + int qemuMonitorTextScreendump(qemuMonitorPtr mon, const char *file);
#endif /* QEMU_MONITOR_TEXT_H */
Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 06/13/2011 05:06 PM, Daniel P. Berrange wrote:
On Tue, Jun 07, 2011 at 05:11:17PM +0800, Lai Jiangshan wrote:
Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- src/qemu/qemu_driver.c | 50 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 27 ++++++++++++++++++++++ src/qemu/qemu_monitor.h | 6 +++++ src/qemu/qemu_monitor_json.c | 15 ++++++++++++ src/qemu/qemu_monitor_json.h | 5 ++++ src/qemu/qemu_monitor_text.c | 47 +++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_text.h | 5 ++++ 7 files changed, 155 insertions(+), 0 deletions(-)
In addition to Daniel's comments:
+ + if (!virDomainObjIsActive(vm)) { + qemuReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto cleanup; + }
This check should be moved down...
+ + priv = vm->privateData; + + if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) + goto cleanup;
...to here, since qemuDomainObjBeginJobWithDriver temporarily drops locks, and therefore vm could die before you get the lock.
+ qemuDomainObjEnterMonitorWithDriver(driver, vm); + ret = qemuMonitorSendKey(priv->mon, codeset, holdtime, nkeycodes, keycodes);
Yuck. Let's fix the qemuMonitorSendKey parameter order to match the API order of keycodes before nkeycodes.
+int qemuMonitorSendKey(qemuMonitorPtr mon, + unsigned int codeset, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes)
Fix this API...
+{ + int ret; + + VIR_DEBUG("mon=%p, codeset=%u, holdtime=%u, nkeycodes=%u", + mon, codeset, holdtime, nkeycodes); + + if (codeset != LIBVIRT_KEYCODE_XT) { + qemuReportError(VIR_ERR_NO_SUPPORT, + "qemu monitor can not support the codeset: %d", + codeset); + return -1; + }
Hmm, so your proposed virsh command defaults to --codeset linux, but right now you only support --codeset xt. That's kind of mean to the users. It would be nice to get the codeset translations going at the virsh level.
+ + if (mon->json) + ret = qemuMonitorJSONSendKey(mon, holdtime, nkeycodes, keycodes); + else + ret = qemuMonitorTextSendKey(mon, holdtime, nkeycodes, keycodes);
...and these callbacks, to do array before length.
+++ b/src/qemu/qemu_monitor_text.c @@ -2718,6 +2718,53 @@ fail: return -1; }
+int qemuMonitorTextSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int nkeycodes, + unsigned int *keycodes) +{ + int i; + virBuffer buf = VIR_BUFFER_INITIALIZER; + char *cmd, *reply = NULL; + + if (nkeycodes > 16 || nkeycodes == 0)
Magic number. Use VIR_DOMAIN_SEND_KEY_MAX_KEYS from libvirt.h. Or don't even bother to check - we already guaranteed that libvirt.c filtered out invalid calls, so by the time you get here, nkeycodes has already been validated to be in range.
+ return -1; + + virBufferAddLit(&buf, "sendkey "); + for (i = 0; i < nkeycodes; i++) { + if (keycodes[i] > 255) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("the %dth keycode is invalid: 0x%02X"), + i, keycodes[i]);
Printing %02X with a value that is > 255 will use more than 2 digits, at which point, you could have just used %X instead of %02X. Also, "1th" doesn't read well in English, let alone translate well to other languages. The error message should probably be: _("keycode %d is invalid: 0x%X") -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

CC: Daniel Hey guys, any comments about this revision? Gui Lai Jiangshan wrote:
Add API virDomainSendKey() and virsh send-key command.
PATCH 01~04 prepare PATCH 05~10 Add support for send keys to guest
Python version of virDomainSendKey() has not been implemented yet, it will be done soon.
Some usage-improvment patches will be sent later(after these 10 are applied) these usage-improvment patches does not touch any APIs nor change the behaviors: support KEY_XXX names for the linux keycode for virsh command(auto detect), translate keycodes between different codesets, ...etc.
Lai Jiangshan (10): allow name for VSH_OT_ARGV options improve the iteration of VSH_OT_ARGV options add VSH_OFLAG_REQ_OPT options remote generator: Handle (unsigned) int arrays send-key: Defining the public API send-key: Defining the internal API send-key: Implementing the public API send-key: Implementing the remote protocol send-key: Expose the new API in virsh qemu:send-key: Implement the driver methods
daemon/remote_generator.pl | 17 ++++ include/libvirt/libvirt.h.in | 7 ++ include/libvirt/virtkeys.h | 22 +++++ python/generator.py | 1 + src/driver.h | 8 ++ src/libvirt.c | 63 +++++++++++++++ src/libvirt_public.syms | 5 + src/qemu/qemu_driver.c | 50 ++++++++++++ src/qemu/qemu_monitor.c | 27 +++++++ src/qemu/qemu_monitor.h | 6 ++ src/qemu/qemu_monitor_json.c | 15 ++++ src/qemu/qemu_monitor_json.h | 5 + src/qemu/qemu_monitor_text.c | 47 +++++++++++ src/qemu/qemu_monitor_text.h | 5 + src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 16 ++++- src/remote_protocol-structs | 11 +++ tools/virsh.c | 175 +++++++++++++++++++++++++++++++++++------- tools/virsh.pod | 4 + 19 files changed, 455 insertions(+), 30 deletions(-) create mode 100644 include/libvirt/virtkeys.h

Add virtkey lib for usage-improvment and keycode translating. Expose send-key in virsh Implement send-key function for the qemu driver Lai Jiangshan (3): lib: add virtkey send-key: Expose the new API in virsh qemu:send-key: Implement the driver methods include/libvirt/libvirt.h.in | 6 + src/Makefile.am | 3 +- src/libvirt_private.syms | 5 + src/qemu/qemu_driver.c | 51 ++++ src/qemu/qemu_monitor.c | 37 +++ src/qemu/qemu_monitor.h | 6 + src/qemu/qemu_monitor_json.c | 15 + src/qemu/qemu_monitor_json.h | 5 + src/qemu/qemu_monitor_text.c | 49 ++++ src/qemu/qemu_monitor_text.h | 5 + src/util/virtkey.c | 633 ++++++++++++++++++++++++++++++++++++++++++ src/util/virtkey.h | 21 ++ tools/virsh.c | 94 +++++++ tools/virsh.pod | 4 + 14 files changed, 933 insertions(+), 1 deletions(-) create mode 100644 src/util/virtkey.c create mode 100644 src/util/virtkey.h -- 1.7.4.4

Add virtkey lib for usage-improvment and keycode translating. Add 4 internal API for the aim const char *virKeycodeSetName(virKeycodeSet codeset); virKeycodeSet virParseKeycodeSet(const char *name); int virParseKeyName(virKeycodeSet codeset, const char *keyname); int virTranslateKeyCode(virKeycodeSet from_codeset, virKeycodeSet to_offset, int key_value); Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- include/libvirt/libvirt.h.in | 6 + src/Makefile.am | 3 +- src/libvirt_private.syms | 5 + src/util/virtkey.c | 633 ++++++++++++++++++++++++++++++++++++++++++ src/util/virtkey.h | 21 ++ tools/virsh.c | 1 + 6 files changed, 668 insertions(+), 1 deletions(-) create mode 100644 src/util/virtkey.c create mode 100644 src/util/virtkey.h diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 3f634e6..2f2efe7 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1815,6 +1815,12 @@ typedef enum { VIR_KEYCODE_SET_ATSET1 = 2, VIR_KEYCODE_SET_ATSET2 = 3, VIR_KEYCODE_SET_ATSET3 = 4, + VIR_KEYCODE_SET_OSX = 5, + VIR_KEYCODE_SET_XT_KBD = 6, + VIR_KEYCODE_SET_USB = 7, + VIR_KEYCODE_SET_WIN32 = 8, + VIR_KEYCODE_SET_XWIN_XT = 9, + VIR_KEYCODE_SET_XFREE86_KBD_XT = 10, } virKeycodeSet; /** diff --git a/src/Makefile.am b/src/Makefile.am index 4f9bfc9..e34ea74 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -81,7 +81,8 @@ UTIL_SOURCES = \ util/util.c util/util.h \ util/xml.c util/xml.h \ util/virtaudit.c util/virtaudit.h \ - util/virterror.c util/virterror_internal.h + util/virterror.c util/virterror_internal.hi \ + util/virtkey.c util/virtkey.h EXTRA_DIST += util/threads-pthread.c util/threads-win32.c diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 09b0159..151e256 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1059,6 +1059,11 @@ virSetError; virSetErrorLogPriorityFunc; virStrerror; +# virtkey.h +virKeycodeSetName; +virParseKeycodeSet; +virParseKeyName; +virTranslateKeyCode; # xml.h virXMLParseFileHelper; diff --git a/src/util/virtkey.c b/src/util/virtkey.c new file mode 100644 index 0000000..48fbfcc --- /dev/null +++ b/src/util/virtkey.c @@ -0,0 +1,633 @@ + +/* + * Copyright (c) 2011 Lai Jiangshan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <config.h> +#include <string.h> +#include <stddef.h> +#include <libvirt/libvirt.h> +#include "virtkey.h" + +#define ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0])) +#define getfield(object, field_type, field_offset) \ + (*(typeof(field_type) *)((char *)(object) + field_offset)) + +struct keycode { + const char *linux_name; + const char *os_x_name; + const char *win32_name; + unsigned short linux_keycode; + unsigned short xt; + unsigned short atset1; + unsigned short atset2; + unsigned short atset3; + unsigned short os_x; + unsigned short xt_kbd; + unsigned short usb; + unsigned short win32; + unsigned short xwin_xt; + unsigned short xfree86_kbd_xt; +}; + +/* + * generated from http://git.gnome.org/browse/gtk-vnc/plain/src/keymaps.csv + * script: + * + * #!/bin/python + * import sys + * import re + * + * for line in sys.stdin.xreadlines(): + * a = re.match("([^,]*)," * 13 + "([^,]*)$", line[0:-1]).groups() + * b = "" + * for i in (0,2,10,1,7,4,5,6,3,8,9,11,12,13): + * if i in (0, 2, 10): + * b = b + (a[i] and ('"' + a[i] + '"') or 'NULL') + ',' + * else: + * b = b + (a[i] or '0') + ',' + * print " { " + b + "}," + */ +static struct keycode keycodes[] = { + { "KEY_RESERVED",NULL,NULL,0,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_ESC","Escape","VK_ESCAPE",1,1,1,118,8,0x35,1,41,0x1b,1,1,}, + { "KEY_1","ANSI_1","VK_1",2,2,2,22,22,0x12,2,30,0x31,2,2,}, + { "KEY_2","ANSI_2","VK_2",3,3,3,30,30,0x13,3,31,0x32,3,3,}, + { "KEY_3","ANSI_3","VK_3",4,4,4,38,38,0x14,4,32,0x33,4,4,}, + { "KEY_4","ANSI_4","VK_4",5,5,5,37,37,0x15,5,33,0x34,5,5,}, + { "KEY_5","ANSI_5","VK_5",6,6,6,46,46,0x17,6,34,0x35,6,6,}, + { "KEY_6","ANSI_6","VK_6",7,7,7,54,54,0x16,7,35,0x36,7,7,}, + { "KEY_7","ANSI_7","VK_7",8,8,8,61,61,0x1a,8,36,0x37,8,8,}, + { "KEY_8","ANSI_8","VK_8",9,9,9,62,62,0x1c,9,37,0x38,9,9,}, + { "KEY_9","ANSI_9","VK_9",10,10,10,70,70,0x19,10,38,0x39,10,10,}, + { "KEY_0","ANSI_0","VK_0",11,11,11,69,69,0x1d,11,39,0x30,11,11,}, + { "KEY_MINUS","ANSI_Minus","VK_OEM_MINUS",12,12,12,78,78,0x1b,12,45,0xbd,12,12,}, + { "KEY_EQUAL","ANSI_Equal","VK_OEM_PLUS",13,13,13,85,85,0x18,13,46,0xbb,13,13,}, + { "KEY_BACKSPACE","Delete","VK_BACK",14,14,14,102,102,0x33,14,42,0x08,14,14,}, + { "KEY_TAB","Tab","VK_TAB",15,15,15,13,13,0x30,15,43,0x09,15,15,}, + { "KEY_Q","ANSI_Q","VK_Q",16,16,16,21,21,0xc,16,20,0x51,16,16,}, + { "KEY_W","ANSI_W","VK_W",17,17,17,29,29,0xd,17,26,0x57,17,17,}, + { "KEY_E","ANSI_E","VK_E",18,18,18,36,36,0xe,18,8,0x45,18,18,}, + { "KEY_R","ANSI_R","VK_R",19,19,19,45,45,0xf,19,21,0x52,19,19,}, + { "KEY_T","ANSI_T","VK_T",20,20,20,44,44,0x11,20,23,0x54,20,20,}, + { "KEY_Y","ANSI_Y","VK_Y",21,21,21,53,53,0x10,21,28,0x59,21,21,}, + { "KEY_U","ANSI_U","VK_U",22,22,22,60,60,0x20,22,24,0x55,22,22,}, + { "KEY_I","ANSI_I","VK_I",23,23,23,67,67,0x22,23,12,0x49,23,23,}, + { "KEY_O","ANSI_O","VK_O",24,24,24,68,68,0x1f,24,18,0x4f,24,24,}, + { "KEY_P","ANSI_P","VK_P",25,25,25,77,77,0x23,25,19,0x50,25,25,}, + { "KEY_LEFTBRACE","ANSI_LeftBracket","VK_OEM_4",26,26,26,84,84,0x21,26,47,0xdb,26,26,}, + { "KEY_RIGHTBRACE","ANSI_RightBracket","VK_OEM_6",27,27,27,91,91,0x1e,27,48,0xdd,27,27,}, + { "KEY_ENTER","Return","VK_RETURN",28,28,28,90,90,0x24,28,40,0x0d,28,28,}, + { "KEY_LEFTCTRL","Control","VK_LCONTROL",29,29,29,20,17,0x3b,29,224,0xa2,29,29,}, + { "KEY_A","ANSI_A","VK_A",30,30,30,28,28,0x0,30,4,0x41,30,30,}, + { "KEY_S","ANSI_S","VK_S",31,31,31,27,27,0x1,31,22,0x53,31,31,}, + { "KEY_D","ANSI_D","VK_D",32,32,32,35,35,0x2,32,7,0x44,32,32,}, + { "KEY_F","ANSI_F","VK_F",33,33,33,43,43,0x3,33,9,0x46,33,33,}, + { "KEY_G","ANSI_G","VK_G",34,34,34,52,52,0x5,34,10,0x47,34,34,}, + { "KEY_H","ANSI_H","VK_H",35,35,35,51,51,0x4,35,11,0x48,35,35,}, + { "KEY_J","ANSI_J","VK_J",36,36,36,59,59,0x26,36,13,0x4a,36,36,}, + { "KEY_K","ANSI_K","VK_K",37,37,37,66,66,0x28,37,14,0x4b,37,37,}, + { "KEY_L","ANSI_L","VK_L",38,38,38,75,75,0x25,38,15,0x4c,38,38,}, + { "KEY_SEMICOLON","ANSI_Semicolon","VK_OEM_1",39,39,39,76,76,0x29,39,51,0xba,39,39,}, + { "KEY_APOSTROPHE","ANSI_Quote","VK_OEM_2",40,40,40,82,82,0x27,40,52,0xbf,40,40,}, + { "KEY_GRAVE","ANSI_Grave","VK_OEM_3",41,41,41,14,14,0x32,41,53,0xc0,41,41,}, + { "KEY_LEFTSHIFT","Shift","VK_LSHIFT",42,42,42,18,18,0x38,42,225,0xa0,42,42,}, + { "KEY_BACKSLASH","ANSI_Backslash","VK_OEM_5",43,43,43,93,93,0x2a,43,50,0xdc,43,43,}, + { "KEY_Z","ANSI_Z","VK_Z",44,44,44,26,26,0x6,44,29,0x5a,44,44,}, + { "KEY_X","ANSI_X","VK_X",45,45,45,34,34,0x7,45,27,0x58,45,45,}, + { "KEY_C","ANSI_C","VK_C",46,46,46,33,33,0x8,46,6,0x43,46,46,}, + { "KEY_V","ANSI_V","VK_V",47,47,47,42,42,0x9,47,25,0x56,47,47,}, + { "KEY_B","ANSI_B","VK_B",48,48,48,50,50,0xb,48,5,0x42,48,48,}, + { "KEY_N","ANSI_N","VK_N",49,49,49,49,49,0x2d,49,17,0x4e,49,49,}, + { "KEY_M","ANSI_M","VK_M",50,50,50,58,58,0x2e,50,16,0x4d,50,50,}, + { "KEY_COMMA","ANSI_Comma","VK_OEM_COMMA",51,51,51,65,65,0x2b,51,54,0xbc,51,51,}, + { "KEY_DOT","ANSI_Period","VK_OEM_PERIOD",52,52,52,73,73,0x2f,52,55,0xbe,52,52,}, + { "KEY_SLASH","ANSI_Slash","VK_OEM_2",53,53,53,74,74,0x2c,53,56,0xbf,53,53,}, + { "KEY_RIGHTSHIFT","RightShift","VK_RSHIFT",54,54,54,89,89,0x3c,54,229,0xa1,54,54,}, + { "KEY_KPASTERISK","ANSI_KeypadMultiply","VK_MULTIPLY",55,55,55,124,126,0x43,55,85,0x6a,55,55,}, + { "KEY_LEFTALT","Option","VK_LMENU",56,56,56,17,25,0x3a,56,226,0xa4,56,56,}, + { "KEY_SPACE","Space","VK_SPACE",57,57,57,41,41,0x31,57,44,0x20,57,57,}, + { "KEY_CAPSLOCK","CapsLock","VK_CAPITAL",58,58,58,88,20,0x39,58,57,0x14,58,58,}, + { "KEY_F1","F1","VK_F1",59,59,59,5,7,0x7a,59,58,0x70,59,59,}, + { "KEY_F2","F2","VK_F2",60,60,60,6,15,0x78,60,59,0x71,60,60,}, + { "KEY_F3","F3","VK_F3",61,61,61,4,23,0x63,61,60,0x72,61,61,}, + { "KEY_F4","F4","VK_F4",62,62,62,12,31,0x76,62,61,0x73,62,62,}, + { "KEY_F5","F5","VK_F5",63,63,63,3,39,0x60,63,62,0x74,63,63,}, + { "KEY_F6","F6","VK_F6",64,64,64,11,47,0x61,64,63,0x75,64,64,}, + { "KEY_F7","F7","VK_F7",65,65,65,259,55,0x62,65,64,0x76,65,65,}, + { "KEY_F8","F8","VK_F8",66,66,66,10,63,0x64,66,65,0x77,66,66,}, + { "KEY_F9","F9","VK_F9",67,67,67,1,71,0x65,67,66,0x78,67,67,}, + { "KEY_F10","F10","VK_F10",68,68,68,9,79,0x6d,68,67,0x79,68,68,}, + { "KEY_NUMLOCK",NULL,"VK_NUMLOCK",69,69,69,119,118,0,69,83,0x90,69,69,}, + { "KEY_SCROLLLOCK",NULL,"VK_SCROLL",70,70,70,126,95,0,70,71,0x91,70,70,}, + { "KEY_KP7","ANSI_Keypad7","VK_NUMPAD7",71,71,71,108,108,0x59,71,95,0x67,71,71,}, + { "KEY_KP8","ANSI_Keypad8","VK_NUMPAD8",72,72,72,117,117,0x5b,72,96,0x68,72,72,}, + { "KEY_KP9","ANSI_Keypad9","VK_NUMPAD9",73,73,73,125,125,0x5c,73,97,0x69,73,73,}, + { "KEY_KPMINUS","ANSI_KeypadMinus","VK_SUBTRACT",74,74,74,123,132,0x4e,74,86,0x6d,74,74,}, + { "KEY_KP4","ANSI_Keypad4","VK_NUMPAD4",75,75,75,107,107,0x56,75,92,0x64,75,75,}, + { "KEY_KP5","ANSI_Keypad5","VK_NUMPAD5",76,76,76,115,115,0x57,76,93,0x65,76,76,}, + { "KEY_KP6","ANSI_Keypad6","VK_NUMPAD6",77,77,77,116,116,0x58,77,94,0x66,77,77,}, + { "KEY_KPPLUS","ANSI_KeypadPlus","VK_ADD",78,78,78,121,124,0x45,78,87,0x6b,78,78,}, + { "KEY_KP1","ANSI_Keypad1","VK_NUMPAD1",79,79,79,105,105,0x53,79,89,0x61,79,79,}, + { "KEY_KP2","ANSI_Keypad2","VK_NUMPAD2",80,80,80,114,114,0x54,80,90,0x62,80,80,}, + { "KEY_KP3","ANSI_Keypad3","VK_NUMPAD3",81,81,81,122,122,0x55,81,91,0x63,81,81,}, + { "KEY_KP0","ANSI_Keypad0","VK_NUMPAD0",82,82,82,112,112,0x52,82,98,0x60,82,82,}, + { "KEY_KPDOT","ANSI_KeypadDecimal","VK_DECIMAL",83,83,83,113,113,0x41,83,99,0x6e,83,83,}, + { NULL,NULL,NULL,84,0,0,0,0,0,84,0,0,0,0,}, + { "KEY_ZENKAKUHANKAKU",NULL,NULL,85,0,118,95,0,0,118,148,0,0,0,}, + { "KEY_102ND",NULL,"VK_OEM_102",86,0,86,97,19,0,86,100,0xe1,0,0,}, + { "KEY_F11","F11","VK_F11",87,101,87,120,86,0x67,87,68,0x7a,0,0,}, + { "KEY_F12","F12","VK_F12",88,102,88,7,94,0x6f,88,69,0x7b,0,0,}, + { "KEY_RO",NULL,NULL,89,0,115,81,0,0,115,135,0,0,0,}, + { "KEY_KATAKANA","JIS_Kana????","VK_KANA",90,0,120,99,0,0x68,120,146,0x15,0,0,}, + { "KEY_HIRAGANA",NULL,NULL,91,0,119,98,0,0,119,147,0,0,0,}, + { "KEY_HENKAN",NULL,NULL,92,0,121,100,134,0,121,138,0,0,0,}, + { "KEY_KATAKANAHIRAGANA",NULL,NULL,93,0,112,19,135,0,112,136,0,0xc8,0xc8,}, + { "KEY_MUHENKAN",NULL,NULL,94,0,123,103,133,0,123,139,0,0,0,}, + { "KEY_KPJPCOMMA","JIS_KeypadComma",NULL,95,0,92,39,0,0x5f,92,140,0,0,0,}, + { "KEY_KPENTER","ANSI_KeypadEnter",NULL,96,0,0,158,121,0x4c,284,88,0,0x64,0x64,}, + { "KEY_RIGHTCTRL","RightControl","VK_RCONTROL",97,0,0,0,88,0x3e,285,228,0xa3,0x65,0x65,}, + { "KEY_KPSLASH","ANSI_KeypadDivide","VK_DIVIDE",98,0,0,181,119,0x4b,309,84,0x6f,0x68,0x68,}, + { "KEY_SYSRQ",NULL,"VK_SNAPSHOT ???",99,0,84,260,87,0,84,70,0x2c,0x67,0x67,}, + { "KEY_RIGHTALT","RightOption","VK_RMENU",100,0,0,0,57,0x3d,312,230,0xa5,0x69,0x69,}, + { "KEY_LINEFEED",NULL,NULL,101,0,0,0,0,0,91,0,0,0,0,}, + { "KEY_HOME","Home","VK_HOME",102,0,0,224,110,0x73,327,74,0x24,0x59,0x59,}, + { "KEY_UP","UpArrow","VK_UP",103,109,0,236,99,0x7e,328,82,0x26,0x5a,0x5a,}, + { "KEY_PAGEUP","PageUp","VK_PRIOR",104,0,0,201,111,0x74,329,75,0x21,0x5b,0x5b,}, + { "KEY_LEFT","LeftArrow","VK_LEFT",105,111,0,203,97,0x7b,331,80,0x25,0x5c,0x5c,}, + { "KEY_RIGHT","RightArrow","VK_RIGHT",106,112,0,205,106,0x7c,333,79,0x27,0x5e,0x5e,}, + { "KEY_END","End","VK_END",107,0,0,225,101,0x77,335,77,0x23,0x5f,0x5f,}, + { "KEY_DOWN","DownArrow","VK_DOWN",108,110,0,254,96,0x7d,336,81,0x28,0x60,0x60,}, + { "KEY_PAGEDOWN","PageDown","VK_NEXT",109,0,0,243,109,0x79,337,78,0x22,0x61,0x61,}, + { "KEY_INSERT",NULL,"VK_INSERT",110,107,0,210,103,0,338,73,0x2d,0x62,0x62,}, + { "KEY_DELETE","ForwardDelete","VK_DELETE",111,108,0,244,100,0x75,339,76,0x2e,0x63,0x63,}, + { "KEY_MACRO",NULL,NULL,112,0,0,239,142,0,367,0,0,0,0,}, + { "KEY_MUTE","Mute","VK_VOLUME_MUTE",113,0,0,251,156,0x4a,288,239,0xad,0,0,}, + { "KEY_VOLUMEDOWN","VolumeDown","VK_VOLUME_DOWN",114,0,0,0,157,0x49,302,238,0xae,0,0,}, + { "KEY_VOLUMEUP","VolumeUp","VK_VOLUME_UP",115,0,0,233,149,0x48,304,237,0xaf,0,0,}, + { "KEY_POWER",NULL,NULL,116,0,0,0,0,0,350,102,0,0,0,}, + { "KEY_KPEQUAL","ANSI_KeypadEquals",NULL,117,0,89,15,0,0x51,89,103,0,0x76,0x76,}, + { "KEY_KPPLUSMINUS",NULL,NULL,118,0,0,206,0,0,334,0,0,0,0,}, + { "KEY_PAUSE",NULL,"VK_PAUSE",119,0,0,198,98,0,326,72,0x013,0x66,0x66,}, + { "KEY_SCALE",NULL,NULL,120,0,0,0,0,0,267,0,0,0,0,}, + { "KEY_KPCOMMA","ANSI_KeypadClear????","VK_SEPARATOR??",121,0,126,109,0,0x47,126,133,0x6c,0,0,}, + { "KEY_HANGEUL",NULL,"VK_HANGEUL",122,0,0,0,0,0,0,144,0x15,0,0,}, + { "KEY_HANJA",NULL,"VK_HANJA",123,0,0,0,0,0,269,145,0x19,0,0,}, + { "KEY_YEN","JIS_Yen",NULL,124,0,125,106,0,0x5d,125,137,0,0x7d,0x7d,}, + { "KEY_LEFTMETA","Command","VK_LWIN",125,0,0,0,139,0x37,347,227,0x5b,0x6b,0x6b,}, + { "KEY_RIGHTMETA",NULL,"VK_RWIN",126,0,0,0,140,0,348,231,0x5c,0x6c,0x6c,}, + { "KEY_COMPOSE","Function","VK_APPS",127,0,0,0,141,0x3f,349,101,0x5d,0x6d,0x6d,}, + { "KEY_STOP",NULL,"VK_BROWSER_STOP",128,0,0,0,10,0,360,243,0xa9,0,0,}, + { "KEY_AGAIN",NULL,NULL,129,0,0,0,11,0,261,121,0,0,0,}, + { "KEY_PROPS",NULL,NULL,130,0,0,0,12,0,262,118,0,0,0,}, + { "KEY_UNDO",NULL,NULL,131,0,0,0,16,0,263,122,0,0,0,}, + { "KEY_FRONT",NULL,NULL,132,0,0,0,0,0,268,119,0,0,0,}, + { "KEY_COPY",NULL,NULL,133,0,0,0,24,0,376,124,0,0,0,}, + { "KEY_OPEN",NULL,NULL,134,0,0,0,32,0,100,116,0,0,0,}, + { "KEY_PASTE",NULL,NULL,135,0,0,0,40,0,101,125,0,0,0,}, + { "KEY_FIND",NULL,NULL,136,0,0,0,48,0,321,244,0,0,0,}, + { "KEY_CUT",NULL,NULL,137,0,0,0,56,0,316,123,0,0,0,}, + { "KEY_HELP",NULL,"VK_HELP",138,0,0,0,9,0,373,117,0x2f,0,0,}, + { "KEY_MENU",NULL,NULL,139,0,0,0,145,0,286,0,0,0,0,}, + { "KEY_CALC",NULL,NULL,140,0,0,174,163,0,289,251,0,0,0,}, + { "KEY_SETUP",NULL,NULL,141,0,0,0,0,0,102,0,0,0,0,}, + { "KEY_SLEEP",NULL,"VK_SLEEP",142,0,0,0,0,0,351,248,0x5f,0,0,}, + { "KEY_WAKEUP",NULL,NULL,143,0,0,0,0,0,355,0,0,0,0,}, + { "KEY_FILE",NULL,NULL,144,0,0,0,0,0,103,0,0,0,0,}, + { "KEY_SENDFILE",NULL,NULL,145,0,0,0,0,0,104,0,0,0,0,}, + { "KEY_DELETEFILE",NULL,NULL,146,0,0,0,0,0,105,0,0,0,0,}, + { "KEY_XFER",NULL,NULL,147,0,0,0,162,0,275,0,0,0,0,}, + { "KEY_PROG1",NULL,NULL,148,0,0,0,160,0,287,0,0,0,0,}, + { "KEY_PROG2",NULL,NULL,149,0,0,0,161,0,279,0,0,0,0,}, + { "KEY_WWW",NULL,NULL,150,0,0,0,0,0,258,240,0,0,0,}, + { "KEY_MSDOS",NULL,NULL,151,0,0,0,0,0,106,0,0,0,0,}, + { "KEY_SCREENLOCK",NULL,NULL,152,0,0,0,150,0,274,249,0,0,0,}, + { "KEY_DIRECTION",NULL,NULL,153,0,0,0,0,0,107,0,0,0,0,}, + { "KEY_CYCLEWINDOWS",NULL,NULL,154,0,0,0,155,0,294,0,0,0,0,}, + { "KEY_MAIL",NULL,NULL,155,0,0,0,0,0,364,0,0,0,0,}, + { "KEY_BOOKMARKS",NULL,NULL,156,0,0,0,0,0,358,0,0,0,0,}, + { "KEY_COMPUTER",NULL,NULL,157,0,0,0,0,0,363,0,0,0,0,}, + { "KEY_BACK",NULL,"VK_BROWSER_BACK",158,0,0,0,0,0,362,241,0xa6,0,0,}, + { "KEY_FORWARD",NULL,"VK_BROWSER_FORWARD",159,0,0,0,0,0,361,242,0xa7,0,0,}, + { "KEY_CLOSECD",NULL,NULL,160,0,0,0,154,0,291,0,0,0,0,}, + { "KEY_EJECTCD",NULL,NULL,161,0,0,0,0,0,108,236,0,0,0,}, + { "KEY_EJECTCLOSECD",NULL,NULL,162,0,0,0,0,0,381,0,0,0,0,}, + { "KEY_NEXTSONG",NULL,"VK_MEDIA_NEXT_TRACK",163,0,0,241,147,0,281,235,0xb0,0,0,}, + { "KEY_PLAYPAUSE",NULL,"VK_MEDIA_PLAY_PAUSE",164,0,0,173,0,0,290,232,0xb3,0,0,}, + { "KEY_PREVIOUSSONG",NULL,"VK_MEDIA_PREV_TRACK",165,0,0,250,148,0,272,234,0xb1,0,0,}, + { "KEY_STOPCD",NULL,"VK_MEDIA_STOP",166,0,0,164,152,0,292,233,0xb2,0,0,}, + { "KEY_RECORD",NULL,NULL,167,0,0,0,158,0,305,0,0,0,0,}, + { "KEY_REWIND",NULL,NULL,168,0,0,0,159,0,280,0,0,0,0,}, + { "KEY_PHONE",NULL,NULL,169,0,0,0,0,0,99,0,0,0,0,}, + { "KEY_ISO","ISO_Section",NULL,170,0,0,0,0,0xa,112,0,0,0,0,}, + { "KEY_CONFIG",NULL,NULL,171,0,0,0,0,0,257,0,0,0,0,}, + { "KEY_HOMEPAGE",NULL,"VK_BROWSER_HOME",172,0,0,178,151,0,306,0,0xac,0,0,}, + { "KEY_REFRESH",NULL,"VK_BROWSER_REFRESH",173,0,0,0,0,0,359,250,0xa8,0,0,}, + { "KEY_EXIT",NULL,NULL,174,0,0,0,0,0,113,0,0,0,0,}, + { "KEY_MOVE",NULL,NULL,175,0,0,0,0,0,114,0,0,0,0,}, + { "KEY_EDIT",NULL,NULL,176,0,0,0,0,0,264,247,0,0,0,}, + { "KEY_SCROLLUP",NULL,NULL,177,0,0,0,0,0,117,245,0,0,0,}, + { "KEY_SCROLLDOWN",NULL,NULL,178,0,0,0,0,0,271,246,0,0,0,}, + { "KEY_KPLEFTPAREN",NULL,NULL,179,0,0,0,0,0,374,182,0,0,0,}, + { "KEY_KPRIGHTPAREN",NULL,NULL,180,0,0,0,0,0,379,183,0,0,0,}, + { "KEY_NEW",NULL,NULL,181,0,0,0,0,0,265,0,0,0,0,}, + { "KEY_REDO",NULL,NULL,182,0,0,0,0,0,266,0,0,0,0,}, + { "KEY_F13","F13","VK_F13",183,0,93,47,127,0x69,93,104,0x7c,0x6e,0x6e,}, + { "KEY_F14","F14","VK_F14",184,0,94,55,128,0x6b,94,105,0x7d,0x6f,0x6f,}, + { "KEY_F15","F15","VK_F15",185,0,95,63,129,0x71,95,106,0x7e,0x70,0x70,}, + { "KEY_F16","F16","VK_F16",186,0,0,0,130,0x6a,85,107,0x7f,0x71,0x71,}, + { "KEY_F17","F17","VK_F17",187,0,0,0,131,0x40,259,108,0x80,0x72,0x72,}, + { "KEY_F18","F18","VK_F18",188,0,0,0,0,0x4f,375,109,0x81,0,0,}, + { "KEY_F19","F19","VK_F19",189,0,0,0,0,0x50,260,110,0x82,0,0,}, + { "KEY_F20","F20","VK_F20",190,0,0,0,0,0x5a,90,111,0x83,0,0,}, + { "KEY_F21",NULL,"VK_F21",191,0,0,0,0,0,116,112,0x84,0,0,}, + { "KEY_F22",NULL,"VK_F22",192,0,0,0,0,0,377,113,0x85,0,0,}, + { "KEY_F23",NULL,"VK_F23",193,0,0,0,0,0,109,114,0x86,0,0,}, + { "KEY_F24",NULL,"VK_F24",194,0,0,0,0,0,111,115,0x87,0,0,}, + { NULL,NULL,NULL,195,0,0,0,0,0,277,0,0,0,0,}, + { NULL,NULL,NULL,196,0,0,0,0,0,278,0,0,0,0,}, + { NULL,NULL,NULL,197,0,0,0,0,0,282,0,0,0,0,}, + { NULL,NULL,NULL,198,0,0,0,0,0,283,0,0,0,0,}, + { NULL,NULL,NULL,199,0,0,0,0,0,295,0,0,0,0,}, + { "KEY_PLAYCD",NULL,NULL,200,0,0,0,0,0,296,0,0,0,0,}, + { "KEY_PAUSECD",NULL,NULL,201,0,0,0,0,0,297,0,0,0,0,}, + { "KEY_PROG3",NULL,NULL,202,0,0,0,0,0,299,0,0,0,0,}, + { "KEY_PROG4",NULL,NULL,203,0,0,0,0,0,300,0,0,0,0,}, + { "KEY_DASHBOARD",NULL,NULL,204,0,0,0,0,0,301,0,0,0,0,}, + { "KEY_SUSPEND",NULL,NULL,205,0,0,0,0,0,293,0,0,0,0,}, + { "KEY_CLOSE",NULL,NULL,206,0,0,0,0,0,303,0,0,0,0,}, + { "KEY_PLAY",NULL,"VK_PLAY",207,0,0,0,0,0,307,0,0xfa,0,0,}, + { "KEY_FASTFORWARD",NULL,NULL,208,0,0,0,0,0,308,0,0,0,0,}, + { "KEY_BASSBOOST",NULL,NULL,209,0,0,0,0,0,310,0,0,0,0,}, + { "KEY_PRINT",NULL,"VK_PRINT",210,0,0,0,0,0,313,0,0x2a,0,0,}, + { "KEY_HP",NULL,NULL,211,0,0,0,0,0,314,0,0,0,0,}, + { "KEY_CAMERA",NULL,NULL,212,0,0,0,0,0,315,0,0,0,0,}, + { "KEY_SOUND",NULL,NULL,213,0,0,0,0,0,317,0,0,0,0,}, + { "KEY_QUESTION",NULL,NULL,214,0,0,0,0,0,318,0,0,0,0,}, + { "KEY_EMAIL",NULL,"VK_LAUNCH_MAIL",215,0,0,0,0,0,319,0,0xb4,0,0,}, + { "KEY_CHAT",NULL,NULL,216,0,0,0,0,0,320,0,0,0,0,}, + { "KEY_SEARCH",NULL,"VK_BROWSER_SEARCH",217,0,0,0,0,0,357,0,0xaa,0,0,}, + { "KEY_CONNECT",NULL,NULL,218,0,0,0,0,0,322,0,0,0,0,}, + { "KEY_FINANCE",NULL,NULL,219,0,0,0,0,0,323,0,0,0,0,}, + { "KEY_SPORT",NULL,NULL,220,0,0,0,0,0,324,0,0,0,0,}, + { "KEY_SHOP",NULL,NULL,221,0,0,0,0,0,325,0,0,0,0,}, + { "KEY_ALTERASE",NULL,NULL,222,0,0,0,0,0,276,0,0,0,0,}, + { "KEY_CANCEL",NULL,NULL,223,0,0,0,0,0,330,0,0,0,0,}, + { "KEY_BRIGHTNESSDOWN",NULL,NULL,224,0,0,0,0,0,332,0,0,0,0,}, + { "KEY_BRIGHTNESSUP",NULL,NULL,225,0,0,0,0,0,340,0,0,0,0,}, + { "KEY_MEDIA",NULL,NULL,226,0,0,0,0,0,365,0,0,0,0,}, + { "KEY_SWITCHVIDEOMODE",NULL,NULL,227,0,0,0,0,0,342,0,0,0,0,}, + { "KEY_KBDILLUMTOGGLE",NULL,NULL,228,0,0,0,0,0,343,0,0,0,0,}, + { "KEY_KBDILLUMDOWN",NULL,NULL,229,0,0,0,0,0,344,0,0,0,0,}, + { "KEY_KBDILLUMUP",NULL,NULL,230,0,0,0,0,0,345,0,0,0,0,}, + { "KEY_SEND",NULL,NULL,231,0,0,0,0,0,346,0,0,0,0,}, + { "KEY_REPLY",NULL,NULL,232,0,0,0,0,0,356,0,0,0,0,}, + { "KEY_FORWARDMAIL",NULL,NULL,233,0,0,0,0,0,270,0,0,0,0,}, + { "KEY_SAVE",NULL,NULL,234,0,0,0,0,0,341,0,0,0,0,}, + { "KEY_DOCUMENTS",NULL,NULL,235,0,0,0,0,0,368,0,0,0,0,}, + { "KEY_BATTERY",NULL,NULL,236,0,0,0,0,0,369,0,0,0,0,}, + { "KEY_BLUETOOTH",NULL,NULL,237,0,0,0,0,0,370,0,0,0,0,}, + { "KEY_WLAN",NULL,NULL,238,0,0,0,0,0,371,0,0,0,0,}, + { "KEY_UWB",NULL,NULL,239,0,0,0,0,0,372,0,0,0,0,}, + { "KEY_UNKNOWN",NULL,NULL,240,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_VIDEO_NEXT",NULL,NULL,241,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_VIDEO_PREV",NULL,NULL,242,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_BRIGHTNESS_CYCLE",NULL,NULL,243,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_BRIGHTNESS_ZERO",NULL,NULL,244,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_DISPLAY_OFF",NULL,NULL,245,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_WIMAX",NULL,NULL,246,0,0,0,0,0,0,0,0,0,0,}, + { NULL,NULL,NULL,247,0,0,0,0,0,0,0,0,0,0,}, + { NULL,NULL,NULL,248,0,0,0,0,0,0,0,0,0,0,}, + { NULL,NULL,NULL,249,0,0,0,0,0,0,0,0,0,0,}, + { NULL,NULL,NULL,250,0,0,0,0,0,0,0,0,0,0,}, + { NULL,NULL,NULL,251,0,0,0,0,0,0,0,0,0,0,}, + { NULL,NULL,NULL,252,0,0,0,0,0,0,0,0,0,0,}, + { NULL,NULL,NULL,253,0,0,0,0,0,0,0,0,0,0,}, + { NULL,NULL,NULL,254,0,0,0,0,0,0,0,0,0,0,}, + { NULL,NULL,NULL,255,0,0,182,0,0,0,0,0,0,0,}, + { "BTN_MISC",NULL,NULL,0x100,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_0",NULL,"VK_LBUTTON",0x100,0,0,0,0,0,0,0,0x01,0,0,}, + { "BTN_1",NULL,"VK_RBUTTON",0x101,0,0,0,0,0,0,0,0x02,0,0,}, + { "BTN_2",NULL,"VK_MBUTTON",0x102,0,0,0,0,0,0,0,0x04,0,0,}, + { "BTN_3",NULL,"VK_XBUTTON1",0x103,0,0,0,0,0,0,0,0x05,0,0,}, + { "BTN_4",NULL,"VK_XBUTTON2",0x104,0,0,0,0,0,0,0,0x06,0,0,}, + { "BTN_5",NULL,NULL,0x105,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_6",NULL,NULL,0x106,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_7",NULL,NULL,0x107,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_8",NULL,NULL,0x108,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_9",NULL,NULL,0x109,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_MOUSE",NULL,NULL,0x110,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_LEFT",NULL,NULL,0x110,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_RIGHT",NULL,NULL,0x111,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_MIDDLE",NULL,NULL,0x112,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_SIDE",NULL,NULL,0x113,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_EXTRA",NULL,NULL,0x114,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_FORWARD",NULL,NULL,0x115,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_BACK",NULL,NULL,0x116,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_TASK",NULL,NULL,0x117,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_JOYSTICK",NULL,NULL,0x120,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_TRIGGER",NULL,NULL,0x120,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_THUMB",NULL,NULL,0x121,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_THUMB2",NULL,NULL,0x122,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_TOP",NULL,NULL,0x123,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_TOP2",NULL,NULL,0x124,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_PINKIE",NULL,NULL,0x125,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_BASE",NULL,NULL,0x126,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_BASE2",NULL,NULL,0x127,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_BASE3",NULL,NULL,0x128,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_BASE4",NULL,NULL,0x129,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_BASE5",NULL,NULL,0x12a,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_BASE6",NULL,NULL,0x12b,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_DEAD",NULL,NULL,0x12f,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_GAMEPAD",NULL,NULL,0x130,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_A",NULL,NULL,0x130,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_B",NULL,NULL,0x131,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_C",NULL,NULL,0x132,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_X",NULL,NULL,0x133,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_Y",NULL,NULL,0x134,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_Z",NULL,NULL,0x135,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_TL",NULL,NULL,0x136,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_TR",NULL,NULL,0x137,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_TL2",NULL,NULL,0x138,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_TR2",NULL,NULL,0x139,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_SELECT",NULL,NULL,0x13a,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_START",NULL,NULL,0x13b,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_MODE",NULL,NULL,0x13c,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_THUMBL",NULL,NULL,0x13d,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_THUMBR",NULL,NULL,0x13e,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_DIGI",NULL,NULL,0x140,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_TOOL_PEN",NULL,NULL,0x140,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_TOOL_RUBBER",NULL,NULL,0x141,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_TOOL_BRUSH",NULL,NULL,0x142,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_TOOL_PENCIL",NULL,NULL,0x143,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_TOOL_AIRBRUSH",NULL,NULL,0x144,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_TOOL_FINGER",NULL,NULL,0x145,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_TOOL_MOUSE",NULL,NULL,0x146,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_TOOL_LENS",NULL,NULL,0x147,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_TOUCH",NULL,NULL,0x14a,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_STYLUS",NULL,NULL,0x14b,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_STYLUS2",NULL,NULL,0x14c,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_TOOL_DOUBLETAP",NULL,NULL,0x14d,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_TOOL_TRIPLETAP",NULL,NULL,0x14e,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_TOOL_QUADTAP",NULL,NULL,0x14f,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_WHEEL",NULL,NULL,0x150,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_GEAR_DOWN",NULL,NULL,0x150,0,0,0,0,0,0,0,0,0,0,}, + { "BTN_GEAR_UP",NULL,NULL,0x151,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_OK",NULL,NULL,0x160,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_SELECT",NULL,"VK_SELECT",0x161,0,0,0,0,0,0,0,0x29,0,0,}, + { "KEY_GOTO",NULL,NULL,0x162,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_CLEAR",NULL,NULL,0x163,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_POWER2",NULL,NULL,0x164,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_OPTION",NULL,NULL,0x165,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_INFO",NULL,NULL,0x166,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_TIME",NULL,NULL,0x167,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_VENDOR",NULL,NULL,0x168,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_ARCHIVE",NULL,NULL,0x169,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_PROGRAM",NULL,NULL,0x16a,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_CHANNEL",NULL,NULL,0x16b,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_FAVORITES",NULL,"VK_BROWSER_FAVOURITES",0x16c,0,0,0,0,0,0,0,0xab,0,0,}, + { "KEY_EPG",NULL,NULL,0x16d,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_PVR",NULL,NULL,0x16e,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_MHP",NULL,NULL,0x16f,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_LANGUAGE",NULL,NULL,0x170,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_TITLE",NULL,NULL,0x171,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_SUBTITLE",NULL,NULL,0x172,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_ANGLE",NULL,NULL,0x173,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_ZOOM",NULL,"VK_ZOOM",0x174,0,0,0,0,0,0,0,0xfb,0,0,}, + { "KEY_MODE",NULL,NULL,0x175,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_KEYBOARD",NULL,NULL,0x176,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_SCREEN",NULL,NULL,0x177,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_PC",NULL,NULL,0x178,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_TV",NULL,NULL,0x179,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_TV2",NULL,NULL,0x17a,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_VCR",NULL,NULL,0x17b,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_VCR2",NULL,NULL,0x17c,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_SAT",NULL,NULL,0x17d,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_SAT2",NULL,NULL,0x17e,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_CD",NULL,NULL,0x17f,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_TAPE",NULL,NULL,0x180,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_RADIO",NULL,NULL,0x181,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_TUNER",NULL,NULL,0x182,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_PLAYER",NULL,NULL,0x183,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_TEXT",NULL,NULL,0x184,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_DVD",NULL,NULL,0x185,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_AUX",NULL,NULL,0x186,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_MP3",NULL,NULL,0x187,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_AUDIO",NULL,NULL,0x188,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_VIDEO",NULL,NULL,0x189,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_DIRECTORY",NULL,NULL,0x18a,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_LIST",NULL,NULL,0x18b,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_MEMO",NULL,NULL,0x18c,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_CALENDAR",NULL,NULL,0x18d,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_RED",NULL,NULL,0x18e,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_GREEN",NULL,NULL,0x18f,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_YELLOW",NULL,NULL,0x190,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_BLUE",NULL,NULL,0x191,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_CHANNELUP",NULL,NULL,0x192,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_CHANNELDOWN",NULL,NULL,0x193,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_FIRST",NULL,NULL,0x194,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_LAST",NULL,NULL,0x195,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_AB",NULL,NULL,0x196,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_NEXT",NULL,NULL,0x197,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_RESTART",NULL,NULL,0x198,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_SLOW",NULL,NULL,0x199,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_SHUFFLE",NULL,NULL,0x19a,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_BREAK",NULL,NULL,0x19b,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_PREVIOUS",NULL,NULL,0x19c,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_DIGITS",NULL,NULL,0x19d,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_TEEN",NULL,NULL,0x19e,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_TWEN",NULL,NULL,0x19f,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_VIDEOPHONE",NULL,NULL,0x1a0,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_GAMES",NULL,NULL,0x1a1,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_ZOOMIN",NULL,NULL,0x1a2,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_ZOOMOUT",NULL,NULL,0x1a3,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_ZOOMRESET",NULL,NULL,0x1a4,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_WORDPROCESSOR",NULL,NULL,0x1a5,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_EDITOR",NULL,NULL,0x1a6,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_SPREADSHEET",NULL,NULL,0x1a7,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_GRAPHICSEDITOR",NULL,NULL,0x1a8,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_PRESENTATION",NULL,NULL,0x1a9,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_DATABASE",NULL,NULL,0x1aa,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_NEWS",NULL,NULL,0x1ab,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_VOICEMAIL",NULL,NULL,0x1ac,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_ADDRESSBOOK",NULL,NULL,0x1ad,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_MESSENGER",NULL,NULL,0x1ae,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_DISPLAYTOGGLE",NULL,NULL,0x1af,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_SPELLCHECK",NULL,NULL,0x1b0,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_LOGOFF",NULL,NULL,0x1b1,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_DOLLAR",NULL,NULL,0x1b2,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_EURO",NULL,NULL,0x1b3,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_FRAMEBACK",NULL,NULL,0x1b4,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_FRAMEFORWARD",NULL,NULL,0x1b5,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_CONTEXT_MENU",NULL,NULL,0x1b6,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_MEDIA_REPEAT",NULL,NULL,0x1b7,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_DEL_EOL",NULL,NULL,0x1c0,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_DEL_EOS",NULL,NULL,0x1c1,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_INS_LINE",NULL,NULL,0x1c2,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_DEL_LINE",NULL,NULL,0x1c3,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_FN",NULL,NULL,0x1d0,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_FN_ESC",NULL,NULL,0x1d1,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_FN_F1",NULL,NULL,0x1d2,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_FN_F2",NULL,NULL,0x1d3,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_FN_F3",NULL,NULL,0x1d4,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_FN_F4",NULL,NULL,0x1d5,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_FN_F5",NULL,NULL,0x1d6,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_FN_F6",NULL,NULL,0x1d7,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_FN_F7",NULL,NULL,0x1d8,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_FN_F8",NULL,NULL,0x1d9,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_FN_F9",NULL,NULL,0x1da,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_FN_F10",NULL,NULL,0x1db,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_FN_F11",NULL,NULL,0x1dc,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_FN_F12",NULL,NULL,0x1dd,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_FN_1",NULL,NULL,0x1de,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_FN_2",NULL,NULL,0x1df,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_FN_D",NULL,NULL,0x1e0,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_FN_E",NULL,NULL,0x1e1,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_FN_F",NULL,NULL,0x1e2,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_FN_S",NULL,NULL,0x1e3,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_FN_B",NULL,NULL,0x1e4,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_BRL_DOT1",NULL,NULL,0x1f1,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_BRL_DOT2",NULL,NULL,0x1f2,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_BRL_DOT3",NULL,NULL,0x1f3,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_BRL_DOT4",NULL,NULL,0x1f4,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_BRL_DOT5",NULL,NULL,0x1f5,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_BRL_DOT6",NULL,NULL,0x1f6,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_BRL_DOT7",NULL,NULL,0x1f7,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_BRL_DOT8",NULL,NULL,0x1f8,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_BRL_DOT9",NULL,NULL,0x1f9,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_BRL_DOT10",NULL,NULL,0x1fa,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_NUMERIC_0",NULL,NULL,0x200,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_NUMERIC_1",NULL,NULL,0x201,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_NUMERIC_2",NULL,NULL,0x202,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_NUMERIC_3",NULL,NULL,0x203,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_NUMERIC_4",NULL,NULL,0x204,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_NUMERIC_5",NULL,NULL,0x205,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_NUMERIC_6",NULL,NULL,0x206,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_NUMERIC_7",NULL,NULL,0x207,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_NUMERIC_8",NULL,NULL,0x208,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_NUMERIC_9",NULL,NULL,0x209,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_NUMERIC_STAR",NULL,NULL,0x20a,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_NUMERIC_POUND",NULL,NULL,0x20b,0,0,0,0,0,0,0,0,0,0,}, + { "KEY_RFKILL",NULL,NULL,0x20c,0,0,0,0,0,0,0,0,0,0,}, +}; + +struct virCodesetInfo { + const char *name; + unsigned int offset; +}; + +struct virCodesetInfo codesetInfo[] = { + [VIR_KEYCODE_SET_LINUX] = + { "linux", offsetof(struct keycode, linux_keycode)}, + [VIR_KEYCODE_SET_XT] = + { "xt", offsetof(struct keycode, xt)}, + [VIR_KEYCODE_SET_ATSET1] = + { "atset1", offsetof(struct keycode, atset1)}, + [VIR_KEYCODE_SET_ATSET2] = + { "atset2", offsetof(struct keycode, atset2)}, + [VIR_KEYCODE_SET_ATSET3] = + { "atset3", offsetof(struct keycode, atset3)}, + [VIR_KEYCODE_SET_OSX] = + { "os-x", offsetof(struct keycode, os_x)}, + [VIR_KEYCODE_SET_XT_KBD] = + { "xt_kbd", offsetof(struct keycode, xt_kbd)}, + [VIR_KEYCODE_SET_USB] = + { "usb", offsetof(struct keycode, usb)}, + [VIR_KEYCODE_SET_WIN32] = + { "win32", offsetof(struct keycode, win32)}, + [VIR_KEYCODE_SET_XWIN_XT] = + { "xwin_xt", offsetof(struct keycode, xwin_xt)}, + [VIR_KEYCODE_SET_XFREE86_KBD_XT] = + { "xfree86_kbd_xt", offsetof(struct keycode, xfree86_kbd_xt)}, +}; + +const char *virKeycodeSetName(virKeycodeSet codeset) +{ + int i = (int)codeset; + + if (i < 0 || i >= ARRAY_SIZE(codesetInfo)) + return "UNKNOWN"; + + return codesetInfo[i].name; +} + +virKeycodeSet virParseKeycodeSet(const char *name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(codesetInfo); i++) { + if (!strcmp(codesetInfo[i].name, name)) + return (virKeycodeSet)i; + } + + return (virKeycodeSet)-1; +} + +static int virParseKeyNameOffset(unsigned int name_offset, + unsigned int code_offset, + const char *keyname) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(keycodes); i++) { + const char *name = getfield(keycodes + i, const char *, name_offset); + + if (name && !strcmp(name, keyname)) + return getfield(keycodes + i, unsigned short, code_offset); + } + + return -1; +} + +int virParseKeyName(virKeycodeSet codeset, const char *keyname) +{ + switch (codeset) { + case VIR_KEYCODE_SET_LINUX: + return virParseKeyNameOffset(offsetof(struct keycode, linux_name), + offsetof(struct keycode, linux_keycode), keyname); + case VIR_KEYCODE_SET_OSX: + return virParseKeyNameOffset(offsetof(struct keycode, os_x_name), + offsetof(struct keycode, os_x), keyname); + case VIR_KEYCODE_SET_WIN32: + return virParseKeyNameOffset(offsetof(struct keycode, win32_name), + offsetof(struct keycode, win32), keyname); + default: + return -1; + } +} + +static int virTranslateKeyCodeOffset(unsigned int from_offset, + unsigned int to_offset, + int key_value) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(keycodes); i++) { + if (getfield(keycodes + i, unsigned short, from_offset) == key_value) + return getfield(keycodes + i, unsigned short, to_offset); + } + + return -1; +} + +int virTranslateKeyCode(virKeycodeSet from_codeset, + virKeycodeSet to_codeset, + int key_value) +{ + if (key_value <= 0) + return -1; + + key_value = virTranslateKeyCodeOffset(codesetInfo[from_codeset].offset, + codesetInfo[to_codeset].offset, + key_value); + if (key_value <= 0) + return -1; + + return key_value; +} + + diff --git a/src/util/virtkey.h b/src/util/virtkey.h new file mode 100644 index 0000000..f81bcb1 --- /dev/null +++ b/src/util/virtkey.h @@ -0,0 +1,21 @@ +#ifndef __UTIL_VIRTKEY_H__ +#define __UTIL_VIRTKEY_H__ + +/* + * Copyright (c) 2011 Lai Jiangshan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <libvirt/libvirt.h> + +const char *virKeycodeSetName(virKeycodeSet codeset); +virKeycodeSet virParseKeycodeSet(const char *name); +int virParseKeyName(virKeycodeSet codeset, const char *keyname); +int virTranslateKeyCode(virKeycodeSet from_codeset, + virKeycodeSet to_offset, + int key_value); + +#endif diff --git a/tools/virsh.c b/tools/virsh.c index fcd254d..a1e2f83 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -58,6 +58,7 @@ #include "threads.h" #include "command.h" #include "count-one-bits.h" +#include "virtkey.h" static char *progname; -- 1.7.4.4

On Fri, Jun 24, 2011 at 02:33:29PM +0800, Lai Jiangshan wrote:
Add virtkey lib for usage-improvment and keycode translating. Add 4 internal API for the aim
const char *virKeycodeSetName(virKeycodeSet codeset); virKeycodeSet virParseKeycodeSet(const char *name);
These should just be done using the standard VIR_ENUM_DECL/IMPL macros.
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 3f634e6..2f2efe7 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1815,6 +1815,12 @@ typedef enum { VIR_KEYCODE_SET_ATSET1 = 2, VIR_KEYCODE_SET_ATSET2 = 3, VIR_KEYCODE_SET_ATSET3 = 4, + VIR_KEYCODE_SET_OSX = 5, + VIR_KEYCODE_SET_XT_KBD = 6, + VIR_KEYCODE_SET_USB = 7, + VIR_KEYCODE_SET_WIN32 = 8, + VIR_KEYCODE_SET_XWIN_XT = 9, + VIR_KEYCODE_SET_XFREE86_KBD_XT = 10, } virKeycodeSet;
IMHO, we don't really need to include the XT_KBD, XWIN_XT or XFREE86_KBD_XT codesets, since these are all special purpose sets which are just derived from the based XT set. Lets just stick to the core interesting sets. So add OSX, USB and WIN32 only.
diff --git a/src/util/virtkey.c b/src/util/virtkey.c new file mode 100644 index 0000000..48fbfcc --- /dev/null +++ b/src/util/virtkey.c @@ -0,0 +1,633 @@ + +/* + * Copyright (c) 2011 Lai Jiangshan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <config.h> +#include <string.h> +#include <stddef.h> +#include <libvirt/libvirt.h> +#include "virtkey.h" + +#define ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0])) +#define getfield(object, field_type, field_offset) \ + (*(typeof(field_type) *)((char *)(object) + field_offset)) + +struct keycode { + const char *linux_name; + const char *os_x_name; + const char *win32_name; + unsigned short linux_keycode; + unsigned short xt; + unsigned short atset1; + unsigned short atset2; + unsigned short atset3; + unsigned short os_x; + unsigned short xt_kbd; + unsigned short usb; + unsigned short win32; + unsigned short xwin_xt; + unsigned short xfree86_kbd_xt; +}; + +/* + * generated from http://git.gnome.org/browse/gtk-vnc/plain/src/keymaps.csv + * script: + * + * #!/bin/python + * import sys + * import re + * + * for line in sys.stdin.xreadlines(): + * a = re.match("([^,]*)," * 13 + "([^,]*)$", line[0:-1]).groups() + * b = "" + * for i in (0,2,10,1,7,4,5,6,3,8,9,11,12,13): + * if i in (0, 2, 10): + * b = b + (a[i] and ('"' + a[i] + '"') or 'NULL') + ',' + * else: + * b = b + (a[i] or '0') + ',' + * print " { " + b + "}," + */
One of the goals of having the keymap data in the CSV file was that it makes it trivially updatable across apps using it, without making code changes. In fact my goal is to actually put 'keymaps.csv' and 'keymaps.pl' into a separate shared package at some point. So rather than hardcoding this giant array in libvirt, just include the GTK-VNC keymaps.csv and keymaps.pl file as-is, and run them to generate the mapping tables for combinations we need. NB, keymaps.pl will need to be updated to be able to output a table for doing "string->keycode" mapping since it doesn't do that yet. For the plain keycode->keycode mappings though just use its currently functionality.
+const char *virKeycodeSetName(virKeycodeSet codeset) +{ + int i = (int)codeset; + + if (i < 0 || i >= ARRAY_SIZE(codesetInfo)) + return "UNKNOWN"; + + return codesetInfo[i].name; +} + +virKeycodeSet virParseKeycodeSet(const char *name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(codesetInfo); i++) { + if (!strcmp(codesetInfo[i].name, name)) + return (virKeycodeSet)i; + } + + return (virKeycodeSet)-1; +}
These just get replaced by VIR_ENUM_IMPL
+static int virParseKeyNameOffset(unsigned int name_offset, + unsigned int code_offset, + const char *keyname) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(keycodes); i++) { + const char *name = getfield(keycodes + i, const char *, name_offset); + + if (name && !strcmp(name, keyname)) + return getfield(keycodes + i, unsigned short, code_offset); + } + + return -1; +}
This will want to use a number table that keymaps.pl will need to generate for name -> code mapping.
+static int virTranslateKeyCodeOffset(unsigned int from_offset, + unsigned int to_offset, + int key_value) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(keycodes); i++) { + if (getfield(keycodes + i, unsigned short, from_offset) == key_value) + return getfield(keycodes + i, unsigned short, to_offset); + } + + return -1; +}
This is not nice because it is O(n) lookups. If you just use the keymaps.pl script to generate all the conversion tables we need, we get O(1) lookups & simpler code.
diff --git a/tools/virsh.c b/tools/virsh.c index fcd254d..a1e2f83 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -58,6 +58,7 @@ #include "threads.h" #include "command.h" #include "count-one-bits.h" +#include "virtkey.h"
static char *progname;
This ought to be in the next patch, since this doesn't need it yet Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 06/24/2011 09:18 PM, Daniel P. Berrange wrote:
On Fri, Jun 24, 2011 at 02:33:29PM +0800, Lai Jiangshan wrote:
Add virtkey lib for usage-improvment and keycode translating. Add 4 internal API for the aim
const char *virKeycodeSetName(virKeycodeSet codeset); virKeycodeSet virParseKeycodeSet(const char *name);
These should just be done using the standard VIR_ENUM_DECL/IMPL macros.
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 3f634e6..2f2efe7 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1815,6 +1815,12 @@ typedef enum { VIR_KEYCODE_SET_ATSET1 = 2, VIR_KEYCODE_SET_ATSET2 = 3, VIR_KEYCODE_SET_ATSET3 = 4, + VIR_KEYCODE_SET_OSX = 5, + VIR_KEYCODE_SET_XT_KBD = 6, + VIR_KEYCODE_SET_USB = 7, + VIR_KEYCODE_SET_WIN32 = 8, + VIR_KEYCODE_SET_XWIN_XT = 9, + VIR_KEYCODE_SET_XFREE86_KBD_XT = 10, } virKeycodeSet;
IMHO, we don't really need to include the XT_KBD, XWIN_XT or XFREE86_KBD_XT codesets, since these are all special purpose sets which are just derived from the based XT set. Lets just stick to the core interesting sets. So add OSX, USB and WIN32 only.
I found qemu monitor just accept XT_KBD, not XT, maybe I'm wrong.
diff --git a/src/util/virtkey.c b/src/util/virtkey.c new file mode 100644 index 0000000..48fbfcc --- /dev/null +++ b/src/util/virtkey.c @@ -0,0 +1,633 @@ + +/* + * Copyright (c) 2011 Lai Jiangshan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <config.h> +#include <string.h> +#include <stddef.h> +#include <libvirt/libvirt.h> +#include "virtkey.h" + +#define ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0])) +#define getfield(object, field_type, field_offset) \ + (*(typeof(field_type) *)((char *)(object) + field_offset)) + +struct keycode { + const char *linux_name; + const char *os_x_name; + const char *win32_name; + unsigned short linux_keycode; + unsigned short xt; + unsigned short atset1; + unsigned short atset2; + unsigned short atset3; + unsigned short os_x; + unsigned short xt_kbd; + unsigned short usb; + unsigned short win32; + unsigned short xwin_xt; + unsigned short xfree86_kbd_xt; +}; + +/* + * generated from http://git.gnome.org/browse/gtk-vnc/plain/src/keymaps.csv + * script: + * + * #!/bin/python + * import sys + * import re + * + * for line in sys.stdin.xreadlines(): + * a = re.match("([^,]*)," * 13 + "([^,]*)$", line[0:-1]).groups() + * b = "" + * for i in (0,2,10,1,7,4,5,6,3,8,9,11,12,13): + * if i in (0, 2, 10): + * b = b + (a[i] and ('"' + a[i] + '"') or 'NULL') + ',' + * else: + * b = b + (a[i] or '0') + ',' + * print " { " + b + "}," + */
One of the goals of having the keymap data in the CSV file was that it makes it trivially updatable across apps using it, without making code changes. In fact my goal is to actually put 'keymaps.csv' and 'keymaps.pl' into a separate shared package at some point. So rather than hardcoding this giant array in libvirt, just include the GTK-VNC keymaps.csv and keymaps.pl file as-is, and run them to generate the mapping tables for combinations we need.
NB, keymaps.pl will need to be updated to be able to output a table for doing "string->keycode" mapping since it doesn't do that yet. For the plain keycode->keycode mappings though just use its currently functionality.
I didn't find separate git repository for keymaps.csv. Should I copy keymaps.csv to libvirt? keymaps.pl need to be run O(N*N) times and it will generate O(N*N) tables for different translating, I think that 1 table is the best, even the table are bigger.
+const char *virKeycodeSetName(virKeycodeSet codeset) +{ + int i = (int)codeset; + + if (i < 0 || i >= ARRAY_SIZE(codesetInfo)) + return "UNKNOWN"; + + return codesetInfo[i].name; +} + +virKeycodeSet virParseKeycodeSet(const char *name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(codesetInfo); i++) { + if (!strcmp(codesetInfo[i].name, name)) + return (virKeycodeSet)i; + } + + return (virKeycodeSet)-1; +}
These just get replaced by VIR_ENUM_IMPL
Will do, thanks,
+static int virParseKeyNameOffset(unsigned int name_offset, + unsigned int code_offset, + const char *keyname) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(keycodes); i++) { + const char *name = getfield(keycodes + i, const char *, name_offset); + + if (name && !strcmp(name, keyname)) + return getfield(keycodes + i, unsigned short, code_offset); + } + + return -1; +}
This will want to use a number table that keymaps.pl will need to generate for name -> code mapping.
+static int virTranslateKeyCodeOffset(unsigned int from_offset, + unsigned int to_offset, + int key_value) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(keycodes); i++) { + if (getfield(keycodes + i, unsigned short, from_offset) == key_value) + return getfield(keycodes + i, unsigned short, to_offset); + } + + return -1; +}
This is not nice because it is O(n) lookups. If you just use the keymaps.pl script to generate all the conversion tables we need, we get O(1) lookups & simpler code.
I think O(n) lookups is OK for <=16 keycodes. Thank you very much. I need to investigate/think more Lai
diff --git a/tools/virsh.c b/tools/virsh.c index fcd254d..a1e2f83 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -58,6 +58,7 @@ #include "threads.h" #include "command.h" #include "count-one-bits.h" +#include "virtkey.h"
static char *progname;
This ought to be in the next patch, since this doesn't need it yet
Regards, Daniel

Also support string names for the linux keycode(auto detect). Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- tools/virsh.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 4 ++ 2 files changed, 97 insertions(+), 0 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index a1e2f83..d7fb8a6 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -3263,6 +3263,98 @@ cmdInjectNMI(vshControl *ctl, const vshCmd *cmd) } /* + * "send-key" command + */ +static const vshCmdInfo info_send_key[] = { + {"help", N_("Send keycodes to the guest")}, + {"desc", N_("Send keycodes to the guest, the keycodes must be integers\n" + " Examples:\n\n" + " virsh # send-key <domain> 37 18 21\n" + " virsh # send-key <domain> KEY_RIGHTCTRL KEY_C\n" + " virsh # send-key <domain> --codeset xt 37 18 21\n" + " virsh # send-key <domain> --holdtime 1000 0x15 18 0xf\n" + )}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_send_key[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, + {"codeset", VSH_OT_STRING, VSH_OFLAG_REQ_OPT, N_("the codeset of keycodes, default:linux")}, + {"holdtime", VSH_OT_INT, VSH_OFLAG_REQ_OPT, + N_("the time (in millsecond) how long the keys will be held")}, + {"keycode", VSH_OT_ARGV, VSH_OFLAG_REQ, N_("the key code")}, + {NULL, 0, 0, NULL} +}; + +static int get_integer_keycode(const char *key_name) +{ + long val; + char *endptr; + + val = strtol(key_name, &endptr, 0); + if (*endptr != '\0' || val > 0xffff || val <= 0) + return -1; + + return val; +} + +static bool +cmdSendKey(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom; + int ret = false; + const char *codeset_option; + int codeset; + int holdtime; + int count = 0; + const vshCmdOpt *opt = NULL; + int keycode; + unsigned int keycodes[VIR_DOMAIN_SEND_KEY_MAX_KEYS]; + + if (!vshConnectionUsability(ctl, ctl->conn)) + return false; + + if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) + return false; + + if (vshCommandOptString(cmd, "codeset", &codeset_option) <= 0) + codeset_option = "linux"; + + if (vshCommandOptInt(cmd, "holdtime", &holdtime) <= 0) + holdtime = 0; + + codeset = virParseKeycodeSet(codeset_option); + if ((int)codeset < 0) { + vshError(ctl, _("unknown codeset: '%s'"), codeset_option); + goto cleanup; + } + + while ((opt = vshCommandOptArgv(cmd, opt))) { + if (count == VIR_DOMAIN_SEND_KEY_MAX_KEYS) { + vshError(ctl, _("too many keycodes")); + goto cleanup; + } + + if ((keycode = get_integer_keycode(opt->data)) <= 0) { + if ((keycode = virParseKeyName(codeset, opt->data)) <= 0) { + vshError(ctl, _("invalid keycode: '%s'"), opt->data); + goto cleanup; + } + } + + keycodes[count] = keycode; + count++; + } + + if (!(virDomainSendKey(dom, codeset, holdtime, keycodes, count, 0) < 0)) + ret = true; + +cleanup: + virDomainFree(dom); + return ret; +} + +/* * "setmemory" command */ static const vshCmdInfo info_setmem[] = { @@ -11672,6 +11764,7 @@ static const vshCmdDef domManagementCmds[] = { {"dumpxml", cmdDumpXML, opts_dumpxml, info_dumpxml, 0}, {"edit", cmdEdit, opts_edit, info_edit, 0}, {"inject-nmi", cmdInjectNMI, opts_inject_nmi, info_inject_nmi, 0}, + {"send-key", cmdSendKey, opts_send_key, info_send_key}, {"managedsave", cmdManagedSave, opts_managedsave, info_managedsave, 0}, {"managedsave-remove", cmdManagedSaveRemove, opts_managedsaveremove, info_managedsaveremove, 0}, diff --git a/tools/virsh.pod b/tools/virsh.pod index 023ab42..50c4800 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -308,6 +308,10 @@ scheduling by the hypervisor. Inject NMI to the guest +=item B<send-key> I<domain-id> optional I<--codeset> B<codeset> optional I<--holdtime> B<holdtime> B<keycode>... + +Send keys to the guest + =item B<shutdown> The domain is in the process of shutting down, i.e. the guest operating system -- 1.7.4.4

On Fri, Jun 24, 2011 at 02:33:30PM +0800, Lai Jiangshan wrote:
Also support string names for the linux keycode(auto detect).
Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- tools/virsh.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 4 ++ 2 files changed, 97 insertions(+), 0 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c index a1e2f83..d7fb8a6 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -3263,6 +3263,98 @@ cmdInjectNMI(vshControl *ctl, const vshCmd *cmd) }
/* + * "send-key" command + */ +static const vshCmdInfo info_send_key[] = { + {"help", N_("Send keycodes to the guest")}, + {"desc", N_("Send keycodes to the guest, the keycodes must be integers\n" + " Examples:\n\n" + " virsh # send-key <domain> 37 18 21\n" + " virsh # send-key <domain> KEY_RIGHTCTRL KEY_C\n" + " virsh # send-key <domain> --codeset xt 37 18 21\n" + " virsh # send-key <domain> --holdtime 1000 0x15 18 0xf\n"
These examples should likely be in the virsh.pod file too.
+ )}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_send_key[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, + {"codeset", VSH_OT_STRING, VSH_OFLAG_REQ_OPT, N_("the codeset of keycodes, default:linux")}, + {"holdtime", VSH_OT_INT, VSH_OFLAG_REQ_OPT, + N_("the time (in millsecond) how long the keys will be held")}, + {"keycode", VSH_OT_ARGV, VSH_OFLAG_REQ, N_("the key code")}, + {NULL, 0, 0, NULL} +}; + +static int get_integer_keycode(const char *key_name) +{ + long val; + char *endptr; + + val = strtol(key_name, &endptr, 0); + if (*endptr != '\0' || val > 0xffff || val <= 0) + return -1; + + return val; +} + +static bool +cmdSendKey(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom; + int ret = false; + const char *codeset_option; + int codeset; + int holdtime; + int count = 0; + const vshCmdOpt *opt = NULL; + int keycode; + unsigned int keycodes[VIR_DOMAIN_SEND_KEY_MAX_KEYS]; + + if (!vshConnectionUsability(ctl, ctl->conn)) + return false; + + if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) + return false; + + if (vshCommandOptString(cmd, "codeset", &codeset_option) <= 0) + codeset_option = "linux"; + + if (vshCommandOptInt(cmd, "holdtime", &holdtime) <= 0) + holdtime = 0; + + codeset = virParseKeycodeSet(codeset_option); + if ((int)codeset < 0) { + vshError(ctl, _("unknown codeset: '%s'"), codeset_option); + goto cleanup; + } + + while ((opt = vshCommandOptArgv(cmd, opt))) { + if (count == VIR_DOMAIN_SEND_KEY_MAX_KEYS) { + vshError(ctl, _("too many keycodes")); + goto cleanup; + } + + if ((keycode = get_integer_keycode(opt->data)) <= 0) { + if ((keycode = virParseKeyName(codeset, opt->data)) <= 0) { + vshError(ctl, _("invalid keycode: '%s'"), opt->data); + goto cleanup; + } + } + + keycodes[count] = keycode; + count++; + } + + if (!(virDomainSendKey(dom, codeset, holdtime, keycodes, count, 0) < 0)) + ret = true; + +cleanup: + virDomainFree(dom); + return ret; +} + +/* * "setmemory" command */ static const vshCmdInfo info_setmem[] = { @@ -11672,6 +11764,7 @@ static const vshCmdDef domManagementCmds[] = { {"dumpxml", cmdDumpXML, opts_dumpxml, info_dumpxml, 0}, {"edit", cmdEdit, opts_edit, info_edit, 0}, {"inject-nmi", cmdInjectNMI, opts_inject_nmi, info_inject_nmi, 0}, + {"send-key", cmdSendKey, opts_send_key, info_send_key}, {"managedsave", cmdManagedSave, opts_managedsave, info_managedsave, 0}, {"managedsave-remove", cmdManagedSaveRemove, opts_managedsaveremove, info_managedsaveremove, 0}, diff --git a/tools/virsh.pod b/tools/virsh.pod index 023ab42..50c4800 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -308,6 +308,10 @@ scheduling by the hypervisor.
Inject NMI to the guest
+=item B<send-key> I<domain-id> optional I<--codeset> B<codeset> optional I<--holdtime> B<holdtime> B<keycode>... + +Send keys to the guest + =item B<shutdown>
The domain is in the process of shutting down, i.e. the guest operating system
ACK Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 06/24/2011 07:19 AM, Daniel P. Berrange wrote:
+static const vshCmdInfo info_send_key[] = { + {"help", N_("Send keycodes to the guest")}, + {"desc", N_("Send keycodes to the guest, the keycodes must be integers\n" + " Examples:\n\n" + " virsh # send-key<domain> 37 18 21\n" + " virsh # send-key<domain> KEY_RIGHTCTRL KEY_C\n" + " virsh # send-key<domain> --codeset xt 37 18 21\n" + " virsh # send-key<domain> --holdtime 1000 0x15 18 0xf\n"
These examples should likely be in the virsh.pod file too.
Oops, we didn't get that done yet. I'm working up a patch. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

qemu driver just accept xt_kbd codeset's keycode, so the lib virtkey is used for translating keycodes from other codesets. Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- src/qemu/qemu_driver.c | 51 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 37 ++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.h | 6 +++++ src/qemu/qemu_monitor_json.c | 15 ++++++++++++ src/qemu/qemu_monitor_json.h | 5 ++++ src/qemu/qemu_monitor_text.c | 49 ++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_text.h | 5 ++++ 7 files changed, 168 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 01587e8..994d7bd 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -1761,6 +1761,56 @@ cleanup: return ret; } +static int qemuDomainSendKey(virDomainPtr domain, + unsigned int codeset, + unsigned int holdtime, + unsigned int *keycodes, + int nkeycodes, + unsigned int flags) +{ + struct qemud_driver *driver = domain->conn->privateData; + virDomainObjPtr vm = NULL; + int ret = -1; + qemuDomainObjPrivatePtr priv; + + virCheckFlags(0, -1); + + qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, domain->uuid); + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(domain->uuid, uuidstr); + qemuReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + priv = vm->privateData; + + if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) + goto cleanup; + + if (!virDomainObjIsActive(vm)) { + qemuReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto cleanup; + } + + qemuDomainObjEnterMonitorWithDriver(driver, vm); + ret = qemuMonitorSendKey(priv->mon, codeset, holdtime, keycodes, nkeycodes); + qemuDomainObjExitMonitorWithDriver(driver, vm); + if (qemuDomainObjEndJob(vm) == 0) { + vm = NULL; + goto cleanup; + } + +cleanup: + if (vm) + virDomainObjUnlock(vm); + qemuDriverUnlock(driver); + return ret; +} + static int qemudDomainGetInfo(virDomainPtr dom, virDomainInfoPtr info) { @@ -8436,6 +8486,7 @@ static virDriver qemuDriver = { .domainMigratePerform3 = qemuDomainMigratePerform3, /* 0.9.2 */ .domainMigrateFinish3 = qemuDomainMigrateFinish3, /* 0.9.2 */ .domainMigrateConfirm3 = qemuDomainMigrateConfirm3, /* 0.9.2 */ + .domainSendKey = qemuDomainSendKey, /* 0.9.3 */ .domainBlockPull = qemuDomainBlockPull, /* 0.9.3 */ .domainBlockPullAll = qemuDomainBlockPullAll, /* 0.9.3 */ .domainBlockPullAbort = qemuDomainBlockPullAbort, /* 0.9.3 */ diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 89a3f64..e14703b 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -36,6 +36,7 @@ #include "memory.h" #include "logging.h" #include "files.h" +#include "virtkey.h" #define VIR_FROM_THIS VIR_FROM_QEMU @@ -2369,6 +2370,42 @@ int qemuMonitorInjectNMI(qemuMonitorPtr mon) return ret; } +int qemuMonitorSendKey(qemuMonitorPtr mon, + unsigned int codeset, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes) +{ + int ret; + + VIR_DEBUG("mon=%p, codeset=%s(%u), holdtime=%u, nkeycodes=%u", + mon, virKeycodeSetName(codeset), codeset, holdtime, nkeycodes); + + if (codeset != VIR_KEYCODE_SET_XT_KBD) { + int i; + int keycode; + + for (i = 0; i < nkeycodes; i++) { + keycode = virTranslateKeyCode(codeset, VIR_KEYCODE_SET_XT_KBD, + keycodes[i]); + if (keycode < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "can not translate " + "keycode %u of %s codeset to xt_kbd codeset " + "keycode", keycodes[i], + virKeycodeSetName(codeset)); + return -1; + } + keycodes[i] = keycode; + } + } + + if (mon->json) + ret = qemuMonitorJSONSendKey(mon, holdtime, keycodes, nkeycodes); + else + ret = qemuMonitorTextSendKey(mon, holdtime, keycodes, nkeycodes); + return ret; +} + int qemuMonitorScreendump(qemuMonitorPtr mon, const char *file) { diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 3bb0269..ed80c4b 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -462,6 +462,12 @@ int qemuMonitorBlockPull(qemuMonitorPtr mon, virDomainBlockPullInfoPtr info, int mode); +int qemuMonitorSendKey(qemuMonitorPtr mon, + unsigned int codeset, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes); + /** * When running two dd process and using <> redirection, we need a * shell that will not truncate files. These two strings serve that diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 56ec65b..604f04c 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -2661,6 +2661,21 @@ cleanup: return ret; } +int qemuMonitorJSONSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes) +{ + /* + * FIXME: qmp sendkey has not been implemented yet, + * and qmp API of it can not be anticipated, so we use hmp temporary. + */ + if (qemuMonitorCheckHMP(mon, "sendkey")) { + return qemuMonitorTextSendKey(mon, holdtime, keycodes, nkeycodes); + } else + return -1; +} + int qemuMonitorJSONScreendump(qemuMonitorPtr mon, const char *file) { diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 393d8fc..6023843 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -214,6 +214,11 @@ int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon, int qemuMonitorJSONInjectNMI(qemuMonitorPtr mon); +int qemuMonitorJSONSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes); + int qemuMonitorJSONScreendump(qemuMonitorPtr mon, const char *file); diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index a16ea91..0221bf1 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -2720,6 +2720,55 @@ fail: return -1; } +int qemuMonitorTextSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes) +{ + int i; + virBuffer buf = VIR_BUFFER_INITIALIZER; + char *cmd, *reply = NULL; + + if (nkeycodes > VIR_DOMAIN_SEND_KEY_MAX_KEYS || nkeycodes == 0) + return -1; + + virBufferAddLit(&buf, "sendkey "); + for (i = 0; i < nkeycodes; i++) { + if (keycodes[i] > 0xffff) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("keycode %d is invalid: 0x%X"), + i, keycodes[i]); + virBufferFreeAndReset(&buf); + return -1; + } + + if (i) + virBufferAddChar(&buf, '-'); + virBufferAsprintf(&buf, "0x%02X", keycodes[i]); + } + + if (holdtime) + virBufferAsprintf(&buf, " %u", holdtime); + + if (virBufferError(&buf)) { + virReportOOMError(); + return -1; + } + + cmd = virBufferContentAndReset(&buf); + if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("failed to send key using command '%s'"), + cmd); + VIR_FREE(cmd); + return -1; + } + + VIR_FREE(cmd); + VIR_FREE(reply); + return 0; +} + /* Returns -1 on error, -2 if not supported */ int qemuMonitorTextScreendump(qemuMonitorPtr mon, const char *file) { diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h index 4fa5064..5e0abcc 100644 --- a/src/qemu/qemu_monitor_text.h +++ b/src/qemu/qemu_monitor_text.h @@ -208,6 +208,11 @@ int qemuMonitorTextArbitraryCommand(qemuMonitorPtr mon, const char *cmd, int qemuMonitorTextInjectNMI(qemuMonitorPtr mon); +int qemuMonitorTextSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes); + int qemuMonitorTextScreendump(qemuMonitorPtr mon, const char *file); int qemuMonitorTextBlockPull(qemuMonitorPtr mon, -- 1.7.4.4

On Fri, Jun 24, 2011 at 02:33:31PM +0800, Lai Jiangshan wrote:
qemu driver just accept xt_kbd codeset's keycode, so the lib virtkey is used for translating keycodes from other codesets.
Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- src/qemu/qemu_driver.c | 51 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 37 ++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.h | 6 +++++ src/qemu/qemu_monitor_json.c | 15 ++++++++++++ src/qemu/qemu_monitor_json.h | 5 ++++ src/qemu/qemu_monitor_text.c | 49 ++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_text.h | 5 ++++ 7 files changed, 168 insertions(+), 0 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 01587e8..994d7bd 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -1761,6 +1761,56 @@ cleanup: return ret; }
+static int qemuDomainSendKey(virDomainPtr domain, + unsigned int codeset, + unsigned int holdtime, + unsigned int *keycodes, + int nkeycodes, + unsigned int flags) +{ + struct qemud_driver *driver = domain->conn->privateData; + virDomainObjPtr vm = NULL; + int ret = -1; + qemuDomainObjPrivatePtr priv; + + virCheckFlags(0, -1); + + qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, domain->uuid); + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(domain->uuid, uuidstr); + qemuReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + priv = vm->privateData; + + if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) + goto cleanup; + + if (!virDomainObjIsActive(vm)) { + qemuReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto cleanup; + } + + qemuDomainObjEnterMonitorWithDriver(driver, vm); + ret = qemuMonitorSendKey(priv->mon, codeset, holdtime, keycodes, nkeycodes); + qemuDomainObjExitMonitorWithDriver(driver, vm); + if (qemuDomainObjEndJob(vm) == 0) { + vm = NULL; + goto cleanup; + } + +cleanup: + if (vm) + virDomainObjUnlock(vm); + qemuDriverUnlock(driver); + return ret; +} + static int qemudDomainGetInfo(virDomainPtr dom, virDomainInfoPtr info) { @@ -8436,6 +8486,7 @@ static virDriver qemuDriver = { .domainMigratePerform3 = qemuDomainMigratePerform3, /* 0.9.2 */ .domainMigrateFinish3 = qemuDomainMigrateFinish3, /* 0.9.2 */ .domainMigrateConfirm3 = qemuDomainMigrateConfirm3, /* 0.9.2 */ + .domainSendKey = qemuDomainSendKey, /* 0.9.3 */ .domainBlockPull = qemuDomainBlockPull, /* 0.9.3 */ .domainBlockPullAll = qemuDomainBlockPullAll, /* 0.9.3 */ .domainBlockPullAbort = qemuDomainBlockPullAbort, /* 0.9.3 */ diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 89a3f64..e14703b 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -36,6 +36,7 @@ #include "memory.h" #include "logging.h" #include "files.h" +#include "virtkey.h"
#define VIR_FROM_THIS VIR_FROM_QEMU
@@ -2369,6 +2370,42 @@ int qemuMonitorInjectNMI(qemuMonitorPtr mon) return ret; }
+int qemuMonitorSendKey(qemuMonitorPtr mon, + unsigned int codeset, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes)
Remove the 'codeset' here and require the caller to always pass in XT keycodes, and have the caller do the translation
+{ + int ret; + + VIR_DEBUG("mon=%p, codeset=%s(%u), holdtime=%u, nkeycodes=%u", + mon, virKeycodeSetName(codeset), codeset, holdtime, nkeycodes); + + if (codeset != VIR_KEYCODE_SET_XT_KBD) { + int i; + int keycode; + + for (i = 0; i < nkeycodes; i++) { + keycode = virTranslateKeyCode(codeset, VIR_KEYCODE_SET_XT_KBD, + keycodes[i]); + if (keycode < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "can not translate " + "keycode %u of %s codeset to xt_kbd codeset " + "keycode", keycodes[i], + virKeycodeSetName(codeset)); + return -1; + } + keycodes[i] = keycode; + } + }
I think this code should be in the qemuDomainSendKey(). We like to keep the qemuMonitorXXX() methods to only contain code that is directly related to using the QEMU monitor.
+ + if (mon->json) + ret = qemuMonitorJSONSendKey(mon, holdtime, keycodes, nkeycodes); + else + ret = qemuMonitorTextSendKey(mon, holdtime, keycodes, nkeycodes); + return ret; +} + int qemuMonitorScreendump(qemuMonitorPtr mon, const char *file) {
Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Add virtkey lib for usage-improvment and keycode translating. Expose send-key in virsh Implement send-key function for the qemu driver Daniel P. Berrange (1): util: Add keymaps.csv Lai Jiangshan (3): util: add virtkey send-key: Expose the new API in virsh qemu:send-key: Implement the driver methods include/libvirt/libvirt.h.in | 8 + src/Makefile.am | 11 +- src/libvirt_private.syms | 5 + src/qemu/qemu_driver.c | 71 +++++++ src/qemu/qemu_monitor.c | 17 ++ src/qemu/qemu_monitor.h | 5 + src/qemu/qemu_monitor_json.c | 15 ++ src/qemu/qemu_monitor_json.h | 5 + src/qemu/qemu_monitor_text.c | 49 +++++ src/qemu/qemu_monitor_text.h | 5 + src/util/keymaps.csv | 463 ++++++++++++++++++++++++++++++++++++++++++ src/util/virtkey.c | 117 +++++++++++ src/util/virtkey.h | 41 ++++ src/util/virtkeymap-gen.py | 47 +++++ tools/virsh.c | 94 +++++++++ tools/virsh.pod | 4 + 16 files changed, 956 insertions(+), 1 deletions(-) create mode 100644 src/util/keymaps.csv create mode 100644 src/util/virtkey.c create mode 100644 src/util/virtkey.h create mode 100644 src/util/virtkeymap-gen.py -- 1.7.4.4

From: Daniel P. Berrange <berrange@redhat.com> Should keep it as the same as: http://git.gnome.org/browse/gtk-vnc/commit/src/keymaps.csv All master keymaps are defined in a CSV file. THis covers Linux keycodes, OSX keycodes, AT set1, 2 & 3, XT keycodes, the XT encoding used by the Linux KBD driver, USB keycodes, Win32 keycodes, the XT encoding used by Xorg on Cygwin, the XT encoding used by Xorg on Linux with kbd driver. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- src/util/keymaps.csv | 463 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 463 insertions(+), 0 deletions(-) create mode 100644 src/util/keymaps.csv diff --git a/src/util/keymaps.csv b/src/util/keymaps.csv new file mode 100644 index 0000000..c447f46 --- /dev/null +++ b/src/util/keymaps.csv @@ -0,0 +1,463 @@ +"Linux Name","Linux Keycode","OS-X Name","OS-X Keycode","AT set1 keycode","AT set2 keycode","AT set3 keycode",XT,"XT KBD","USB Keycodes","Win32 Name","Win32 Keycode","Xwin XT","Xfree86 KBD XT" +KEY_RESERVED,0,,,,,,,,,,,, +KEY_ESC,1,Escape,0x35,1,118,8,1,1,41,VK_ESCAPE,0x1b,1,1 +KEY_1,2,ANSI_1,0x12,2,22,22,2,2,30,VK_1,0x31,2,2 +KEY_2,3,ANSI_2,0x13,3,30,30,3,3,31,VK_2,0x32,3,3 +KEY_3,4,ANSI_3,0x14,4,38,38,4,4,32,VK_3,0x33,4,4 +KEY_4,5,ANSI_4,0x15,5,37,37,5,5,33,VK_4,0x34,5,5 +KEY_5,6,ANSI_5,0x17,6,46,46,6,6,34,VK_5,0x35,6,6 +KEY_6,7,ANSI_6,0x16,7,54,54,7,7,35,VK_6,0x36,7,7 +KEY_7,8,ANSI_7,0x1a,8,61,61,8,8,36,VK_7,0x37,8,8 +KEY_8,9,ANSI_8,0x1c,9,62,62,9,9,37,VK_8,0x38,9,9 +KEY_9,10,ANSI_9,0x19,10,70,70,10,10,38,VK_9,0x39,10,10 +KEY_0,11,ANSI_0,0x1d,11,69,69,11,11,39,VK_0,0x30,11,11 +KEY_MINUS,12,ANSI_Minus,0x1b,12,78,78,12,12,45,VK_OEM_MINUS,0xbd,12,12 +KEY_EQUAL,13,ANSI_Equal,0x18,13,85,85,13,13,46,VK_OEM_PLUS,0xbb,13,13 +KEY_BACKSPACE,14,Delete,0x33,14,102,102,14,14,42,VK_BACK,0x08,14,14 +KEY_TAB,15,Tab,0x30,15,13,13,15,15,43,VK_TAB,0x09,15,15 +KEY_Q,16,ANSI_Q,0xc,16,21,21,16,16,20,VK_Q,0x51,16,16 +KEY_W,17,ANSI_W,0xd,17,29,29,17,17,26,VK_W,0x57,17,17 +KEY_E,18,ANSI_E,0xe,18,36,36,18,18,8,VK_E,0x45,18,18 +KEY_R,19,ANSI_R,0xf,19,45,45,19,19,21,VK_R,0x52,19,19 +KEY_T,20,ANSI_T,0x11,20,44,44,20,20,23,VK_T,0x54,20,20 +KEY_Y,21,ANSI_Y,0x10,21,53,53,21,21,28,VK_Y,0x59,21,21 +KEY_U,22,ANSI_U,0x20,22,60,60,22,22,24,VK_U,0x55,22,22 +KEY_I,23,ANSI_I,0x22,23,67,67,23,23,12,VK_I,0x49,23,23 +KEY_O,24,ANSI_O,0x1f,24,68,68,24,24,18,VK_O,0x4f,24,24 +KEY_P,25,ANSI_P,0x23,25,77,77,25,25,19,VK_P,0x50,25,25 +KEY_LEFTBRACE,26,ANSI_LeftBracket,0x21,26,84,84,26,26,47,VK_OEM_4,0xdb,26,26 +KEY_RIGHTBRACE,27,ANSI_RightBracket,0x1e,27,91,91,27,27,48,VK_OEM_6,0xdd,27,27 +KEY_ENTER,28,Return,0x24,28,90,90,28,28,40,VK_RETURN,0x0d,28,28 +KEY_LEFTCTRL,29,Control,0x3b,29,20,17,29,29,224,VK_LCONTROL,0xa2,29,29 +KEY_LEFTCTRL,29,Control,0x3b,29,20,17,29,29,224,VK_CONTROL,0x11,29,29 +KEY_A,30,ANSI_A,0x0,30,28,28,30,30,4,VK_A,0x41,30,30 +KEY_S,31,ANSI_S,0x1,31,27,27,31,31,22,VK_S,0x53,31,31 +KEY_D,32,ANSI_D,0x2,32,35,35,32,32,7,VK_D,0x44,32,32 +KEY_F,33,ANSI_F,0x3,33,43,43,33,33,9,VK_F,0x46,33,33 +KEY_G,34,ANSI_G,0x5,34,52,52,34,34,10,VK_G,0x47,34,34 +KEY_H,35,ANSI_H,0x4,35,51,51,35,35,11,VK_H,0x48,35,35 +KEY_J,36,ANSI_J,0x26,36,59,59,36,36,13,VK_J,0x4a,36,36 +KEY_K,37,ANSI_K,0x28,37,66,66,37,37,14,VK_K,0x4b,37,37 +KEY_L,38,ANSI_L,0x25,38,75,75,38,38,15,VK_L,0x4c,38,38 +KEY_SEMICOLON,39,ANSI_Semicolon,0x29,39,76,76,39,39,51,VK_OEM_1,0xba,39,39 +KEY_APOSTROPHE,40,ANSI_Quote,0x27,40,82,82,40,40,52,VK_OEM_2,0xbf,40,40 +KEY_GRAVE,41,ANSI_Grave,0x32,41,14,14,41,41,53,VK_OEM_3,0xc0,41,41 +KEY_LEFTSHIFT,42,Shift,0x38,42,18,18,42,42,225,VK_LSHIFT,0xa0,42,42 +KEY_BACKSLASH,43,ANSI_Backslash,0x2a,43,93,93,43,43,50,VK_OEM_5,0xdc,43,43 +KEY_Z,44,ANSI_Z,0x6,44,26,26,44,44,29,VK_Z,0x5a,44,44 +KEY_X,45,ANSI_X,0x7,45,34,34,45,45,27,VK_X,0x58,45,45 +KEY_C,46,ANSI_C,0x8,46,33,33,46,46,6,VK_C,0x43,46,46 +KEY_V,47,ANSI_V,0x9,47,42,42,47,47,25,VK_V,0x56,47,47 +KEY_B,48,ANSI_B,0xb,48,50,50,48,48,5,VK_B,0x42,48,48 +KEY_N,49,ANSI_N,0x2d,49,49,49,49,49,17,VK_N,0x4e,49,49 +KEY_M,50,ANSI_M,0x2e,50,58,58,50,50,16,VK_M,0x4d,50,50 +KEY_COMMA,51,ANSI_Comma,0x2b,51,65,65,51,51,54,VK_OEM_COMMA,0xbc,51,51 +KEY_DOT,52,ANSI_Period,0x2f,52,73,73,52,52,55,VK_OEM_PERIOD,0xbe,52,52 +KEY_SLASH,53,ANSI_Slash,0x2c,53,74,74,53,53,56,VK_OEM_2,0xbf,53,53 +KEY_RIGHTSHIFT,54,RightShift,0x3c,54,89,89,54,54,229,VK_RSHIFT,0xa1,54,54 +KEY_KPASTERISK,55,ANSI_KeypadMultiply,0x43,55,124,126,55,55,85,VK_MULTIPLY,0x6a,55,55 +KEY_LEFTALT,56,Option,0x3a,56,17,25,56,56,226,VK_LMENU,0xa4,56,56 +KEY_LEFTALT,56,Option,0x3a,56,17,25,56,56,226,VK_MENU,0x12,56,56 +KEY_SPACE,57,Space,0x31,57,41,41,57,57,44,VK_SPACE,0x20,57,57 +KEY_CAPSLOCK,58,CapsLock,0x39,58,88,20,58,58,57,VK_CAPITAL,0x14,58,58 +KEY_F1,59,F1,0x7a,59,5,7,59,59,58,VK_F1,0x70,59,59 +KEY_F2,60,F2,0x78,60,6,15,60,60,59,VK_F2,0x71,60,60 +KEY_F3,61,F3,0x63,61,4,23,61,61,60,VK_F3,0x72,61,61 +KEY_F4,62,F4,0x76,62,12,31,62,62,61,VK_F4,0x73,62,62 +KEY_F5,63,F5,0x60,63,3,39,63,63,62,VK_F5,0x74,63,63 +KEY_F6,64,F6,0x61,64,11,47,64,64,63,VK_F6,0x75,64,64 +KEY_F7,65,F7,0x62,65,259,55,65,65,64,VK_F7,0x76,65,65 +KEY_F8,66,F8,0x64,66,10,63,66,66,65,VK_F8,0x77,66,66 +KEY_F9,67,F9,0x65,67,1,71,67,67,66,VK_F9,0x78,67,67 +KEY_F10,68,F10,0x6d,68,9,79,68,68,67,VK_F10,0x79,68,68 +KEY_NUMLOCK,69,,,69,119,118,69,69,83,VK_NUMLOCK,0x90,69,69 +KEY_SCROLLLOCK,70,,,70,126,95,70,70,71,VK_SCROLL,0x91,70,70 +KEY_KP7,71,ANSI_Keypad7,0x59,71,108,108,71,71,95,VK_NUMPAD7,0x67,71,71 +KEY_KP8,72,ANSI_Keypad8,0x5b,72,117,117,72,72,96,VK_NUMPAD8,0x68,72,72 +KEY_KP9,73,ANSI_Keypad9,0x5c,73,125,125,73,73,97,VK_NUMPAD9,0x69,73,73 +KEY_KPMINUS,74,ANSI_KeypadMinus,0x4e,74,123,132,74,74,86,VK_SUBTRACT,0x6d,74,74 +KEY_KP4,75,ANSI_Keypad4,0x56,75,107,107,75,75,92,VK_NUMPAD4,0x64,75,75 +KEY_KP5,76,ANSI_Keypad5,0x57,76,115,115,76,76,93,VK_NUMPAD5,0x65,76,76 +KEY_KP6,77,ANSI_Keypad6,0x58,77,116,116,77,77,94,VK_NUMPAD6,0x66,77,77 +KEY_KPPLUS,78,ANSI_KeypadPlus,0x45,78,121,124,78,78,87,VK_ADD,0x6b,78,78 +KEY_KP1,79,ANSI_Keypad1,0x53,79,105,105,79,79,89,VK_NUMPAD1,0x61,79,79 +KEY_KP2,80,ANSI_Keypad2,0x54,80,114,114,80,80,90,VK_NUMPAD2,0x62,80,80 +KEY_KP3,81,ANSI_Keypad3,0x55,81,122,122,81,81,91,VK_NUMPAD3,0x63,81,81 +KEY_KP0,82,ANSI_Keypad0,0x52,82,112,112,82,82,98,VK_NUMPAD0,0x60,82,82 +KEY_KPDOT,83,ANSI_KeypadDecimal,0x41,83,113,113,83,83,99,VK_DECIMAL,0x6e,83,83 +,84,,,,,,,84,,,,, +KEY_ZENKAKUHANKAKU,85,,,118,95,,,118,148,,,, +KEY_102ND,86,,,86,97,19,,86,100,VK_OEM_102,0xe1,, +KEY_F11,87,F11,0x67,87,120,86,101,87,68,VK_F11,0x7a,, +KEY_F12,88,F12,0x6f,88,7,94,102,88,69,VK_F12,0x7b,, +KEY_RO,89,,,115,81,,,115,135,,,, +KEY_KATAKANA,90,JIS_Kana????,0x68,120,99,,,120,146,VK_KANA,0x15,, +KEY_HIRAGANA,91,,,119,98,,,119,147,,,, +KEY_HENKAN,92,,,121,100,134,,121,138,,,, +KEY_KATAKANAHIRAGANA,93,,,112,19,135,,112,136,,,0xc8,0xc8 +KEY_MUHENKAN,94,,,123,103,133,,123,139,,,, +KEY_KPJPCOMMA,95,JIS_KeypadComma,0x5f,92,39,,,92,140,,,, +KEY_KPENTER,96,ANSI_KeypadEnter,0x4c,,158,121,,284,88,,,0x64,0x64 +KEY_RIGHTCTRL,97,RightControl,0x3e,,,88,,285,228,VK_RCONTROL,0xa3,0x65,0x65 +KEY_KPSLASH,98,ANSI_KeypadDivide,0x4b,,181,119,,309,84,VK_DIVIDE,0x6f,0x68,0x68 +KEY_SYSRQ,99,,,84,260,87,,84,70,"VK_SNAPSHOT ???",0x2c,0x67,0x67 +KEY_RIGHTALT,100,RightOption,0x3d,,,57,,312,230,VK_RMENU,0xa5,0x69,0x69 +KEY_LINEFEED,101,,,,,,,91,,,,, +KEY_HOME,102,Home,0x73,,224,110,,327,74,VK_HOME,0x24,0x59,0x59 +KEY_UP,103,UpArrow,0x7e,,236,99,109,328,82,VK_UP,0x26,0x5a,0x5a +KEY_PAGEUP,104,PageUp,0x74,,201,111,,329,75,VK_PRIOR,0x21,0x5b,0x5b +KEY_LEFT,105,LeftArrow,0x7b,,203,97,111,331,80,VK_LEFT,0x25,0x5c,0x5c +KEY_RIGHT,106,RightArrow,0x7c,,205,106,112,333,79,VK_RIGHT,0x27,0x5e,0x5e +KEY_END,107,End,0x77,,225,101,,335,77,VK_END,0x23,0x5f,0x5f +KEY_DOWN,108,DownArrow,0x7d,,254,96,110,336,81,VK_DOWN,0x28,0x60,0x60 +KEY_PAGEDOWN,109,PageDown,0x79,,243,109,,337,78,VK_NEXT,0x22,0x61,0x61 +KEY_INSERT,110,,,,210,103,107,338,73,VK_INSERT,0x2d,0x62,0x62 +KEY_DELETE,111,ForwardDelete,0x75,,244,100,108,339,76,VK_DELETE,0x2e,0x63,0x63 +KEY_MACRO,112,,,,239,142,,367,,,,, +KEY_MUTE,113,Mute,0x4a,,251,156,,288,239,VK_VOLUME_MUTE,0xad,, +KEY_VOLUMEDOWN,114,VolumeDown,0x49,,,157,,302,238,VK_VOLUME_DOWN,0xae,, +KEY_VOLUMEUP,115,VolumeUp,0x48,,233,149,,304,237,VK_VOLUME_UP,0xaf,, +KEY_POWER,116,,,,,,,350,102,,,, +KEY_KPEQUAL,117,ANSI_KeypadEquals,0x51,89,15,,,89,103,,,0x76,0x76 +KEY_KPPLUSMINUS,118,,,,206,,,334,,,,, +KEY_PAUSE,119,,,,198,98,,326,72,VK_PAUSE,0x013,0x66,0x66 +KEY_SCALE,120,,,,,,,267,,,,, +KEY_KPCOMMA,121,ANSI_KeypadClear????,0x47,126,109,,,126,133,VK_SEPARATOR??,0x6c,, +KEY_HANGEUL,122,,,,,,,,144,VK_HANGEUL,0x15,, +KEY_HANJA,123,,,,,,,269,145,VK_HANJA,0x19,, +KEY_YEN,124,JIS_Yen,0x5d,125,106,,,125,137,,,0x7d,0x7d +KEY_LEFTMETA,125,Command,0x37,,,139,,347,227,VK_LWIN,0x5b,0x6b,0x6b +KEY_RIGHTMETA,126,,,,,140,,348,231,VK_RWIN,0x5c,0x6c,0x6c +KEY_COMPOSE,127,Function,0x3f,,,141,,349,101,VK_APPS,0x5d,0x6d,0x6d +KEY_STOP,128,,,,,10,,360,243,VK_BROWSER_STOP,0xa9,, +KEY_AGAIN,129,,,,,11,,261,121,,,, +KEY_PROPS,130,,,,,12,,262,118,,,, +KEY_UNDO,131,,,,,16,,263,122,,,, +KEY_FRONT,132,,,,,,,268,119,,,, +KEY_COPY,133,,,,,24,,376,124,,,, +KEY_OPEN,134,,,,,32,,100,116,,,, +KEY_PASTE,135,,,,,40,,101,125,,,, +KEY_FIND,136,,,,,48,,321,244,,,, +KEY_CUT,137,,,,,56,,316,123,,,, +KEY_HELP,138,,,,,9,,373,117,VK_HELP,0x2f,, +KEY_MENU,139,,,,,145,,286,,,,, +KEY_CALC,140,,,,174,163,,289,251,,,, +KEY_SETUP,141,,,,,,,102,,,,, +KEY_SLEEP,142,,,,,,,351,248,VK_SLEEP,0x5f,, +KEY_WAKEUP,143,,,,,,,355,,,,, +KEY_FILE,144,,,,,,,103,,,,, +KEY_SENDFILE,145,,,,,,,104,,,,, +KEY_DELETEFILE,146,,,,,,,105,,,,, +KEY_XFER,147,,,,,162,,275,,,,, +KEY_PROG1,148,,,,,160,,287,,,,, +KEY_PROG2,149,,,,,161,,279,,,,, +KEY_WWW,150,,,,,,,258,240,,,, +KEY_MSDOS,151,,,,,,,106,,,,, +KEY_SCREENLOCK,152,,,,,150,,274,249,,,, +KEY_DIRECTION,153,,,,,,,107,,,,, +KEY_CYCLEWINDOWS,154,,,,,155,,294,,,,, +KEY_MAIL,155,,,,,,,364,,,,, +KEY_BOOKMARKS,156,,,,,,,358,,,,, +KEY_COMPUTER,157,,,,,,,363,,,,, +KEY_BACK,158,,,,,,,362,241,VK_BROWSER_BACK,0xa6,, +KEY_FORWARD,159,,,,,,,361,242,VK_BROWSER_FORWARD,0xa7,, +KEY_CLOSECD,160,,,,,154,,291,,,,, +KEY_EJECTCD,161,,,,,,,108,236,,,, +KEY_EJECTCLOSECD,162,,,,,,,381,,,,, +KEY_NEXTSONG,163,,,,241,147,,281,235,VK_MEDIA_NEXT_TRACK,0xb0,, +KEY_PLAYPAUSE,164,,,,173,,,290,232,VK_MEDIA_PLAY_PAUSE,0xb3,, +KEY_PREVIOUSSONG,165,,,,250,148,,272,234,VK_MEDIA_PREV_TRACK,0xb1,, +KEY_STOPCD,166,,,,164,152,,292,233,VK_MEDIA_STOP,0xb2,, +KEY_RECORD,167,,,,,158,,305,,,,, +KEY_REWIND,168,,,,,159,,280,,,,, +KEY_PHONE,169,,,,,,,99,,,,, +KEY_ISO,170,ISO_Section,0xa,,,,,112,,,,, +KEY_CONFIG,171,,,,,,,257,,,,, +KEY_HOMEPAGE,172,,,,178,151,,306,,VK_BROWSER_HOME,0xac,, +KEY_REFRESH,173,,,,,,,359,250,VK_BROWSER_REFRESH,0xa8,, +KEY_EXIT,174,,,,,,,113,,,,, +KEY_MOVE,175,,,,,,,114,,,,, +KEY_EDIT,176,,,,,,,264,247,,,, +KEY_SCROLLUP,177,,,,,,,117,245,,,, +KEY_SCROLLDOWN,178,,,,,,,271,246,,,, +KEY_KPLEFTPAREN,179,,,,,,,374,182,,,, +KEY_KPRIGHTPAREN,180,,,,,,,379,183,,,, +KEY_NEW,181,,,,,,,265,,,,, +KEY_REDO,182,,,,,,,266,,,,, +KEY_F13,183,F13,0x69,93,47,127,,93,104,VK_F13,0x7c,0x6e,0x6e +KEY_F14,184,F14,0x6b,94,55,128,,94,105,VK_F14,0x7d,0x6f,0x6f +KEY_F15,185,F15,0x71,95,63,129,,95,106,VK_F15,0x7e,0x70,0x70 +KEY_F16,186,F16,0x6a,,,130,,85,107,VK_F16,0x7f,0x71,0x71 +KEY_F17,187,F17,0x40,,,131,,259,108,VK_F17,0x80,0x72,0x72 +KEY_F18,188,F18,0x4f,,,,,375,109,VK_F18,0x81,, +KEY_F19,189,F19,0x50,,,,,260,110,VK_F19,0x82,, +KEY_F20,190,F20,0x5a,,,,,90,111,VK_F20,0x83,, +KEY_F21,191,,,,,,,116,112,VK_F21,0x84,, +KEY_F22,192,,,,,,,377,113,VK_F22,0x85,, +KEY_F23,193,,,,,,,109,114,VK_F23,0x86,, +KEY_F24,194,,,,,,,111,115,VK_F24,0x87,, +,195,,,,,,,277,,,,, +,196,,,,,,,278,,,,, +,197,,,,,,,282,,,,, +,198,,,,,,,283,,,,, +,199,,,,,,,295,,,,, +KEY_PLAYCD,200,,,,,,,296,,,,, +KEY_PAUSECD,201,,,,,,,297,,,,, +KEY_PROG3,202,,,,,,,299,,,,, +KEY_PROG4,203,,,,,,,300,,,,, +KEY_DASHBOARD,204,,,,,,,301,,,,, +KEY_SUSPEND,205,,,,,,,293,,,,, +KEY_CLOSE,206,,,,,,,303,,,,, +KEY_PLAY,207,,,,,,,307,,VK_PLAY,0xfa,, +KEY_FASTFORWARD,208,,,,,,,308,,,,, +KEY_BASSBOOST,209,,,,,,,310,,,,, +KEY_PRINT,210,,,,,,,313,,VK_PRINT,0x2a,, +KEY_HP,211,,,,,,,314,,,,, +KEY_CAMERA,212,,,,,,,315,,,,, +KEY_SOUND,213,,,,,,,317,,,,, +KEY_QUESTION,214,,,,,,,318,,,,, +KEY_EMAIL,215,,,,,,,319,,VK_LAUNCH_MAIL,0xb4,, +KEY_CHAT,216,,,,,,,320,,,,, +KEY_SEARCH,217,,,,,,,357,,VK_BROWSER_SEARCH,0xaa,, +KEY_CONNECT,218,,,,,,,322,,,,, +KEY_FINANCE,219,,,,,,,323,,,,, +KEY_SPORT,220,,,,,,,324,,,,, +KEY_SHOP,221,,,,,,,325,,,,, +KEY_ALTERASE,222,,,,,,,276,,,,, +KEY_CANCEL,223,,,,,,,330,,,,, +KEY_BRIGHTNESSDOWN,224,,,,,,,332,,,,, +KEY_BRIGHTNESSUP,225,,,,,,,340,,,,, +KEY_MEDIA,226,,,,,,,365,,,,, +KEY_SWITCHVIDEOMODE,227,,,,,,,342,,,,, +KEY_KBDILLUMTOGGLE,228,,,,,,,343,,,,, +KEY_KBDILLUMDOWN,229,,,,,,,344,,,,, +KEY_KBDILLUMUP,230,,,,,,,345,,,,, +KEY_SEND,231,,,,,,,346,,,,, +KEY_REPLY,232,,,,,,,356,,,,, +KEY_FORWARDMAIL,233,,,,,,,270,,,,, +KEY_SAVE,234,,,,,,,341,,,,, +KEY_DOCUMENTS,235,,,,,,,368,,,,, +KEY_BATTERY,236,,,,,,,369,,,,, +KEY_BLUETOOTH,237,,,,,,,370,,,,, +KEY_WLAN,238,,,,,,,371,,,,, +KEY_UWB,239,,,,,,,372,,,,, +KEY_UNKNOWN,240,,,,,,,,,,,, +KEY_VIDEO_NEXT,241,,,,,,,,,,,, +KEY_VIDEO_PREV,242,,,,,,,,,,,, +KEY_BRIGHTNESS_CYCLE,243,,,,,,,,,,,, +KEY_BRIGHTNESS_ZERO,244,,,,,,,,,,,, +KEY_DISPLAY_OFF,245,,,,,,,,,,,, +KEY_WIMAX,246,,,,,,,,,,,, +,247,,,,,,,,,,,, +,248,,,,,,,,,,,, +,249,,,,,,,,,,,, +,250,,,,,,,,,,,, +,251,,,,,,,,,,,, +,252,,,,,,,,,,,, +,253,,,,,,,,,,,, +,254,,,,,,,,,,,, +,255,,,,182,,,,,,,, +BTN_MISC,0x100,,,,,,,,,,,, +BTN_0,0x100,,,,,,,,,VK_LBUTTON,0x01,, +BTN_1,0x101,,,,,,,,,VK_RBUTTON,0x02,, +BTN_2,0x102,,,,,,,,,VK_MBUTTON,0x04,, +BTN_3,0x103,,,,,,,,,VK_XBUTTON1,0x05,, +BTN_4,0x104,,,,,,,,,VK_XBUTTON2,0x06,, +BTN_5,0x105,,,,,,,,,,,, +BTN_6,0x106,,,,,,,,,,,, +BTN_7,0x107,,,,,,,,,,,, +BTN_8,0x108,,,,,,,,,,,, +BTN_9,0x109,,,,,,,,,,,, +BTN_MOUSE,0x110,,,,,,,,,,,, +BTN_LEFT,0x110,,,,,,,,,,,, +BTN_RIGHT,0x111,,,,,,,,,,,, +BTN_MIDDLE,0x112,,,,,,,,,,,, +BTN_SIDE,0x113,,,,,,,,,,,, +BTN_EXTRA,0x114,,,,,,,,,,,, +BTN_FORWARD,0x115,,,,,,,,,,,, +BTN_BACK,0x116,,,,,,,,,,,, +BTN_TASK,0x117,,,,,,,,,,,, +BTN_JOYSTICK,0x120,,,,,,,,,,,, +BTN_TRIGGER,0x120,,,,,,,,,,,, +BTN_THUMB,0x121,,,,,,,,,,,, +BTN_THUMB2,0x122,,,,,,,,,,,, +BTN_TOP,0x123,,,,,,,,,,,, +BTN_TOP2,0x124,,,,,,,,,,,, +BTN_PINKIE,0x125,,,,,,,,,,,, +BTN_BASE,0x126,,,,,,,,,,,, +BTN_BASE2,0x127,,,,,,,,,,,, +BTN_BASE3,0x128,,,,,,,,,,,, +BTN_BASE4,0x129,,,,,,,,,,,, +BTN_BASE5,0x12a,,,,,,,,,,,, +BTN_BASE6,0x12b,,,,,,,,,,,, +BTN_DEAD,0x12f,,,,,,,,,,,, +BTN_GAMEPAD,0x130,,,,,,,,,,,, +BTN_A,0x130,,,,,,,,,,,, +BTN_B,0x131,,,,,,,,,,,, +BTN_C,0x132,,,,,,,,,,,, +BTN_X,0x133,,,,,,,,,,,, +BTN_Y,0x134,,,,,,,,,,,, +BTN_Z,0x135,,,,,,,,,,,, +BTN_TL,0x136,,,,,,,,,,,, +BTN_TR,0x137,,,,,,,,,,,, +BTN_TL2,0x138,,,,,,,,,,,, +BTN_TR2,0x139,,,,,,,,,,,, +BTN_SELECT,0x13a,,,,,,,,,,,, +BTN_START,0x13b,,,,,,,,,,,, +BTN_MODE,0x13c,,,,,,,,,,,, +BTN_THUMBL,0x13d,,,,,,,,,,,, +BTN_THUMBR,0x13e,,,,,,,,,,,, +BTN_DIGI,0x140,,,,,,,,,,,, +BTN_TOOL_PEN,0x140,,,,,,,,,,,, +BTN_TOOL_RUBBER,0x141,,,,,,,,,,,, +BTN_TOOL_BRUSH,0x142,,,,,,,,,,,, +BTN_TOOL_PENCIL,0x143,,,,,,,,,,,, +BTN_TOOL_AIRBRUSH,0x144,,,,,,,,,,,, +BTN_TOOL_FINGER,0x145,,,,,,,,,,,, +BTN_TOOL_MOUSE,0x146,,,,,,,,,,,, +BTN_TOOL_LENS,0x147,,,,,,,,,,,, +BTN_TOUCH,0x14a,,,,,,,,,,,, +BTN_STYLUS,0x14b,,,,,,,,,,,, +BTN_STYLUS2,0x14c,,,,,,,,,,,, +BTN_TOOL_DOUBLETAP,0x14d,,,,,,,,,,,, +BTN_TOOL_TRIPLETAP,0x14e,,,,,,,,,,,, +BTN_TOOL_QUADTAP,0x14f,,,,,,,,,,,, +BTN_WHEEL,0x150,,,,,,,,,,,, +BTN_GEAR_DOWN,0x150,,,,,,,,,,,, +BTN_GEAR_UP,0x151,,,,,,,,,,,, +KEY_OK,0x160,,,,,,,,,,,, +KEY_SELECT,0x161,,,,,,,,,VK_SELECT,0x29,, +KEY_GOTO,0x162,,,,,,,,,,,, +KEY_CLEAR,0x163,,,,,,,,,,,, +KEY_POWER2,0x164,,,,,,,,,,,, +KEY_OPTION,0x165,,,,,,,,,,,, +KEY_INFO,0x166,,,,,,,,,,,, +KEY_TIME,0x167,,,,,,,,,,,, +KEY_VENDOR,0x168,,,,,,,,,,,, +KEY_ARCHIVE,0x169,,,,,,,,,,,, +KEY_PROGRAM,0x16a,,,,,,,,,,,, +KEY_CHANNEL,0x16b,,,,,,,,,,,, +KEY_FAVORITES,0x16c,,,,,,,,,VK_BROWSER_FAVOURITES,0xab,, +KEY_EPG,0x16d,,,,,,,,,,,, +KEY_PVR,0x16e,,,,,,,,,,,, +KEY_MHP,0x16f,,,,,,,,,,,, +KEY_LANGUAGE,0x170,,,,,,,,,,,, +KEY_TITLE,0x171,,,,,,,,,,,, +KEY_SUBTITLE,0x172,,,,,,,,,,,, +KEY_ANGLE,0x173,,,,,,,,,,,, +KEY_ZOOM,0x174,,,,,,,,,VK_ZOOM,0xfb,, +KEY_MODE,0x175,,,,,,,,,,,, +KEY_KEYBOARD,0x176,,,,,,,,,,,, +KEY_SCREEN,0x177,,,,,,,,,,,, +KEY_PC,0x178,,,,,,,,,,,, +KEY_TV,0x179,,,,,,,,,,,, +KEY_TV2,0x17a,,,,,,,,,,,, +KEY_VCR,0x17b,,,,,,,,,,,, +KEY_VCR2,0x17c,,,,,,,,,,,, +KEY_SAT,0x17d,,,,,,,,,,,, +KEY_SAT2,0x17e,,,,,,,,,,,, +KEY_CD,0x17f,,,,,,,,,,,, +KEY_TAPE,0x180,,,,,,,,,,,, +KEY_RADIO,0x181,,,,,,,,,,,, +KEY_TUNER,0x182,,,,,,,,,,,, +KEY_PLAYER,0x183,,,,,,,,,,,, +KEY_TEXT,0x184,,,,,,,,,,,, +KEY_DVD,0x185,,,,,,,,,,,, +KEY_AUX,0x186,,,,,,,,,,,, +KEY_MP3,0x187,,,,,,,,,,,, +KEY_AUDIO,0x188,,,,,,,,,,,, +KEY_VIDEO,0x189,,,,,,,,,,,, +KEY_DIRECTORY,0x18a,,,,,,,,,,,, +KEY_LIST,0x18b,,,,,,,,,,,, +KEY_MEMO,0x18c,,,,,,,,,,,, +KEY_CALENDAR,0x18d,,,,,,,,,,,, +KEY_RED,0x18e,,,,,,,,,,,, +KEY_GREEN,0x18f,,,,,,,,,,,, +KEY_YELLOW,0x190,,,,,,,,,,,, +KEY_BLUE,0x191,,,,,,,,,,,, +KEY_CHANNELUP,0x192,,,,,,,,,,,, +KEY_CHANNELDOWN,0x193,,,,,,,,,,,, +KEY_FIRST,0x194,,,,,,,,,,,, +KEY_LAST,0x195,,,,,,,,,,,, +KEY_AB,0x196,,,,,,,,,,,, +KEY_NEXT,0x197,,,,,,,,,,,, +KEY_RESTART,0x198,,,,,,,,,,,, +KEY_SLOW,0x199,,,,,,,,,,,, +KEY_SHUFFLE,0x19a,,,,,,,,,,,, +KEY_BREAK,0x19b,,,,,,,,,,,, +KEY_PREVIOUS,0x19c,,,,,,,,,,,, +KEY_DIGITS,0x19d,,,,,,,,,,,, +KEY_TEEN,0x19e,,,,,,,,,,,, +KEY_TWEN,0x19f,,,,,,,,,,,, +KEY_VIDEOPHONE,0x1a0,,,,,,,,,,,, +KEY_GAMES,0x1a1,,,,,,,,,,,, +KEY_ZOOMIN,0x1a2,,,,,,,,,,,, +KEY_ZOOMOUT,0x1a3,,,,,,,,,,,, +KEY_ZOOMRESET,0x1a4,,,,,,,,,,,, +KEY_WORDPROCESSOR,0x1a5,,,,,,,,,,,, +KEY_EDITOR,0x1a6,,,,,,,,,,,, +KEY_SPREADSHEET,0x1a7,,,,,,,,,,,, +KEY_GRAPHICSEDITOR,0x1a8,,,,,,,,,,,, +KEY_PRESENTATION,0x1a9,,,,,,,,,,,, +KEY_DATABASE,0x1aa,,,,,,,,,,,, +KEY_NEWS,0x1ab,,,,,,,,,,,, +KEY_VOICEMAIL,0x1ac,,,,,,,,,,,, +KEY_ADDRESSBOOK,0x1ad,,,,,,,,,,,, +KEY_MESSENGER,0x1ae,,,,,,,,,,,, +KEY_DISPLAYTOGGLE,0x1af,,,,,,,,,,,, +KEY_SPELLCHECK,0x1b0,,,,,,,,,,,, +KEY_LOGOFF,0x1b1,,,,,,,,,,,, +KEY_DOLLAR,0x1b2,,,,,,,,,,,, +KEY_EURO,0x1b3,,,,,,,,,,,, +KEY_FRAMEBACK,0x1b4,,,,,,,,,,,, +KEY_FRAMEFORWARD,0x1b5,,,,,,,,,,,, +KEY_CONTEXT_MENU,0x1b6,,,,,,,,,,,, +KEY_MEDIA_REPEAT,0x1b7,,,,,,,,,,,, +KEY_DEL_EOL,0x1c0,,,,,,,,,,,, +KEY_DEL_EOS,0x1c1,,,,,,,,,,,, +KEY_INS_LINE,0x1c2,,,,,,,,,,,, +KEY_DEL_LINE,0x1c3,,,,,,,,,,,, +KEY_FN,0x1d0,,,,,,,,,,,, +KEY_FN_ESC,0x1d1,,,,,,,,,,,, +KEY_FN_F1,0x1d2,,,,,,,,,,,, +KEY_FN_F2,0x1d3,,,,,,,,,,,, +KEY_FN_F3,0x1d4,,,,,,,,,,,, +KEY_FN_F4,0x1d5,,,,,,,,,,,, +KEY_FN_F5,0x1d6,,,,,,,,,,,, +KEY_FN_F6,0x1d7,,,,,,,,,,,, +KEY_FN_F7,0x1d8,,,,,,,,,,,, +KEY_FN_F8,0x1d9,,,,,,,,,,,, +KEY_FN_F9,0x1da,,,,,,,,,,,, +KEY_FN_F10,0x1db,,,,,,,,,,,, +KEY_FN_F11,0x1dc,,,,,,,,,,,, +KEY_FN_F12,0x1dd,,,,,,,,,,,, +KEY_FN_1,0x1de,,,,,,,,,,,, +KEY_FN_2,0x1df,,,,,,,,,,,, +KEY_FN_D,0x1e0,,,,,,,,,,,, +KEY_FN_E,0x1e1,,,,,,,,,,,, +KEY_FN_F,0x1e2,,,,,,,,,,,, +KEY_FN_S,0x1e3,,,,,,,,,,,, +KEY_FN_B,0x1e4,,,,,,,,,,,, +KEY_BRL_DOT1,0x1f1,,,,,,,,,,,, +KEY_BRL_DOT2,0x1f2,,,,,,,,,,,, +KEY_BRL_DOT3,0x1f3,,,,,,,,,,,, +KEY_BRL_DOT4,0x1f4,,,,,,,,,,,, +KEY_BRL_DOT5,0x1f5,,,,,,,,,,,, +KEY_BRL_DOT6,0x1f6,,,,,,,,,,,, +KEY_BRL_DOT7,0x1f7,,,,,,,,,,,, +KEY_BRL_DOT8,0x1f8,,,,,,,,,,,, +KEY_BRL_DOT9,0x1f9,,,,,,,,,,,, +KEY_BRL_DOT10,0x1fa,,,,,,,,,,,, +KEY_NUMERIC_0,0x200,,,,,,,,,,,, +KEY_NUMERIC_1,0x201,,,,,,,,,,,, +KEY_NUMERIC_2,0x202,,,,,,,,,,,, +KEY_NUMERIC_3,0x203,,,,,,,,,,,, +KEY_NUMERIC_4,0x204,,,,,,,,,,,, +KEY_NUMERIC_5,0x205,,,,,,,,,,,, +KEY_NUMERIC_6,0x206,,,,,,,,,,,, +KEY_NUMERIC_7,0x207,,,,,,,,,,,, +KEY_NUMERIC_8,0x208,,,,,,,,,,,, +KEY_NUMERIC_9,0x209,,,,,,,,,,,, +KEY_NUMERIC_STAR,0x20a,,,,,,,,,,,, +KEY_NUMERIC_POUND,0x20b,,,,,,,,,,,, +KEY_RFKILL,0x20c,,,,,,,,,,,, -- 1.7.4.4

On Thu, Jul 14, 2011 at 11:32:15AM +0800, Lai Jiangshan wrote:
From: Daniel P. Berrange <berrange@redhat.com>
Should keep it as the same as: http://git.gnome.org/browse/gtk-vnc/commit/src/keymaps.csv
All master keymaps are defined in a CSV file. THis covers Linux keycodes, OSX keycodes, AT set1, 2 & 3, XT keycodes, the XT encoding used by the Linux KBD driver, USB keycodes, Win32 keycodes, the XT encoding used by Xorg on Cygwin, the XT encoding used by Xorg on Linux with kbd driver.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- src/util/keymaps.csv | 463 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 463 insertions(+), 0 deletions(-) create mode 100644 src/util/keymaps.csv
ACK, but this should likely go into 'EXTRA_DIST' too, in case a distro maintainer needs to patch it. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 07/19/2011 08:57 AM, Daniel P. Berrange wrote:
On Thu, Jul 14, 2011 at 11:32:15AM +0800, Lai Jiangshan wrote:
From: Daniel P. Berrange<berrange@redhat.com>
Should keep it as the same as: http://git.gnome.org/browse/gtk-vnc/commit/src/keymaps.csv
We probably also ought to add a rule to cfg.mk that automatically runs wget on the canonical upstream location any time a maintainer runs 'make dist', although I haven't written that part.
ACK, but this should likely go into 'EXTRA_DIST' too, in case a distro maintainer needs to patch it.
Squash this in to address Dan's comment: diff --git i/src/Makefile.am w/src/Makefile.am index f4ff489..2a6b0e4 100644 --- i/src/Makefile.am +++ w/src/Makefile.am @@ -18,7 +18,7 @@ AM_CFLAGS = $(DRIVER_MODULE_CFLAGS) \ $(COVERAGE_CFLAGS) AM_LDFLAGS = $(COVERAGE_LDFLAGS) -EXTRA_DIST = $(conf_DATA) +EXTRA_DIST = $(conf_DATA) util/keymaps.csv BUILT_SOURCES = -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

Add virtkey lib for usage-improvment and keycode translating. Add 4 internal API for the aim const char *virKeycodeSetTypeToString(int codeset); int virKeycodeSetTypeFromString(const char *name); int virParseKeyName(virKeycodeSet codeset, const char *keyname); int virTranslateKeyCode(virKeycodeSet from_codeset, virKeycodeSet to_offset, int key_value); Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- include/libvirt/libvirt.h.in | 8 +++ src/Makefile.am | 11 ++++- src/libvirt_private.syms | 5 ++ src/util/virtkey.c | 117 ++++++++++++++++++++++++++++++++++++++++++ src/util/virtkey.h | 41 +++++++++++++++ src/util/virtkeymap-gen.py | 47 +++++++++++++++++ 6 files changed, 228 insertions(+), 1 deletions(-) create mode 100644 src/util/virtkey.c create mode 100644 src/util/virtkey.h create mode 100644 src/util/virtkeymap-gen.py diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index d5a7105..acfe9d9 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1778,6 +1778,14 @@ typedef enum { VIR_KEYCODE_SET_ATSET1 = 2, VIR_KEYCODE_SET_ATSET2 = 3, VIR_KEYCODE_SET_ATSET3 = 4, + VIR_KEYCODE_SET_OSX = 5, + VIR_KEYCODE_SET_XT_KBD = 6, + VIR_KEYCODE_SET_USB = 7, + VIR_KEYCODE_SET_WIN32 = 8, + VIR_KEYCODE_SET_XWIN_XT = 9, + VIR_KEYCODE_SET_XFREE86_KBD_XT = 10, + + VIR_KEYCODE_SET_LAST, } virKeycodeSet; /** diff --git a/src/Makefile.am b/src/Makefile.am index 39f0cf8..90b0743 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -81,7 +81,16 @@ UTIL_SOURCES = \ util/util.c util/util.h \ util/viraudit.c util/viraudit.h \ util/xml.c util/xml.h \ - util/virterror.c util/virterror_internal.h + util/virterror.c util/virterror_internal.h \ + util/virtkey.c util/virtkey.h \ + util/virtkeymaps.c + +EXTRA_DIST += $(srcdir)/util/virtkeymaps.c $(srcdir)/util/keymaps.csv \ + $(srcdir)/util/virtkeymap-gen.py + +$(srcdir)/util/virtkeymaps.c: $(srcdir)/util/keymaps.csv \ + $(srcdir)/util/virtkeymap-gen.py + python $(srcdir)/util/virtkeymap-gen.py <$(srcdir)/util/keymaps.csv >$@ EXTRA_DIST += util/threads-pthread.c util/threads-win32.c diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 3237d18..6611471 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1094,6 +1094,11 @@ virSetError; virSetErrorLogPriorityFunc; virStrerror; +# virtkey.h +virKeycodeSetTypeToString; +virKeycodeSetTypeFromString; +virParseKeyName; +virTranslateKeyCode; # xml.h virXMLParseFileHelper; diff --git a/src/util/virtkey.c b/src/util/virtkey.c new file mode 100644 index 0000000..1eb3c62 --- /dev/null +++ b/src/util/virtkey.c @@ -0,0 +1,117 @@ + +/* + * Copyright (c) 2011 Lai Jiangshan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include "virtkey.h" +#include <string.h> +#include <stddef.h> + +#define getfield(object, field_type, field_offset) \ + (*(typeof(field_type) *)((char *)(object) + field_offset)) + +static unsigned int codeOffset[] = { + [VIR_KEYCODE_SET_LINUX] = + offsetof(struct keycode, linux_keycode), + [VIR_KEYCODE_SET_XT] = + offsetof(struct keycode, xt), + [VIR_KEYCODE_SET_ATSET1] = + offsetof(struct keycode, atset1), + [VIR_KEYCODE_SET_ATSET2] = + offsetof(struct keycode, atset2), + [VIR_KEYCODE_SET_ATSET3] = + offsetof(struct keycode, atset3), + [VIR_KEYCODE_SET_OSX] = + offsetof(struct keycode, os_x), + [VIR_KEYCODE_SET_XT_KBD] = + offsetof(struct keycode, xt_kbd), + [VIR_KEYCODE_SET_USB] = + offsetof(struct keycode, usb), + [VIR_KEYCODE_SET_WIN32] = + offsetof(struct keycode, win32), + [VIR_KEYCODE_SET_XWIN_XT] = + offsetof(struct keycode, xwin_xt), + [VIR_KEYCODE_SET_XFREE86_KBD_XT] = + offsetof(struct keycode, xfree86_kbd_xt), +}; + +VIR_ENUM_IMPL(virKeycodeSet, VIR_KEYCODE_SET_LAST, + "linux", + "xt", + "atset1", + "atset2", + "atset3", + "os_x", + "xt_kbd", + "usb", + "win32", + "xwin_xt", + "xfree86_kbd_xt"); + +static int virParseKeyNameOffset(unsigned int name_offset, + unsigned int code_offset, + const char *keyname) +{ + int i; + + for (i = 0; i < virtKeycodesSize; i++) { + const char *name = getfield(virtKeycodes + i, const char *, name_offset); + + if (name && !strcmp(name, keyname)) + return getfield(virtKeycodes + i, unsigned short, code_offset); + } + + return -1; +} + +int virParseKeyName(virKeycodeSet codeset, const char *keyname) +{ + switch (codeset) { + case VIR_KEYCODE_SET_LINUX: + return virParseKeyNameOffset(offsetof(struct keycode, linux_name), + offsetof(struct keycode, linux_keycode), keyname); + case VIR_KEYCODE_SET_OSX: + return virParseKeyNameOffset(offsetof(struct keycode, os_x_name), + offsetof(struct keycode, os_x), keyname); + case VIR_KEYCODE_SET_WIN32: + return virParseKeyNameOffset(offsetof(struct keycode, win32_name), + offsetof(struct keycode, win32), keyname); + default: + return -1; + } +} + +static int virTranslateKeyCodeOffset(unsigned int from_offset, + unsigned int to_offset, + int key_value) +{ + int i; + + for (i = 0; i < virtKeycodesSize; i++) { + if (getfield(virtKeycodes + i, unsigned short, from_offset) == key_value) + return getfield(virtKeycodes + i, unsigned short, to_offset); + } + + return -1; +} + +int virTranslateKeyCode(virKeycodeSet from_codeset, + virKeycodeSet to_codeset, + int key_value) +{ + if (key_value <= 0) + return -1; + + key_value = virTranslateKeyCodeOffset(codeOffset[from_codeset], + codeOffset[to_codeset], + key_value); + if (key_value <= 0) + return -1; + + return key_value; +} + diff --git a/src/util/virtkey.h b/src/util/virtkey.h new file mode 100644 index 0000000..45c5f08 --- /dev/null +++ b/src/util/virtkey.h @@ -0,0 +1,41 @@ +#ifndef __UTIL_VIRTKEY_H__ +#define __UTIL_VIRTKEY_H__ + +/* + * Copyright (c) 2011 Lai Jiangshan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <config.h> +#include "util.h" +#include "libvirt/libvirt.h" + +struct keycode { + const char *linux_name; + const char *os_x_name; + const char *win32_name; + unsigned short linux_keycode; + unsigned short os_x; + unsigned short atset1; + unsigned short atset2; + unsigned short atset3; + unsigned short xt; + unsigned short xt_kbd; + unsigned short usb; + unsigned short win32; + unsigned short xwin_xt; + unsigned short xfree86_kbd_xt; +}; +extern struct keycode virtKeycodes[]; +extern int virtKeycodesSize; + +VIR_ENUM_DECL(virKeycodeSet); +int virParseKeyName(virKeycodeSet codeset, const char *keyname); +int virTranslateKeyCode(virKeycodeSet from_codeset, + virKeycodeSet to_offset, + int key_value); + +#endif diff --git a/src/util/virtkeymap-gen.py b/src/util/virtkeymap-gen.py new file mode 100644 index 0000000..69ddec1 --- /dev/null +++ b/src/util/virtkeymap-gen.py @@ -0,0 +1,47 @@ +#!/bin/python + +""" +Generate the big keycodes table for virtkeys. +It read keymaps.csv from stdin and put the generated code to stdout. + +Please keep keymaps.csv be exactly the same as: +http://git.gnome.org/browse/gtk-vnc/plain/src/keymaps.csv. +If anything inconsistent happens, please change this file +instead of keymaps.csv which is a mirror. +""" + +import sys +import re + +namecolums = (0,2,10) + +def quotestring(str): + if str[0] != '"': + return '"' + str + '"' + return str + +print '#include "virtkey.h"' +print + +print "/* Generated file, DON'T edit it */" +print + +print 'struct keycode virtKeycodes[] = {' + +sys.stdin.readline() # eat the fist line. + +for line in sys.stdin.xreadlines(): + a = re.match("([^,]*)," * 13 + "([^,]*)$", line[0:-1]).groups() + b = "" + for i in namecolums: + b = b + (a[i] and quotestring(a[i]) or 'NULL') + ',' + for i in [ x for x in range(14) if not x in namecolums ]: + b = b + (a[i] or '0') + ',' + print " { " + b + "}," + +print '};' + +print +print 'int virtKeycodesSize = ARRAY_CARDINALITY(virtKeycodes);' +print + -- 1.7.4.4

On Thu, Jul 14, 2011 at 11:32:16AM +0800, Lai Jiangshan wrote:
Add virtkey lib for usage-improvment and keycode translating. Add 4 internal API for the aim
const char *virKeycodeSetTypeToString(int codeset); int virKeycodeSetTypeFromString(const char *name); int virParseKeyName(virKeycodeSet codeset, const char *keyname); int virTranslateKeyCode(virKeycodeSet from_codeset, virKeycodeSet to_offset, int key_value);
Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- include/libvirt/libvirt.h.in | 8 +++ src/Makefile.am | 11 ++++- src/libvirt_private.syms | 5 ++ src/util/virtkey.c | 117 ++++++++++++++++++++++++++++++++++++++++++ src/util/virtkey.h | 41 +++++++++++++++
Can we call these two 'virkeycode.h' and 'virkeycode.c'
src/util/virtkeymap-gen.py | 47 +++++++++++++++++
And 'virkeycode-mapgen.py'
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index d5a7105..acfe9d9 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1778,6 +1778,14 @@ typedef enum { VIR_KEYCODE_SET_ATSET1 = 2, VIR_KEYCODE_SET_ATSET2 = 3, VIR_KEYCODE_SET_ATSET3 = 4, + VIR_KEYCODE_SET_OSX = 5, + VIR_KEYCODE_SET_XT_KBD = 6, + VIR_KEYCODE_SET_USB = 7, + VIR_KEYCODE_SET_WIN32 = 8, + VIR_KEYCODE_SET_XWIN_XT = 9, + VIR_KEYCODE_SET_XFREE86_KBD_XT = 10,
I have confirmed that "XT_KBD" is right for the QEMU keys, because this matches what we use for the VNC protocol. I would still like us to leave XWIN_XT and XFREE86_KBD_XT out of the headers for now.
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 3237d18..6611471 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1094,6 +1094,11 @@ virSetError; virSetErrorLogPriorityFunc; virStrerror;
+# virtkey.h +virKeycodeSetTypeToString; +virKeycodeSetTypeFromString; +virParseKeyName; +virTranslateKeyCode;
As a (newish) rule, all function name prefixes should match the source filename. So can you change these to virKeycodeValueFromString virKeycodeValueTranslate
+static unsigned int codeOffset[] = { + [VIR_KEYCODE_SET_LINUX] = + offsetof(struct keycode, linux_keycode), + [VIR_KEYCODE_SET_XT] = + offsetof(struct keycode, xt), + [VIR_KEYCODE_SET_ATSET1] = + offsetof(struct keycode, atset1), + [VIR_KEYCODE_SET_ATSET2] = + offsetof(struct keycode, atset2), + [VIR_KEYCODE_SET_ATSET3] = + offsetof(struct keycode, atset3), + [VIR_KEYCODE_SET_OSX] = + offsetof(struct keycode, os_x), + [VIR_KEYCODE_SET_XT_KBD] = + offsetof(struct keycode, xt_kbd), + [VIR_KEYCODE_SET_USB] = + offsetof(struct keycode, usb), + [VIR_KEYCODE_SET_WIN32] = + offsetof(struct keycode, win32), + [VIR_KEYCODE_SET_XWIN_XT] = + offsetof(struct keycode, xwin_xt), + [VIR_KEYCODE_SET_XFREE86_KBD_XT] = + offsetof(struct keycode, xfree86_kbd_xt), +}; + +VIR_ENUM_IMPL(virKeycodeSet, VIR_KEYCODE_SET_LAST, + "linux", + "xt", + "atset1", + "atset2", + "atset3", + "os_x", + "xt_kbd", + "usb", + "win32", + "xwin_xt", + "xfree86_kbd_xt");
Likewise leave out xin_xt and xfree86_kbd_xt
diff --git a/src/util/virtkey.h b/src/util/virtkey.h new file mode 100644 index 0000000..45c5f08 --- /dev/null +++ b/src/util/virtkey.h @@ -0,0 +1,41 @@ +#ifndef __UTIL_VIRTKEY_H__ +#define __UTIL_VIRTKEY_H__ + +/* + * Copyright (c) 2011 Lai Jiangshan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <config.h> +#include "util.h" +#include "libvirt/libvirt.h" + +struct keycode { + const char *linux_name; + const char *os_x_name; + const char *win32_name; + unsigned short linux_keycode; + unsigned short os_x; + unsigned short atset1; + unsigned short atset2; + unsigned short atset3; + unsigned short xt; + unsigned short xt_kbd; + unsigned short usb; + unsigned short win32; + unsigned short xwin_xt; + unsigned short xfree86_kbd_xt; +}; +extern struct keycode virtKeycodes[]; +extern int virtKeycodesSize;
None of these three declarations need to be in the header file since they are private impl details of the source fiel.
+ +VIR_ENUM_DECL(virKeycodeSet); +int virParseKeyName(virKeycodeSet codeset, const char *keyname); +int virTranslateKeyCode(virKeycodeSet from_codeset, + virKeycodeSet to_offset, + int key_value); + +#endif diff --git a/src/util/virtkeymap-gen.py b/src/util/virtkeymap-gen.py
Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 07/19/2011 09:04 AM, Daniel P. Berrange wrote:
On Thu, Jul 14, 2011 at 11:32:16AM +0800, Lai Jiangshan wrote:
Add virtkey lib for usage-improvment and keycode translating. Add 4 internal API for the aim
+ +struct keycode {
+extern struct keycode virtKeycodes[]; +extern int virtKeycodesSize;
None of these three declarations need to be in the header file since they are private impl details of the source fiel.
Well, as written in this patch, they are used in both virtkey.c, and by the generated virkeymaps.c. But, a better idea would be: virkey.h: public functions (virKeycodeSetToString, virKeycodeSetFromString [both from VIR_ENUM_DECL], virKeycodeValueFromString, virKeycodeValueTranslate) virkey.c: #include "virkey.h" #define VIR_KEY_INTERNAL struct keycode {} ... #include "virkeymap.h" implement the public functions virkeymap-gen.pl: generates virkeymap.h virkeymap.h: #ifndef VIR_KEY_INTERNAL # error do not use this; it is not a public header #endif static struct keycode keycodes[] = { ... } that way, the generated file is not a separate c file, but a chunk of included code to complete the single virkey.c. See for example how remote_driver.c does a #include "remote_client_bodies.h" in the middle of the file, and make sure that the Makefile.am snippets for virkeymap.h (making sure it is in EXTRA_DIST and so forth) resemble the rules for remote_client_bodies.h. I was debating about whipping this series into shape according to Dan's review comments, since you got acks on the other 3 out of 4, but the changes to patch 2 are looking to be significant enough to need a v5. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

Add virtkey lib for usage-improvment and keycode translating. Expose send-key in virsh Implement send-key function for the qemu driver Daniel P. Berrange (1): util: Add keymaps.csv Lai Jiangshan (3): util: add virtkey send-key: Expose the new API in virsh qemu:send-key: Implement the driver methods include/libvirt/libvirt.h.in | 6 + src/Makefile.am | 14 +- src/libvirt_private.syms | 5 + src/qemu/qemu_driver.c | 71 +++++++ src/qemu/qemu_monitor.c | 17 ++ src/qemu/qemu_monitor.h | 5 + src/qemu/qemu_monitor_json.c | 15 ++ src/qemu/qemu_monitor_json.h | 5 + src/qemu/qemu_monitor_text.c | 49 +++++ src/qemu/qemu_monitor_text.h | 5 + src/util/keymaps.csv | 463 +++++++++++++++++++++++++++++++++++++++++ src/util/virkey.c | 145 +++++++++++++ src/util/virkeycode-mapgen.py | 45 ++++ src/util/virkeycode.h | 35 +++ tools/virsh.c | 94 +++++++++ tools/virsh.pod | 4 + 16 files changed, 976 insertions(+), 2 deletions(-) create mode 100644 src/util/keymaps.csv create mode 100644 src/util/virkey.c create mode 100644 src/util/virkeycode-mapgen.py create mode 100644 src/util/virkeycode.h -- 1.7.4.4

From: Daniel P. Berrange <berrange@redhat.com> Should keep it as the same as: http://git.gnome.org/browse/gtk-vnc/commit/src/keymaps.csv All master keymaps are defined in a CSV file. THis covers Linux keycodes, OSX keycodes, AT set1, 2 & 3, XT keycodes, the XT encoding used by the Linux KBD driver, USB keycodes, Win32 keycodes, the XT encoding used by Xorg on Cygwin, the XT encoding used by Xorg on Linux with kbd driver. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- src/Makefile.am | 2 +- src/util/keymaps.csv | 463 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 464 insertions(+), 1 deletions(-) create mode 100644 src/util/keymaps.csv diff --git a/src/Makefile.am b/src/Makefile.am index f4ff489..2a6b0e4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -18,7 +18,7 @@ AM_CFLAGS = $(DRIVER_MODULE_CFLAGS) \ $(COVERAGE_CFLAGS) AM_LDFLAGS = $(COVERAGE_LDFLAGS) -EXTRA_DIST = $(conf_DATA) +EXTRA_DIST = $(conf_DATA) util/keymaps.csv BUILT_SOURCES = diff --git a/src/util/keymaps.csv b/src/util/keymaps.csv new file mode 100644 index 0000000..c447f46 --- /dev/null +++ b/src/util/keymaps.csv @@ -0,0 +1,463 @@ +"Linux Name","Linux Keycode","OS-X Name","OS-X Keycode","AT set1 keycode","AT set2 keycode","AT set3 keycode",XT,"XT KBD","USB Keycodes","Win32 Name","Win32 Keycode","Xwin XT","Xfree86 KBD XT" +KEY_RESERVED,0,,,,,,,,,,,, +KEY_ESC,1,Escape,0x35,1,118,8,1,1,41,VK_ESCAPE,0x1b,1,1 +KEY_1,2,ANSI_1,0x12,2,22,22,2,2,30,VK_1,0x31,2,2 +KEY_2,3,ANSI_2,0x13,3,30,30,3,3,31,VK_2,0x32,3,3 +KEY_3,4,ANSI_3,0x14,4,38,38,4,4,32,VK_3,0x33,4,4 +KEY_4,5,ANSI_4,0x15,5,37,37,5,5,33,VK_4,0x34,5,5 +KEY_5,6,ANSI_5,0x17,6,46,46,6,6,34,VK_5,0x35,6,6 +KEY_6,7,ANSI_6,0x16,7,54,54,7,7,35,VK_6,0x36,7,7 +KEY_7,8,ANSI_7,0x1a,8,61,61,8,8,36,VK_7,0x37,8,8 +KEY_8,9,ANSI_8,0x1c,9,62,62,9,9,37,VK_8,0x38,9,9 +KEY_9,10,ANSI_9,0x19,10,70,70,10,10,38,VK_9,0x39,10,10 +KEY_0,11,ANSI_0,0x1d,11,69,69,11,11,39,VK_0,0x30,11,11 +KEY_MINUS,12,ANSI_Minus,0x1b,12,78,78,12,12,45,VK_OEM_MINUS,0xbd,12,12 +KEY_EQUAL,13,ANSI_Equal,0x18,13,85,85,13,13,46,VK_OEM_PLUS,0xbb,13,13 +KEY_BACKSPACE,14,Delete,0x33,14,102,102,14,14,42,VK_BACK,0x08,14,14 +KEY_TAB,15,Tab,0x30,15,13,13,15,15,43,VK_TAB,0x09,15,15 +KEY_Q,16,ANSI_Q,0xc,16,21,21,16,16,20,VK_Q,0x51,16,16 +KEY_W,17,ANSI_W,0xd,17,29,29,17,17,26,VK_W,0x57,17,17 +KEY_E,18,ANSI_E,0xe,18,36,36,18,18,8,VK_E,0x45,18,18 +KEY_R,19,ANSI_R,0xf,19,45,45,19,19,21,VK_R,0x52,19,19 +KEY_T,20,ANSI_T,0x11,20,44,44,20,20,23,VK_T,0x54,20,20 +KEY_Y,21,ANSI_Y,0x10,21,53,53,21,21,28,VK_Y,0x59,21,21 +KEY_U,22,ANSI_U,0x20,22,60,60,22,22,24,VK_U,0x55,22,22 +KEY_I,23,ANSI_I,0x22,23,67,67,23,23,12,VK_I,0x49,23,23 +KEY_O,24,ANSI_O,0x1f,24,68,68,24,24,18,VK_O,0x4f,24,24 +KEY_P,25,ANSI_P,0x23,25,77,77,25,25,19,VK_P,0x50,25,25 +KEY_LEFTBRACE,26,ANSI_LeftBracket,0x21,26,84,84,26,26,47,VK_OEM_4,0xdb,26,26 +KEY_RIGHTBRACE,27,ANSI_RightBracket,0x1e,27,91,91,27,27,48,VK_OEM_6,0xdd,27,27 +KEY_ENTER,28,Return,0x24,28,90,90,28,28,40,VK_RETURN,0x0d,28,28 +KEY_LEFTCTRL,29,Control,0x3b,29,20,17,29,29,224,VK_LCONTROL,0xa2,29,29 +KEY_LEFTCTRL,29,Control,0x3b,29,20,17,29,29,224,VK_CONTROL,0x11,29,29 +KEY_A,30,ANSI_A,0x0,30,28,28,30,30,4,VK_A,0x41,30,30 +KEY_S,31,ANSI_S,0x1,31,27,27,31,31,22,VK_S,0x53,31,31 +KEY_D,32,ANSI_D,0x2,32,35,35,32,32,7,VK_D,0x44,32,32 +KEY_F,33,ANSI_F,0x3,33,43,43,33,33,9,VK_F,0x46,33,33 +KEY_G,34,ANSI_G,0x5,34,52,52,34,34,10,VK_G,0x47,34,34 +KEY_H,35,ANSI_H,0x4,35,51,51,35,35,11,VK_H,0x48,35,35 +KEY_J,36,ANSI_J,0x26,36,59,59,36,36,13,VK_J,0x4a,36,36 +KEY_K,37,ANSI_K,0x28,37,66,66,37,37,14,VK_K,0x4b,37,37 +KEY_L,38,ANSI_L,0x25,38,75,75,38,38,15,VK_L,0x4c,38,38 +KEY_SEMICOLON,39,ANSI_Semicolon,0x29,39,76,76,39,39,51,VK_OEM_1,0xba,39,39 +KEY_APOSTROPHE,40,ANSI_Quote,0x27,40,82,82,40,40,52,VK_OEM_2,0xbf,40,40 +KEY_GRAVE,41,ANSI_Grave,0x32,41,14,14,41,41,53,VK_OEM_3,0xc0,41,41 +KEY_LEFTSHIFT,42,Shift,0x38,42,18,18,42,42,225,VK_LSHIFT,0xa0,42,42 +KEY_BACKSLASH,43,ANSI_Backslash,0x2a,43,93,93,43,43,50,VK_OEM_5,0xdc,43,43 +KEY_Z,44,ANSI_Z,0x6,44,26,26,44,44,29,VK_Z,0x5a,44,44 +KEY_X,45,ANSI_X,0x7,45,34,34,45,45,27,VK_X,0x58,45,45 +KEY_C,46,ANSI_C,0x8,46,33,33,46,46,6,VK_C,0x43,46,46 +KEY_V,47,ANSI_V,0x9,47,42,42,47,47,25,VK_V,0x56,47,47 +KEY_B,48,ANSI_B,0xb,48,50,50,48,48,5,VK_B,0x42,48,48 +KEY_N,49,ANSI_N,0x2d,49,49,49,49,49,17,VK_N,0x4e,49,49 +KEY_M,50,ANSI_M,0x2e,50,58,58,50,50,16,VK_M,0x4d,50,50 +KEY_COMMA,51,ANSI_Comma,0x2b,51,65,65,51,51,54,VK_OEM_COMMA,0xbc,51,51 +KEY_DOT,52,ANSI_Period,0x2f,52,73,73,52,52,55,VK_OEM_PERIOD,0xbe,52,52 +KEY_SLASH,53,ANSI_Slash,0x2c,53,74,74,53,53,56,VK_OEM_2,0xbf,53,53 +KEY_RIGHTSHIFT,54,RightShift,0x3c,54,89,89,54,54,229,VK_RSHIFT,0xa1,54,54 +KEY_KPASTERISK,55,ANSI_KeypadMultiply,0x43,55,124,126,55,55,85,VK_MULTIPLY,0x6a,55,55 +KEY_LEFTALT,56,Option,0x3a,56,17,25,56,56,226,VK_LMENU,0xa4,56,56 +KEY_LEFTALT,56,Option,0x3a,56,17,25,56,56,226,VK_MENU,0x12,56,56 +KEY_SPACE,57,Space,0x31,57,41,41,57,57,44,VK_SPACE,0x20,57,57 +KEY_CAPSLOCK,58,CapsLock,0x39,58,88,20,58,58,57,VK_CAPITAL,0x14,58,58 +KEY_F1,59,F1,0x7a,59,5,7,59,59,58,VK_F1,0x70,59,59 +KEY_F2,60,F2,0x78,60,6,15,60,60,59,VK_F2,0x71,60,60 +KEY_F3,61,F3,0x63,61,4,23,61,61,60,VK_F3,0x72,61,61 +KEY_F4,62,F4,0x76,62,12,31,62,62,61,VK_F4,0x73,62,62 +KEY_F5,63,F5,0x60,63,3,39,63,63,62,VK_F5,0x74,63,63 +KEY_F6,64,F6,0x61,64,11,47,64,64,63,VK_F6,0x75,64,64 +KEY_F7,65,F7,0x62,65,259,55,65,65,64,VK_F7,0x76,65,65 +KEY_F8,66,F8,0x64,66,10,63,66,66,65,VK_F8,0x77,66,66 +KEY_F9,67,F9,0x65,67,1,71,67,67,66,VK_F9,0x78,67,67 +KEY_F10,68,F10,0x6d,68,9,79,68,68,67,VK_F10,0x79,68,68 +KEY_NUMLOCK,69,,,69,119,118,69,69,83,VK_NUMLOCK,0x90,69,69 +KEY_SCROLLLOCK,70,,,70,126,95,70,70,71,VK_SCROLL,0x91,70,70 +KEY_KP7,71,ANSI_Keypad7,0x59,71,108,108,71,71,95,VK_NUMPAD7,0x67,71,71 +KEY_KP8,72,ANSI_Keypad8,0x5b,72,117,117,72,72,96,VK_NUMPAD8,0x68,72,72 +KEY_KP9,73,ANSI_Keypad9,0x5c,73,125,125,73,73,97,VK_NUMPAD9,0x69,73,73 +KEY_KPMINUS,74,ANSI_KeypadMinus,0x4e,74,123,132,74,74,86,VK_SUBTRACT,0x6d,74,74 +KEY_KP4,75,ANSI_Keypad4,0x56,75,107,107,75,75,92,VK_NUMPAD4,0x64,75,75 +KEY_KP5,76,ANSI_Keypad5,0x57,76,115,115,76,76,93,VK_NUMPAD5,0x65,76,76 +KEY_KP6,77,ANSI_Keypad6,0x58,77,116,116,77,77,94,VK_NUMPAD6,0x66,77,77 +KEY_KPPLUS,78,ANSI_KeypadPlus,0x45,78,121,124,78,78,87,VK_ADD,0x6b,78,78 +KEY_KP1,79,ANSI_Keypad1,0x53,79,105,105,79,79,89,VK_NUMPAD1,0x61,79,79 +KEY_KP2,80,ANSI_Keypad2,0x54,80,114,114,80,80,90,VK_NUMPAD2,0x62,80,80 +KEY_KP3,81,ANSI_Keypad3,0x55,81,122,122,81,81,91,VK_NUMPAD3,0x63,81,81 +KEY_KP0,82,ANSI_Keypad0,0x52,82,112,112,82,82,98,VK_NUMPAD0,0x60,82,82 +KEY_KPDOT,83,ANSI_KeypadDecimal,0x41,83,113,113,83,83,99,VK_DECIMAL,0x6e,83,83 +,84,,,,,,,84,,,,, +KEY_ZENKAKUHANKAKU,85,,,118,95,,,118,148,,,, +KEY_102ND,86,,,86,97,19,,86,100,VK_OEM_102,0xe1,, +KEY_F11,87,F11,0x67,87,120,86,101,87,68,VK_F11,0x7a,, +KEY_F12,88,F12,0x6f,88,7,94,102,88,69,VK_F12,0x7b,, +KEY_RO,89,,,115,81,,,115,135,,,, +KEY_KATAKANA,90,JIS_Kana????,0x68,120,99,,,120,146,VK_KANA,0x15,, +KEY_HIRAGANA,91,,,119,98,,,119,147,,,, +KEY_HENKAN,92,,,121,100,134,,121,138,,,, +KEY_KATAKANAHIRAGANA,93,,,112,19,135,,112,136,,,0xc8,0xc8 +KEY_MUHENKAN,94,,,123,103,133,,123,139,,,, +KEY_KPJPCOMMA,95,JIS_KeypadComma,0x5f,92,39,,,92,140,,,, +KEY_KPENTER,96,ANSI_KeypadEnter,0x4c,,158,121,,284,88,,,0x64,0x64 +KEY_RIGHTCTRL,97,RightControl,0x3e,,,88,,285,228,VK_RCONTROL,0xa3,0x65,0x65 +KEY_KPSLASH,98,ANSI_KeypadDivide,0x4b,,181,119,,309,84,VK_DIVIDE,0x6f,0x68,0x68 +KEY_SYSRQ,99,,,84,260,87,,84,70,"VK_SNAPSHOT ???",0x2c,0x67,0x67 +KEY_RIGHTALT,100,RightOption,0x3d,,,57,,312,230,VK_RMENU,0xa5,0x69,0x69 +KEY_LINEFEED,101,,,,,,,91,,,,, +KEY_HOME,102,Home,0x73,,224,110,,327,74,VK_HOME,0x24,0x59,0x59 +KEY_UP,103,UpArrow,0x7e,,236,99,109,328,82,VK_UP,0x26,0x5a,0x5a +KEY_PAGEUP,104,PageUp,0x74,,201,111,,329,75,VK_PRIOR,0x21,0x5b,0x5b +KEY_LEFT,105,LeftArrow,0x7b,,203,97,111,331,80,VK_LEFT,0x25,0x5c,0x5c +KEY_RIGHT,106,RightArrow,0x7c,,205,106,112,333,79,VK_RIGHT,0x27,0x5e,0x5e +KEY_END,107,End,0x77,,225,101,,335,77,VK_END,0x23,0x5f,0x5f +KEY_DOWN,108,DownArrow,0x7d,,254,96,110,336,81,VK_DOWN,0x28,0x60,0x60 +KEY_PAGEDOWN,109,PageDown,0x79,,243,109,,337,78,VK_NEXT,0x22,0x61,0x61 +KEY_INSERT,110,,,,210,103,107,338,73,VK_INSERT,0x2d,0x62,0x62 +KEY_DELETE,111,ForwardDelete,0x75,,244,100,108,339,76,VK_DELETE,0x2e,0x63,0x63 +KEY_MACRO,112,,,,239,142,,367,,,,, +KEY_MUTE,113,Mute,0x4a,,251,156,,288,239,VK_VOLUME_MUTE,0xad,, +KEY_VOLUMEDOWN,114,VolumeDown,0x49,,,157,,302,238,VK_VOLUME_DOWN,0xae,, +KEY_VOLUMEUP,115,VolumeUp,0x48,,233,149,,304,237,VK_VOLUME_UP,0xaf,, +KEY_POWER,116,,,,,,,350,102,,,, +KEY_KPEQUAL,117,ANSI_KeypadEquals,0x51,89,15,,,89,103,,,0x76,0x76 +KEY_KPPLUSMINUS,118,,,,206,,,334,,,,, +KEY_PAUSE,119,,,,198,98,,326,72,VK_PAUSE,0x013,0x66,0x66 +KEY_SCALE,120,,,,,,,267,,,,, +KEY_KPCOMMA,121,ANSI_KeypadClear????,0x47,126,109,,,126,133,VK_SEPARATOR??,0x6c,, +KEY_HANGEUL,122,,,,,,,,144,VK_HANGEUL,0x15,, +KEY_HANJA,123,,,,,,,269,145,VK_HANJA,0x19,, +KEY_YEN,124,JIS_Yen,0x5d,125,106,,,125,137,,,0x7d,0x7d +KEY_LEFTMETA,125,Command,0x37,,,139,,347,227,VK_LWIN,0x5b,0x6b,0x6b +KEY_RIGHTMETA,126,,,,,140,,348,231,VK_RWIN,0x5c,0x6c,0x6c +KEY_COMPOSE,127,Function,0x3f,,,141,,349,101,VK_APPS,0x5d,0x6d,0x6d +KEY_STOP,128,,,,,10,,360,243,VK_BROWSER_STOP,0xa9,, +KEY_AGAIN,129,,,,,11,,261,121,,,, +KEY_PROPS,130,,,,,12,,262,118,,,, +KEY_UNDO,131,,,,,16,,263,122,,,, +KEY_FRONT,132,,,,,,,268,119,,,, +KEY_COPY,133,,,,,24,,376,124,,,, +KEY_OPEN,134,,,,,32,,100,116,,,, +KEY_PASTE,135,,,,,40,,101,125,,,, +KEY_FIND,136,,,,,48,,321,244,,,, +KEY_CUT,137,,,,,56,,316,123,,,, +KEY_HELP,138,,,,,9,,373,117,VK_HELP,0x2f,, +KEY_MENU,139,,,,,145,,286,,,,, +KEY_CALC,140,,,,174,163,,289,251,,,, +KEY_SETUP,141,,,,,,,102,,,,, +KEY_SLEEP,142,,,,,,,351,248,VK_SLEEP,0x5f,, +KEY_WAKEUP,143,,,,,,,355,,,,, +KEY_FILE,144,,,,,,,103,,,,, +KEY_SENDFILE,145,,,,,,,104,,,,, +KEY_DELETEFILE,146,,,,,,,105,,,,, +KEY_XFER,147,,,,,162,,275,,,,, +KEY_PROG1,148,,,,,160,,287,,,,, +KEY_PROG2,149,,,,,161,,279,,,,, +KEY_WWW,150,,,,,,,258,240,,,, +KEY_MSDOS,151,,,,,,,106,,,,, +KEY_SCREENLOCK,152,,,,,150,,274,249,,,, +KEY_DIRECTION,153,,,,,,,107,,,,, +KEY_CYCLEWINDOWS,154,,,,,155,,294,,,,, +KEY_MAIL,155,,,,,,,364,,,,, +KEY_BOOKMARKS,156,,,,,,,358,,,,, +KEY_COMPUTER,157,,,,,,,363,,,,, +KEY_BACK,158,,,,,,,362,241,VK_BROWSER_BACK,0xa6,, +KEY_FORWARD,159,,,,,,,361,242,VK_BROWSER_FORWARD,0xa7,, +KEY_CLOSECD,160,,,,,154,,291,,,,, +KEY_EJECTCD,161,,,,,,,108,236,,,, +KEY_EJECTCLOSECD,162,,,,,,,381,,,,, +KEY_NEXTSONG,163,,,,241,147,,281,235,VK_MEDIA_NEXT_TRACK,0xb0,, +KEY_PLAYPAUSE,164,,,,173,,,290,232,VK_MEDIA_PLAY_PAUSE,0xb3,, +KEY_PREVIOUSSONG,165,,,,250,148,,272,234,VK_MEDIA_PREV_TRACK,0xb1,, +KEY_STOPCD,166,,,,164,152,,292,233,VK_MEDIA_STOP,0xb2,, +KEY_RECORD,167,,,,,158,,305,,,,, +KEY_REWIND,168,,,,,159,,280,,,,, +KEY_PHONE,169,,,,,,,99,,,,, +KEY_ISO,170,ISO_Section,0xa,,,,,112,,,,, +KEY_CONFIG,171,,,,,,,257,,,,, +KEY_HOMEPAGE,172,,,,178,151,,306,,VK_BROWSER_HOME,0xac,, +KEY_REFRESH,173,,,,,,,359,250,VK_BROWSER_REFRESH,0xa8,, +KEY_EXIT,174,,,,,,,113,,,,, +KEY_MOVE,175,,,,,,,114,,,,, +KEY_EDIT,176,,,,,,,264,247,,,, +KEY_SCROLLUP,177,,,,,,,117,245,,,, +KEY_SCROLLDOWN,178,,,,,,,271,246,,,, +KEY_KPLEFTPAREN,179,,,,,,,374,182,,,, +KEY_KPRIGHTPAREN,180,,,,,,,379,183,,,, +KEY_NEW,181,,,,,,,265,,,,, +KEY_REDO,182,,,,,,,266,,,,, +KEY_F13,183,F13,0x69,93,47,127,,93,104,VK_F13,0x7c,0x6e,0x6e +KEY_F14,184,F14,0x6b,94,55,128,,94,105,VK_F14,0x7d,0x6f,0x6f +KEY_F15,185,F15,0x71,95,63,129,,95,106,VK_F15,0x7e,0x70,0x70 +KEY_F16,186,F16,0x6a,,,130,,85,107,VK_F16,0x7f,0x71,0x71 +KEY_F17,187,F17,0x40,,,131,,259,108,VK_F17,0x80,0x72,0x72 +KEY_F18,188,F18,0x4f,,,,,375,109,VK_F18,0x81,, +KEY_F19,189,F19,0x50,,,,,260,110,VK_F19,0x82,, +KEY_F20,190,F20,0x5a,,,,,90,111,VK_F20,0x83,, +KEY_F21,191,,,,,,,116,112,VK_F21,0x84,, +KEY_F22,192,,,,,,,377,113,VK_F22,0x85,, +KEY_F23,193,,,,,,,109,114,VK_F23,0x86,, +KEY_F24,194,,,,,,,111,115,VK_F24,0x87,, +,195,,,,,,,277,,,,, +,196,,,,,,,278,,,,, +,197,,,,,,,282,,,,, +,198,,,,,,,283,,,,, +,199,,,,,,,295,,,,, +KEY_PLAYCD,200,,,,,,,296,,,,, +KEY_PAUSECD,201,,,,,,,297,,,,, +KEY_PROG3,202,,,,,,,299,,,,, +KEY_PROG4,203,,,,,,,300,,,,, +KEY_DASHBOARD,204,,,,,,,301,,,,, +KEY_SUSPEND,205,,,,,,,293,,,,, +KEY_CLOSE,206,,,,,,,303,,,,, +KEY_PLAY,207,,,,,,,307,,VK_PLAY,0xfa,, +KEY_FASTFORWARD,208,,,,,,,308,,,,, +KEY_BASSBOOST,209,,,,,,,310,,,,, +KEY_PRINT,210,,,,,,,313,,VK_PRINT,0x2a,, +KEY_HP,211,,,,,,,314,,,,, +KEY_CAMERA,212,,,,,,,315,,,,, +KEY_SOUND,213,,,,,,,317,,,,, +KEY_QUESTION,214,,,,,,,318,,,,, +KEY_EMAIL,215,,,,,,,319,,VK_LAUNCH_MAIL,0xb4,, +KEY_CHAT,216,,,,,,,320,,,,, +KEY_SEARCH,217,,,,,,,357,,VK_BROWSER_SEARCH,0xaa,, +KEY_CONNECT,218,,,,,,,322,,,,, +KEY_FINANCE,219,,,,,,,323,,,,, +KEY_SPORT,220,,,,,,,324,,,,, +KEY_SHOP,221,,,,,,,325,,,,, +KEY_ALTERASE,222,,,,,,,276,,,,, +KEY_CANCEL,223,,,,,,,330,,,,, +KEY_BRIGHTNESSDOWN,224,,,,,,,332,,,,, +KEY_BRIGHTNESSUP,225,,,,,,,340,,,,, +KEY_MEDIA,226,,,,,,,365,,,,, +KEY_SWITCHVIDEOMODE,227,,,,,,,342,,,,, +KEY_KBDILLUMTOGGLE,228,,,,,,,343,,,,, +KEY_KBDILLUMDOWN,229,,,,,,,344,,,,, +KEY_KBDILLUMUP,230,,,,,,,345,,,,, +KEY_SEND,231,,,,,,,346,,,,, +KEY_REPLY,232,,,,,,,356,,,,, +KEY_FORWARDMAIL,233,,,,,,,270,,,,, +KEY_SAVE,234,,,,,,,341,,,,, +KEY_DOCUMENTS,235,,,,,,,368,,,,, +KEY_BATTERY,236,,,,,,,369,,,,, +KEY_BLUETOOTH,237,,,,,,,370,,,,, +KEY_WLAN,238,,,,,,,371,,,,, +KEY_UWB,239,,,,,,,372,,,,, +KEY_UNKNOWN,240,,,,,,,,,,,, +KEY_VIDEO_NEXT,241,,,,,,,,,,,, +KEY_VIDEO_PREV,242,,,,,,,,,,,, +KEY_BRIGHTNESS_CYCLE,243,,,,,,,,,,,, +KEY_BRIGHTNESS_ZERO,244,,,,,,,,,,,, +KEY_DISPLAY_OFF,245,,,,,,,,,,,, +KEY_WIMAX,246,,,,,,,,,,,, +,247,,,,,,,,,,,, +,248,,,,,,,,,,,, +,249,,,,,,,,,,,, +,250,,,,,,,,,,,, +,251,,,,,,,,,,,, +,252,,,,,,,,,,,, +,253,,,,,,,,,,,, +,254,,,,,,,,,,,, +,255,,,,182,,,,,,,, +BTN_MISC,0x100,,,,,,,,,,,, +BTN_0,0x100,,,,,,,,,VK_LBUTTON,0x01,, +BTN_1,0x101,,,,,,,,,VK_RBUTTON,0x02,, +BTN_2,0x102,,,,,,,,,VK_MBUTTON,0x04,, +BTN_3,0x103,,,,,,,,,VK_XBUTTON1,0x05,, +BTN_4,0x104,,,,,,,,,VK_XBUTTON2,0x06,, +BTN_5,0x105,,,,,,,,,,,, +BTN_6,0x106,,,,,,,,,,,, +BTN_7,0x107,,,,,,,,,,,, +BTN_8,0x108,,,,,,,,,,,, +BTN_9,0x109,,,,,,,,,,,, +BTN_MOUSE,0x110,,,,,,,,,,,, +BTN_LEFT,0x110,,,,,,,,,,,, +BTN_RIGHT,0x111,,,,,,,,,,,, +BTN_MIDDLE,0x112,,,,,,,,,,,, +BTN_SIDE,0x113,,,,,,,,,,,, +BTN_EXTRA,0x114,,,,,,,,,,,, +BTN_FORWARD,0x115,,,,,,,,,,,, +BTN_BACK,0x116,,,,,,,,,,,, +BTN_TASK,0x117,,,,,,,,,,,, +BTN_JOYSTICK,0x120,,,,,,,,,,,, +BTN_TRIGGER,0x120,,,,,,,,,,,, +BTN_THUMB,0x121,,,,,,,,,,,, +BTN_THUMB2,0x122,,,,,,,,,,,, +BTN_TOP,0x123,,,,,,,,,,,, +BTN_TOP2,0x124,,,,,,,,,,,, +BTN_PINKIE,0x125,,,,,,,,,,,, +BTN_BASE,0x126,,,,,,,,,,,, +BTN_BASE2,0x127,,,,,,,,,,,, +BTN_BASE3,0x128,,,,,,,,,,,, +BTN_BASE4,0x129,,,,,,,,,,,, +BTN_BASE5,0x12a,,,,,,,,,,,, +BTN_BASE6,0x12b,,,,,,,,,,,, +BTN_DEAD,0x12f,,,,,,,,,,,, +BTN_GAMEPAD,0x130,,,,,,,,,,,, +BTN_A,0x130,,,,,,,,,,,, +BTN_B,0x131,,,,,,,,,,,, +BTN_C,0x132,,,,,,,,,,,, +BTN_X,0x133,,,,,,,,,,,, +BTN_Y,0x134,,,,,,,,,,,, +BTN_Z,0x135,,,,,,,,,,,, +BTN_TL,0x136,,,,,,,,,,,, +BTN_TR,0x137,,,,,,,,,,,, +BTN_TL2,0x138,,,,,,,,,,,, +BTN_TR2,0x139,,,,,,,,,,,, +BTN_SELECT,0x13a,,,,,,,,,,,, +BTN_START,0x13b,,,,,,,,,,,, +BTN_MODE,0x13c,,,,,,,,,,,, +BTN_THUMBL,0x13d,,,,,,,,,,,, +BTN_THUMBR,0x13e,,,,,,,,,,,, +BTN_DIGI,0x140,,,,,,,,,,,, +BTN_TOOL_PEN,0x140,,,,,,,,,,,, +BTN_TOOL_RUBBER,0x141,,,,,,,,,,,, +BTN_TOOL_BRUSH,0x142,,,,,,,,,,,, +BTN_TOOL_PENCIL,0x143,,,,,,,,,,,, +BTN_TOOL_AIRBRUSH,0x144,,,,,,,,,,,, +BTN_TOOL_FINGER,0x145,,,,,,,,,,,, +BTN_TOOL_MOUSE,0x146,,,,,,,,,,,, +BTN_TOOL_LENS,0x147,,,,,,,,,,,, +BTN_TOUCH,0x14a,,,,,,,,,,,, +BTN_STYLUS,0x14b,,,,,,,,,,,, +BTN_STYLUS2,0x14c,,,,,,,,,,,, +BTN_TOOL_DOUBLETAP,0x14d,,,,,,,,,,,, +BTN_TOOL_TRIPLETAP,0x14e,,,,,,,,,,,, +BTN_TOOL_QUADTAP,0x14f,,,,,,,,,,,, +BTN_WHEEL,0x150,,,,,,,,,,,, +BTN_GEAR_DOWN,0x150,,,,,,,,,,,, +BTN_GEAR_UP,0x151,,,,,,,,,,,, +KEY_OK,0x160,,,,,,,,,,,, +KEY_SELECT,0x161,,,,,,,,,VK_SELECT,0x29,, +KEY_GOTO,0x162,,,,,,,,,,,, +KEY_CLEAR,0x163,,,,,,,,,,,, +KEY_POWER2,0x164,,,,,,,,,,,, +KEY_OPTION,0x165,,,,,,,,,,,, +KEY_INFO,0x166,,,,,,,,,,,, +KEY_TIME,0x167,,,,,,,,,,,, +KEY_VENDOR,0x168,,,,,,,,,,,, +KEY_ARCHIVE,0x169,,,,,,,,,,,, +KEY_PROGRAM,0x16a,,,,,,,,,,,, +KEY_CHANNEL,0x16b,,,,,,,,,,,, +KEY_FAVORITES,0x16c,,,,,,,,,VK_BROWSER_FAVOURITES,0xab,, +KEY_EPG,0x16d,,,,,,,,,,,, +KEY_PVR,0x16e,,,,,,,,,,,, +KEY_MHP,0x16f,,,,,,,,,,,, +KEY_LANGUAGE,0x170,,,,,,,,,,,, +KEY_TITLE,0x171,,,,,,,,,,,, +KEY_SUBTITLE,0x172,,,,,,,,,,,, +KEY_ANGLE,0x173,,,,,,,,,,,, +KEY_ZOOM,0x174,,,,,,,,,VK_ZOOM,0xfb,, +KEY_MODE,0x175,,,,,,,,,,,, +KEY_KEYBOARD,0x176,,,,,,,,,,,, +KEY_SCREEN,0x177,,,,,,,,,,,, +KEY_PC,0x178,,,,,,,,,,,, +KEY_TV,0x179,,,,,,,,,,,, +KEY_TV2,0x17a,,,,,,,,,,,, +KEY_VCR,0x17b,,,,,,,,,,,, +KEY_VCR2,0x17c,,,,,,,,,,,, +KEY_SAT,0x17d,,,,,,,,,,,, +KEY_SAT2,0x17e,,,,,,,,,,,, +KEY_CD,0x17f,,,,,,,,,,,, +KEY_TAPE,0x180,,,,,,,,,,,, +KEY_RADIO,0x181,,,,,,,,,,,, +KEY_TUNER,0x182,,,,,,,,,,,, +KEY_PLAYER,0x183,,,,,,,,,,,, +KEY_TEXT,0x184,,,,,,,,,,,, +KEY_DVD,0x185,,,,,,,,,,,, +KEY_AUX,0x186,,,,,,,,,,,, +KEY_MP3,0x187,,,,,,,,,,,, +KEY_AUDIO,0x188,,,,,,,,,,,, +KEY_VIDEO,0x189,,,,,,,,,,,, +KEY_DIRECTORY,0x18a,,,,,,,,,,,, +KEY_LIST,0x18b,,,,,,,,,,,, +KEY_MEMO,0x18c,,,,,,,,,,,, +KEY_CALENDAR,0x18d,,,,,,,,,,,, +KEY_RED,0x18e,,,,,,,,,,,, +KEY_GREEN,0x18f,,,,,,,,,,,, +KEY_YELLOW,0x190,,,,,,,,,,,, +KEY_BLUE,0x191,,,,,,,,,,,, +KEY_CHANNELUP,0x192,,,,,,,,,,,, +KEY_CHANNELDOWN,0x193,,,,,,,,,,,, +KEY_FIRST,0x194,,,,,,,,,,,, +KEY_LAST,0x195,,,,,,,,,,,, +KEY_AB,0x196,,,,,,,,,,,, +KEY_NEXT,0x197,,,,,,,,,,,, +KEY_RESTART,0x198,,,,,,,,,,,, +KEY_SLOW,0x199,,,,,,,,,,,, +KEY_SHUFFLE,0x19a,,,,,,,,,,,, +KEY_BREAK,0x19b,,,,,,,,,,,, +KEY_PREVIOUS,0x19c,,,,,,,,,,,, +KEY_DIGITS,0x19d,,,,,,,,,,,, +KEY_TEEN,0x19e,,,,,,,,,,,, +KEY_TWEN,0x19f,,,,,,,,,,,, +KEY_VIDEOPHONE,0x1a0,,,,,,,,,,,, +KEY_GAMES,0x1a1,,,,,,,,,,,, +KEY_ZOOMIN,0x1a2,,,,,,,,,,,, +KEY_ZOOMOUT,0x1a3,,,,,,,,,,,, +KEY_ZOOMRESET,0x1a4,,,,,,,,,,,, +KEY_WORDPROCESSOR,0x1a5,,,,,,,,,,,, +KEY_EDITOR,0x1a6,,,,,,,,,,,, +KEY_SPREADSHEET,0x1a7,,,,,,,,,,,, +KEY_GRAPHICSEDITOR,0x1a8,,,,,,,,,,,, +KEY_PRESENTATION,0x1a9,,,,,,,,,,,, +KEY_DATABASE,0x1aa,,,,,,,,,,,, +KEY_NEWS,0x1ab,,,,,,,,,,,, +KEY_VOICEMAIL,0x1ac,,,,,,,,,,,, +KEY_ADDRESSBOOK,0x1ad,,,,,,,,,,,, +KEY_MESSENGER,0x1ae,,,,,,,,,,,, +KEY_DISPLAYTOGGLE,0x1af,,,,,,,,,,,, +KEY_SPELLCHECK,0x1b0,,,,,,,,,,,, +KEY_LOGOFF,0x1b1,,,,,,,,,,,, +KEY_DOLLAR,0x1b2,,,,,,,,,,,, +KEY_EURO,0x1b3,,,,,,,,,,,, +KEY_FRAMEBACK,0x1b4,,,,,,,,,,,, +KEY_FRAMEFORWARD,0x1b5,,,,,,,,,,,, +KEY_CONTEXT_MENU,0x1b6,,,,,,,,,,,, +KEY_MEDIA_REPEAT,0x1b7,,,,,,,,,,,, +KEY_DEL_EOL,0x1c0,,,,,,,,,,,, +KEY_DEL_EOS,0x1c1,,,,,,,,,,,, +KEY_INS_LINE,0x1c2,,,,,,,,,,,, +KEY_DEL_LINE,0x1c3,,,,,,,,,,,, +KEY_FN,0x1d0,,,,,,,,,,,, +KEY_FN_ESC,0x1d1,,,,,,,,,,,, +KEY_FN_F1,0x1d2,,,,,,,,,,,, +KEY_FN_F2,0x1d3,,,,,,,,,,,, +KEY_FN_F3,0x1d4,,,,,,,,,,,, +KEY_FN_F4,0x1d5,,,,,,,,,,,, +KEY_FN_F5,0x1d6,,,,,,,,,,,, +KEY_FN_F6,0x1d7,,,,,,,,,,,, +KEY_FN_F7,0x1d8,,,,,,,,,,,, +KEY_FN_F8,0x1d9,,,,,,,,,,,, +KEY_FN_F9,0x1da,,,,,,,,,,,, +KEY_FN_F10,0x1db,,,,,,,,,,,, +KEY_FN_F11,0x1dc,,,,,,,,,,,, +KEY_FN_F12,0x1dd,,,,,,,,,,,, +KEY_FN_1,0x1de,,,,,,,,,,,, +KEY_FN_2,0x1df,,,,,,,,,,,, +KEY_FN_D,0x1e0,,,,,,,,,,,, +KEY_FN_E,0x1e1,,,,,,,,,,,, +KEY_FN_F,0x1e2,,,,,,,,,,,, +KEY_FN_S,0x1e3,,,,,,,,,,,, +KEY_FN_B,0x1e4,,,,,,,,,,,, +KEY_BRL_DOT1,0x1f1,,,,,,,,,,,, +KEY_BRL_DOT2,0x1f2,,,,,,,,,,,, +KEY_BRL_DOT3,0x1f3,,,,,,,,,,,, +KEY_BRL_DOT4,0x1f4,,,,,,,,,,,, +KEY_BRL_DOT5,0x1f5,,,,,,,,,,,, +KEY_BRL_DOT6,0x1f6,,,,,,,,,,,, +KEY_BRL_DOT7,0x1f7,,,,,,,,,,,, +KEY_BRL_DOT8,0x1f8,,,,,,,,,,,, +KEY_BRL_DOT9,0x1f9,,,,,,,,,,,, +KEY_BRL_DOT10,0x1fa,,,,,,,,,,,, +KEY_NUMERIC_0,0x200,,,,,,,,,,,, +KEY_NUMERIC_1,0x201,,,,,,,,,,,, +KEY_NUMERIC_2,0x202,,,,,,,,,,,, +KEY_NUMERIC_3,0x203,,,,,,,,,,,, +KEY_NUMERIC_4,0x204,,,,,,,,,,,, +KEY_NUMERIC_5,0x205,,,,,,,,,,,, +KEY_NUMERIC_6,0x206,,,,,,,,,,,, +KEY_NUMERIC_7,0x207,,,,,,,,,,,, +KEY_NUMERIC_8,0x208,,,,,,,,,,,, +KEY_NUMERIC_9,0x209,,,,,,,,,,,, +KEY_NUMERIC_STAR,0x20a,,,,,,,,,,,, +KEY_NUMERIC_POUND,0x20b,,,,,,,,,,,, +KEY_RFKILL,0x20c,,,,,,,,,,,, -- 1.7.4.4

On Thu, Jul 21, 2011 at 11:26:58AM +0800, Lai Jiangshan wrote:
From: Daniel P. Berrange <berrange@redhat.com>
Should keep it as the same as: http://git.gnome.org/browse/gtk-vnc/commit/src/keymaps.csv
All master keymaps are defined in a CSV file. THis covers Linux keycodes, OSX keycodes, AT set1, 2 & 3, XT keycodes, the XT encoding used by the Linux KBD driver, USB keycodes, Win32 keycodes, the XT encoding used by Xorg on Cygwin, the XT encoding used by Xorg on Linux with kbd driver.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- src/Makefile.am | 2 +- src/util/keymaps.csv | 463 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 464 insertions(+), 1 deletions(-) create mode 100644 src/util/keymaps.csv
ACK Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Add virtkey lib for usage-improvment and keycode translating. Add 4 internal API for the aim const char *virKeycodeSetTypeToString(int codeset); int virKeycodeSetTypeFromString(const char *name); int virKeycodeValueFromString(virKeycodeSet codeset, const char *keyname); int virKeycodeValueTranslate(virKeycodeSet from_codeset, virKeycodeSet to_offset, int key_value); Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- include/libvirt/libvirt.h.in | 6 ++ src/Makefile.am | 12 +++- src/libvirt_private.syms | 5 ++ src/util/virkey.c | 145 +++++++++++++++++++++++++++++++++++++++++ src/util/virkeycode-mapgen.py | 45 +++++++++++++ src/util/virkeycode.h | 35 ++++++++++ 6 files changed, 247 insertions(+), 1 deletions(-) create mode 100644 src/util/virkey.c create mode 100644 src/util/virkeycode-mapgen.py create mode 100644 src/util/virkeycode.h diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 40ce0fc..6afd591 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1788,6 +1788,12 @@ typedef enum { VIR_KEYCODE_SET_ATSET1 = 2, VIR_KEYCODE_SET_ATSET2 = 3, VIR_KEYCODE_SET_ATSET3 = 4, + VIR_KEYCODE_SET_OSX = 5, + VIR_KEYCODE_SET_XT_KBD = 6, + VIR_KEYCODE_SET_USB = 7, + VIR_KEYCODE_SET_WIN32 = 8, + + VIR_KEYCODE_SET_LAST, } virKeycodeSet; /** diff --git a/src/Makefile.am b/src/Makefile.am index 2a6b0e4..87117de 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -81,7 +81,17 @@ UTIL_SOURCES = \ util/util.c util/util.h \ util/viraudit.c util/viraudit.h \ util/xml.c util/xml.h \ - util/virterror.c util/virterror_internal.h + util/virterror.c util/virterror_internal.h \ + util/virkey.c util/virkeycode.h util/virkeymaps.h + +EXTRA_DIST += $(srcdir)/util/virkeymaps.h $(srcdir)/util/keymaps.csv \ + $(srcdir)/util/virkeycode-mapgen.py + +$(srcdir)/util/virkeymaps.h: $(srcdir)/util/keymaps.csv \ + $(srcdir)/util/virkeycode-mapgen.py + python $(srcdir)/util/virkeycode-mapgen.py <$(srcdir)/util/keymaps.csv >$@ + +$(srcdir)/util/virkey.c: $(srcdir)/util/virkeycode.h $(srcdir)/util/virkeymaps.h EXTRA_DIST += util/threads-pthread.c util/threads-win32.c diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 3e3b1dd..e9ae018 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1097,6 +1097,11 @@ virSetError; virSetErrorLogPriorityFunc; virStrerror; +# virkeycode.h +virKeycodeSetTypeToString; +virKeycodeSetTypeFromString; +virKeycodeValueFromString; +virKeycodeValueTranslate; # xml.h virXMLParseFileHelper; diff --git a/src/util/virkey.c b/src/util/virkey.c new file mode 100644 index 0000000..e94768a --- /dev/null +++ b/src/util/virkey.c @@ -0,0 +1,145 @@ + +/* + * Copyright (c) 2011 Lai Jiangshan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <config.h> +#include "virkeycode.h" +#include <string.h> +#include <stddef.h> + +#define getfield(object, field_type, field_offset) \ + (*(typeof(field_type) *)((char *)(object) + field_offset)) + +struct keycode { + const char *linux_name; + const char *os_x_name; + const char *win32_name; + unsigned short linux_keycode; + unsigned short os_x; + unsigned short atset1; + unsigned short atset2; + unsigned short atset3; + unsigned short xt; + unsigned short xt_kbd; + unsigned short usb; + unsigned short win32; +}; + +#define VIRT_KEY_INTERNAL +#include "virkeymaps.h" + +static unsigned int codeOffset[] = { + [VIR_KEYCODE_SET_LINUX] = + offsetof(struct keycode, linux_keycode), + [VIR_KEYCODE_SET_XT] = + offsetof(struct keycode, xt), + [VIR_KEYCODE_SET_ATSET1] = + offsetof(struct keycode, atset1), + [VIR_KEYCODE_SET_ATSET2] = + offsetof(struct keycode, atset2), + [VIR_KEYCODE_SET_ATSET3] = + offsetof(struct keycode, atset3), + [VIR_KEYCODE_SET_OSX] = + offsetof(struct keycode, os_x), + [VIR_KEYCODE_SET_XT_KBD] = + offsetof(struct keycode, xt_kbd), + [VIR_KEYCODE_SET_USB] = + offsetof(struct keycode, usb), + [VIR_KEYCODE_SET_WIN32] = + offsetof(struct keycode, win32), +}; + +VIR_ENUM_IMPL(virKeycodeSet, VIR_KEYCODE_SET_LAST, + "linux", + "xt", + "atset1", + "atset2", + "atset3", + "os_x", + "xt_kbd", + "usb", + "win32", +); + +static int __virKeycodeValueFromString(unsigned int name_offset, + unsigned int code_offset, + const char *keyname) +{ + int i; + + for (i = 0; i < ARRAY_CARDINALITY(virKeycodes); i++) { + const char *name = getfield(virKeycodes + i, const char *, name_offset); + + if (name && !strcmp(name, keyname)) + return getfield(virKeycodes + i, unsigned short, code_offset); + } + + return -1; +} + +int virKeycodeValueFromString(virKeycodeSet codeset, const char *keyname) +{ + switch (codeset) { + case VIR_KEYCODE_SET_LINUX: + return __virKeycodeValueFromString(offsetof(struct keycode, linux_name), + offsetof(struct keycode, linux_keycode), + keyname); + case VIR_KEYCODE_SET_OSX: + return __virKeycodeValueFromString(offsetof(struct keycode, os_x_name), + offsetof(struct keycode, os_x), + keyname); + case VIR_KEYCODE_SET_WIN32: + return __virKeycodeValueFromString(offsetof(struct keycode, win32_name), + offsetof(struct keycode, win32), + keyname); + default: + return -1; + } +} + +static int __virKeycodeValueTranslate(unsigned int from_offset, + unsigned int to_offset, + int key_value) +{ + int i; + + for (i = 0; ARRAY_CARDINALITY(virKeycodes); i++) { + if (getfield(virKeycodes + i, unsigned short, from_offset) == key_value) + return getfield(virKeycodes + i, unsigned short, to_offset); + } + + return -1; +} + +int virKeycodeValueTranslate(virKeycodeSet from_codeset, + virKeycodeSet to_codeset, + int key_value) +{ + if (key_value <= 0) + return -1; + + key_value = __virKeycodeValueTranslate(codeOffset[from_codeset], + codeOffset[to_codeset], + key_value); + if (key_value <= 0) + return -1; + + return key_value; +} + diff --git a/src/util/virkeycode-mapgen.py b/src/util/virkeycode-mapgen.py new file mode 100644 index 0000000..7340e76 --- /dev/null +++ b/src/util/virkeycode-mapgen.py @@ -0,0 +1,45 @@ +#!/bin/python + +""" +Generate the big keycodes table for virkeys. +It read keymaps.csv from stdin and put the generated code to stdout. + +Please keep keymaps.csv be exactly the same as: +http://git.gnome.org/browse/gtk-vnc/plain/src/keymaps.csv. +If anything inconsistent happens, please change this file +instead of keymaps.csv which is a mirror. +""" + +import sys +import re + +namecolums = (0,2,10) + +def quotestring(str): + if str[0] != '"': + return '"' + str + '"' + return str + +print ''' +/* Generated file, DON'T edit it */ + +#ifndef VIRT_KEY_INTERNAL +# error do not use this; it is not a public header +#endif + +struct keycode virKeycodes[] = { +''' + +sys.stdin.readline() # eat the fist line. + +for line in sys.stdin.xreadlines(): + a = re.match("([^,]*)," * 13 + "([^,]*)$", line[0:-1]).groups() + b = "" + for i in namecolums: + b = b + (a[i] and quotestring(a[i]) or 'NULL') + ',' + for i in [ x for x in range(12) if not x in namecolums ]: + b = b + (a[i] or '0') + ',' + print " { " + b + "}," + +print '};' + diff --git a/src/util/virkeycode.h b/src/util/virkeycode.h new file mode 100644 index 0000000..c10c4dd --- /dev/null +++ b/src/util/virkeycode.h @@ -0,0 +1,35 @@ + +/* + * virkeycode.h: keycodes definitions and declarations + * + * Copyright (c) 2011 Lai Jiangshan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __VIR_UTIL_VIRTKEYCODE_H__ +#define __VIR_UTIL_VIRTKEYCODE_H__ + +#include "util.h" +#include "libvirt/libvirt.h" + +VIR_ENUM_DECL(virKeycodeSet); +int virKeycodeValueFromString(virKeycodeSet codeset, const char *keyname); +int virKeycodeValueTranslate(virKeycodeSet from_codeset, + virKeycodeSet to_offset, + int key_value); + +#endif -- 1.7.4.4

On Thu, Jul 21, 2011 at 11:26:59AM +0800, Lai Jiangshan wrote:
Add virtkey lib for usage-improvment and keycode translating. Add 4 internal API for the aim
const char *virKeycodeSetTypeToString(int codeset); int virKeycodeSetTypeFromString(const char *name); int virKeycodeValueFromString(virKeycodeSet codeset, const char *keyname); int virKeycodeValueTranslate(virKeycodeSet from_codeset, virKeycodeSet to_offset, int key_value);
Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- include/libvirt/libvirt.h.in | 6 ++ src/Makefile.am | 12 +++- src/libvirt_private.syms | 5 ++ src/util/virkey.c | 145 +++++++++++++++++++++++++++++++++++++++++ src/util/virkeycode-mapgen.py | 45 +++++++++++++ src/util/virkeycode.h | 35 ++++++++++ 6 files changed, 247 insertions(+), 1 deletions(-) create mode 100644 src/util/virkey.c create mode 100644 src/util/virkeycode-mapgen.py create mode 100644 src/util/virkeycode.h
Slight mistake there I think. s/virkey.c/virtkeycode.c/
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 40ce0fc..6afd591 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1788,6 +1788,12 @@ typedef enum { VIR_KEYCODE_SET_ATSET1 = 2, VIR_KEYCODE_SET_ATSET2 = 3, VIR_KEYCODE_SET_ATSET3 = 4, + VIR_KEYCODE_SET_OSX = 5, + VIR_KEYCODE_SET_XT_KBD = 6, + VIR_KEYCODE_SET_USB = 7, + VIR_KEYCODE_SET_WIN32 = 8, + + VIR_KEYCODE_SET_LAST, } virKeycodeSet;
/** diff --git a/src/Makefile.am b/src/Makefile.am index 2a6b0e4..87117de 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -81,7 +81,17 @@ UTIL_SOURCES = \ util/util.c util/util.h \ util/viraudit.c util/viraudit.h \ util/xml.c util/xml.h \ - util/virterror.c util/virterror_internal.h + util/virterror.c util/virterror_internal.h \ + util/virkey.c util/virkeycode.h util/virkeymaps.h
Also s/virkey.c/virkeycode.c/ here
+ +EXTRA_DIST += $(srcdir)/util/virkeymaps.h $(srcdir)/util/keymaps.csv \ + $(srcdir)/util/virkeycode-mapgen.py + +$(srcdir)/util/virkeymaps.h: $(srcdir)/util/keymaps.csv \ + $(srcdir)/util/virkeycode-mapgen.py + python $(srcdir)/util/virkeycode-mapgen.py <$(srcdir)/util/keymaps.csv >$@ + +$(srcdir)/util/virkey.c: $(srcdir)/util/virkeycode.h $(srcdir)/util/virkeymaps.h
I don't think you need the explicit dep rule here. The automake rules will already detect that virkeycode.c has a include of virkeymaps.h and rebuild. You should add virkeymaps.h to the BUILT_SOURCES variable & MAINTAINERCLEANFILES though
EXTRA_DIST += util/threads-pthread.c util/threads-win32.c
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 3e3b1dd..e9ae018 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1097,6 +1097,11 @@ virSetError; virSetErrorLogPriorityFunc; virStrerror;
+# virkeycode.h +virKeycodeSetTypeToString; +virKeycodeSetTypeFromString; +virKeycodeValueFromString; +virKeycodeValueTranslate;
# xml.h virXMLParseFileHelper; diff --git a/src/util/virkey.c b/src/util/virkey.c new file mode 100644 index 0000000..e94768a --- /dev/null +++ b/src/util/virkey.c @@ -0,0 +1,145 @@ + +/* + * Copyright (c) 2011 Lai Jiangshan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <config.h> +#include "virkeycode.h" +#include <string.h> +#include <stddef.h> + +#define getfield(object, field_type, field_offset) \ + (*(typeof(field_type) *)((char *)(object) + field_offset)) + +struct keycode { + const char *linux_name; + const char *os_x_name; + const char *win32_name; + unsigned short linux_keycode; + unsigned short os_x; + unsigned short atset1; + unsigned short atset2; + unsigned short atset3; + unsigned short xt; + unsigned short xt_kbd; + unsigned short usb; + unsigned short win32; +}; + +#define VIRT_KEY_INTERNAL +#include "virkeymaps.h" + +static unsigned int codeOffset[] = { + [VIR_KEYCODE_SET_LINUX] = + offsetof(struct keycode, linux_keycode), + [VIR_KEYCODE_SET_XT] = + offsetof(struct keycode, xt), + [VIR_KEYCODE_SET_ATSET1] = + offsetof(struct keycode, atset1), + [VIR_KEYCODE_SET_ATSET2] = + offsetof(struct keycode, atset2), + [VIR_KEYCODE_SET_ATSET3] = + offsetof(struct keycode, atset3), + [VIR_KEYCODE_SET_OSX] = + offsetof(struct keycode, os_x), + [VIR_KEYCODE_SET_XT_KBD] = + offsetof(struct keycode, xt_kbd), + [VIR_KEYCODE_SET_USB] = + offsetof(struct keycode, usb), + [VIR_KEYCODE_SET_WIN32] = + offsetof(struct keycode, win32), +}; + +VIR_ENUM_IMPL(virKeycodeSet, VIR_KEYCODE_SET_LAST, + "linux", + "xt", + "atset1", + "atset2", + "atset3", + "os_x", + "xt_kbd", + "usb", + "win32", +); + +static int __virKeycodeValueFromString(unsigned int name_offset, + unsigned int code_offset, + const char *keyname) +{ + int i; + + for (i = 0; i < ARRAY_CARDINALITY(virKeycodes); i++) { + const char *name = getfield(virKeycodes + i, const char *, name_offset); + + if (name && !strcmp(name, keyname)) + return getfield(virKeycodes + i, unsigned short, code_offset); + } + + return -1; +} + +int virKeycodeValueFromString(virKeycodeSet codeset, const char *keyname) +{ + switch (codeset) { + case VIR_KEYCODE_SET_LINUX: + return __virKeycodeValueFromString(offsetof(struct keycode, linux_name), + offsetof(struct keycode, linux_keycode), + keyname); + case VIR_KEYCODE_SET_OSX: + return __virKeycodeValueFromString(offsetof(struct keycode, os_x_name), + offsetof(struct keycode, os_x), + keyname); + case VIR_KEYCODE_SET_WIN32: + return __virKeycodeValueFromString(offsetof(struct keycode, win32_name), + offsetof(struct keycode, win32), + keyname); + default: + return -1; + } +} + +static int __virKeycodeValueTranslate(unsigned int from_offset, + unsigned int to_offset, + int key_value) +{ + int i; + + for (i = 0; ARRAY_CARDINALITY(virKeycodes); i++) { + if (getfield(virKeycodes + i, unsigned short, from_offset) == key_value) + return getfield(virKeycodes + i, unsigned short, to_offset); + } + + return -1; +} + +int virKeycodeValueTranslate(virKeycodeSet from_codeset, + virKeycodeSet to_codeset, + int key_value) +{ + if (key_value <= 0) + return -1; + + key_value = __virKeycodeValueTranslate(codeOffset[from_codeset], + codeOffset[to_codeset], + key_value); + if (key_value <= 0) + return -1; + + return key_value; +}
There's no need for the "__" prefix on static functions here. It just causes code-churn, if we ever decide to then make some of the functions non-static. Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Also support string names for the linux keycode(auto detect). Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> Acked-by: Daniel P. Berrange <berrange@redhat.com> --- tools/virsh.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 4 ++ 2 files changed, 98 insertions(+), 0 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index ca92f0c..e9294f8 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -58,6 +58,7 @@ #include "threads.h" #include "command.h" #include "count-one-bits.h" +#include "virkeycode.h" static char *progname; @@ -3447,6 +3448,98 @@ cmdInjectNMI(vshControl *ctl, const vshCmd *cmd) } /* + * "send-key" command + */ +static const vshCmdInfo info_send_key[] = { + {"help", N_("Send keycodes to the guest")}, + {"desc", N_("Send keycodes to the guest, the keycodes must be integers\n" + " Examples:\n\n" + " virsh # send-key <domain> 37 18 21\n" + " virsh # send-key <domain> KEY_RIGHTCTRL KEY_C\n" + " virsh # send-key <domain> --codeset xt 37 18 21\n" + " virsh # send-key <domain> --holdtime 1000 0x15 18 0xf\n" + )}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_send_key[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, + {"codeset", VSH_OT_STRING, VSH_OFLAG_REQ_OPT, N_("the codeset of keycodes, default:linux")}, + {"holdtime", VSH_OT_INT, VSH_OFLAG_REQ_OPT, + N_("the time (in millsecond) how long the keys will be held")}, + {"keycode", VSH_OT_ARGV, VSH_OFLAG_REQ, N_("the key code")}, + {NULL, 0, 0, NULL} +}; + +static int get_integer_keycode(const char *key_name) +{ + long val; + char *endptr; + + val = strtol(key_name, &endptr, 0); + if (*endptr != '\0' || val > 0xffff || val <= 0) + return -1; + + return val; +} + +static bool +cmdSendKey(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom; + int ret = false; + const char *codeset_option; + int codeset; + int holdtime; + int count = 0; + const vshCmdOpt *opt = NULL; + int keycode; + unsigned int keycodes[VIR_DOMAIN_SEND_KEY_MAX_KEYS]; + + if (!vshConnectionUsability(ctl, ctl->conn)) + return false; + + if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) + return false; + + if (vshCommandOptString(cmd, "codeset", &codeset_option) <= 0) + codeset_option = "linux"; + + if (vshCommandOptInt(cmd, "holdtime", &holdtime) <= 0) + holdtime = 0; + + codeset = virKeycodeSetTypeFromString(codeset_option); + if ((int)codeset < 0) { + vshError(ctl, _("unknown codeset: '%s'"), codeset_option); + goto cleanup; + } + + while ((opt = vshCommandOptArgv(cmd, opt))) { + if (count == VIR_DOMAIN_SEND_KEY_MAX_KEYS) { + vshError(ctl, _("too many keycodes")); + goto cleanup; + } + + if ((keycode = get_integer_keycode(opt->data)) <= 0) { + if ((keycode = virKeycodeValueFromString(codeset, opt->data)) <= 0) { + vshError(ctl, _("invalid keycode: '%s'"), opt->data); + goto cleanup; + } + } + + keycodes[count] = keycode; + count++; + } + + if (!(virDomainSendKey(dom, codeset, holdtime, keycodes, count, 0) < 0)) + ret = true; + +cleanup: + virDomainFree(dom); + return ret; +} + +/* * "setmemory" command */ static const vshCmdInfo info_setmem[] = { @@ -12005,6 +12098,7 @@ static const vshCmdDef domManagementCmds[] = { {"dumpxml", cmdDumpXML, opts_dumpxml, info_dumpxml, 0}, {"edit", cmdEdit, opts_edit, info_edit, 0}, {"inject-nmi", cmdInjectNMI, opts_inject_nmi, info_inject_nmi, 0}, + {"send-key", cmdSendKey, opts_send_key, info_send_key}, {"managedsave", cmdManagedSave, opts_managedsave, info_managedsave, 0}, {"managedsave-remove", cmdManagedSaveRemove, opts_managedsaveremove, info_managedsaveremove, 0}, diff --git a/tools/virsh.pod b/tools/virsh.pod index 9cd2853..60ae8ff 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -309,6 +309,10 @@ running B<virsh suspend>. When in a paused state the domain will still consume allocated resources like memory, but will not be eligible for scheduling by the hypervisor. +=item B<send-key> I<domain-id> optional I<--codeset> B<codeset> optional I<--holdtime> B<holdtime> B<keycode>... + +Send keys to the guest + =item B<shutdown> The domain is in the process of shutting down, i.e. the guest operating system -- 1.7.4.4

On Thu, Jul 21, 2011 at 11:27:00AM +0800, Lai Jiangshan wrote:
Also support string names for the linux keycode(auto detect).
Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> Acked-by: Daniel P. Berrange <berrange@redhat.com> --- tools/virsh.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 4 ++ 2 files changed, 98 insertions(+), 0 deletions(-)
ACK Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

qemu driver just accept xt_kbd codeset's keycode, so the lib virtkey is used for translating keycodes from other codesets. Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- src/qemu/qemu_driver.c | 71 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 17 ++++++++++ src/qemu/qemu_monitor.h | 5 +++ src/qemu/qemu_monitor_json.c | 15 +++++++++ src/qemu/qemu_monitor_json.h | 5 +++ src/qemu/qemu_monitor_text.c | 49 +++++++++++++++++++++++++++++ src/qemu/qemu_monitor_text.h | 5 +++ 7 files changed, 167 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index cd65bce..f1ecebf 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -87,6 +87,7 @@ #include "configmake.h" #include "threadpool.h" #include "locking/lock_manager.h" +#include "virkeycode.h" #define VIR_FROM_THIS VIR_FROM_QEMU @@ -1849,6 +1850,75 @@ cleanup: return ret; } +static int qemuDomainSendKey(virDomainPtr domain, + unsigned int codeset, + unsigned int holdtime, + unsigned int *keycodes, + int nkeycodes, + unsigned int flags) +{ + struct qemud_driver *driver = domain->conn->privateData; + virDomainObjPtr vm = NULL; + int ret = -1; + qemuDomainObjPrivatePtr priv; + + virCheckFlags(0, -1); + + /* translate the keycode to XT_KBD for qemu driver */ + if (codeset != VIR_KEYCODE_SET_XT_KBD) { + int i; + int keycode; + + for (i = 0; i < nkeycodes; i++) { + keycode = virKeycodeValueTranslate(codeset, VIR_KEYCODE_SET_XT_KBD, + keycodes[i]); + if (keycode < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "can not translate " + "keycode %u of %s codeset to xt_kbd codeset " + "keycode", keycodes[i], + virKeycodeSetTypeToString(codeset)); + return -1; + } + keycodes[i] = keycode; + } + } + + qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, domain->uuid); + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(domain->uuid, uuidstr); + qemuReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + priv = vm->privateData; + + if (qemuDomainObjBeginJobWithDriver(driver, vm, QEMU_JOB_MODIFY) < 0) + goto cleanup; + + if (!virDomainObjIsActive(vm)) { + qemuReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto cleanup; + } + + ignore_value(qemuDomainObjEnterMonitorWithDriver(driver, vm)); + ret = qemuMonitorSendKey(priv->mon, holdtime, keycodes, nkeycodes); + qemuDomainObjExitMonitorWithDriver(driver, vm); + if (qemuDomainObjEndJob(driver, vm) == 0) { + vm = NULL; + goto cleanup; + } + +cleanup: + if (vm) + virDomainObjUnlock(vm); + qemuDriverUnlock(driver); + return ret; +} + static int qemudDomainGetInfo(virDomainPtr dom, virDomainInfoPtr info) { @@ -8653,6 +8723,7 @@ static virDriver qemuDriver = { .domainMigratePerform3 = qemuDomainMigratePerform3, /* 0.9.2 */ .domainMigrateFinish3 = qemuDomainMigrateFinish3, /* 0.9.2 */ .domainMigrateConfirm3 = qemuDomainMigrateConfirm3, /* 0.9.2 */ + .domainSendKey = qemuDomainSendKey, /* 0.9.4 */ }; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 3a30a15..016f8f0 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2408,6 +2408,23 @@ int qemuMonitorInjectNMI(qemuMonitorPtr mon) return ret; } +int qemuMonitorSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes) +{ + int ret; + + VIR_DEBUG("mon=%p, holdtime=%u, nkeycodes=%u", + mon, holdtime, nkeycodes); + + if (mon->json) + ret = qemuMonitorJSONSendKey(mon, holdtime, keycodes, nkeycodes); + else + ret = qemuMonitorTextSendKey(mon, holdtime, keycodes, nkeycodes); + return ret; +} + int qemuMonitorScreendump(qemuMonitorPtr mon, const char *file) { diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index f246d21..0419147 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -447,6 +447,11 @@ int qemuMonitorInjectNMI(qemuMonitorPtr mon); int qemuMonitorScreendump(qemuMonitorPtr mon, const char *file); +int qemuMonitorSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes); + /** * When running two dd process and using <> redirection, we need a * shell that will not truncate files. These two strings serve that diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 4db2b78..8385085 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -2695,6 +2695,21 @@ cleanup: return ret; } +int qemuMonitorJSONSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes) +{ + /* + * FIXME: qmp sendkey has not been implemented yet, + * and qmp API of it can not be anticipated, so we use hmp temporary. + */ + if (qemuMonitorCheckHMP(mon, "sendkey")) { + return qemuMonitorTextSendKey(mon, holdtime, keycodes, nkeycodes); + } else + return -1; +} + int qemuMonitorJSONScreendump(qemuMonitorPtr mon, const char *file) { diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 380e26a..ddd121e 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -217,6 +217,11 @@ int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon, int qemuMonitorJSONInjectNMI(qemuMonitorPtr mon); +int qemuMonitorJSONSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes); + int qemuMonitorJSONScreendump(qemuMonitorPtr mon, const char *file); diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index 0965a08..52b9a34 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -2755,6 +2755,55 @@ fail: return -1; } +int qemuMonitorTextSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes) +{ + int i; + virBuffer buf = VIR_BUFFER_INITIALIZER; + char *cmd, *reply = NULL; + + if (nkeycodes > VIR_DOMAIN_SEND_KEY_MAX_KEYS || nkeycodes == 0) + return -1; + + virBufferAddLit(&buf, "sendkey "); + for (i = 0; i < nkeycodes; i++) { + if (keycodes[i] > 0xffff) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("keycode %d is invalid: 0x%X"), + i, keycodes[i]); + virBufferFreeAndReset(&buf); + return -1; + } + + if (i) + virBufferAddChar(&buf, '-'); + virBufferAsprintf(&buf, "0x%02X", keycodes[i]); + } + + if (holdtime) + virBufferAsprintf(&buf, " %u", holdtime); + + if (virBufferError(&buf)) { + virReportOOMError(); + return -1; + } + + cmd = virBufferContentAndReset(&buf); + if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("failed to send key using command '%s'"), + cmd); + VIR_FREE(cmd); + return -1; + } + + VIR_FREE(cmd); + VIR_FREE(reply); + return 0; +} + /* Returns -1 on error, -2 if not supported */ int qemuMonitorTextScreendump(qemuMonitorPtr mon, const char *file) { diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h index e53f693..042e581 100644 --- a/src/qemu/qemu_monitor_text.h +++ b/src/qemu/qemu_monitor_text.h @@ -211,6 +211,11 @@ int qemuMonitorTextArbitraryCommand(qemuMonitorPtr mon, const char *cmd, int qemuMonitorTextInjectNMI(qemuMonitorPtr mon); +int qemuMonitorTextSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes); + int qemuMonitorTextScreendump(qemuMonitorPtr mon, const char *file); #endif /* QEMU_MONITOR_TEXT_H */ -- 1.7.4.4

On Thu, Jul 21, 2011 at 11:27:01AM +0800, Lai Jiangshan wrote:
qemu driver just accept xt_kbd codeset's keycode, so the lib virtkey is used for translating keycodes from other codesets.
Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- src/qemu/qemu_driver.c | 71 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 17 ++++++++++ src/qemu/qemu_monitor.h | 5 +++ src/qemu/qemu_monitor_json.c | 15 +++++++++ src/qemu/qemu_monitor_json.h | 5 +++ src/qemu/qemu_monitor_text.c | 49 +++++++++++++++++++++++++++++ src/qemu/qemu_monitor_text.h | 5 +++ 7 files changed, 167 insertions(+), 0 deletions(-)
ACK Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On Thu, Jul 21, 2011 at 11:26:57AM +0800, Lai Jiangshan wrote:
Add virtkey lib for usage-improvment and keycode translating. Expose send-key in virsh Implement send-key function for the qemu driver
Daniel P. Berrange (1): util: Add keymaps.csv
Lai Jiangshan (3): util: add virtkey send-key: Expose the new API in virsh qemu:send-key: Implement the driver methods
Okay, reviewed the patch set, ACK I applied after fixing a number of small nits and renaming src/util/virkey.c into src/util/virkeycode.c as Dan suggested. So pushed, thanks, but please run "make syntax-check" before generating patches in the future :-) Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- diff --git a/python/libvirt-override.c b/python/libvirt-override.c index b713b6a..1ef5bfa 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -3789,6 +3789,53 @@ libvirt_virStreamSend(PyObject *self ATTRIBUTE_UNUSED, return py_retval; } +static PyObject * +libvirt_virDomainSendKey(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) +{ + PyObject *py_retval; + virDomainPtr domain; + PyObject *pyobj_domain; + PyObject *pyobj_list; + int codeset; + int holdtime; + unsigned int flags; + int ret; + int i; + unsigned int keycodes[VIR_DOMAIN_SEND_KEY_MAX_KEYS]; + unsigned int nkeycodes; + + if (!PyArg_ParseTuple(args, (char *)"OiiOii:virDomainSendKey", + &pyobj_domain, &codeset, &holdtime, &pyobj_list, + &nkeycodes, &flags)) { + DEBUG("%s failed to parse tuple\n", __FUNCTION__); + return VIR_PY_INT_FAIL; + } + domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + + if (!PyList_Check(pyobj_list)) { + return VIR_PY_INT_FAIL; + } + + if (nkeycodes != PyList_Size(pyobj_list) || + nkeycodes > VIR_DOMAIN_SEND_KEY_MAX_KEYS) { + return VIR_PY_INT_FAIL; + } + + for (i = 0; i < nkeycodes; i++) { + keycodes[i] = (int)PyInt_AsLong(PyList_GetItem(pyobj_list, i)); + } + + LIBVIRT_BEGIN_ALLOW_THREADS; + ret = virDomainSendKey(domain, codeset, holdtime, keycodes, nkeycodes, flags); + LIBVIRT_END_ALLOW_THREADS; + + DEBUG("virDomainSendKey ret=%d\n", ret); + + py_retval = libvirt_intWrap(ret); + return py_retval; +} + /************************************************************************ * * * The registration stuff * @@ -3872,6 +3919,7 @@ static PyMethodDef libvirtMethods[] = { {(char *) "virDomainGetJobInfo", libvirt_virDomainGetJobInfo, METH_VARARGS, NULL}, {(char *) "virDomainSnapshotListNames", libvirt_virDomainSnapshotListNames, METH_VARARGS, NULL}, {(char *) "virDomainRevertToSnapshot", libvirt_virDomainRevertToSnapshot, METH_VARARGS, NULL}, + {(char *) "virDomainSendKey", libvirt_virDomainSendKey, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} };

ping. On 07/21/2011 05:21 PM, Lai Jiangshan wrote:
Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- diff --git a/python/libvirt-override.c b/python/libvirt-override.c index b713b6a..1ef5bfa 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -3789,6 +3789,53 @@ libvirt_virStreamSend(PyObject *self ATTRIBUTE_UNUSED, return py_retval; }
+static PyObject * +libvirt_virDomainSendKey(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) +{ + PyObject *py_retval; + virDomainPtr domain; + PyObject *pyobj_domain; + PyObject *pyobj_list; + int codeset; + int holdtime; + unsigned int flags; + int ret; + int i; + unsigned int keycodes[VIR_DOMAIN_SEND_KEY_MAX_KEYS]; + unsigned int nkeycodes; + + if (!PyArg_ParseTuple(args, (char *)"OiiOii:virDomainSendKey", + &pyobj_domain, &codeset, &holdtime, &pyobj_list, + &nkeycodes, &flags)) { + DEBUG("%s failed to parse tuple\n", __FUNCTION__); + return VIR_PY_INT_FAIL; + } + domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + + if (!PyList_Check(pyobj_list)) { + return VIR_PY_INT_FAIL; + } + + if (nkeycodes != PyList_Size(pyobj_list) || + nkeycodes > VIR_DOMAIN_SEND_KEY_MAX_KEYS) { + return VIR_PY_INT_FAIL; + } + + for (i = 0; i < nkeycodes; i++) { + keycodes[i] = (int)PyInt_AsLong(PyList_GetItem(pyobj_list, i)); + } + + LIBVIRT_BEGIN_ALLOW_THREADS; + ret = virDomainSendKey(domain, codeset, holdtime, keycodes, nkeycodes, flags); + LIBVIRT_END_ALLOW_THREADS; + + DEBUG("virDomainSendKey ret=%d\n", ret); + + py_retval = libvirt_intWrap(ret); + return py_retval; +} + /************************************************************************ * * * The registration stuff * @@ -3872,6 +3919,7 @@ static PyMethodDef libvirtMethods[] = { {(char *) "virDomainGetJobInfo", libvirt_virDomainGetJobInfo, METH_VARARGS, NULL}, {(char *) "virDomainSnapshotListNames", libvirt_virDomainSnapshotListNames, METH_VARARGS, NULL}, {(char *) "virDomainRevertToSnapshot", libvirt_virDomainRevertToSnapshot, METH_VARARGS, NULL}, + {(char *) "virDomainSendKey", libvirt_virDomainSendKey, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} };

On Thu, Jul 21, 2011 at 05:21:10PM +0800, Lai Jiangshan wrote:
Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- diff --git a/python/libvirt-override.c b/python/libvirt-override.c index b713b6a..1ef5bfa 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -3789,6 +3789,53 @@ libvirt_virStreamSend(PyObject *self ATTRIBUTE_UNUSED, return py_retval; }
+static PyObject * +libvirt_virDomainSendKey(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) +{ + PyObject *py_retval; + virDomainPtr domain; + PyObject *pyobj_domain; + PyObject *pyobj_list; + int codeset; + int holdtime; + unsigned int flags; + int ret; + int i; + unsigned int keycodes[VIR_DOMAIN_SEND_KEY_MAX_KEYS]; + unsigned int nkeycodes; + + if (!PyArg_ParseTuple(args, (char *)"OiiOii:virDomainSendKey", + &pyobj_domain, &codeset, &holdtime, &pyobj_list, + &nkeycodes, &flags)) { + DEBUG("%s failed to parse tuple\n", __FUNCTION__); + return VIR_PY_INT_FAIL; + } + domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + + if (!PyList_Check(pyobj_list)) { + return VIR_PY_INT_FAIL; + } + + if (nkeycodes != PyList_Size(pyobj_list) || + nkeycodes > VIR_DOMAIN_SEND_KEY_MAX_KEYS) { + return VIR_PY_INT_FAIL; + } + + for (i = 0; i < nkeycodes; i++) { + keycodes[i] = (int)PyInt_AsLong(PyList_GetItem(pyobj_list, i)); + } + + LIBVIRT_BEGIN_ALLOW_THREADS; + ret = virDomainSendKey(domain, codeset, holdtime, keycodes, nkeycodes, flags); + LIBVIRT_END_ALLOW_THREADS; + + DEBUG("virDomainSendKey ret=%d\n", ret); + + py_retval = libvirt_intWrap(ret); + return py_retval; +} + /************************************************************************ * * * The registration stuff * @@ -3872,6 +3919,7 @@ static PyMethodDef libvirtMethods[] = { {(char *) "virDomainGetJobInfo", libvirt_virDomainGetJobInfo, METH_VARARGS, NULL}, {(char *) "virDomainSnapshotListNames", libvirt_virDomainSnapshotListNames, METH_VARARGS, NULL}, {(char *) "virDomainRevertToSnapshot", libvirt_virDomainRevertToSnapshot, METH_VARARGS, NULL}, + {(char *) "virDomainSendKey", libvirt_virDomainSendKey, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} };
ACK Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

At 07/26/2011 06:26 PM, Daniel P. Berrange Write:
On Thu, Jul 21, 2011 at 05:21:10PM +0800, Lai Jiangshan wrote:
Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- diff --git a/python/libvirt-override.c b/python/libvirt-override.c index b713b6a..1ef5bfa 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -3789,6 +3789,53 @@ libvirt_virStreamSend(PyObject *self ATTRIBUTE_UNUSED, return py_retval; }
+static PyObject * +libvirt_virDomainSendKey(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) +{ + PyObject *py_retval; + virDomainPtr domain; + PyObject *pyobj_domain; + PyObject *pyobj_list; + int codeset; + int holdtime; + unsigned int flags; + int ret; + int i; + unsigned int keycodes[VIR_DOMAIN_SEND_KEY_MAX_KEYS]; + unsigned int nkeycodes; + + if (!PyArg_ParseTuple(args, (char *)"OiiOii:virDomainSendKey", + &pyobj_domain, &codeset, &holdtime, &pyobj_list, + &nkeycodes, &flags)) { + DEBUG("%s failed to parse tuple\n", __FUNCTION__); + return VIR_PY_INT_FAIL; + } + domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + + if (!PyList_Check(pyobj_list)) { + return VIR_PY_INT_FAIL; + } + + if (nkeycodes != PyList_Size(pyobj_list) || + nkeycodes > VIR_DOMAIN_SEND_KEY_MAX_KEYS) { + return VIR_PY_INT_FAIL; + } + + for (i = 0; i < nkeycodes; i++) { + keycodes[i] = (int)PyInt_AsLong(PyList_GetItem(pyobj_list, i)); + } + + LIBVIRT_BEGIN_ALLOW_THREADS; + ret = virDomainSendKey(domain, codeset, holdtime, keycodes, nkeycodes, flags); + LIBVIRT_END_ALLOW_THREADS; + + DEBUG("virDomainSendKey ret=%d\n", ret); + + py_retval = libvirt_intWrap(ret); + return py_retval; +} + /************************************************************************ * * * The registration stuff * @@ -3872,6 +3919,7 @@ static PyMethodDef libvirtMethods[] = { {(char *) "virDomainGetJobInfo", libvirt_virDomainGetJobInfo, METH_VARARGS, NULL}, {(char *) "virDomainSnapshotListNames", libvirt_virDomainSnapshotListNames, METH_VARARGS, NULL}, {(char *) "virDomainRevertToSnapshot", libvirt_virDomainRevertToSnapshot, METH_VARARGS, NULL}, + {(char *) "virDomainSendKey", libvirt_virDomainSendKey, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} };
ACK
I read the other patch about implementing Python API, and find that the file python/libvirt-override-api.xml is updated in the other patch. But it is not updated in this patch. Is there no need to update this file here? Thanks Wen Congyang
Daniel

On 07/26/2011 04:26 AM, Daniel P. Berrange wrote:
On Thu, Jul 21, 2011 at 05:21:10PM +0800, Lai Jiangshan wrote:
Signed-off-by: Lai Jiangshan<laijs@cn.fujitsu.com>
ACK
Pushed. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On 07/13/2011 09:32 PM, Lai Jiangshan wrote:
Add virtkey lib for usage-improvment and keycode translating. Add 4 internal API for the aim
const char *virKeycodeSetTypeToString(int codeset); int virKeycodeSetTypeFromString(const char *name); int virParseKeyName(virKeycodeSet codeset, const char *keyname); int virTranslateKeyCode(virKeycodeSet from_codeset, virKeycodeSet to_offset, int key_value);
Signed-off-by: Lai Jiangshan<laijs@cn.fujitsu.com> +++ b/src/util/virtkey.c @@ -0,0 +1,117 @@ + +/* + * Copyright (c) 2011 Lai Jiangshan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */
Incomplete copyright header.
+ +#include "virtkey.h"
C files should include <config.h> first, themselves.
--- /dev/null +++ b/src/util/virtkey.h @@ -0,0 +1,41 @@ +#ifndef __UTIL_VIRTKEY_H__ +#define __UTIL_VIRTKEY_H__
Copyright header should come before double-inclusion guard.
+ +/* + * Copyright (c) 2011 Lai Jiangshan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */
Incomplete header.
+ +#include<config.h>
No header should include <config.h> - it should have already been included by every .c file.
diff --git a/src/util/virtkeymap-gen.py b/src/util/virtkeymap-gen.py new file mode 100644
.py files should be mode 100755 (basically, chmod +x). -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

Also support string names for the linux keycode(auto detect). Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> Acked-by: Daniel P. Berrange <berrange@redhat.com> --- tools/virsh.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 4 ++ 2 files changed, 98 insertions(+), 0 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index cd17f42..741e726 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -58,6 +58,7 @@ #include "threads.h" #include "command.h" #include "count-one-bits.h" +#include "virtkey.h" static char *progname; @@ -3350,6 +3351,98 @@ cmdInjectNMI(vshControl *ctl, const vshCmd *cmd) } /* + * "send-key" command + */ +static const vshCmdInfo info_send_key[] = { + {"help", N_("Send keycodes to the guest")}, + {"desc", N_("Send keycodes to the guest, the keycodes must be integers\n" + " Examples:\n\n" + " virsh # send-key <domain> 37 18 21\n" + " virsh # send-key <domain> KEY_RIGHTCTRL KEY_C\n" + " virsh # send-key <domain> --codeset xt 37 18 21\n" + " virsh # send-key <domain> --holdtime 1000 0x15 18 0xf\n" + )}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_send_key[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, + {"codeset", VSH_OT_STRING, VSH_OFLAG_REQ_OPT, N_("the codeset of keycodes, default:linux")}, + {"holdtime", VSH_OT_INT, VSH_OFLAG_REQ_OPT, + N_("the time (in millsecond) how long the keys will be held")}, + {"keycode", VSH_OT_ARGV, VSH_OFLAG_REQ, N_("the key code")}, + {NULL, 0, 0, NULL} +}; + +static int get_integer_keycode(const char *key_name) +{ + long val; + char *endptr; + + val = strtol(key_name, &endptr, 0); + if (*endptr != '\0' || val > 0xffff || val <= 0) + return -1; + + return val; +} + +static bool +cmdSendKey(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom; + int ret = false; + const char *codeset_option; + int codeset; + int holdtime; + int count = 0; + const vshCmdOpt *opt = NULL; + int keycode; + unsigned int keycodes[VIR_DOMAIN_SEND_KEY_MAX_KEYS]; + + if (!vshConnectionUsability(ctl, ctl->conn)) + return false; + + if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) + return false; + + if (vshCommandOptString(cmd, "codeset", &codeset_option) <= 0) + codeset_option = "linux"; + + if (vshCommandOptInt(cmd, "holdtime", &holdtime) <= 0) + holdtime = 0; + + codeset = virKeycodeSetTypeFromString(codeset_option); + if ((int)codeset < 0) { + vshError(ctl, _("unknown codeset: '%s'"), codeset_option); + goto cleanup; + } + + while ((opt = vshCommandOptArgv(cmd, opt))) { + if (count == VIR_DOMAIN_SEND_KEY_MAX_KEYS) { + vshError(ctl, _("too many keycodes")); + goto cleanup; + } + + if ((keycode = get_integer_keycode(opt->data)) <= 0) { + if ((keycode = virParseKeyName(codeset, opt->data)) <= 0) { + vshError(ctl, _("invalid keycode: '%s'"), opt->data); + goto cleanup; + } + } + + keycodes[count] = keycode; + count++; + } + + if (!(virDomainSendKey(dom, codeset, holdtime, keycodes, count, 0) < 0)) + ret = true; + +cleanup: + virDomainFree(dom); + return ret; +} + +/* * "setmemory" command */ static const vshCmdInfo info_setmem[] = { @@ -11684,6 +11777,7 @@ static const vshCmdDef domManagementCmds[] = { {"dumpxml", cmdDumpXML, opts_dumpxml, info_dumpxml, 0}, {"edit", cmdEdit, opts_edit, info_edit, 0}, {"inject-nmi", cmdInjectNMI, opts_inject_nmi, info_inject_nmi, 0}, + {"send-key", cmdSendKey, opts_send_key, info_send_key}, {"managedsave", cmdManagedSave, opts_managedsave, info_managedsave, 0}, {"managedsave-remove", cmdManagedSaveRemove, opts_managedsaveremove, info_managedsaveremove, 0}, diff --git a/tools/virsh.pod b/tools/virsh.pod index 52f1549..d8d0a18 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -309,6 +309,10 @@ running B<virsh suspend>. When in a paused state the domain will still consume allocated resources like memory, but will not be eligible for scheduling by the hypervisor. +=item B<send-key> I<domain-id> optional I<--codeset> B<codeset> optional I<--holdtime> B<holdtime> B<keycode>... + +Send keys to the guest + =item B<shutdown> The domain is in the process of shutting down, i.e. the guest operating system -- 1.7.4.4

On Thu, Jul 14, 2011 at 11:32:17AM +0800, Lai Jiangshan wrote:
Also support string names for the linux keycode(auto detect).
Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> Acked-by: Daniel P. Berrange <berrange@redhat.com> --- tools/virsh.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 4 ++ 2 files changed, 98 insertions(+), 0 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c index cd17f42..741e726 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -58,6 +58,7 @@ #include "threads.h" #include "command.h" #include "count-one-bits.h" +#include "virtkey.h"
static char *progname;
@@ -3350,6 +3351,98 @@ cmdInjectNMI(vshControl *ctl, const vshCmd *cmd) }
/* + * "send-key" command + */ +static const vshCmdInfo info_send_key[] = { + {"help", N_("Send keycodes to the guest")}, + {"desc", N_("Send keycodes to the guest, the keycodes must be integers\n" + " Examples:\n\n" + " virsh # send-key <domain> 37 18 21\n" + " virsh # send-key <domain> KEY_RIGHTCTRL KEY_C\n" + " virsh # send-key <domain> --codeset xt 37 18 21\n" + " virsh # send-key <domain> --holdtime 1000 0x15 18 0xf\n" + )}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_send_key[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, + {"codeset", VSH_OT_STRING, VSH_OFLAG_REQ_OPT, N_("the codeset of keycodes, default:linux")}, + {"holdtime", VSH_OT_INT, VSH_OFLAG_REQ_OPT, + N_("the time (in millsecond) how long the keys will be held")}, + {"keycode", VSH_OT_ARGV, VSH_OFLAG_REQ, N_("the key code")}, + {NULL, 0, 0, NULL} +}; + +static int get_integer_keycode(const char *key_name) +{ + long val; + char *endptr; + + val = strtol(key_name, &endptr, 0); + if (*endptr != '\0' || val > 0xffff || val <= 0) + return -1; + + return val; +} + +static bool +cmdSendKey(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom; + int ret = false; + const char *codeset_option; + int codeset; + int holdtime; + int count = 0; + const vshCmdOpt *opt = NULL; + int keycode; + unsigned int keycodes[VIR_DOMAIN_SEND_KEY_MAX_KEYS]; + + if (!vshConnectionUsability(ctl, ctl->conn)) + return false; + + if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) + return false; + + if (vshCommandOptString(cmd, "codeset", &codeset_option) <= 0) + codeset_option = "linux"; + + if (vshCommandOptInt(cmd, "holdtime", &holdtime) <= 0) + holdtime = 0; + + codeset = virKeycodeSetTypeFromString(codeset_option); + if ((int)codeset < 0) { + vshError(ctl, _("unknown codeset: '%s'"), codeset_option); + goto cleanup; + } + + while ((opt = vshCommandOptArgv(cmd, opt))) { + if (count == VIR_DOMAIN_SEND_KEY_MAX_KEYS) { + vshError(ctl, _("too many keycodes")); + goto cleanup; + } + + if ((keycode = get_integer_keycode(opt->data)) <= 0) { + if ((keycode = virParseKeyName(codeset, opt->data)) <= 0) { + vshError(ctl, _("invalid keycode: '%s'"), opt->data); + goto cleanup; + } + } + + keycodes[count] = keycode; + count++; + } + + if (!(virDomainSendKey(dom, codeset, holdtime, keycodes, count, 0) < 0)) + ret = true; + +cleanup: + virDomainFree(dom); + return ret; +} + +/* * "setmemory" command */ static const vshCmdInfo info_setmem[] = { @@ -11684,6 +11777,7 @@ static const vshCmdDef domManagementCmds[] = { {"dumpxml", cmdDumpXML, opts_dumpxml, info_dumpxml, 0}, {"edit", cmdEdit, opts_edit, info_edit, 0}, {"inject-nmi", cmdInjectNMI, opts_inject_nmi, info_inject_nmi, 0}, + {"send-key", cmdSendKey, opts_send_key, info_send_key}, {"managedsave", cmdManagedSave, opts_managedsave, info_managedsave, 0}, {"managedsave-remove", cmdManagedSaveRemove, opts_managedsaveremove, info_managedsaveremove, 0}, diff --git a/tools/virsh.pod b/tools/virsh.pod index 52f1549..d8d0a18 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -309,6 +309,10 @@ running B<virsh suspend>. When in a paused state the domain will still consume allocated resources like memory, but will not be eligible for scheduling by the hypervisor.
+=item B<send-key> I<domain-id> optional I<--codeset> B<codeset> optional I<--holdtime> B<holdtime> B<keycode>... + +Send keys to the guest + =item B<shutdown>
The domain is in the process of shutting down, i.e. the guest operating system
ACK when updated to deal with recommended changes to previous patch Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

qemu driver just accept xt_kbd codeset's keycode, so the lib virtkey is used for translating keycodes from other codesets. Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- src/qemu/qemu_driver.c | 71 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 17 ++++++++++ src/qemu/qemu_monitor.h | 5 +++ src/qemu/qemu_monitor_json.c | 15 +++++++++ src/qemu/qemu_monitor_json.h | 5 +++ src/qemu/qemu_monitor_text.c | 49 +++++++++++++++++++++++++++++ src/qemu/qemu_monitor_text.h | 5 +++ 7 files changed, 167 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 0ea182d..c93885b 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -87,6 +87,7 @@ #include "configmake.h" #include "threadpool.h" #include "locking/lock_manager.h" +#include "virtkey.h" #define VIR_FROM_THIS VIR_FROM_QEMU @@ -1843,6 +1844,75 @@ cleanup: return ret; } +static int qemuDomainSendKey(virDomainPtr domain, + unsigned int codeset, + unsigned int holdtime, + unsigned int *keycodes, + int nkeycodes, + unsigned int flags) +{ + struct qemud_driver *driver = domain->conn->privateData; + virDomainObjPtr vm = NULL; + int ret = -1; + qemuDomainObjPrivatePtr priv; + + virCheckFlags(0, -1); + + /* translate the keycode to XT_KBD for qemu driver */ + if (codeset != VIR_KEYCODE_SET_XT_KBD) { + int i; + int keycode; + + for (i = 0; i < nkeycodes; i++) { + keycode = virTranslateKeyCode(codeset, VIR_KEYCODE_SET_XT_KBD, + keycodes[i]); + if (keycode < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "can not translate " + "keycode %u of %s codeset to xt_kbd codeset " + "keycode", keycodes[i], + virKeycodeSetTypeToString(codeset)); + return -1; + } + keycodes[i] = keycode; + } + } + + qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, domain->uuid); + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(domain->uuid, uuidstr); + qemuReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + priv = vm->privateData; + + if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) + goto cleanup; + + if (!virDomainObjIsActive(vm)) { + qemuReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto cleanup; + } + + qemuDomainObjEnterMonitorWithDriver(driver, vm); + ret = qemuMonitorSendKey(priv->mon, holdtime, keycodes, nkeycodes); + qemuDomainObjExitMonitorWithDriver(driver, vm); + if (qemuDomainObjEndJob(vm) == 0) { + vm = NULL; + goto cleanup; + } + +cleanup: + if (vm) + virDomainObjUnlock(vm); + qemuDriverUnlock(driver); + return ret; +} + static int qemudDomainGetInfo(virDomainPtr dom, virDomainInfoPtr info) { @@ -8646,6 +8716,7 @@ static virDriver qemuDriver = { .domainMigratePerform3 = qemuDomainMigratePerform3, /* 0.9.2 */ .domainMigrateFinish3 = qemuDomainMigrateFinish3, /* 0.9.2 */ .domainMigrateConfirm3 = qemuDomainMigrateConfirm3, /* 0.9.2 */ + .domainSendKey = qemuDomainSendKey, /* 0.9.3 */ }; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index e593642..35fb999 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2398,6 +2398,23 @@ int qemuMonitorInjectNMI(qemuMonitorPtr mon) return ret; } +int qemuMonitorSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes) +{ + int ret; + + VIR_DEBUG("mon=%p, holdtime=%u, nkeycodes=%u", + mon, holdtime, nkeycodes); + + if (mon->json) + ret = qemuMonitorJSONSendKey(mon, holdtime, keycodes, nkeycodes); + else + ret = qemuMonitorTextSendKey(mon, holdtime, keycodes, nkeycodes); + return ret; +} + int qemuMonitorScreendump(qemuMonitorPtr mon, const char *file) { diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 893f3e9..73a95f1 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -444,6 +444,11 @@ int qemuMonitorInjectNMI(qemuMonitorPtr mon); int qemuMonitorScreendump(qemuMonitorPtr mon, const char *file); +int qemuMonitorSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes); + /** * When running two dd process and using <> redirection, we need a * shell that will not truncate files. These two strings serve that diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 4db2b78..8385085 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -2695,6 +2695,21 @@ cleanup: return ret; } +int qemuMonitorJSONSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes) +{ + /* + * FIXME: qmp sendkey has not been implemented yet, + * and qmp API of it can not be anticipated, so we use hmp temporary. + */ + if (qemuMonitorCheckHMP(mon, "sendkey")) { + return qemuMonitorTextSendKey(mon, holdtime, keycodes, nkeycodes); + } else + return -1; +} + int qemuMonitorJSONScreendump(qemuMonitorPtr mon, const char *file) { diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 380e26a..ddd121e 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -217,6 +217,11 @@ int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon, int qemuMonitorJSONInjectNMI(qemuMonitorPtr mon); +int qemuMonitorJSONSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes); + int qemuMonitorJSONScreendump(qemuMonitorPtr mon, const char *file); diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index 0965a08..52b9a34 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -2755,6 +2755,55 @@ fail: return -1; } +int qemuMonitorTextSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes) +{ + int i; + virBuffer buf = VIR_BUFFER_INITIALIZER; + char *cmd, *reply = NULL; + + if (nkeycodes > VIR_DOMAIN_SEND_KEY_MAX_KEYS || nkeycodes == 0) + return -1; + + virBufferAddLit(&buf, "sendkey "); + for (i = 0; i < nkeycodes; i++) { + if (keycodes[i] > 0xffff) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("keycode %d is invalid: 0x%X"), + i, keycodes[i]); + virBufferFreeAndReset(&buf); + return -1; + } + + if (i) + virBufferAddChar(&buf, '-'); + virBufferAsprintf(&buf, "0x%02X", keycodes[i]); + } + + if (holdtime) + virBufferAsprintf(&buf, " %u", holdtime); + + if (virBufferError(&buf)) { + virReportOOMError(); + return -1; + } + + cmd = virBufferContentAndReset(&buf); + if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("failed to send key using command '%s'"), + cmd); + VIR_FREE(cmd); + return -1; + } + + VIR_FREE(cmd); + VIR_FREE(reply); + return 0; +} + /* Returns -1 on error, -2 if not supported */ int qemuMonitorTextScreendump(qemuMonitorPtr mon, const char *file) { diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h index e53f693..042e581 100644 --- a/src/qemu/qemu_monitor_text.h +++ b/src/qemu/qemu_monitor_text.h @@ -211,6 +211,11 @@ int qemuMonitorTextArbitraryCommand(qemuMonitorPtr mon, const char *cmd, int qemuMonitorTextInjectNMI(qemuMonitorPtr mon); +int qemuMonitorTextSendKey(qemuMonitorPtr mon, + unsigned int holdtime, + unsigned int *keycodes, + unsigned int nkeycodes); + int qemuMonitorTextScreendump(qemuMonitorPtr mon, const char *file); #endif /* QEMU_MONITOR_TEXT_H */ -- 1.7.4.4

On Thu, Jul 14, 2011 at 11:32:18AM +0800, Lai Jiangshan wrote:
qemu driver just accept xt_kbd codeset's keycode, so the lib virtkey is used for translating keycodes from other codesets.
Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> --- src/qemu/qemu_driver.c | 71 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 17 ++++++++++ src/qemu/qemu_monitor.h | 5 +++ src/qemu/qemu_monitor_json.c | 15 +++++++++ src/qemu/qemu_monitor_json.h | 5 +++ src/qemu/qemu_monitor_text.c | 49 +++++++++++++++++++++++++++++ src/qemu/qemu_monitor_text.h | 5 +++ 7 files changed, 167 insertions(+), 0 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 0ea182d..c93885b 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -8646,6 +8716,7 @@ static virDriver qemuDriver = { .domainMigratePerform3 = qemuDomainMigratePerform3, /* 0.9.2 */ .domainMigrateFinish3 = qemuDomainMigrateFinish3, /* 0.9.2 */ .domainMigrateConfirm3 = qemuDomainMigrateConfirm3, /* 0.9.2 */ + .domainSendKey = qemuDomainSendKey, /* 0.9.3 */ };
s/0.9.3/0.9.4/ ACK with that fix Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Hi, Daniel Any comments? Thanks, Lai. On 07/14/2011 11:32 AM, Lai Jiangshan wrote:
Add virtkey lib for usage-improvment and keycode translating. Expose send-key in virsh Implement send-key function for the qemu driver
Daniel P. Berrange (1): util: Add keymaps.csv
Lai Jiangshan (3): util: add virtkey send-key: Expose the new API in virsh qemu:send-key: Implement the driver methods
include/libvirt/libvirt.h.in | 8 + src/Makefile.am | 11 +- src/libvirt_private.syms | 5 + src/qemu/qemu_driver.c | 71 +++++++ src/qemu/qemu_monitor.c | 17 ++ src/qemu/qemu_monitor.h | 5 + src/qemu/qemu_monitor_json.c | 15 ++ src/qemu/qemu_monitor_json.h | 5 + src/qemu/qemu_monitor_text.c | 49 +++++ src/qemu/qemu_monitor_text.h | 5 + src/util/keymaps.csv | 463 ++++++++++++++++++++++++++++++++++++++++++ src/util/virtkey.c | 117 +++++++++++ src/util/virtkey.h | 41 ++++ src/util/virtkeymap-gen.py | 47 +++++ tools/virsh.c | 94 +++++++++ tools/virsh.pod | 4 + 16 files changed, 956 insertions(+), 1 deletions(-) create mode 100644 src/util/keymaps.csv create mode 100644 src/util/virtkey.c create mode 100644 src/util/virtkey.h create mode 100644 src/util/virtkeymap-gen.py
participants (7)
-
Daniel P. Berrange
-
Daniel Veillard
-
Eric Blake
-
Gui Jianfeng
-
Lai Jiangshan
-
Matthias Bolte
-
Wen Congyang