diff options
author | Aurelien Jarno <aurelien@aurel32.net> | 2012-09-10 15:04:36 +0200 |
---|---|---|
committer | Aurelien Jarno <aurelien@aurel32.net> | 2012-09-10 15:04:36 +0200 |
commit | e22b1e9907833d72f4a959b34c5eefc3533c7711 (patch) | |
tree | 662cf685e04726bb68d8eeee94a06d694f35f95a | |
parent | a32354e206895400d17c3de9a8df1de96d3df289 (diff) | |
parent | 0ab6b63655ba59d7c05089017acb721a44979526 (diff) |
Merge branch 'queue/qmp' of git://repo.or.cz/qemu/qmp-unstable
* 'queue/qmp' of git://repo.or.cz/qemu/qmp-unstable:
tcx: tcx_screen_dump(): add error handling
tcx: tcx24_screen_dump(): add error handling
g364fb: g364fb_screen_dump(): add error handling
omap_lcdc: omap_ppm_save(): add error handling
omap_lcdc: rename ppm_save() to omap_ppm_save()
vga: ppm_save(): add error handling
qapi: convert screendump
console: vga_hw_screen_dump_ptr: take Error argument
error: add error_setg()
json-parser: Fix potential NULL pointer segfault
qapi: Fix potential NULL pointer segfault
qapi: convert sendkey
monitor: move key_defs[] table and introduce two help functions
qapi: add the QKeyCode enum
qapi: generate list struct and visit_list for enum
hmp: rename arguments
monitor: rename keyname '<' to 'less'
fix doc of using raw values with sendkey
Add support for pretty-printing response in qmp-shell
-rwxr-xr-x | QMP/qmp-shell | 46 | ||||
-rw-r--r-- | console.c | 7 | ||||
-rw-r--r-- | console.h | 10 | ||||
-rw-r--r-- | error.h | 6 | ||||
-rw-r--r-- | hmp-commands.hx | 13 | ||||
-rw-r--r-- | hmp.c | 64 | ||||
-rw-r--r-- | hmp.h | 2 | ||||
-rw-r--r-- | hw/blizzard.c | 4 | ||||
-rw-r--r-- | hw/g364fb.c | 55 | ||||
-rw-r--r-- | hw/omap_lcdc.c | 66 | ||||
-rw-r--r-- | hw/qxl.c | 7 | ||||
-rw-r--r-- | hw/tcx.c | 97 | ||||
-rw-r--r-- | hw/vga.c | 38 | ||||
-rw-r--r-- | hw/vga_int.h | 3 | ||||
-rw-r--r-- | hw/vmware_vga.c | 7 | ||||
-rw-r--r-- | input.c | 251 | ||||
-rw-r--r-- | monitor.c | 251 | ||||
-rw-r--r-- | qapi-schema.json | 59 | ||||
-rw-r--r-- | qmp-commands.hx | 33 | ||||
-rw-r--r-- | qobject.h | 2 | ||||
-rw-r--r-- | scripts/qapi-types.py | 16 | ||||
-rw-r--r-- | scripts/qapi-visit.py | 16 |
22 files changed, 704 insertions, 349 deletions
diff --git a/QMP/qmp-shell b/QMP/qmp-shell index 42dabc8c6d..24b665c8c0 100755 --- a/QMP/qmp-shell +++ b/QMP/qmp-shell @@ -33,6 +33,7 @@ import qmp import readline import sys +import pprint class QMPCompleter(list): def complete(self, text, state): @@ -52,10 +53,11 @@ class QMPShellBadPort(QMPShellError): # TODO: QMPShell's interface is a bit ugly (eg. _fill_completion() and # _execute_cmd()). Let's design a better one. class QMPShell(qmp.QEMUMonitorProtocol): - def __init__(self, address): + def __init__(self, address, pp=None): qmp.QEMUMonitorProtocol.__init__(self, self.__get_address(address)) self._greeting = None self._completer = None + self._pp = pp def __get_address(self, arg): """ @@ -114,7 +116,11 @@ class QMPShell(qmp.QEMUMonitorProtocol): if resp is None: print 'Disconnected' return False - print resp + + if self._pp is not None: + self._pp.pprint(resp) + else: + print resp return True def connect(self): @@ -222,22 +228,36 @@ def die(msg): def fail_cmdline(option=None): if option: sys.stderr.write('ERROR: bad command-line option \'%s\'\n' % option) - sys.stderr.write('qemu-shell [ -H ] < UNIX socket path> | < TCP address:port >\n') + sys.stderr.write('qemu-shell [ -p ] [ -H ] < UNIX socket path> | < TCP address:port >\n') sys.exit(1) def main(): addr = '' + qemu = None + hmp = False + pp = None + try: - if len(sys.argv) == 2: - qemu = QMPShell(sys.argv[1]) - addr = sys.argv[1] - elif len(sys.argv) == 3: - if sys.argv[1] != '-H': - fail_cmdline(sys.argv[1]) - qemu = HMPShell(sys.argv[2]) - addr = sys.argv[2] - else: - fail_cmdline() + for arg in sys.argv[1:]: + if arg == "-H": + if qemu is not None: + fail_cmdline(arg) + hmp = True + elif arg == "-p": + if pp is not None: + fail_cmdline(arg) + pp = pprint.PrettyPrinter(indent=4) + else: + if qemu is not None: + fail_cmdline(arg) + if hmp: + qemu = HMPShell(arg) + else: + qemu = QMPShell(arg, pp) + addr = arg + + if qemu is None: + fail_cmdline() except QMPShellBadPort: die('bad port number in command-line') @@ -24,6 +24,7 @@ #include "qemu-common.h" #include "console.h" #include "qemu-timer.h" +#include "qmp-commands.h" //#define DEBUG_CONSOLE #define DEFAULT_BACKSCROLL 512 @@ -176,7 +177,7 @@ void vga_hw_invalidate(void) active_console->hw_invalidate(active_console->hw); } -void vga_hw_screen_dump(const char *filename) +void qmp_screendump(const char *filename, Error **errp) { TextConsole *previous_active_console; bool cswitch; @@ -190,9 +191,9 @@ void vga_hw_screen_dump(const char *filename) console_select(0); } if (consoles[0] && consoles[0]->hw_screen_dump) { - consoles[0]->hw_screen_dump(consoles[0]->hw, filename, cswitch); + consoles[0]->hw_screen_dump(consoles[0]->hw, filename, cswitch, errp); } else { - error_report("screen dump not implemented"); + error_setg(errp, "device doesn't support screendump\n"); } if (cswitch) { @@ -6,6 +6,8 @@ #include "notify.h" #include "monitor.h" #include "trace.h" +#include "qapi-types.h" +#include "error.h" /* keyboard/mouse support */ @@ -343,7 +345,8 @@ static inline void console_write_ch(console_ch_t *dest, uint32_t ch) typedef void (*vga_hw_update_ptr)(void *); typedef void (*vga_hw_invalidate_ptr)(void *); -typedef void (*vga_hw_screen_dump_ptr)(void *, const char *, bool cswitch); +typedef void (*vga_hw_screen_dump_ptr)(void *, const char *, bool cswitch, + Error **errp); typedef void (*vga_hw_text_update_ptr)(void *, console_ch_t *); DisplayState *graphic_console_init(vga_hw_update_ptr update, @@ -354,7 +357,6 @@ DisplayState *graphic_console_init(vga_hw_update_ptr update, void vga_hw_update(void); void vga_hw_invalidate(void); -void vga_hw_screen_dump(const char *filename); void vga_hw_text_update(console_ch_t *chardata); int is_graphic_console(void); @@ -397,4 +399,8 @@ static inline int vnc_display_pw_expire(DisplayState *ds, time_t expires) /* curses.c */ void curses_display_init(DisplayState *ds, int full_screen); +/* input.c */ +int index_from_key(const char *key); +int index_from_keycode(int code); + #endif @@ -30,6 +30,12 @@ typedef struct Error Error; void error_set(Error **err, ErrorClass err_class, const char *fmt, ...) GCC_FMT_ATTR(3, 4); /** + * Same as error_set(), but sets a generic error + */ +#define error_setg(err, fmt, ...) \ + error_set(err, ERROR_CLASS_GENERIC_ERROR, fmt, ## __VA_ARGS__) + +/** * Returns true if an indirect pointer to an error is pointing to a valid * error object. */ diff --git a/hmp-commands.hx b/hmp-commands.hx index f6104b099f..ed67e997fd 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -194,8 +194,7 @@ ETEXI .args_type = "filename:F", .params = "filename", .help = "save screen into PPM image 'filename'", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_screen_dump, + .mhandler.cmd = hmp_screen_dump, }, STEXI @@ -502,19 +501,19 @@ ETEXI { .name = "sendkey", - .args_type = "string:s,hold_time:i?", + .args_type = "keys:s,hold-time:i?", .params = "keys [hold_ms]", .help = "send keys to the VM (e.g. 'sendkey ctrl-alt-f1', default hold time=100 ms)", - .mhandler.cmd = do_sendkey, + .mhandler.cmd = hmp_send_key, }, STEXI @item sendkey @var{keys} @findex sendkey -Send @var{keys} to the emulator. @var{keys} could be the name of the -key or @code{#} followed by the raw value in either decimal or hexadecimal -format. Use @code{-} to press several keys simultaneously. Example: +Send @var{keys} to the guest. @var{keys} could be the name of the +key or the raw value in hexadecimal format. Use @code{-} to press +several keys simultaneously. Example: @example sendkey ctrl-alt-f1 @end example @@ -19,6 +19,7 @@ #include "qemu-timer.h" #include "qmp-commands.h" #include "monitor.h" +#include "console.h" static void hmp_handle_error(Monitor *mon, Error **errp) { @@ -1102,3 +1103,66 @@ void hmp_closefd(Monitor *mon, const QDict *qdict) qmp_closefd(fdname, &errp); hmp_handle_error(mon, &errp); } + +void hmp_send_key(Monitor *mon, const QDict *qdict) +{ + const char *keys = qdict_get_str(qdict, "keys"); + QKeyCodeList *keylist, *head = NULL, *tmp = NULL; + int has_hold_time = qdict_haskey(qdict, "hold-time"); + int hold_time = qdict_get_try_int(qdict, "hold-time", -1); + Error *err = NULL; + char keyname_buf[16]; + char *separator; + int keyname_len, idx; + + while (1) { + separator = strchr(keys, '-'); + keyname_len = separator ? separator - keys : strlen(keys); + pstrcpy(keyname_buf, sizeof(keyname_buf), keys); + + /* Be compatible with old interface, convert user inputted "<" */ + if (!strncmp(keyname_buf, "<", 1) && keyname_len == 1) { + pstrcpy(keyname_buf, sizeof(keyname_buf), "less"); + keyname_len = 4; + } + keyname_buf[keyname_len] = 0; + + idx = index_from_key(keyname_buf); + if (idx == Q_KEY_CODE_MAX) { + monitor_printf(mon, "invalid parameter: %s\n", keyname_buf); + break; + } + + keylist = g_malloc0(sizeof(*keylist)); + keylist->value = idx; + keylist->next = NULL; + + if (!head) { + head = keylist; + } + if (tmp) { + tmp->next = keylist; + } + tmp = keylist; + + if (!separator) { + break; + } + keys = separator + 1; + } + + if (idx != Q_KEY_CODE_MAX) { + qmp_send_key(head, has_hold_time, hold_time, &err); + } + hmp_handle_error(mon, &err); + qapi_free_QKeyCodeList(head); +} + +void hmp_screen_dump(Monitor *mon, const QDict *qdict) +{ + const char *filename = qdict_get_str(qdict, "filename"); + Error *err = NULL; + + qmp_screendump(filename, &err); + hmp_handle_error(mon, &err); +} @@ -71,5 +71,7 @@ void hmp_netdev_add(Monitor *mon, const QDict *qdict); void hmp_netdev_del(Monitor *mon, const QDict *qdict); void hmp_getfd(Monitor *mon, const QDict *qdict); void hmp_closefd(Monitor *mon, const QDict *qdict); +void hmp_send_key(Monitor *mon, const QDict *qdict); +void hmp_screen_dump(Monitor *mon, const QDict *qdict); #endif diff --git a/hw/blizzard.c b/hw/blizzard.c index 29074c4fa7..d1c9d81510 100644 --- a/hw/blizzard.c +++ b/hw/blizzard.c @@ -933,13 +933,13 @@ static void blizzard_update_display(void *opaque) } static void blizzard_screen_dump(void *opaque, const char *filename, - bool cswitch) + bool cswitch, Error **errp) { BlizzardState *s = (BlizzardState *) opaque; blizzard_update_display(opaque); if (s && ds_get_data(s->state)) - ppm_save(filename, s->state->surface); + ppm_save(filename, s->state->surface, errp); } #define DEPTH 8 diff --git a/hw/g364fb.c b/hw/g364fb.c index 3a0b68fbae..059e6220e0 100644 --- a/hw/g364fb.c +++ b/hw/g364fb.c @@ -289,10 +289,11 @@ static void g364fb_reset(G364State *s) g364fb_invalidate_display(s); } -static void g364fb_screen_dump(void *opaque, const char *filename, bool cswitch) +static void g364fb_screen_dump(void *opaque, const char *filename, bool cswitch, + Error **errp) { G364State *s = opaque; - int y, x; + int ret, y, x; uint8_t index; uint8_t *data_buffer; FILE *f; @@ -300,35 +301,63 @@ static void g364fb_screen_dump(void *opaque, const char *filename, bool cswitch) qemu_flush_coalesced_mmio_buffer(); if (s->depth != 8) { - error_report("g364: unknown guest depth %d", s->depth); + error_setg(errp, "g364: unknown guest depth %d", s->depth); return; } f = fopen(filename, "wb"); - if (!f) + if (!f) { + error_setg(errp, "failed to open file '%s': %s", filename, + strerror(errno)); return; + } if (s->ctla & CTLA_FORCE_BLANK) { /* blank screen */ - fprintf(f, "P4\n%d %d\n", - s->width, s->height); + ret = fprintf(f, "P4\n%d %d\n", s->width, s->height); + if (ret < 0) { + goto write_err; + } for (y = 0; y < s->height; y++) - for (x = 0; x < s->width; x++) - fputc(0, f); + for (x = 0; x < s->width; x++) { + ret = fputc(0, f); + if (ret == EOF) { + goto write_err; + } + } } else { data_buffer = s->vram + s->top_of_screen; - fprintf(f, "P6\n%d %d\n%d\n", - s->width, s->height, 255); + ret = fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255); + if (ret < 0) { + goto write_err; + } for (y = 0; y < s->height; y++) for (x = 0; x < s->width; x++, data_buffer++) { index = *data_buffer; - fputc(s->color_palette[index][0], f); - fputc(s->color_palette[index][1], f); - fputc(s->color_palette[index][2], f); + ret = fputc(s->color_palette[index][0], f); + if (ret == EOF) { + goto write_err; + } + ret = fputc(s->color_palette[index][1], f); + if (ret == EOF) { + goto write_err; + } + ret = fputc(s->color_palette[index][2], f); + if (ret == EOF) { + goto write_err; + } } } +out: fclose(f); + return; + +write_err: + error_setg(errp, "failed to write to file '%s': %s", filename, + strerror(errno)); + unlink(filename); + goto out; } /* called for accesses to io ports */ diff --git a/hw/omap_lcdc.c b/hw/omap_lcdc.c index 4a08e9d002..e2ba10834e 100644 --- a/hw/omap_lcdc.c +++ b/hw/omap_lcdc.c @@ -224,18 +224,24 @@ static void omap_update_display(void *opaque) omap_lcd->invalidate = 0; } -static int ppm_save(const char *filename, uint8_t *data, - int w, int h, int linesize) +static void omap_ppm_save(const char *filename, uint8_t *data, + int w, int h, int linesize, Error **errp) { FILE *f; uint8_t *d, *d1; unsigned int v; - int y, x, bpp; + int ret, y, x, bpp; f = fopen(filename, "wb"); - if (!f) - return -1; - fprintf(f, "P6\n%d %d\n%d\n", w, h, 255); + if (!f) { + error_setg(errp, "failed to open file '%s': %s", filename, + strerror(errno)); + return; + } + ret = fprintf(f, "P6\n%d %d\n%d\n", w, h, 255); + if (ret < 0) { + goto write_err; + } d1 = data; bpp = linesize / w; for (y = 0; y < h; y ++) { @@ -244,35 +250,61 @@ static int ppm_save(const char *filename, uint8_t *data, v = *(uint32_t *) d; switch (bpp) { case 2: - fputc((v >> 8) & 0xf8, f); - fputc((v >> 3) & 0xfc, f); - fputc((v << 3) & 0xf8, f); + ret = fputc((v >> 8) & 0xf8, f); + if (ret == EOF) { + goto write_err; + } + ret = fputc((v >> 3) & 0xfc, f); + if (ret == EOF) { + goto write_err; + } + ret = fputc((v << 3) & 0xf8, f); + if (ret == EOF) { + goto write_err; + } break; case 3: case 4: default: - fputc((v >> 16) & 0xff, f); - fputc((v >> 8) & 0xff, f); - fputc((v) & 0xff, f); + ret = fputc((v >> 16) & 0xff, f); + if (ret == EOF) { + goto write_err; + } + ret = fputc((v >> 8) & 0xff, f); + if (ret == EOF) { + goto write_err; + } + ret = fputc((v) & 0xff, f); + if (ret == EOF) { + goto write_err; + } break; } d += bpp; } d1 += linesize; } +out: fclose(f); - return 0; + return; + +write_err: + error_setg(errp, "failed to write to file '%s': %s", filename, + strerror(errno)); + unlink(filename); + goto out; } -static void omap_screen_dump(void *opaque, const char *filename, bool cswitch) +static void omap_screen_dump(void *opaque, const char *filename, bool cswitch, + Error **errp) { struct omap_lcd_panel_s *omap_lcd = opaque; omap_update_display(opaque); if (omap_lcd && ds_get_data(omap_lcd->state)) - ppm_save(filename, ds_get_data(omap_lcd->state), - omap_lcd->width, omap_lcd->height, - ds_get_linesize(omap_lcd->state)); + omap_ppm_save(filename, ds_get_data(omap_lcd->state), + omap_lcd->width, omap_lcd->height, + ds_get_linesize(omap_lcd->state), errp); } static void omap_invalidate_display(void *opaque) { @@ -1595,7 +1595,8 @@ static void qxl_hw_invalidate(void *opaque) vga->invalidate(vga); } -static void qxl_hw_screen_dump(void *opaque, const char *filename, bool cswitch) +static void qxl_hw_screen_dump(void *opaque, const char *filename, bool cswitch, + Error **errp) { PCIQXLDevice *qxl = opaque; VGACommonState *vga = &qxl->vga; @@ -1604,10 +1605,10 @@ static void qxl_hw_screen_dump(void *opaque, const char *filename, bool cswitch) case QXL_MODE_COMPAT: case QXL_MODE_NATIVE: qxl_render_update(qxl); - ppm_save(filename, qxl->ssd.ds->surface); + ppm_save(filename, qxl->ssd.ds->surface, errp); break; case QXL_MODE_VGA: - vga->screen_dump(vga, filename, cswitch); + vga->screen_dump(vga, filename, cswitch, errp); break; default: break; @@ -56,8 +56,10 @@ typedef struct TCXState { uint8_t dac_index, dac_state; } TCXState; -static void tcx_screen_dump(void *opaque, const char *filename, bool cswitch); -static void tcx24_screen_dump(void *opaque, const char *filename, bool cswitch); +static void tcx_screen_dump(void *opaque, const char *filename, bool cswitch, + Error **errp); +static void tcx24_screen_dump(void *opaque, const char *filename, bool cswitch, + Error **errp); static void tcx_set_dirty(TCXState *s) { @@ -574,45 +576,76 @@ static int tcx_init1(SysBusDevice *dev) return 0; } -static void tcx_screen_dump(void *opaque, const char *filename, bool cswitch) +static void tcx_screen_dump(void *opaque, const char *filename, bool cswitch, + Error **errp) { TCXState *s = opaque; FILE *f; uint8_t *d, *d1, v; - int y, x; + int ret, y, x; f = fopen(filename, "wb"); - if (!f) + if (!f) { + error_setg(errp, "failed to open file '%s': %s", filename, + strerror(errno)); return; - fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255); + } + ret = fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255); + if (ret < 0) { + goto write_err; + } d1 = s->vram; for(y = 0; y < s->height; y++) { d = d1; for(x = 0; x < s->width; x++) { v = *d; - fputc(s->r[v], f); - fputc(s->g[v], f); - fputc(s->b[v], f); + ret = fputc(s->r[v], f); + if (ret == EOF) { + goto write_err; + } + ret = fputc(s->g[v], f); + if (ret == EOF) { + goto write_err; + } + ret = fputc(s->b[v], f); + if (ret == EOF) { + goto write_err; + } d++; } d1 += MAXX; } + +out: fclose(f); return; + +write_err: + error_setg(errp, "failed to write to file '%s': %s", filename, + strerror(errno)); + unlink(filename); + goto out; } -static void tcx24_screen_dump(void *opaque, const char *filename, bool cswitch) +static void tcx24_screen_dump(void *opaque, const char *filename, bool cswitch, + Error **errp) { TCXState *s = opaque; FILE *f; uint8_t *d, *d1, v; uint32_t *s24, *cptr, dval; - int y, x; + int ret, y, x; f = fopen(filename, "wb"); - if (!f) + if (!f) { + error_setg(errp, "failed to open file '%s': %s", filename, + strerror(errno)); return; - fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255); + } + ret = fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255); + if (ret < 0) { + goto write_err; + } d1 = s->vram; s24 = s->vram24; cptr = s->cplane; @@ -621,20 +654,46 @@ static void tcx24_screen_dump(void *opaque, const char *filename, bool cswitch) for(x = 0; x < s->width; x++, d++, s24++) { if ((*cptr++ & 0xff000000) == 0x03000000) { // 24-bit direct dval = *s24 & 0x00ffffff; - fputc((dval >> 16) & 0xff, f); - fputc((dval >> 8) & 0xff, f); - fputc(dval & 0xff, f); + ret = fputc((dval >> 16) & 0xff, f); + if (ret == EOF) { + goto write_err; + } + ret = fputc((dval >> 8) & 0xff, f); + if (ret == EOF) { + goto write_err; + } + ret = fputc(dval & 0xff, f); + if (ret == EOF) { + goto write_err; + } } else { v = *d; - fputc(s->r[v], f); - fputc(s->g[v], f); - fputc(s->b[v], f); + ret = fputc(s->r[v], f); + if (ret == EOF) { + goto write_err; + } + ret = fputc(s->g[v], f); + if (ret == EOF) { + goto write_err; + } + ret = fputc(s->b[v], f); + if (ret == EOF) { + goto write_err; + } } } d1 += MAXX; } + +out: fclose(f); return; + +write_err: + error_setg(errp, "failed to write to file '%s': %s", filename, + strerror(errno)); + unlink(filename); + goto out; } static Property tcx_properties[] = { @@ -166,7 +166,8 @@ static uint32_t expand4[256]; static uint16_t expand2[256]; static uint8_t expand4to8[16]; -static void vga_screen_dump(void *opaque, const char *filename, bool cswitch); +static void vga_screen_dump(void *opaque, const char *filename, bool cswitch, + Error **errp); static void vga_update_memory_access(VGACommonState *s) { @@ -2389,7 +2390,7 @@ void vga_init_vbe(VGACommonState *s, MemoryRegion *system_memory) /********************************************************/ /* vga screen dump */ -int ppm_save(const char *filename, struct DisplaySurface *ds) +void ppm_save(const char *filename, struct DisplaySurface *ds, Error **errp) { FILE *f; uint8_t *d, *d1; @@ -2401,10 +2402,16 @@ int ppm_save(const char *filename, struct DisplaySurface *ds) trace_ppm_save(filename, ds); f = fopen(filename, "wb"); - if (!f) - return -1; - fprintf(f, "P6\n%d %d\n%d\n", - ds->width, ds->height, 255); + if (!f) { + error_setg(errp, "failed to open file '%s': %s", filename, + strerror(errno)); + return; + } + ret = fprintf(f, "P6\n%d %d\n%d\n", ds->width, ds->height, 255); + if (ret < 0) { + linebuf = NULL; + goto write_err; + } linebuf = g_malloc(ds->width * 3); d1 = ds->data; for(y = 0; y < ds->height; y++) { @@ -2425,17 +2432,30 @@ int ppm_save(const char *filename, struct DisplaySurface *ds) d += ds->pf.bytes_per_pixel; } d1 += ds->linesize; + clearerr(f); ret = fwrite(linebuf, 1, pbuf - linebuf, f); (void)ret; + if (ferror(f)) { + goto write_err; + } } + +out: g_free(linebuf); fclose(f); - return 0; + return; + +write_err: + error_setg(errp, "failed to write to file '%s': %s", filename, + strerror(errno)); + unlink(filename); + goto out; } /* save the vga display in a PPM image even if no display is available */ -static void vga_screen_dump(void *opaque, const char *filename, bool cswitch) +static void vga_screen_dump(void *opaque, const char *filename, bool cswitch, + Error **errp) { VGACommonState *s = opaque; @@ -2443,5 +2463,5 @@ static void vga_screen_dump(void *opaque, const char *filename, bool cswitch) vga_invalidate_display(s); } vga_hw_update(); - ppm_save(filename, s->ds->surface); + ppm_save(filename, s->ds->surface, errp); } diff --git a/hw/vga_int.h b/hw/vga_int.h index 8938093682..330a32f77d 100644 --- a/hw/vga_int.h +++ b/hw/vga_int.h @@ -23,6 +23,7 @@ */ #include <hw/hw.h> +#include "error.h" #include "memory.h" #define ST01_V_RETRACE 0x08 @@ -204,7 +205,7 @@ void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val); uint32_t vga_mem_readb(VGACommonState *s, target_phys_addr_t addr); void vga_mem_writeb(VGACommonState *s, target_phys_addr_t addr, uint32_t val); void vga_invalidate_scanlines(VGACommonState *s, int y1, int y2); -int ppm_save(const char *filename, struct DisplaySurface *ds); +void ppm_save(const char *filename, struct DisplaySurface *ds, Error **errp); int vga_ioport_invalid(VGACommonState *s, uint32_t addr); void vga_init_vbe(VGACommonState *s, MemoryRegion *address_space); diff --git a/hw/vmware_vga.c b/hw/vmware_vga.c index f5e4f440d5..b68e88367f 100644 --- a/hw/vmware_vga.c +++ b/hw/vmware_vga.c @@ -1003,18 +1003,19 @@ static void vmsvga_invalidate_display(void *opaque) /* save the vga display in a PPM image even if no display is available */ -static void vmsvga_screen_dump(void *opaque, const char *filename, bool cswitch) +static void vmsvga_screen_dump(void *opaque, const char *filename, bool cswitch, + Error **errp) { struct vmsvga_state_s *s = opaque; if (!s->enable) { - s->vga.screen_dump(&s->vga, filename, cswitch); + s->vga.screen_dump(&s->vga, filename, cswitch, errp); return; } if (s->depth == 32) { DisplaySurface *ds = qemu_create_displaysurface_from(s->width, s->height, 32, ds_get_linesize(s->vga.ds), s->vga.vram_ptr); - ppm_save(filename, ds); + ppm_save(filename, ds, errp); g_free(ds); } } @@ -28,6 +28,7 @@ #include "console.h" #include "error.h" #include "qmp-commands.h" +#include "qapi-types.h" static QEMUPutKBDEvent *qemu_put_kbd_event; static void *qemu_put_kbd_event_opaque; @@ -37,6 +38,256 @@ static QTAILQ_HEAD(, QEMUPutMouseEntry) mouse_handlers = static NotifierList mouse_mode_notifiers = NOTIFIER_LIST_INITIALIZER(mouse_mode_notifiers); +static const int key_defs[] = { + [Q_KEY_CODE_SHIFT] = 0x2a, + [Q_KEY_CODE_SHIFT_R] = 0x36, + + [Q_KEY_CODE_ALT] = 0x38, + [Q_KEY_CODE_ALT_R] = 0xb8, + [Q_KEY_CODE_ALTGR] = 0x64, + [Q_KEY_CODE_ALTGR_R] = 0xe4, + [Q_KEY_CODE_CTRL] = 0x1d, + [Q_KEY_CODE_CTRL_R] = 0x9d, + + [Q_KEY_CODE_MENU] = 0xdd, + + [Q_KEY_CODE_ESC] = 0x01, + + [Q_KEY_CODE_1] = 0x02, + [Q_KEY_CODE_2] = 0x03, + [Q_KEY_CODE_3] = 0x04, + [Q_KEY_CODE_4] = 0x05, + [Q_KEY_CODE_5] = 0x06, + [Q_KEY_CODE_6] = 0x07, + [Q_KEY_CODE_7] = 0x08, + [Q_KEY_CODE_8] = 0x09, + [Q_KEY_CODE_9] = 0x0a, + [Q_KEY_CODE_0] = 0x0b, + [Q_KEY_CODE_MINUS] = 0x0c, + [Q_KEY_CODE_EQUAL] = 0x0d, + [Q_KEY_CODE_BACKSPACE] = 0x0e, + + [Q_KEY_CODE_TAB] = 0x0f, + [Q_KEY_CODE_Q] = 0x10, + [Q_KEY_CODE_W] = 0x11, + [Q_KEY_CODE_E] = 0x12, + [Q_KEY_CODE_R] = 0x13, + [Q_KEY_CODE_T] = 0x14, + [Q_KEY_CODE_Y] = 0x15, + [Q_KEY_CODE_U] = 0x16, + [Q_KEY_CODE_I] = 0x17, + [Q_KEY_CODE_O] = 0x18, + [Q_KEY_CODE_P] = 0x19, + [Q_KEY_CODE_BRACKET_LEFT] = 0x1a, + [Q_KEY_CODE_BRACKET_RIGHT] = 0x1b, + [Q_KEY_CODE_RET] = 0x1c, + + [Q_KEY_CODE_A] = 0x1e, + [Q_KEY_CODE_S] = 0x1f, + [Q_KEY_CODE_D] = 0x20, + [Q_KEY_CODE_F] = 0x21, + [Q_KEY_CODE_G] = 0x22, + [Q_KEY_CODE_H] = 0x23, + [Q_KEY_CODE_J] = 0x24, + [Q_KEY_CODE_K] = 0x25, + [Q_KEY_CODE_L] = 0x26, + [Q_KEY_CODE_SEMICOLON] = 0x27, + [Q_KEY_CODE_APOSTROPHE] = 0x28, + [Q_KEY_CODE_GRAVE_ACCENT] = 0x29, + + [Q_KEY_CODE_BACKSLASH] = 0x2b, + [Q_KEY_CODE_Z] = 0x2c, + [Q_KEY_CODE_X] = 0x2d, + [Q_KEY_CODE_C] = 0x2e, + [Q_KEY_CODE_V] = 0x2f, + [Q_KEY_CODE_B] = 0x30, + [Q_KEY_CODE_N] = 0x31, + [Q_KEY_CODE_M] = 0x32, + [Q_KEY_CODE_COMMA] = 0x33, + [Q_KEY_CODE_DOT] = 0x34, + [Q_KEY_CODE_SLASH] = 0x35, + + [Q_KEY_CODE_ASTERISK] = 0x37, + + [Q_KEY_CODE_SPC] = 0x39, + [Q_KEY_CODE_CAPS_LOCK] = 0x3a, + [Q_KEY_CODE_F1] = 0x3b, + [Q_KEY_CODE_F2] = 0x3c, + [Q_KEY_CODE_F3] = 0x3d, + [Q_KEY_CODE_F4] = 0x3e, + [Q_KEY_CODE_F5] = 0x3f, + [Q_KEY_CODE_F6] = 0x40, + [Q_KEY_CODE_F7] = 0x41, + [Q_KEY_CODE_F8] = 0x42, + [Q_KEY_CODE_F9] = 0x43, + [Q_KEY_CODE_F10] = 0x44, + [Q_KEY_CODE_NUM_LOCK] = 0x45, + [Q_KEY_CODE_SCROLL_LOCK] = 0x46, + + [Q_KEY_CODE_KP_DIVIDE] = 0xb5, + [Q_KEY_CODE_KP_MULTIPLY] = 0x37, + [Q_KEY_CODE_KP_SUBTRACT] = 0x4a, + [Q_KEY_CODE_KP_ADD] = 0x4e, + [Q_KEY_CODE_KP_ENTER] = 0x9c, + [Q_KEY_CODE_KP_DECIMAL] = 0x53, + [Q_KEY_CODE_SYSRQ] = 0x54, + + [Q_KEY_CODE_KP_0] = 0x52, + [Q_KEY_CODE_KP_1] = 0x4f, + [Q_KEY_CODE_KP_2] = 0x50, + [Q_KEY_CODE_KP_3] = 0x51, + [Q_KEY_CODE_KP_4] = 0x4b, + [Q_KEY_CODE_KP_5] = 0x4c, + [Q_KEY_CODE_KP_6] = 0x4d, + [Q_KEY_CODE_KP_7] = 0x47, + [Q_KEY_CODE_KP_8] = 0x48, + [Q_KEY_CODE_KP_9] = 0x49, + + [Q_KEY_CODE_LESS] = 0x56, + + [Q_KEY_CODE_F11] = 0x57, + [Q_KEY_CODE_F12] = 0x58, + + [Q_KEY_CODE_PRINT] = 0xb7, + + [Q_KEY_CODE_HOME] = 0xc7, + [Q_KEY_CODE_PGUP] = 0xc9, + [Q_KEY_CODE_PGDN] = 0xd1, + [Q_KEY_CODE_END] = 0xcf, + + [Q_KEY_CODE_LEFT] = 0xcb, + [Q_KEY_CODE_UP] = 0xc8, + [Q_KEY_CODE_DOWN] = 0xd0, + [Q_KEY_CODE_RIGHT] = 0xcd, + + [Q_KEY_CODE_INSERT] = 0xd2, + [Q_KEY_CODE_DELETE] = 0xd3, +#ifdef NEED_CPU_H +#if defined(TARGET_SPARC) && !defined(TARGET_SPARC64) + [Q_KEY_CODE_STOP] = 0xf0, + [Q_KEY_CODE_AGAIN] = 0xf1, + [Q_KEY_CODE_PROPS] = 0xf2, + [Q_KEY_CODE_UNDO] = 0xf3, + [Q_KEY_CODE_FRONT] = 0xf4, + [Q_KEY_CODE_COPY] = 0xf5, + [Q_KEY_CODE_OPEN] = 0xf6, + [Q_KEY_CODE_PASTE] = 0xf7, + [Q_KEY_CODE_FIND] = 0xf8, + [Q_KEY_CODE_CUT] = 0xf9, + [Q_KEY_CODE_LF] = 0xfa, + [Q_KEY_CODE_HELP] = 0xfb, + [Q_KEY_CODE_META_L] = 0xfc, + [Q_KEY_CODE_META_R] = 0xfd, + [Q_KEY_CODE_COMPOSE] = 0xfe, +#endif +#endif + [Q_KEY_CODE_MAX] = 0, +}; + +int index_from_key(const char *key) +{ + int i, keycode; + char *endp; + + for (i = 0; QKeyCode_lookup[i] != NULL; i++) { + if (!strcmp(key, QKeyCode_lookup[i])) { + break; + } + } + + if (strstart(key, "0x", NULL)) { + keycode = strtoul(key, &endp, 0); + if (*endp == '\0' && keycode >= 0x01 && keycode <= 0xff) { + for (i = 0; i < Q_KEY_CODE_MAX; i++) { + if (keycode == key_defs[i]) { + break; + } + } + } + } + + /* Return Q_KEY_CODE_MAX if the key is invalid */ + return i; +} + +int index_from_keycode(int code) +{ + int i; + + for (i = 0; i < Q_KEY_CODE_MAX; i++) { + if (key_defs[i] == code) { + break; + } + } + + /* Return Q_KEY_CODE_MAX if the code is invalid */ + return i; +} + +static QKeyCodeList *keycodes; +static QEMUTimer *key_timer; + +static void release_keys(void *opaque) +{ + int keycode; + QKeyCodeList *p; + + for (p = keycodes; p != NULL; p = p->next) { + keycode = key_defs[p->value]; + if (keycode & 0x80) { + kbd_put_keycode(0xe0); + } + kbd_put_keycode(keycode | 0x80); + } + qapi_free_QKeyCodeList(keycodes); + keycodes = NULL; +} + +void qmp_send_key(QKeyCodeList *keys, bool has_hold_time, int64_t hold_time, + Error **errp) +{ + int keycode; + QKeyCodeList *p, *keylist, *head = NULL, *tmp = NULL; + + if (!key_timer) { + key_timer = qemu_new_timer_ns(vm_clock, release_keys, NULL); + } + + if (keycodes != NULL) { + qemu_del_timer(key_timer); + release_keys(NULL); + } + if (!has_hold_time) { + hold_time = 100; + } + + for (p = keys; p != NULL; p = p->next) { + keylist = g_malloc0(sizeof(*keylist)); + keylist->value = p->value; + keylist->next = NULL; + + if (!head) { + head = keylist; + } + if (tmp) { + tmp->next = keylist; + } + tmp = keylist; + + /* key down events */ + keycode = key_defs[p->value]; + if (keycode & 0x80) { + kbd_put_keycode(0xe0); + } + kbd_put_keycode(keycode & 0x7f); + } + keycodes = head; + + /* delayed key up events */ + qemu_mod_timer(key_timer, qemu_get_clock_ns(vm_clock) + + muldiv64(get_ticks_per_sec(), hold_time, 1000)); +} + void qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque) { qemu_put_kbd_event_opaque = opaque; @@ -1016,12 +1016,6 @@ static int client_migrate_info(Monitor *mon, const QDict *qdict, return -1; } -static int do_screen_dump(Monitor *mon, const QDict *qdict, QObject **ret_data) -{ - vga_hw_screen_dump(qdict_get_str(qdict, "filename")); - return 0; -} - static void do_logfile(Monitor *mon, const QDict *qdict) { cpu_set_log_filename(qdict_get_str(qdict, "filename")); @@ -1290,245 +1284,6 @@ static void do_sum(Monitor *mon, const QDict *qdict) monitor_printf(mon, "%05d\n", sum); } -typedef struct { - int keycode; - const char *name; -} KeyDef; - -static const KeyDef key_defs[] = { - { 0x2a, "shift" }, - { 0x36, "shift_r" }, - - { 0x38, "alt" }, - { 0xb8, "alt_r" }, - { 0x64, "altgr" }, - { 0xe4, "altgr_r" }, - { 0x1d, "ctrl" }, - { 0x9d, "ctrl_r" }, - - { 0xdd, "menu" }, - - { 0x01, "esc" }, - - { 0x02, "1" }, - { 0x03, "2" }, - { 0x04, "3" }, - { 0x05, "4" }, - { 0x06, "5" }, - { 0x07, "6" }, - { 0x08, "7" }, - { 0x09, "8" }, - { 0x0a, "9" }, - { 0x0b, "0" }, - { 0x0c, "minus" }, - { 0x0d, "equal" }, - { 0x0e, "backspace" }, - - { 0x0f, "tab" }, - { 0x10, "q" }, - { 0x11, "w" }, - { 0x12, "e" }, - { 0x13, "r" }, - { 0x14, "t" }, - { 0x15, "y" }, - { 0x16, "u" }, - { 0x17, "i" }, - { 0x18, "o" }, - { 0x19, "p" }, - { 0x1a, "bracket_left" }, - { 0x1b, "bracket_right" }, - { 0x1c, "ret" }, - - { 0x1e, "a" }, - { 0x1f, "s" }, - { 0x20, "d" }, - { 0x21, "f" }, - { 0x22, "g" }, - { 0x23, "h" }, - { 0x24, "j" }, - { 0x25, "k" }, - { 0x26, "l" }, - { 0x27, "semicolon" }, - { 0x28, "apostrophe" }, - { 0x29, "grave_accent" }, - - { 0x2b, "backslash" }, - { 0x2c, "z" }, - { 0x2d, "x" }, - { 0x2e, "c" }, - { 0x2f, "v" }, - { 0x30, "b" }, - { 0x31, "n" }, - { 0x32, "m" }, - { 0x33, "comma" }, - { 0x34, "dot" }, - { 0x35, "slash" }, - - { 0x37, "asterisk" }, - - { 0x39, "spc" }, - { 0x3a, "caps_lock" }, - { 0x3b, "f1" }, - { 0x3c, "f2" }, - { 0x3d, "f3" }, - { 0x3e, "f4" }, - { 0x3f, "f5" }, - { 0x40, "f6" }, - { 0x41, "f7" }, - { 0x42, "f8" }, - { 0x43, "f9" }, - { 0x44, "f10" }, - { 0x45, "num_lock" }, - { 0x46, "scroll_lock" }, - - { 0xb5, "kp_divide" }, - { 0x37, "kp_multiply" }, - { 0x4a, "kp_subtract" }, - { 0x4e, "kp_add" }, - { 0x9c, "kp_enter" }, - { 0x53, "kp_decimal" }, - { 0x54, "sysrq" }, - - { 0x52, "kp_0" }, - { 0x4f, "kp_1" }, - { 0x50, "kp_2" }, - { 0x51, "kp_3" }, - { 0x4b, "kp_4" }, - { 0x4c, "kp_5" }, - { 0x4d, "kp_6" }, - { 0x47, "kp_7" }, - { 0x48, "kp_8" }, - { 0x49, "kp_9" }, - - { 0x56, "<" }, - - { 0x57, "f11" }, - { 0x58, "f12" }, - - { 0xb7, "print" }, - - { 0xc7, "home" }, - { 0xc9, "pgup" }, - { 0xd1, "pgdn" }, - { 0xcf, "end" }, - - { 0xcb, "left" }, - { 0xc8, "up" }, - { 0xd0, "down" }, - { 0xcd, "right" }, - - { 0xd2, "insert" }, - { 0xd3, "delete" }, -#if defined(TARGET_SPARC) && !defined(TARGET_SPARC64) - { 0xf0, "stop" }, - { 0xf1, "again" }, - { 0xf2, "props" }, - { 0xf3, "undo" }, - { 0xf4, "front" }, - { 0xf5, "copy" }, - { 0xf6, "open" }, - { 0xf7, "paste" }, - { 0xf8, "find" }, - { 0xf9, "cut" }, - { 0xfa, "lf" }, - { 0xfb, "help" }, - { 0xfc, "meta_l" }, - { 0xfd, "meta_r" }, - { 0xfe, "compose" }, -#endif - { 0, NULL }, -}; - -static int get_keycode(const char *key) -{ - const KeyDef *p; - char *endp; - int ret; - - for(p = key_defs; p->name != NULL; p++) { - if (!strcmp(key, p->name)) - return p->keycode; - } - if (strstart(key, "0x", NULL)) { - ret = strtoul(key, &endp, 0); - if (*endp == '\0' && ret >= 0x01 && ret <= 0xff) - return ret; - } - return -1; -} - -#define MAX_KEYCODES 16 -static uint8_t keycodes[MAX_KEYCODES]; -static int nb_pending_keycodes; -static QEMUTimer *key_timer; - -static void release_keys(void *opaque) -{ - int keycode; - - while (nb_pending_keycodes > 0) { - nb_pending_keycodes--; - keycode = keycodes[nb_pending_keycodes]; - if (keycode & 0x80) - kbd_put_keycode(0xe0); - kbd_put_keycode(keycode | 0x80); - } -} - -static void do_sendkey(Monitor *mon, const QDict *qdict) -{ - char keyname_buf[16]; - char *separator; - int keyname_len, keycode, i; - const char *string = qdict_get_str(qdict, "string"); - int has_hold_time = qdict_haskey(qdict, "hold_time"); - int hold_time = qdict_get_try_int(qdict, "hold_time", -1); - - if (nb_pending_keycodes > 0) { - qemu_del_timer(key_timer); - release_keys(NULL); - } - if (!has_hold_time) - hold_time = 100; - i = 0; - while (1) { - separator = strchr(string, '-'); - keyname_len = separator ? separator - string : strlen(string); - if (keyname_len > 0) { - pstrcpy(keyname_buf, sizeof(keyname_buf), string); - if (keyname_len > sizeof(keyname_buf) - 1) { - monitor_printf(mon, "invalid key: '%s...'\n", keyname_buf); - return; - } - if (i == MAX_KEYCODES) { - monitor_printf(mon, "too many keys\n"); - return; - } - keyname_buf[keyname_len] = 0; - keycode = get_keycode(keyname_buf); - if (keycode < 0) { - monitor_printf(mon, "unknown key: '%s'\n", keyname_buf); - return; - } - keycodes[i++] = keycode; - } - if (!separator) - break; - string = separator + 1; - } - nb_pending_keycodes = i; - /* key down events */ - for (i = 0; i < nb_pending_keycodes; i++) { - keycode = keycodes[i]; - if (keycode & 0x80) - kbd_put_keycode(0xe0); - kbd_put_keycode(keycode & 0x7f); - } - /* delayed key up events */ - qemu_mod_timer(key_timer, qemu_get_clock_ns(vm_clock) + - muldiv64(get_ticks_per_sec(), hold_time, 1000)); -} - static int mouse_button_state; static void do_mouse_move(Monitor *mon, const QDict *qdict) @@ -4315,7 +4070,6 @@ static void monitor_find_completion(const char *cmdline) int nb_args, i, len; const char *ptype, *str; const mon_cmd_t *cmd; - const KeyDef *key; parse_cmdline(cmdline, &nb_args, args); #ifdef DEBUG_COMPLETION @@ -4389,8 +4143,8 @@ static void monitor_find_completion(const char *cmdline) if (sep) str = sep + 1; readline_set_completion_index(cur_mon->rs, strlen(str)); - for(key = key_defs; key->name != NULL; key++) { - cmd_completion(str, key->name); + for (i = 0; i < Q_KEY_CODE_MAX; i++) { + cmd_completion(str, QKeyCode_lookup[i]); } } else if (!strcmp(cmd->name, "help|?")) { readline_set_completion_index(cur_mon->rs, strlen(str)); @@ -4926,7 +4680,6 @@ void monitor_init(CharDriverState *chr, int flags) Monitor *mon; if (is_first_init) { - key_timer = qemu_new_timer_ns(vm_clock, release_keys, NULL); monitor_protocol_event_init(); is_first_init = 0; } diff --git a/qapi-schema.json b/qapi-schema.json index bd8ad74495..df47fcee2f 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -2493,3 +2493,62 @@ # Since: 1.2.0 ## { 'command': 'query-target', 'returns': 'TargetInfo' } + +## +# @QKeyCode: +# +# An enumeration of key name. +# +# This is used by the send-key command. +# +# Since: 1.3.0 +## +{ 'enum': 'QKeyCode', + 'data': [ 'shift', 'shift_r', 'alt', 'alt_r', 'altgr', 'altgr_r', 'ctrl', + 'ctrl_r', 'menu', 'esc', '1', '2', '3', '4', '5', '6', '7', '8', + '9', '0', 'minus', 'equal', 'backspace', 'tab', 'q', 'w', 'e', + 'r', 't', 'y', 'u', 'i', 'o', 'p', 'bracket_left', 'bracket_right', + 'ret', 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'semicolon', + 'apostrophe', 'grave_accent', 'backslash', 'z', 'x', 'c', 'v', 'b', + 'n', 'm', 'comma', 'dot', 'slash', 'asterisk', 'spc', 'caps_lock', + 'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'f10', + 'num_lock', 'scroll_lock', 'kp_divide', 'kp_multiply', + 'kp_subtract', 'kp_add', 'kp_enter', 'kp_decimal', 'sysrq', 'kp_0', + 'kp_1', 'kp_2', 'kp_3', 'kp_4', 'kp_5', 'kp_6', 'kp_7', 'kp_8', + 'kp_9', 'less', 'f11', 'f12', 'print', 'home', 'pgup', 'pgdn', 'end', + 'left', 'up', 'down', 'right', 'insert', 'delete', 'stop', 'again', + 'props', 'undo', 'front', 'copy', 'open', 'paste', 'find', 'cut', + 'lf', 'help', 'meta_l', 'meta_r', 'compose' ] } + +## +# @send-key: +# +# Send keys to guest. +# +# @keys: key sequence. 'keys' is the name of the key. Use a JSON array to +# press several keys simultaneously. +# +# @hold-time: #optional time to delay key up events, milliseconds. Defaults +# to 100 +# +# Returns: Nothing on success +# If key is unknown or redundant, InvalidParameter +# +# Since: 1.3.0 +# +## +{ 'command': 'send-key', + 'data': { 'keys': ['QKeyCode'], '*hold-time': 'int' } } + +## +# @screendump: +# +# Write a PPM of the VGA screen to a file. +# +# @filename: the path of a new PPM file to store the image +# +# Returns: Nothing on success +# +# Since: 0.14.0 +## +{ 'command': 'screendump', 'data': {'filename': 'str'} } diff --git a/qmp-commands.hx b/qmp-commands.hx index 3745a21199..6e21ddba61 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -146,10 +146,7 @@ EQMP { .name = "screendump", .args_type = "filename:F", - .params = "filename", - .help = "save screen into PPM image 'filename'", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_screen_dump, + .mhandler.cmd_new = qmp_marshal_input_screendump, }, SQMP @@ -335,6 +332,34 @@ Example: EQMP { + .name = "send-key", + .args_type = "keys:O,hold-time:i?", + .mhandler.cmd_new = qmp_marshal_input_send_key, + }, + +SQMP +send-key +---------- + +Send keys to VM. + +Arguments: + +keys array: + - "key": key sequence (a json-array of key enum values) + +- hold-time: time to delay key up events, milliseconds. Defaults to 100 + (json-int, optional) + +Example: + +-> { "execute": "send-key", + "arguments": { 'keys': [ 'ctrl', 'alt', 'delete' ] } } +<- { "return": {} } + +EQMP + + { .name = "cpu", .args_type = "index:i", .mhandler.cmd_new = qmp_marshal_input_cpu, @@ -71,7 +71,7 @@ typedef struct QObject { /* High-level interface for qobject_decref() */ #define QDECREF(obj) \ - qobject_decref(QOBJECT(obj)) + qobject_decref(obj ? QOBJECT(obj) : NULL) /* Initialize an object to default values */ #define QOBJECT_INIT(obj, qtype_type) \ diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py index cf601ae2d2..49ef569a2f 100644 --- a/scripts/qapi-types.py +++ b/scripts/qapi-types.py @@ -28,6 +28,16 @@ typedef struct %(name)sList ''', name=name) +def generate_fwd_enum_struct(name, members): + return mcgen(''' +typedef struct %(name)sList +{ + %(name)s value; + struct %(name)sList *next; +} %(name)sList; +''', + name=name) + def generate_struct(structname, fieldname, members): ret = mcgen(''' struct %(name)s @@ -276,7 +286,8 @@ for expr in exprs: if expr.has_key('type'): ret += generate_fwd_struct(expr['type'], expr['data']) elif expr.has_key('enum'): - ret += generate_enum(expr['enum'], expr['data']) + ret += generate_enum(expr['enum'], expr['data']) + "\n" + ret += generate_fwd_enum_struct(expr['enum'], expr['data']) fdef.write(generate_enum_lookup(expr['enum'], expr['data'])) elif expr.has_key('union'): ret += generate_fwd_struct(expr['union'], expr['data']) + "\n" @@ -300,6 +311,9 @@ for expr in exprs: fdef.write(generate_type_cleanup(expr['union'] + "List") + "\n") ret += generate_type_cleanup_decl(expr['union']) fdef.write(generate_type_cleanup(expr['union']) + "\n") + elif expr.has_key('enum'): + ret += generate_type_cleanup_decl(expr['enum'] + "List") + fdef.write(generate_type_cleanup(expr['enum'] + "List") + "\n") else: continue fdecl.write(ret) diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py index 04ef7c41ab..e2093e8947 100644 --- a/scripts/qapi-visit.py +++ b/scripts/qapi-visit.py @@ -157,7 +157,7 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error ** if (!error_is_set(errp)) { visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err); if (!err) { - if (!obj || *obj) { + if (obj && *obj) { visit_type_%(name)sKind(m, &(*obj)->kind, "type", &err); if (!err) { switch ((*obj)->kind) { @@ -217,6 +217,16 @@ void visit_type_%(name)sList(Visitor *m, %(name)sList ** obj, const char *name, return ret +def generate_enum_declaration(name, members, genlist=True): + ret = "" + if genlist: + ret += mcgen(''' +void visit_type_%(name)sList(Visitor *m, %(name)sList ** obj, const char *name, Error **errp); +''', + name=name) + + return ret + def generate_decl_enum(name, members, genlist=True): return mcgen(''' @@ -335,10 +345,12 @@ for expr in exprs: ret += generate_declaration(expr['union'], expr['data']) fdecl.write(ret) elif expr.has_key('enum'): - ret = generate_visit_enum(expr['enum'], expr['data']) + ret = generate_visit_list(expr['enum'], expr['data']) + ret += generate_visit_enum(expr['enum'], expr['data']) fdef.write(ret) ret = generate_decl_enum(expr['enum'], expr['data']) + ret += generate_enum_declaration(expr['enum'], expr['data']) fdecl.write(ret) fdecl.write(''' |