From fa779b65fa77d5632df1f8b2df33e9023dbc4cc8 Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Wed, 15 Aug 2012 11:33:47 +0100 Subject: [PATCH 01/19] Add support for pretty-printing response in qmp-shell Add a '-p' arg to the QMP/qmp-shell test program, which uses the python pprint module to pretty-print the dictionary returned from a command $ qmp-shell -p /tmp/qemu Welcome to the QMP low-level shell! Connected to QEMU 1.1.50 (QEMU) query-cpus { u'return': [ { u'CPU': 0, u'current': True, u'halted': True, u'pc': 1048556, u'thread_id': 7108}]} Signed-off-by: Daniel P. Berrange Signed-off-by: Luiz Capitulino --- QMP/qmp-shell | 46 +++++++++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 13 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') From 886cc706ce5d4d3d1c296f028ddc2991cfbe3bbe Mon Sep 17 00:00:00 2001 From: Amos Kong Date: Fri, 31 Aug 2012 10:56:20 +0800 Subject: [PATCH 02/19] fix doc of using raw values with sendkey (qemu) sendkey a (qemu) sendkey 0x1e (qemu) sendkey #0x1e unknown key: '#0x1e' The last command doesn't work, '#' is not requested before raw values, and the raw value in decimal format is not supported. Signed-off-by: Amos Kong Signed-off-by: Luiz Capitulino --- hmp-commands.hx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index f6104b099f..6a70a9c598 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -512,9 +512,9 @@ 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 From cd383492bfb5ea805d518e129d7a5402c7b99ef2 Mon Sep 17 00:00:00 2001 From: Amos Kong Date: Fri, 31 Aug 2012 10:56:21 +0800 Subject: [PATCH 03/19] monitor: rename keyname '<' to 'less' There are many maps of keycode 0x56 in pc-bios/keymaps/* pc-bios/keymaps/common:less 0x56 pc-bios/keymaps/common:greater 0x56 shift pc-bios/keymaps/common:bar 0x56 altgr pc-bios/keymaps/common:brokenbar 0x56 shift altgr This patch just renamed '<' to 'less', QAPI might add new variable by adding a prefix to keyname, '$PREFIX_<' is not available, '$PREFIX_less' is ok. For compatibility, convert user inputted '<' to 'less'. Signed-off-by: Amos Kong Signed-off-by: Luiz Capitulino --- monitor.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/monitor.c b/monitor.c index b17b1bb849..c97c120062 100644 --- a/monitor.c +++ b/monitor.c @@ -1400,7 +1400,7 @@ static const KeyDef key_defs[] = { { 0x48, "kp_8" }, { 0x49, "kp_9" }, - { 0x56, "<" }, + { 0x56, "less" }, { 0x57, "f11" }, { 0x58, "f12" }, @@ -1504,6 +1504,13 @@ static void do_sendkey(Monitor *mon, const QDict *qdict) monitor_printf(mon, "too many keys\n"); return; } + + /* Be compatible with old interface, convert user inputted "<" */ + if (!strncmp(keyname_buf, "<", 1) && keyname_len == 1) { + pstrcpy(keyname_buf, sizeof(keyname_buf), "less"); + keyname_len = 4; + } + keyname_buf[keyname_len] = 0; keycode = get_keycode(keyname_buf); if (keycode < 0) { From 2ef20c15b4736c9b0afc7ef2b14239182fbcd3e1 Mon Sep 17 00:00:00 2001 From: Amos Kong Date: Fri, 31 Aug 2012 10:56:22 +0800 Subject: [PATCH 04/19] hmp: rename arguments Rename 'string' to 'keys', rename 'hold_time' to 'hold-time'. Signed-off-by: Amos Kong Signed-off-by: Luiz Capitulino --- hmp-commands.hx | 2 +- monitor.c | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index 6a70a9c598..bd0c6c9191 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -502,7 +502,7 @@ 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, diff --git a/monitor.c b/monitor.c index c97c120062..84176eb8f6 100644 --- a/monitor.c +++ b/monitor.c @@ -1480,9 +1480,9 @@ 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); + const char *keys = qdict_get_str(qdict, "keys"); + int has_hold_time = qdict_haskey(qdict, "hold-time"); + int hold_time = qdict_get_try_int(qdict, "hold-time", -1); if (nb_pending_keycodes > 0) { qemu_del_timer(key_timer); @@ -1492,10 +1492,10 @@ static void do_sendkey(Monitor *mon, const QDict *qdict) hold_time = 100; i = 0; while (1) { - separator = strchr(string, '-'); - keyname_len = separator ? separator - string : strlen(string); + separator = strchr(keys, '-'); + keyname_len = separator ? separator - keys : strlen(keys); if (keyname_len > 0) { - pstrcpy(keyname_buf, sizeof(keyname_buf), string); + pstrcpy(keyname_buf, sizeof(keyname_buf), keys); if (keyname_len > sizeof(keyname_buf) - 1) { monitor_printf(mon, "invalid key: '%s...'\n", keyname_buf); return; @@ -1521,7 +1521,7 @@ static void do_sendkey(Monitor *mon, const QDict *qdict) } if (!separator) break; - string = separator + 1; + keys = separator + 1; } nb_pending_keycodes = i; /* key down events */ From b9c4b48d50057b6aee7c70879b240ff76f39c85b Mon Sep 17 00:00:00 2001 From: Amos Kong Date: Fri, 31 Aug 2012 10:56:23 +0800 Subject: [PATCH 05/19] qapi: generate list struct and visit_list for enum Currently, if we define an 'enum' and use it in one command's data, list struct for enum could not be generated, but it's used in qmp function. For example: KeyCodesList could not be generated. >>> qapi-schema.json: { 'enum': 'KeyCodes', 'data': [ 'shift', 'alt' ... ] } { 'command': 'sendkey', 'data': { 'keys': ['KeyCodes'], '*hold-time': 'int' } } >>> qmp-command.h: void qmp_sendkey(KeyCodesList * keys, bool has_hold_time, int64_t hold_time, Error **errp); This patch lets qapi generate list struct and visit_list for enum. Signed-off-by: Amos Kong Signed-off-by: Luiz Capitulino --- scripts/qapi-types.py | 16 +++++++++++++++- scripts/qapi-visit.py | 14 +++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) 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..cbec24da16 100644 --- a/scripts/qapi-visit.py +++ b/scripts/qapi-visit.py @@ -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(''' From 411656f48d71131d6d1cb5b6db792fc7b451e72e Mon Sep 17 00:00:00 2001 From: Amos Kong Date: Fri, 31 Aug 2012 10:56:24 +0800 Subject: [PATCH 06/19] qapi: add the QKeyCode enum key_defs[] in monitor.c is a mapping table of keys and keycodes, this patch added a QKeyCode enum. Key's index in the enmu is same as keycode's index in key_defs[]. Signed-off-by: Amos Kong Signed-off-by: Luiz Capitulino --- qapi-schema.json | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/qapi-schema.json b/qapi-schema.json index bd8ad74495..856e11a729 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -2493,3 +2493,29 @@ # 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' ] } From 1048c88f03545fa42bdebb077871a743a614d2ab Mon Sep 17 00:00:00 2001 From: Amos Kong Date: Fri, 31 Aug 2012 10:56:25 +0800 Subject: [PATCH 07/19] monitor: move key_defs[] table and introduce two help functions This patch added two help functions to convert key/code to index of mapping table, those functions will return Q_KEY_CODE_MAX if the code/key is invalid. Patch also moved key_defs[] to input.c, and removed useless KeyDef struct. Key's index in QKeyCode enmu is same as keycode's index in new key_defs[]. Monitor functions were changed to access key_defs[] directly. key_defs[] is used in do_send_key(), so export key_defs[]. It will be changed to static in next patch. Signed-off-by: Amos Kong Signed-off-by: Luiz Capitulino --- console.h | 6 ++ input.c | 186 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ monitor.c | 183 ++++------------------------------------------------- 3 files changed, 203 insertions(+), 172 deletions(-) diff --git a/console.h b/console.h index 4334db5ca5..7934b11894 100644 --- a/console.h +++ b/console.h @@ -6,6 +6,7 @@ #include "notify.h" #include "monitor.h" #include "trace.h" +#include "qapi-types.h" /* keyboard/mouse support */ @@ -397,4 +398,9 @@ static inline int vnc_display_pw_expire(DisplayState *ds, time_t expires) /* curses.c */ void curses_display_init(DisplayState *ds, int full_screen); +/* input.c */ +extern const int key_defs[]; +int index_from_key(const char *key); +int index_from_keycode(int code); + #endif diff --git a/input.c b/input.c index 6968b31781..5630cb19d1 100644 --- a/input.c +++ b/input.c @@ -37,6 +37,192 @@ static QTAILQ_HEAD(, QEMUPutMouseEntry) mouse_handlers = static NotifierList mouse_mode_notifiers = NOTIFIER_LIST_INITIALIZER(mouse_mode_notifiers); +const int key_defs[] = { + [Q_KEY_CODE_SHIFT] = 0x2a, + [Q_KEY_CODE_SHIFT_R] = 0x36, + + [Q_KEY_CODE_ALT] = 0x38, + [Q_KEY_CODE_ALT_R] = 0xb8, + [Q_KEY_CODE_ALTGR] = 0x64, + [Q_KEY_CODE_ALTGR_R] = 0xe4, + [Q_KEY_CODE_CTRL] = 0x1d, + [Q_KEY_CODE_CTRL_R] = 0x9d, + + [Q_KEY_CODE_MENU] = 0xdd, + + [Q_KEY_CODE_ESC] = 0x01, + + [Q_KEY_CODE_1] = 0x02, + [Q_KEY_CODE_2] = 0x03, + [Q_KEY_CODE_3] = 0x04, + [Q_KEY_CODE_4] = 0x05, + [Q_KEY_CODE_5] = 0x06, + [Q_KEY_CODE_6] = 0x07, + [Q_KEY_CODE_7] = 0x08, + [Q_KEY_CODE_8] = 0x09, + [Q_KEY_CODE_9] = 0x0a, + [Q_KEY_CODE_0] = 0x0b, + [Q_KEY_CODE_MINUS] = 0x0c, + [Q_KEY_CODE_EQUAL] = 0x0d, + [Q_KEY_CODE_BACKSPACE] = 0x0e, + + [Q_KEY_CODE_TAB] = 0x0f, + [Q_KEY_CODE_Q] = 0x10, + [Q_KEY_CODE_W] = 0x11, + [Q_KEY_CODE_E] = 0x12, + [Q_KEY_CODE_R] = 0x13, + [Q_KEY_CODE_T] = 0x14, + [Q_KEY_CODE_Y] = 0x15, + [Q_KEY_CODE_U] = 0x16, + [Q_KEY_CODE_I] = 0x17, + [Q_KEY_CODE_O] = 0x18, + [Q_KEY_CODE_P] = 0x19, + [Q_KEY_CODE_BRACKET_LEFT] = 0x1a, + [Q_KEY_CODE_BRACKET_RIGHT] = 0x1b, + [Q_KEY_CODE_RET] = 0x1c, + + [Q_KEY_CODE_A] = 0x1e, + [Q_KEY_CODE_S] = 0x1f, + [Q_KEY_CODE_D] = 0x20, + [Q_KEY_CODE_F] = 0x21, + [Q_KEY_CODE_G] = 0x22, + [Q_KEY_CODE_H] = 0x23, + [Q_KEY_CODE_J] = 0x24, + [Q_KEY_CODE_K] = 0x25, + [Q_KEY_CODE_L] = 0x26, + [Q_KEY_CODE_SEMICOLON] = 0x27, + [Q_KEY_CODE_APOSTROPHE] = 0x28, + [Q_KEY_CODE_GRAVE_ACCENT] = 0x29, + + [Q_KEY_CODE_BACKSLASH] = 0x2b, + [Q_KEY_CODE_Z] = 0x2c, + [Q_KEY_CODE_X] = 0x2d, + [Q_KEY_CODE_C] = 0x2e, + [Q_KEY_CODE_V] = 0x2f, + [Q_KEY_CODE_B] = 0x30, + [Q_KEY_CODE_N] = 0x31, + [Q_KEY_CODE_M] = 0x32, + [Q_KEY_CODE_COMMA] = 0x33, + [Q_KEY_CODE_DOT] = 0x34, + [Q_KEY_CODE_SLASH] = 0x35, + + [Q_KEY_CODE_ASTERISK] = 0x37, + + [Q_KEY_CODE_SPC] = 0x39, + [Q_KEY_CODE_CAPS_LOCK] = 0x3a, + [Q_KEY_CODE_F1] = 0x3b, + [Q_KEY_CODE_F2] = 0x3c, + [Q_KEY_CODE_F3] = 0x3d, + [Q_KEY_CODE_F4] = 0x3e, + [Q_KEY_CODE_F5] = 0x3f, + [Q_KEY_CODE_F6] = 0x40, + [Q_KEY_CODE_F7] = 0x41, + [Q_KEY_CODE_F8] = 0x42, + [Q_KEY_CODE_F9] = 0x43, + [Q_KEY_CODE_F10] = 0x44, + [Q_KEY_CODE_NUM_LOCK] = 0x45, + [Q_KEY_CODE_SCROLL_LOCK] = 0x46, + + [Q_KEY_CODE_KP_DIVIDE] = 0xb5, + [Q_KEY_CODE_KP_MULTIPLY] = 0x37, + [Q_KEY_CODE_KP_SUBTRACT] = 0x4a, + [Q_KEY_CODE_KP_ADD] = 0x4e, + [Q_KEY_CODE_KP_ENTER] = 0x9c, + [Q_KEY_CODE_KP_DECIMAL] = 0x53, + [Q_KEY_CODE_SYSRQ] = 0x54, + + [Q_KEY_CODE_KP_0] = 0x52, + [Q_KEY_CODE_KP_1] = 0x4f, + [Q_KEY_CODE_KP_2] = 0x50, + [Q_KEY_CODE_KP_3] = 0x51, + [Q_KEY_CODE_KP_4] = 0x4b, + [Q_KEY_CODE_KP_5] = 0x4c, + [Q_KEY_CODE_KP_6] = 0x4d, + [Q_KEY_CODE_KP_7] = 0x47, + [Q_KEY_CODE_KP_8] = 0x48, + [Q_KEY_CODE_KP_9] = 0x49, + + [Q_KEY_CODE_LESS] = 0x56, + + [Q_KEY_CODE_F11] = 0x57, + [Q_KEY_CODE_F12] = 0x58, + + [Q_KEY_CODE_PRINT] = 0xb7, + + [Q_KEY_CODE_HOME] = 0xc7, + [Q_KEY_CODE_PGUP] = 0xc9, + [Q_KEY_CODE_PGDN] = 0xd1, + [Q_KEY_CODE_END] = 0xcf, + + [Q_KEY_CODE_LEFT] = 0xcb, + [Q_KEY_CODE_UP] = 0xc8, + [Q_KEY_CODE_DOWN] = 0xd0, + [Q_KEY_CODE_RIGHT] = 0xcd, + + [Q_KEY_CODE_INSERT] = 0xd2, + [Q_KEY_CODE_DELETE] = 0xd3, +#ifdef NEED_CPU_H +#if defined(TARGET_SPARC) && !defined(TARGET_SPARC64) + [Q_KEY_CODE_STOP] = 0xf0, + [Q_KEY_CODE_AGAIN] = 0xf1, + [Q_KEY_CODE_PROPS] = 0xf2, + [Q_KEY_CODE_UNDO] = 0xf3, + [Q_KEY_CODE_FRONT] = 0xf4, + [Q_KEY_CODE_COPY] = 0xf5, + [Q_KEY_CODE_OPEN] = 0xf6, + [Q_KEY_CODE_PASTE] = 0xf7, + [Q_KEY_CODE_FIND] = 0xf8, + [Q_KEY_CODE_CUT] = 0xf9, + [Q_KEY_CODE_LF] = 0xfa, + [Q_KEY_CODE_HELP] = 0xfb, + [Q_KEY_CODE_META_L] = 0xfc, + [Q_KEY_CODE_META_R] = 0xfd, + [Q_KEY_CODE_COMPOSE] = 0xfe, +#endif +#endif + [Q_KEY_CODE_MAX] = 0, +}; + +int index_from_key(const char *key) +{ + int i, keycode; + char *endp; + + for (i = 0; QKeyCode_lookup[i] != NULL; i++) { + if (!strcmp(key, QKeyCode_lookup[i])) { + break; + } + } + + if (strstart(key, "0x", NULL)) { + keycode = strtoul(key, &endp, 0); + if (*endp == '\0' && keycode >= 0x01 && keycode <= 0xff) { + for (i = 0; i < Q_KEY_CODE_MAX; i++) { + if (keycode == key_defs[i]) { + break; + } + } + } + } + + /* Return Q_KEY_CODE_MAX if the key is invalid */ + return i; +} + +int index_from_keycode(int code) +{ + int i; + + for (i = 0; i < Q_KEY_CODE_MAX; i++) { + if (key_defs[i] == code) { + break; + } + } + + /* Return Q_KEY_CODE_MAX if the code is invalid */ + return i; +} + void qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque) { qemu_put_kbd_event_opaque = opaque; diff --git a/monitor.c b/monitor.c index 84176eb8f6..0c4f86f639 100644 --- a/monitor.c +++ b/monitor.c @@ -1290,173 +1290,6 @@ static void do_sum(Monitor *mon, const QDict *qdict) monitor_printf(mon, "%05d\n", sum); } -typedef struct { - int keycode; - const char *name; -} KeyDef; - -static const KeyDef key_defs[] = { - { 0x2a, "shift" }, - { 0x36, "shift_r" }, - - { 0x38, "alt" }, - { 0xb8, "alt_r" }, - { 0x64, "altgr" }, - { 0xe4, "altgr_r" }, - { 0x1d, "ctrl" }, - { 0x9d, "ctrl_r" }, - - { 0xdd, "menu" }, - - { 0x01, "esc" }, - - { 0x02, "1" }, - { 0x03, "2" }, - { 0x04, "3" }, - { 0x05, "4" }, - { 0x06, "5" }, - { 0x07, "6" }, - { 0x08, "7" }, - { 0x09, "8" }, - { 0x0a, "9" }, - { 0x0b, "0" }, - { 0x0c, "minus" }, - { 0x0d, "equal" }, - { 0x0e, "backspace" }, - - { 0x0f, "tab" }, - { 0x10, "q" }, - { 0x11, "w" }, - { 0x12, "e" }, - { 0x13, "r" }, - { 0x14, "t" }, - { 0x15, "y" }, - { 0x16, "u" }, - { 0x17, "i" }, - { 0x18, "o" }, - { 0x19, "p" }, - { 0x1a, "bracket_left" }, - { 0x1b, "bracket_right" }, - { 0x1c, "ret" }, - - { 0x1e, "a" }, - { 0x1f, "s" }, - { 0x20, "d" }, - { 0x21, "f" }, - { 0x22, "g" }, - { 0x23, "h" }, - { 0x24, "j" }, - { 0x25, "k" }, - { 0x26, "l" }, - { 0x27, "semicolon" }, - { 0x28, "apostrophe" }, - { 0x29, "grave_accent" }, - - { 0x2b, "backslash" }, - { 0x2c, "z" }, - { 0x2d, "x" }, - { 0x2e, "c" }, - { 0x2f, "v" }, - { 0x30, "b" }, - { 0x31, "n" }, - { 0x32, "m" }, - { 0x33, "comma" }, - { 0x34, "dot" }, - { 0x35, "slash" }, - - { 0x37, "asterisk" }, - - { 0x39, "spc" }, - { 0x3a, "caps_lock" }, - { 0x3b, "f1" }, - { 0x3c, "f2" }, - { 0x3d, "f3" }, - { 0x3e, "f4" }, - { 0x3f, "f5" }, - { 0x40, "f6" }, - { 0x41, "f7" }, - { 0x42, "f8" }, - { 0x43, "f9" }, - { 0x44, "f10" }, - { 0x45, "num_lock" }, - { 0x46, "scroll_lock" }, - - { 0xb5, "kp_divide" }, - { 0x37, "kp_multiply" }, - { 0x4a, "kp_subtract" }, - { 0x4e, "kp_add" }, - { 0x9c, "kp_enter" }, - { 0x53, "kp_decimal" }, - { 0x54, "sysrq" }, - - { 0x52, "kp_0" }, - { 0x4f, "kp_1" }, - { 0x50, "kp_2" }, - { 0x51, "kp_3" }, - { 0x4b, "kp_4" }, - { 0x4c, "kp_5" }, - { 0x4d, "kp_6" }, - { 0x47, "kp_7" }, - { 0x48, "kp_8" }, - { 0x49, "kp_9" }, - - { 0x56, "less" }, - - { 0x57, "f11" }, - { 0x58, "f12" }, - - { 0xb7, "print" }, - - { 0xc7, "home" }, - { 0xc9, "pgup" }, - { 0xd1, "pgdn" }, - { 0xcf, "end" }, - - { 0xcb, "left" }, - { 0xc8, "up" }, - { 0xd0, "down" }, - { 0xcd, "right" }, - - { 0xd2, "insert" }, - { 0xd3, "delete" }, -#if defined(TARGET_SPARC) && !defined(TARGET_SPARC64) - { 0xf0, "stop" }, - { 0xf1, "again" }, - { 0xf2, "props" }, - { 0xf3, "undo" }, - { 0xf4, "front" }, - { 0xf5, "copy" }, - { 0xf6, "open" }, - { 0xf7, "paste" }, - { 0xf8, "find" }, - { 0xf9, "cut" }, - { 0xfa, "lf" }, - { 0xfb, "help" }, - { 0xfc, "meta_l" }, - { 0xfd, "meta_r" }, - { 0xfe, "compose" }, -#endif - { 0, NULL }, -}; - -static int get_keycode(const char *key) -{ - const KeyDef *p; - char *endp; - int ret; - - for(p = key_defs; p->name != NULL; p++) { - if (!strcmp(key, p->name)) - return p->keycode; - } - if (strstart(key, "0x", NULL)) { - ret = strtoul(key, &endp, 0); - if (*endp == '\0' && ret >= 0x01 && ret <= 0xff) - return ret; - } - return -1; -} - #define MAX_KEYCODES 16 static uint8_t keycodes[MAX_KEYCODES]; static int nb_pending_keycodes; @@ -1479,7 +1312,7 @@ static void do_sendkey(Monitor *mon, const QDict *qdict) { char keyname_buf[16]; char *separator; - int keyname_len, keycode, i; + int keyname_len, keycode, i, idx; const char *keys = qdict_get_str(qdict, "keys"); int has_hold_time = qdict_haskey(qdict, "hold-time"); int hold_time = qdict_get_try_int(qdict, "hold-time", -1); @@ -1512,7 +1345,14 @@ static void do_sendkey(Monitor *mon, const QDict *qdict) } keyname_buf[keyname_len] = 0; - keycode = get_keycode(keyname_buf); + + idx = index_from_key(keyname_buf); + if (idx == Q_KEY_CODE_MAX) { + monitor_printf(mon, "invalid parameter: %s\n", keyname_buf); + return; + } + + keycode = key_defs[idx]; if (keycode < 0) { monitor_printf(mon, "unknown key: '%s'\n", keyname_buf); return; @@ -4322,7 +4162,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 @@ -4396,8 +4235,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)); From e4c8f004c55d9da3eae3e14df740238bf805b5d6 Mon Sep 17 00:00:00 2001 From: Amos Kong Date: Fri, 31 Aug 2012 10:56:26 +0800 Subject: [PATCH 08/19] qapi: convert sendkey Convert 'sendkey' to use QAPI. QAPI passes key's index of mapping table to qmp_send_key(), not keycode. So we use help functions to convert key/code to index of key_defs, and 'index' will be converted to 'keycode' inside qmp_send_key(). For qmp, QAPI would check invalid key and raise error. For hmp, invalid key is checked in hmp_send_key(). 'send-key' of QMP doesn't support key in hexadecimal format. Signed-off-by: Amos Kong Signed-off-by: Luiz Capitulino --- console.h | 1 - hmp-commands.hx | 2 +- hmp.c | 55 ++++++++++++++++++++++++++++++ hmp.h | 1 + input.c | 67 ++++++++++++++++++++++++++++++++++++- monitor.c | 87 ------------------------------------------------ qapi-schema.json | 20 +++++++++++ qmp-commands.hx | 28 ++++++++++++++++ 8 files changed, 171 insertions(+), 90 deletions(-) diff --git a/console.h b/console.h index 7934b11894..c702b23721 100644 --- a/console.h +++ b/console.h @@ -399,7 +399,6 @@ static inline int vnc_display_pw_expire(DisplayState *ds, time_t expires) void curses_display_init(DisplayState *ds, int full_screen); /* input.c */ -extern const int key_defs[]; int index_from_key(const char *key); int index_from_keycode(int code); diff --git a/hmp-commands.hx b/hmp-commands.hx index bd0c6c9191..5cee1310cc 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -505,7 +505,7 @@ ETEXI .args_type = "keys:s,hold-time:i?", .params = "keys [hold_ms]", .help = "send keys to the VM (e.g. 'sendkey ctrl-alt-f1', default hold time=100 ms)", - .mhandler.cmd = do_sendkey, + .mhandler.cmd = hmp_send_key, }, STEXI diff --git a/hmp.c b/hmp.c index 81c8acb67a..ba99b2fb30 100644 --- a/hmp.c +++ b/hmp.c @@ -19,6 +19,7 @@ #include "qemu-timer.h" #include "qmp-commands.h" #include "monitor.h" +#include "console.h" static void hmp_handle_error(Monitor *mon, Error **errp) { @@ -1102,3 +1103,57 @@ 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); +} diff --git a/hmp.h b/hmp.h index 7dd93bf0f4..8e7838ce5d 100644 --- a/hmp.h +++ b/hmp.h @@ -71,5 +71,6 @@ void hmp_netdev_add(Monitor *mon, const QDict *qdict); void hmp_netdev_del(Monitor *mon, const QDict *qdict); void hmp_getfd(Monitor *mon, const QDict *qdict); void hmp_closefd(Monitor *mon, const QDict *qdict); +void hmp_send_key(Monitor *mon, const QDict *qdict); #endif diff --git a/input.c b/input.c index 5630cb19d1..c4b0619a74 100644 --- a/input.c +++ b/input.c @@ -28,6 +28,7 @@ #include "console.h" #include "error.h" #include "qmp-commands.h" +#include "qapi-types.h" static QEMUPutKBDEvent *qemu_put_kbd_event; static void *qemu_put_kbd_event_opaque; @@ -37,7 +38,7 @@ static QTAILQ_HEAD(, QEMUPutMouseEntry) mouse_handlers = static NotifierList mouse_mode_notifiers = NOTIFIER_LIST_INITIALIZER(mouse_mode_notifiers); -const int key_defs[] = { +static const int key_defs[] = { [Q_KEY_CODE_SHIFT] = 0x2a, [Q_KEY_CODE_SHIFT_R] = 0x36, @@ -223,6 +224,70 @@ int index_from_keycode(int code) return i; } +static QKeyCodeList *keycodes; +static QEMUTimer *key_timer; + +static void release_keys(void *opaque) +{ + int keycode; + QKeyCodeList *p; + + for (p = keycodes; p != NULL; p = p->next) { + keycode = key_defs[p->value]; + if (keycode & 0x80) { + kbd_put_keycode(0xe0); + } + kbd_put_keycode(keycode | 0x80); + } + qapi_free_QKeyCodeList(keycodes); + keycodes = NULL; +} + +void qmp_send_key(QKeyCodeList *keys, bool has_hold_time, int64_t hold_time, + Error **errp) +{ + int keycode; + QKeyCodeList *p, *keylist, *head = NULL, *tmp = NULL; + + if (!key_timer) { + key_timer = qemu_new_timer_ns(vm_clock, release_keys, NULL); + } + + if (keycodes != NULL) { + qemu_del_timer(key_timer); + release_keys(NULL); + } + if (!has_hold_time) { + hold_time = 100; + } + + for (p = keys; p != NULL; p = p->next) { + keylist = g_malloc0(sizeof(*keylist)); + keylist->value = p->value; + keylist->next = NULL; + + if (!head) { + head = keylist; + } + if (tmp) { + tmp->next = keylist; + } + tmp = keylist; + + /* key down events */ + keycode = key_defs[p->value]; + if (keycode & 0x80) { + kbd_put_keycode(0xe0); + } + kbd_put_keycode(keycode & 0x7f); + } + keycodes = head; + + /* delayed key up events */ + qemu_mod_timer(key_timer, qemu_get_clock_ns(vm_clock) + + muldiv64(get_ticks_per_sec(), hold_time, 1000)); +} + void qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque) { qemu_put_kbd_event_opaque = opaque; diff --git a/monitor.c b/monitor.c index 0c4f86f639..d73bad8316 100644 --- a/monitor.c +++ b/monitor.c @@ -1290,92 +1290,6 @@ static void do_sum(Monitor *mon, const QDict *qdict) monitor_printf(mon, "%05d\n", sum); } -#define MAX_KEYCODES 16 -static uint8_t keycodes[MAX_KEYCODES]; -static int nb_pending_keycodes; -static QEMUTimer *key_timer; - -static void release_keys(void *opaque) -{ - int keycode; - - while (nb_pending_keycodes > 0) { - nb_pending_keycodes--; - keycode = keycodes[nb_pending_keycodes]; - if (keycode & 0x80) - kbd_put_keycode(0xe0); - kbd_put_keycode(keycode | 0x80); - } -} - -static void do_sendkey(Monitor *mon, const QDict *qdict) -{ - char keyname_buf[16]; - char *separator; - int keyname_len, keycode, i, idx; - const char *keys = qdict_get_str(qdict, "keys"); - int has_hold_time = qdict_haskey(qdict, "hold-time"); - int hold_time = qdict_get_try_int(qdict, "hold-time", -1); - - if (nb_pending_keycodes > 0) { - qemu_del_timer(key_timer); - release_keys(NULL); - } - if (!has_hold_time) - hold_time = 100; - i = 0; - while (1) { - separator = strchr(keys, '-'); - keyname_len = separator ? separator - keys : strlen(keys); - if (keyname_len > 0) { - pstrcpy(keyname_buf, sizeof(keyname_buf), keys); - if (keyname_len > sizeof(keyname_buf) - 1) { - monitor_printf(mon, "invalid key: '%s...'\n", keyname_buf); - return; - } - if (i == MAX_KEYCODES) { - monitor_printf(mon, "too many keys\n"); - return; - } - - /* Be compatible with old interface, convert user inputted "<" */ - if (!strncmp(keyname_buf, "<", 1) && keyname_len == 1) { - pstrcpy(keyname_buf, sizeof(keyname_buf), "less"); - keyname_len = 4; - } - - keyname_buf[keyname_len] = 0; - - idx = index_from_key(keyname_buf); - if (idx == Q_KEY_CODE_MAX) { - monitor_printf(mon, "invalid parameter: %s\n", keyname_buf); - return; - } - - keycode = key_defs[idx]; - if (keycode < 0) { - monitor_printf(mon, "unknown key: '%s'\n", keyname_buf); - return; - } - keycodes[i++] = keycode; - } - if (!separator) - break; - keys = separator + 1; - } - nb_pending_keycodes = i; - /* key down events */ - for (i = 0; i < nb_pending_keycodes; i++) { - keycode = keycodes[i]; - if (keycode & 0x80) - kbd_put_keycode(0xe0); - kbd_put_keycode(keycode & 0x7f); - } - /* delayed key up events */ - qemu_mod_timer(key_timer, qemu_get_clock_ns(vm_clock) + - muldiv64(get_ticks_per_sec(), hold_time, 1000)); -} - static int mouse_button_state; static void do_mouse_move(Monitor *mon, const QDict *qdict) @@ -4772,7 +4686,6 @@ 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 856e11a729..5421382991 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -2519,3 +2519,23 @@ 'left', 'up', 'down', 'right', 'insert', 'delete', 'stop', 'again', 'props', 'undo', 'front', 'copy', 'open', 'paste', 'find', 'cut', 'lf', 'help', 'meta_l', 'meta_r', 'compose' ] } + +## +# @send-key: +# +# Send keys to guest. +# +# @keys: key sequence. 'keys' is the name of the key. Use a JSON array to +# press several keys simultaneously. +# +# @hold-time: #optional time to delay key up events, milliseconds. Defaults +# to 100 +# +# Returns: Nothing on success +# If key is unknown or redundant, InvalidParameter +# +# Since: 1.3.0 +# +## +{ 'command': 'send-key', + 'data': { 'keys': ['QKeyCode'], '*hold-time': 'int' } } diff --git a/qmp-commands.hx b/qmp-commands.hx index 3745a21199..470f08ed99 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -332,6 +332,34 @@ Example: -> { "execute": "device_del", "arguments": { "id": "net1" } } <- { "return": {} } +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 { From 227ccf6bff234c29974c2c18ecd3a29e6b965e3d Mon Sep 17 00:00:00 2001 From: Stefan Weil Date: Sat, 1 Sep 2012 09:30:39 +0200 Subject: [PATCH 09/19] qapi: Fix potential NULL pointer segfault Report from smatch: qapi-visit.c:1640 visit_type_BlockdevAction(8) error: we previously assumed 'obj' could be null (see line 1639) qapi-visit.c:2432 visit_type_NetClientOptions(8) error: we previously assumed 'obj' could be null (see line 2431) Signed-off-by: Stefan Weil Reviewed-by: Paolo Bonzini Signed-off-by: Luiz Capitulino --- scripts/qapi-visit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py index cbec24da16..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) { From 149474c93490e1c66f838391bd491db83136d91d Mon Sep 17 00:00:00 2001 From: Stefan Weil Date: Mon, 3 Sep 2012 21:19:11 +0200 Subject: [PATCH 10/19] json-parser: Fix potential NULL pointer segfault Report from smatch: json-parser.c:474 parse_object(62) error: potential null derefence 'dict'. json-parser.c:553 parse_array(75) error: potential null derefence 'list'. Label 'out' in json-parser.c can be called with list == NULL which is passed to QDECREF. Modify QDECREF to handle a NULL argument (inline function qobject_decref already handles them, too). Signed-off-by: Stefan Weil Signed-off-by: Luiz Capitulino --- qobject.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qobject.h b/qobject.h index d42386dde1..9124649ed2 100644 --- a/qobject.h +++ b/qobject.h @@ -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) \ From 75d789f8d38be693bbee3e77b558e59012e33997 Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Wed, 29 Aug 2012 11:20:57 -0300 Subject: [PATCH 11/19] error: add error_setg() Signed-off-by: Luiz Capitulino --- error.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/error.h b/error.h index 96fc20328f..da7fed399e 100644 --- a/error.h +++ b/error.h @@ -29,6 +29,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. From d7098135d4baac8141b2e76f2daa8a7f61599c72 Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Mon, 21 May 2012 16:41:37 -0300 Subject: [PATCH 12/19] console: vga_hw_screen_dump_ptr: take Error argument All devices that register a screen dump callback via graphic_console_init() are updated. The new argument is not used in this commit. Error handling will be added to each device individually later. This change is a preparation to convert the screendump command to the QAPI. Signed-off-by: Luiz Capitulino --- console.c | 2 +- console.h | 4 +++- hw/blizzard.c | 2 +- hw/g364fb.c | 3 ++- hw/omap_lcdc.c | 3 ++- hw/qxl.c | 5 +++-- hw/tcx.c | 12 ++++++++---- hw/vga.c | 6 ++++-- hw/vmware_vga.c | 5 +++-- 9 files changed, 27 insertions(+), 15 deletions(-) diff --git a/console.c b/console.c index 3b5cabba3a..8228773ddc 100644 --- a/console.c +++ b/console.c @@ -190,7 +190,7 @@ 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, NULL); } else { error_report("screen dump not implemented"); } diff --git a/console.h b/console.h index c702b23721..fd68ecca75 100644 --- a/console.h +++ b/console.h @@ -7,6 +7,7 @@ #include "monitor.h" #include "trace.h" #include "qapi-types.h" +#include "error.h" /* keyboard/mouse support */ @@ -344,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, diff --git a/hw/blizzard.c b/hw/blizzard.c index 29074c4fa7..a2b90537c9 100644 --- a/hw/blizzard.c +++ b/hw/blizzard.c @@ -933,7 +933,7 @@ 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; diff --git a/hw/g364fb.c b/hw/g364fb.c index 3a0b68fbae..498154be28 100644 --- a/hw/g364fb.c +++ b/hw/g364fb.c @@ -289,7 +289,8 @@ 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; diff --git a/hw/omap_lcdc.c b/hw/omap_lcdc.c index 4a08e9d002..39b78cd6d5 100644 --- a/hw/omap_lcdc.c +++ b/hw/omap_lcdc.c @@ -264,7 +264,8 @@ static int ppm_save(const char *filename, uint8_t *data, return 0; } -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; diff --git a/hw/qxl.c b/hw/qxl.c index c2dd3b471b..46a929dc35 100644 --- a/hw/qxl.c +++ b/hw/qxl.c @@ -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; @@ -1607,7 +1608,7 @@ static void qxl_hw_screen_dump(void *opaque, const char *filename, bool cswitch) ppm_save(filename, qxl->ssd.ds->surface); break; case QXL_MODE_VGA: - vga->screen_dump(vga, filename, cswitch); + vga->screen_dump(vga, filename, cswitch, errp); break; default: break; diff --git a/hw/tcx.c b/hw/tcx.c index ac7dcb428c..74a7085422 100644 --- a/hw/tcx.c +++ b/hw/tcx.c @@ -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,7 +576,8 @@ 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; @@ -601,7 +604,8 @@ static void tcx_screen_dump(void *opaque, const char *filename, bool cswitch) return; } -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; diff --git a/hw/vga.c b/hw/vga.c index f82ced8e66..dd703cf41c 100644 --- a/hw/vga.c +++ b/hw/vga.c @@ -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) { @@ -2435,7 +2436,8 @@ int ppm_save(const char *filename, struct DisplaySurface *ds) /* 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; diff --git a/hw/vmware_vga.c b/hw/vmware_vga.c index f5e4f440d5..29750e1b0d 100644 --- a/hw/vmware_vga.c +++ b/hw/vmware_vga.c @@ -1003,11 +1003,12 @@ 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; } From ad39cf6d150e951e4c67c8120d495ae7e78054a6 Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Thu, 24 May 2012 13:48:23 -0300 Subject: [PATCH 13/19] qapi: convert screendump Next commits will update devices to propagate errors. Signed-off-by: Luiz Capitulino --- console.c | 7 ++++--- console.h | 1 - hmp-commands.hx | 3 +-- hmp.c | 9 +++++++++ hmp.h | 1 + monitor.c | 6 ------ qapi-schema.json | 13 +++++++++++++ qmp-commands.hx | 5 +---- 8 files changed, 29 insertions(+), 16 deletions(-) diff --git a/console.c b/console.c index 8228773ddc..c1ed5e09e0 100644 --- a/console.c +++ b/console.c @@ -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, NULL); + 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) { diff --git a/console.h b/console.h index fd68ecca75..f990684ef4 100644 --- a/console.h +++ b/console.h @@ -357,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); diff --git a/hmp-commands.hx b/hmp-commands.hx index 5cee1310cc..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 diff --git a/hmp.c b/hmp.c index ba99b2fb30..1bdab22778 100644 --- a/hmp.c +++ b/hmp.c @@ -1157,3 +1157,12 @@ void hmp_send_key(Monitor *mon, const QDict *qdict) 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); +} diff --git a/hmp.h b/hmp.h index 8e7838ce5d..48b9c59f8a 100644 --- a/hmp.h +++ b/hmp.h @@ -72,5 +72,6 @@ void hmp_netdev_del(Monitor *mon, const QDict *qdict); void hmp_getfd(Monitor *mon, const QDict *qdict); void hmp_closefd(Monitor *mon, const QDict *qdict); void hmp_send_key(Monitor *mon, const QDict *qdict); +void hmp_screen_dump(Monitor *mon, const QDict *qdict); #endif diff --git a/monitor.c b/monitor.c index d73bad8316..e315b270ee 100644 --- a/monitor.c +++ b/monitor.c @@ -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")); diff --git a/qapi-schema.json b/qapi-schema.json index 5421382991..df47fcee2f 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -2539,3 +2539,16 @@ ## { '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 470f08ed99..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 From d663174dcd9623c44f3af2a46de2a55d16f15c19 Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Thu, 24 May 2012 10:42:25 -0300 Subject: [PATCH 14/19] vga: ppm_save(): add error handling Signed-off-by: Luiz Capitulino --- hw/blizzard.c | 2 +- hw/qxl.c | 2 +- hw/vga.c | 32 +++++++++++++++++++++++++------- hw/vga_int.h | 3 ++- hw/vmware_vga.c | 2 +- 5 files changed, 30 insertions(+), 11 deletions(-) diff --git a/hw/blizzard.c b/hw/blizzard.c index a2b90537c9..d1c9d81510 100644 --- a/hw/blizzard.c +++ b/hw/blizzard.c @@ -939,7 +939,7 @@ static void blizzard_screen_dump(void *opaque, const char *filename, 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/qxl.c b/hw/qxl.c index 46a929dc35..bae5758195 100644 --- a/hw/qxl.c +++ b/hw/qxl.c @@ -1605,7 +1605,7 @@ 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, errp); diff --git a/hw/vga.c b/hw/vga.c index dd703cf41c..80299ea1d4 100644 --- a/hw/vga.c +++ b/hw/vga.c @@ -2390,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; @@ -2402,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++) { @@ -2426,12 +2432,24 @@ 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 @@ -2445,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 +#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 29750e1b0d..b68e88367f 100644 --- a/hw/vmware_vga.c +++ b/hw/vmware_vga.c @@ -1015,7 +1015,7 @@ static void vmsvga_screen_dump(void *opaque, const char *filename, bool cswitch, 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); } } From 8dc4cc7bb57d3f0d6a1d8ba372d9eda1494bcc64 Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Mon, 21 May 2012 15:18:58 -0300 Subject: [PATCH 15/19] omap_lcdc: rename ppm_save() to omap_ppm_save() Avoids confusion with the global ppm_save() defined in hw/vga.c. Signed-off-by: Luiz Capitulino Reviewed-by: Peter Maydell --- hw/omap_lcdc.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/omap_lcdc.c b/hw/omap_lcdc.c index 39b78cd6d5..3d6328febc 100644 --- a/hw/omap_lcdc.c +++ b/hw/omap_lcdc.c @@ -224,8 +224,8 @@ 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 int omap_ppm_save(const char *filename, uint8_t *data, + int w, int h, int linesize) { FILE *f; uint8_t *d, *d1; @@ -271,9 +271,9 @@ static void omap_screen_dump(void *opaque, const char *filename, bool cswitch, 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)); } static void omap_invalidate_display(void *opaque) { From d9c7ebb10042a7d29d50b47c110e5cb754293d89 Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Thu, 24 May 2012 11:24:34 -0300 Subject: [PATCH 16/19] omap_lcdc: omap_ppm_save(): add error handling Signed-off-by: Luiz Capitulino --- hw/omap_lcdc.c | 59 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 14 deletions(-) diff --git a/hw/omap_lcdc.c b/hw/omap_lcdc.c index 3d6328febc..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 omap_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,24 +250,49 @@ static int omap_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, @@ -273,7 +304,7 @@ static void omap_screen_dump(void *opaque, const char *filename, bool cswitch, if (omap_lcd && ds_get_data(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)); + ds_get_linesize(omap_lcd->state), errp); } static void omap_invalidate_display(void *opaque) { From 61a3f955c1caca775729fc59e97fb9c97808ffca Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Thu, 24 May 2012 11:28:17 -0300 Subject: [PATCH 17/19] g364fb: g364fb_screen_dump(): add error handling Signed-off-by: Luiz Capitulino --- hw/g364fb.c | 52 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/hw/g364fb.c b/hw/g364fb.c index 498154be28..059e6220e0 100644 --- a/hw/g364fb.c +++ b/hw/g364fb.c @@ -293,7 +293,7 @@ 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; @@ -301,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 */ From 537f2d2b0d4c27e21b3e610e57d6511e315d76cc Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Thu, 24 May 2012 11:30:40 -0300 Subject: [PATCH 18/19] tcx: tcx24_screen_dump(): add error handling Signed-off-by: Luiz Capitulino --- hw/tcx.c | 50 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/hw/tcx.c b/hw/tcx.c index 74a7085422..428649e665 100644 --- a/hw/tcx.c +++ b/hw/tcx.c @@ -611,12 +611,18 @@ static void tcx24_screen_dump(void *opaque, const char *filename, bool cswitch, 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; @@ -625,20 +631,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[] = { From 0ab6b63655ba59d7c05089017acb721a44979526 Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Thu, 24 May 2012 11:33:25 -0300 Subject: [PATCH 19/19] tcx: tcx_screen_dump(): add error handling Signed-off-by: Luiz Capitulino --- hw/tcx.c | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/hw/tcx.c b/hw/tcx.c index 428649e665..93994d6440 100644 --- a/hw/tcx.c +++ b/hw/tcx.c @@ -582,26 +582,49 @@ static void tcx_screen_dump(void *opaque, const char *filename, bool cswitch, 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,