UI patch queue
- misc fixes and improvement - cleanups and refactoring in ui/vc code -----BEGIN PGP SIGNATURE----- iQJQBAABCAA6FiEEh6m9kz+HxgbSdvYt2ujhCXWWnOUFAmT1wuYcHG1hcmNhbmRy ZS5sdXJlYXVAcmVkaGF0LmNvbQAKCRDa6OEJdZac5UhmD/wPCVZ/Vipmbexc8eBd wbI7i0zR5Hj7szU4D1MV+fvi5Y6Z7PWvPxnQOIoWbmEGuhOm5P73oRz1jlBDXGLP Nh1kh2RvuWILF0Vu+QjJHL5FyA0XJcl/Qhsn1tc7pYMbEOBCpPfpmWRiXrEUDc7/ S1iSPkB2a7YYwuMW6ksPyKlsb4tjGyea/HYz1lTdw8bJxaFVXMFX35lrqz+A5ZGz XAk/6OyMtkMbBi8hWcd6IweYyc/DYaK8emqppQLIUenZEz7nKSWlEUIKcXpf9U4n 3W+BISACxnw7KbXrrZl2KJf2Bix6LRureoscZTKawnB/D5hV+g7PtEjNMUQsxjg3 RyV9+zSPsIg5zXunrHIs1rrUtGS5SvdQbIQYqHPNdL86iuWKer+EnwA06vflweLw P7FZhuBNvuY3gU2sdCk5Q7My92YT5DRWjoJRHLFGNYTxPA6MYPivIu8RqsBiu+JX BvK1FfhG2JsR9XuuOFR968AXLfMc0hOlHfHWvORk3s/9zIpeEWmQbnGxr1sN9El8 o+rDIkcadELuzcTJcoHCKdCzjFbLdNNKgvbcVQdw3rdp2rvQ6CZalyh+qZEihAy4 xLVO+hUypxNhRAg/DtZilUW6cPavn0OjoH/3BgY0F0GiwvhFMntyVGN7eBdwnC7c sV5s4Xnafmh5xnGf0GS3UyuX9g== =JxZP -----END PGP SIGNATURE----- Merge tag 'ui-pull-request' of https://gitlab.com/marcandre.lureau/qemu into staging UI patch queue - misc fixes and improvement - cleanups and refactoring in ui/vc code # -----BEGIN PGP SIGNATURE----- # # iQJQBAABCAA6FiEEh6m9kz+HxgbSdvYt2ujhCXWWnOUFAmT1wuYcHG1hcmNhbmRy # ZS5sdXJlYXVAcmVkaGF0LmNvbQAKCRDa6OEJdZac5UhmD/wPCVZ/Vipmbexc8eBd # wbI7i0zR5Hj7szU4D1MV+fvi5Y6Z7PWvPxnQOIoWbmEGuhOm5P73oRz1jlBDXGLP # Nh1kh2RvuWILF0Vu+QjJHL5FyA0XJcl/Qhsn1tc7pYMbEOBCpPfpmWRiXrEUDc7/ # S1iSPkB2a7YYwuMW6ksPyKlsb4tjGyea/HYz1lTdw8bJxaFVXMFX35lrqz+A5ZGz # XAk/6OyMtkMbBi8hWcd6IweYyc/DYaK8emqppQLIUenZEz7nKSWlEUIKcXpf9U4n # 3W+BISACxnw7KbXrrZl2KJf2Bix6LRureoscZTKawnB/D5hV+g7PtEjNMUQsxjg3 # RyV9+zSPsIg5zXunrHIs1rrUtGS5SvdQbIQYqHPNdL86iuWKer+EnwA06vflweLw # P7FZhuBNvuY3gU2sdCk5Q7My92YT5DRWjoJRHLFGNYTxPA6MYPivIu8RqsBiu+JX # BvK1FfhG2JsR9XuuOFR968AXLfMc0hOlHfHWvORk3s/9zIpeEWmQbnGxr1sN9El8 # o+rDIkcadELuzcTJcoHCKdCzjFbLdNNKgvbcVQdw3rdp2rvQ6CZalyh+qZEihAy4 # xLVO+hUypxNhRAg/DtZilUW6cPavn0OjoH/3BgY0F0GiwvhFMntyVGN7eBdwnC7c # sV5s4Xnafmh5xnGf0GS3UyuX9g== # =JxZP # -----END PGP SIGNATURE----- # gpg: Signature made Mon 04 Sep 2023 07:43:34 EDT # gpg: using RSA key 87A9BD933F87C606D276F62DDAE8E10975969CE5 # gpg: issuer "marcandre.lureau@redhat.com" # gpg: Good signature from "Marc-André Lureau <marcandre.lureau@redhat.com>" [full] # gpg: aka "Marc-André Lureau <marcandre.lureau@gmail.com>" [full] # Primary key fingerprint: 87A9 BD93 3F87 C606 D276 F62D DAE8 E109 7596 9CE5 * tag 'ui-pull-request' of https://gitlab.com/marcandre.lureau/qemu: (52 commits) ui/gtk: fix leaks found wtih fuzzing ui/vdagent: Unregister input handler of mouse during finalization ui/vdagent: call vdagent_disconnect() when agent connection is lost ui/dbus: implement damage regions for GL ui/dbus: Properly dispose touch/mouse dbus objects ui/vnc-enc-tight: Avoid dynamic stack allocation ui/vnc-enc-hextile: Use static rather than dynamic length stack array ui/spice-display: Avoid dynamic stack allocation ui/vc: change the argument for QemuTextConsole ui/vc: do not parse VC-specific options in Spice and GTK ui/vc: move text console invalidate in helper ui/console: minor stylistic changes ui/vc: skip text console resize when possible ui/console: fold text_console_update_cursor_timer ui/console: assert(surface) where appropriate ui/console: rename vga_ functions with qemu_console_ ui/console: use QEMU_PIXMAN_COLOR helpers ui/console: declare console types in console.h ui/vc: use common text console surface creation ui/console: remove need for g_width/g_height ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
commit
c152379422
@ -320,7 +320,4 @@ GSource *qemu_chr_timeout_add_ms(Chardev *chr, guint ms,
|
||||
void suspend_mux_open(void);
|
||||
void resume_mux_open(void);
|
||||
|
||||
/* console.c */
|
||||
void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, Error **errp);
|
||||
|
||||
#endif
|
||||
|
@ -12,6 +12,27 @@
|
||||
# include "ui/shader.h"
|
||||
#endif
|
||||
|
||||
#define TYPE_QEMU_CONSOLE "qemu-console"
|
||||
OBJECT_DECLARE_TYPE(QemuConsole, QemuConsoleClass, QEMU_CONSOLE)
|
||||
|
||||
#define TYPE_QEMU_GRAPHIC_CONSOLE "qemu-graphic-console"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(QemuGraphicConsole, QEMU_GRAPHIC_CONSOLE)
|
||||
|
||||
#define TYPE_QEMU_TEXT_CONSOLE "qemu-text-console"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(QemuTextConsole, QEMU_TEXT_CONSOLE)
|
||||
|
||||
#define TYPE_QEMU_FIXED_TEXT_CONSOLE "qemu-fixed-text-console"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(QemuFixedTextConsole, QEMU_FIXED_TEXT_CONSOLE)
|
||||
|
||||
#define QEMU_IS_GRAPHIC_CONSOLE(c) \
|
||||
object_dynamic_cast(OBJECT(c), TYPE_QEMU_GRAPHIC_CONSOLE)
|
||||
|
||||
#define QEMU_IS_TEXT_CONSOLE(c) \
|
||||
object_dynamic_cast(OBJECT(c), TYPE_QEMU_TEXT_CONSOLE)
|
||||
|
||||
#define QEMU_IS_FIXED_TEXT_CONSOLE(c) \
|
||||
object_dynamic_cast(OBJECT(c), TYPE_QEMU_FIXED_TEXT_CONSOLE)
|
||||
|
||||
/* keyboard/mouse support */
|
||||
|
||||
#define MOUSE_EVENT_LBUTTON 0x01
|
||||
@ -91,9 +112,9 @@ bool qemu_mouse_set(int index, Error **errp);
|
||||
#define QEMU_KEY_CTRL_PAGEUP 0xe406
|
||||
#define QEMU_KEY_CTRL_PAGEDOWN 0xe407
|
||||
|
||||
void kbd_put_keysym_console(QemuConsole *s, int keysym);
|
||||
bool kbd_put_qcode_console(QemuConsole *s, int qcode, bool ctrl);
|
||||
void kbd_put_string_console(QemuConsole *s, const char *str, int len);
|
||||
void kbd_put_keysym_console(QemuTextConsole *s, int keysym);
|
||||
bool kbd_put_qcode_console(QemuTextConsole *s, int qcode, bool ctrl);
|
||||
void kbd_put_string_console(QemuTextConsole *s, const char *str, int len);
|
||||
void kbd_put_keysym(int keysym);
|
||||
|
||||
/* Touch devices */
|
||||
@ -112,10 +133,6 @@ void console_handle_touch_event(QemuConsole *con,
|
||||
Error **errp);
|
||||
/* consoles */
|
||||
|
||||
#define TYPE_QEMU_CONSOLE "qemu-console"
|
||||
OBJECT_DECLARE_TYPE(QemuConsole, QemuConsoleClass, QEMU_CONSOLE)
|
||||
|
||||
|
||||
struct QemuConsoleClass {
|
||||
ObjectClass parent_class;
|
||||
};
|
||||
@ -484,7 +501,6 @@ QemuConsole *qemu_console_lookup_by_index(unsigned int index);
|
||||
QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head);
|
||||
QemuConsole *qemu_console_lookup_by_device_name(const char *device_id,
|
||||
uint32_t head, Error **errp);
|
||||
QemuConsole *qemu_console_lookup_unused(void);
|
||||
QEMUCursor *qemu_console_get_cursor(QemuConsole *con);
|
||||
bool qemu_console_is_visible(QemuConsole *con);
|
||||
bool qemu_console_is_graphic(QemuConsole *con);
|
||||
@ -504,6 +520,8 @@ void qemu_console_set_window_id(QemuConsole *con, int window_id);
|
||||
void console_select(unsigned int index);
|
||||
void qemu_console_resize(QemuConsole *con, int width, int height);
|
||||
DisplaySurface *qemu_console_surface(QemuConsole *con);
|
||||
void coroutine_fn qemu_console_co_wait_update(QemuConsole *con);
|
||||
int qemu_invalidate_text_consoles(void);
|
||||
|
||||
/* console-gl.c */
|
||||
#ifdef CONFIG_OPENGL
|
||||
|
@ -47,6 +47,12 @@
|
||||
# define PIXMAN_LE_x8r8g8b8 PIXMAN_x8r8g8b8
|
||||
#endif
|
||||
|
||||
#define QEMU_PIXMAN_COLOR(r, g, b) \
|
||||
{ .red = r << 8, .green = g << 8, .blue = b << 8, .alpha = 0xffff }
|
||||
|
||||
#define QEMU_PIXMAN_COLOR_BLACK QEMU_PIXMAN_COLOR(0x00, 0x00, 0x00)
|
||||
#define QEMU_PIXMAN_COLOR_GRAY QEMU_PIXMAN_COLOR(0xaa, 0xaa, 0xaa)
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
typedef struct PixelFormat {
|
||||
@ -72,13 +78,10 @@ pixman_image_t *qemu_pixman_linebuf_create(pixman_format_code_t format,
|
||||
int width);
|
||||
void qemu_pixman_linebuf_fill(pixman_image_t *linebuf, pixman_image_t *fb,
|
||||
int width, int x, int y);
|
||||
void qemu_pixman_linebuf_copy(pixman_image_t *fb, int width, int x, int y,
|
||||
pixman_image_t *linebuf);
|
||||
pixman_image_t *qemu_pixman_mirror_create(pixman_format_code_t format,
|
||||
pixman_image_t *image);
|
||||
void qemu_pixman_image_unref(pixman_image_t *image);
|
||||
|
||||
pixman_color_t qemu_pixman_color(PixelFormat *pf, uint32_t color);
|
||||
pixman_image_t *qemu_pixman_glyph_from_vgafont(int height, const uint8_t *font,
|
||||
unsigned int ch);
|
||||
void qemu_pixman_glyph_render(pixman_image_t *glyph,
|
||||
|
@ -390,6 +390,10 @@
|
||||
#
|
||||
# @rows: console height, in chars
|
||||
#
|
||||
# Note: the options are only effective when the VNC or SDL graphical
|
||||
# display backend is active. They are ignored with the GTK, Spice, VNC
|
||||
# and D-Bus display backends.
|
||||
#
|
||||
# Since: 1.5
|
||||
##
|
||||
{ 'struct': 'ChardevVC',
|
||||
|
1125
ui/console.c
1125
ui/console.c
File diff suppressed because it is too large
Load Diff
@ -150,6 +150,8 @@ dbus_display_console_dispose(GObject *object)
|
||||
DBusDisplayConsole *ddc = DBUS_DISPLAY_CONSOLE(object);
|
||||
|
||||
unregister_displaychangelistener(&ddc->dcl);
|
||||
g_clear_object(&ddc->iface_touch);
|
||||
g_clear_object(&ddc->iface_mouse);
|
||||
g_clear_object(&ddc->iface_kbd);
|
||||
g_clear_object(&ddc->iface);
|
||||
g_clear_pointer(&ddc->listeners, g_hash_table_unref);
|
||||
|
@ -26,6 +26,9 @@
|
||||
#include "qapi/error.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "dbus.h"
|
||||
#ifdef CONFIG_OPENGL
|
||||
#include <pixman.h>
|
||||
#endif
|
||||
#ifdef G_OS_UNIX
|
||||
#include <gio/gunixfdlist.h>
|
||||
#endif
|
||||
@ -59,12 +62,15 @@ struct _DBusDisplayListener {
|
||||
|
||||
QemuDBusDisplay1Listener *proxy;
|
||||
|
||||
#ifdef CONFIG_OPENGL
|
||||
/* Keep track of the damage region */
|
||||
pixman_region32_t gl_damage;
|
||||
#endif
|
||||
|
||||
DisplayChangeListener dcl;
|
||||
DisplaySurface *ds;
|
||||
enum share_kind ds_share;
|
||||
|
||||
int gl_updates;
|
||||
|
||||
bool ds_mapped;
|
||||
bool can_share_map;
|
||||
|
||||
@ -539,11 +545,16 @@ static void dbus_gl_refresh(DisplayChangeListener *dcl)
|
||||
return;
|
||||
}
|
||||
|
||||
if (ddl->gl_updates) {
|
||||
dbus_call_update_gl(dcl, 0, 0,
|
||||
surface_width(ddl->ds), surface_height(ddl->ds));
|
||||
ddl->gl_updates = 0;
|
||||
int n_rects = pixman_region32_n_rects(&ddl->gl_damage);
|
||||
|
||||
for (int i = 0; i < n_rects; i++) {
|
||||
pixman_box32_t *box;
|
||||
box = pixman_region32_rectangles(&ddl->gl_damage, NULL) + i;
|
||||
/* TODO: Add a UpdateList call to send multiple updates at once */
|
||||
dbus_call_update_gl(dcl, box->x1, box->y1,
|
||||
box->x2 - box->x1, box->y2 - box->y1);
|
||||
}
|
||||
pixman_region32_clear(&ddl->gl_damage);
|
||||
}
|
||||
#endif /* OPENGL */
|
||||
|
||||
@ -558,7 +569,10 @@ static void dbus_gl_gfx_update(DisplayChangeListener *dcl,
|
||||
{
|
||||
DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
|
||||
|
||||
ddl->gl_updates++;
|
||||
pixman_region32_t rect_region;
|
||||
pixman_region32_init_rect(&rect_region, x, y, w, h);
|
||||
pixman_region32_union(&ddl->gl_damage, &ddl->gl_damage, &rect_region);
|
||||
pixman_region32_fini(&rect_region);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -738,6 +752,7 @@ dbus_display_listener_dispose(GObject *object)
|
||||
g_clear_object(&ddl->d3d11_proxy);
|
||||
g_clear_pointer(&ddl->peer_process, CloseHandle);
|
||||
#ifdef CONFIG_OPENGL
|
||||
pixman_region32_fini(&ddl->gl_damage);
|
||||
egl_fb_destroy(&ddl->fb);
|
||||
#endif
|
||||
#endif
|
||||
@ -772,6 +787,9 @@ dbus_display_listener_class_init(DBusDisplayListenerClass *klass)
|
||||
static void
|
||||
dbus_display_listener_init(DBusDisplayListener *ddl)
|
||||
{
|
||||
#ifdef CONFIG_OPENGL
|
||||
pixman_region32_init(&ddl->gl_damage);
|
||||
#endif
|
||||
}
|
||||
|
||||
const char *
|
||||
|
6
ui/gtk.c
6
ui/gtk.c
@ -1187,7 +1187,7 @@ static gboolean gd_text_key_down(GtkWidget *widget,
|
||||
GdkEventKey *key, void *opaque)
|
||||
{
|
||||
VirtualConsole *vc = opaque;
|
||||
QemuConsole *con = vc->gfx.dcl.con;
|
||||
QemuTextConsole *con = QEMU_TEXT_CONSOLE(vc->gfx.dcl.con);
|
||||
|
||||
if (key->keyval == GDK_KEY_Delete) {
|
||||
kbd_put_qcode_console(con, Q_KEY_CODE_DELETE, false);
|
||||
@ -1860,7 +1860,6 @@ static void char_gd_vc_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->parse = qemu_chr_parse_vc;
|
||||
cc->open = gd_vc_open;
|
||||
cc->chr_write = gd_vc_chr_write;
|
||||
cc->chr_accept_input = gd_vc_chr_accept_input;
|
||||
@ -2360,7 +2359,7 @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts)
|
||||
{
|
||||
VirtualConsole *vc;
|
||||
|
||||
GtkDisplayState *s = g_malloc0(sizeof(*s));
|
||||
GtkDisplayState *s;
|
||||
GdkDisplay *window_display;
|
||||
GtkIconTheme *theme;
|
||||
char *dir;
|
||||
@ -2370,6 +2369,7 @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts)
|
||||
exit(1);
|
||||
}
|
||||
assert(opts->type == DISPLAY_TYPE_GTK);
|
||||
s = g_malloc0(sizeof(*s));
|
||||
s->opts = opts;
|
||||
|
||||
theme = gtk_icon_theme_get_default();
|
||||
|
@ -200,14 +200,6 @@ void qemu_pixman_linebuf_fill(pixman_image_t *linebuf, pixman_image_t *fb,
|
||||
x, y, 0, 0, 0, 0, width, 1);
|
||||
}
|
||||
|
||||
/* copy linebuf to framebuffer */
|
||||
void qemu_pixman_linebuf_copy(pixman_image_t *fb, int width, int x, int y,
|
||||
pixman_image_t *linebuf)
|
||||
{
|
||||
pixman_image_composite(PIXMAN_OP_SRC, linebuf, NULL, fb,
|
||||
0, 0, 0, 0, x, y, width, 1);
|
||||
}
|
||||
|
||||
pixman_image_t *qemu_pixman_mirror_create(pixman_format_code_t format,
|
||||
pixman_image_t *image)
|
||||
{
|
||||
@ -226,17 +218,6 @@ void qemu_pixman_image_unref(pixman_image_t *image)
|
||||
pixman_image_unref(image);
|
||||
}
|
||||
|
||||
pixman_color_t qemu_pixman_color(PixelFormat *pf, uint32_t color)
|
||||
{
|
||||
pixman_color_t c;
|
||||
|
||||
c.red = ((color & pf->rmask) >> pf->rshift) << (16 - pf->rbits);
|
||||
c.green = ((color & pf->gmask) >> pf->gshift) << (16 - pf->gbits);
|
||||
c.blue = ((color & pf->bmask) >> pf->bshift) << (16 - pf->bbits);
|
||||
c.alpha = ((color & pf->amask) >> pf->ashift) << (16 - pf->abits);
|
||||
return c;
|
||||
}
|
||||
|
||||
pixman_image_t *qemu_pixman_glyph_from_vgafont(int height, const uint8_t *font,
|
||||
unsigned int ch)
|
||||
{
|
||||
|
@ -43,15 +43,16 @@ void sdl2_process_key(struct sdl2_console *scon,
|
||||
ev->type == SDL_KEYDOWN ? "down" : "up");
|
||||
qkbd_state_key_event(scon->kbd, qcode, ev->type == SDL_KEYDOWN);
|
||||
|
||||
if (!qemu_console_is_graphic(con)) {
|
||||
if (QEMU_IS_TEXT_CONSOLE(con)) {
|
||||
QemuTextConsole *s = QEMU_TEXT_CONSOLE(con);
|
||||
bool ctrl = qkbd_state_modifier_get(scon->kbd, QKBD_MOD_CTRL);
|
||||
if (ev->type == SDL_KEYDOWN) {
|
||||
switch (qcode) {
|
||||
case Q_KEY_CODE_RET:
|
||||
kbd_put_keysym_console(con, '\n');
|
||||
kbd_put_keysym_console(s, '\n');
|
||||
break;
|
||||
default:
|
||||
kbd_put_qcode_console(con, qcode, ctrl);
|
||||
kbd_put_qcode_console(s, qcode, ctrl);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -483,10 +483,9 @@ static void handle_textinput(SDL_Event *ev)
|
||||
return;
|
||||
}
|
||||
|
||||
if (qemu_console_is_graphic(con)) {
|
||||
return;
|
||||
if (QEMU_IS_TEXT_CONSOLE(con)) {
|
||||
kbd_put_string_console(QEMU_TEXT_CONSOLE(con), ev->text.text, strlen(ev->text.text));
|
||||
}
|
||||
kbd_put_string_console(con, ev->text.text, strlen(ev->text.text));
|
||||
}
|
||||
|
||||
static void handle_mousemotion(SDL_Event *ev)
|
||||
|
@ -96,6 +96,11 @@ static void vc_chr_set_echo(Chardev *chr, bool echo)
|
||||
/* TODO: set echo for frontends QMP and qtest */
|
||||
}
|
||||
|
||||
static void vc_chr_parse(QemuOpts *opts, ChardevBackend *backend, Error **errp)
|
||||
{
|
||||
/* fqdn is dealt with in vc_chr_open() */
|
||||
}
|
||||
|
||||
static void char_vc_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
VCChardevClass *vc = CHARDEV_VC_CLASS(oc);
|
||||
@ -103,7 +108,7 @@ static void char_vc_class_init(ObjectClass *oc, void *data)
|
||||
|
||||
vc->parent_open = cc->open;
|
||||
|
||||
cc->parse = qemu_chr_parse_vc;
|
||||
cc->parse = vc_chr_parse;
|
||||
cc->open = vc_chr_open;
|
||||
cc->chr_set_echo = vc_chr_set_echo;
|
||||
}
|
||||
|
@ -189,7 +189,7 @@ static void qemu_spice_create_update(SimpleSpiceDisplay *ssd)
|
||||
{
|
||||
static const int blksize = 32;
|
||||
int blocks = DIV_ROUND_UP(surface_width(ssd->ds), blksize);
|
||||
int dirty_top[blocks];
|
||||
g_autofree int *dirty_top = NULL;
|
||||
int y, yoff1, yoff2, x, xoff, blk, bw;
|
||||
int bpp = surface_bytes_per_pixel(ssd->ds);
|
||||
uint8_t *guest, *mirror;
|
||||
@ -198,6 +198,7 @@ static void qemu_spice_create_update(SimpleSpiceDisplay *ssd)
|
||||
return;
|
||||
};
|
||||
|
||||
dirty_top = g_new(int, blocks);
|
||||
for (blk = 0; blk < blocks; blk++) {
|
||||
dirty_top[blk] = -1;
|
||||
}
|
||||
|
187
ui/ui-qmp-cmds.c
187
ui/ui-qmp-cmds.c
@ -14,13 +14,20 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
|
||||
#include "io/channel-file.h"
|
||||
#include "monitor/qmp-helpers.h"
|
||||
#include "qapi/qapi-commands-ui.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qemu/coroutine.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "trace.h"
|
||||
#include "ui/console.h"
|
||||
#include "ui/dbus-display.h"
|
||||
#include "ui/qemu-spice.h"
|
||||
#ifdef CONFIG_PNG
|
||||
#include <png.h>
|
||||
#endif
|
||||
|
||||
void qmp_set_password(SetPasswordOptions *opts, Error **errp)
|
||||
{
|
||||
@ -204,3 +211,183 @@ void qmp_client_migrate_info(const char *protocol, const char *hostname,
|
||||
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "protocol", "'spice'");
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PNG
|
||||
/**
|
||||
* png_save: Take a screenshot as PNG
|
||||
*
|
||||
* Saves screendump as a PNG file
|
||||
*
|
||||
* Returns true for success or false for error.
|
||||
*
|
||||
* @fd: File descriptor for PNG file.
|
||||
* @image: Image data in pixman format.
|
||||
* @errp: Pointer to an error.
|
||||
*/
|
||||
static bool png_save(int fd, pixman_image_t *image, Error **errp)
|
||||
{
|
||||
int width = pixman_image_get_width(image);
|
||||
int height = pixman_image_get_height(image);
|
||||
png_struct *png_ptr;
|
||||
png_info *info_ptr;
|
||||
g_autoptr(pixman_image_t) linebuf =
|
||||
qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, width);
|
||||
uint8_t *buf = (uint8_t *)pixman_image_get_data(linebuf);
|
||||
FILE *f = fdopen(fd, "wb");
|
||||
int y;
|
||||
if (!f) {
|
||||
error_setg_errno(errp, errno,
|
||||
"Failed to create file from file descriptor");
|
||||
return false;
|
||||
}
|
||||
|
||||
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,
|
||||
NULL, NULL);
|
||||
if (!png_ptr) {
|
||||
error_setg(errp, "PNG creation failed. Unable to write struct");
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
info_ptr = png_create_info_struct(png_ptr);
|
||||
|
||||
if (!info_ptr) {
|
||||
error_setg(errp, "PNG creation failed. Unable to write info");
|
||||
fclose(f);
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
png_init_io(png_ptr, f);
|
||||
|
||||
png_set_IHDR(png_ptr, info_ptr, width, height, 8,
|
||||
PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
|
||||
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
|
||||
|
||||
png_write_info(png_ptr, info_ptr);
|
||||
|
||||
for (y = 0; y < height; ++y) {
|
||||
qemu_pixman_linebuf_fill(linebuf, image, width, 0, y);
|
||||
png_write_row(png_ptr, buf);
|
||||
}
|
||||
|
||||
png_write_end(png_ptr, NULL);
|
||||
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
|
||||
if (fclose(f) != 0) {
|
||||
error_setg_errno(errp, errno,
|
||||
"PNG creation failed. Unable to close file");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#else /* no png support */
|
||||
|
||||
static bool png_save(int fd, pixman_image_t *image, Error **errp)
|
||||
{
|
||||
error_setg(errp, "Enable PNG support with libpng for screendump");
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PNG */
|
||||
|
||||
static bool ppm_save(int fd, pixman_image_t *image, Error **errp)
|
||||
{
|
||||
int width = pixman_image_get_width(image);
|
||||
int height = pixman_image_get_height(image);
|
||||
g_autoptr(Object) ioc = OBJECT(qio_channel_file_new_fd(fd));
|
||||
g_autofree char *header = NULL;
|
||||
g_autoptr(pixman_image_t) linebuf = NULL;
|
||||
int y;
|
||||
|
||||
trace_ppm_save(fd, image);
|
||||
|
||||
header = g_strdup_printf("P6\n%d %d\n%d\n", width, height, 255);
|
||||
if (qio_channel_write_all(QIO_CHANNEL(ioc),
|
||||
header, strlen(header), errp) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
linebuf = qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, width);
|
||||
for (y = 0; y < height; y++) {
|
||||
qemu_pixman_linebuf_fill(linebuf, image, width, 0, y);
|
||||
if (qio_channel_write_all(QIO_CHANNEL(ioc),
|
||||
(char *)pixman_image_get_data(linebuf),
|
||||
pixman_image_get_stride(linebuf), errp) < 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Safety: coroutine-only, concurrent-coroutine safe, main thread only */
|
||||
void coroutine_fn
|
||||
qmp_screendump(const char *filename, const char *device,
|
||||
bool has_head, int64_t head,
|
||||
bool has_format, ImageFormat format, Error **errp)
|
||||
{
|
||||
g_autoptr(pixman_image_t) image = NULL;
|
||||
QemuConsole *con;
|
||||
DisplaySurface *surface;
|
||||
int fd;
|
||||
|
||||
if (device) {
|
||||
con = qemu_console_lookup_by_device_name(device, has_head ? head : 0,
|
||||
errp);
|
||||
if (!con) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (has_head) {
|
||||
error_setg(errp, "'head' must be specified together with 'device'");
|
||||
return;
|
||||
}
|
||||
con = qemu_console_lookup_by_index(0);
|
||||
if (!con) {
|
||||
error_setg(errp, "There is no console to take a screendump from");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
qemu_console_co_wait_update(con);
|
||||
|
||||
/*
|
||||
* All pending coroutines are woken up, while the BQL is held. No
|
||||
* further graphic update are possible until it is released. Take
|
||||
* an image ref before that.
|
||||
*/
|
||||
surface = qemu_console_surface(con);
|
||||
if (!surface) {
|
||||
error_setg(errp, "no surface");
|
||||
return;
|
||||
}
|
||||
image = pixman_image_ref(surface->image);
|
||||
|
||||
fd = qemu_open_old(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
|
||||
if (fd == -1) {
|
||||
error_setg(errp, "failed to open file '%s': %s", filename,
|
||||
strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The image content could potentially be updated as the coroutine
|
||||
* yields and releases the BQL. It could produce corrupted dump, but
|
||||
* it should be otherwise safe.
|
||||
*/
|
||||
if (has_format && format == IMAGE_FORMAT_PNG) {
|
||||
/* PNG format specified for screendump */
|
||||
if (!png_save(fd, image, errp)) {
|
||||
qemu_unlink(filename);
|
||||
}
|
||||
} else {
|
||||
/* PPM format specified/default for screendump */
|
||||
if (!ppm_save(fd, image, errp)) {
|
||||
qemu_unlink(filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -870,8 +870,11 @@ static void vdagent_disconnect(VDAgentChardev *vd)
|
||||
|
||||
static void vdagent_chr_set_fe_open(struct Chardev *chr, int fe_open)
|
||||
{
|
||||
VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(chr);
|
||||
|
||||
if (!fe_open) {
|
||||
trace_vdagent_close();
|
||||
vdagent_disconnect(vd);
|
||||
/* To reset_serial, we CLOSED our side. Make sure the other end knows we
|
||||
* are ready again. */
|
||||
qemu_chr_be_event(chr, CHR_EVENT_OPENED);
|
||||
@ -923,6 +926,9 @@ static void vdagent_chr_fini(Object *obj)
|
||||
|
||||
migrate_del_blocker(vd->migration_blocker);
|
||||
vdagent_disconnect(vd);
|
||||
if (vd->mouse_hs) {
|
||||
qemu_input_handler_unregister(vd->mouse_hs);
|
||||
}
|
||||
buffer_free(&vd->outbuf);
|
||||
error_free(vd->migration_blocker);
|
||||
}
|
||||
|
@ -7,6 +7,8 @@
|
||||
#define NAME BPP
|
||||
#endif
|
||||
|
||||
#define MAX_BYTES_PER_PIXEL 4
|
||||
|
||||
static void CONCAT(send_hextile_tile_, NAME)(VncState *vs,
|
||||
int x, int y, int w, int h,
|
||||
void *last_bg_,
|
||||
@ -25,10 +27,13 @@ static void CONCAT(send_hextile_tile_, NAME)(VncState *vs,
|
||||
int bg_count = 0;
|
||||
int fg_count = 0;
|
||||
int flags = 0;
|
||||
uint8_t data[(vs->client_pf.bytes_per_pixel + 2) * 16 * 16];
|
||||
uint8_t data[(MAX_BYTES_PER_PIXEL + 2) * 16 * 16];
|
||||
int n_data = 0;
|
||||
int n_subtiles = 0;
|
||||
|
||||
/* Enforced by set_pixel_format() */
|
||||
assert(vs->client_pf.bytes_per_pixel <= MAX_BYTES_PER_PIXEL);
|
||||
|
||||
for (j = 0; j < h; j++) {
|
||||
for (i = 0; i < w; i++) {
|
||||
switch (n_colors) {
|
||||
@ -205,6 +210,7 @@ static void CONCAT(send_hextile_tile_, NAME)(VncState *vs,
|
||||
}
|
||||
}
|
||||
|
||||
#undef MAX_BYTES_PER_PIXEL
|
||||
#undef NAME
|
||||
#undef pixel_t
|
||||
#undef CONCAT_I
|
||||
|
@ -1097,13 +1097,13 @@ static int send_palette_rect(VncState *vs, int x, int y,
|
||||
switch (vs->client_pf.bytes_per_pixel) {
|
||||
case 4:
|
||||
{
|
||||
size_t old_offset, offset;
|
||||
uint32_t header[palette_size(palette)];
|
||||
size_t old_offset, offset, palette_sz = palette_size(palette);
|
||||
g_autofree uint32_t *header = g_new(uint32_t, palette_sz);
|
||||
struct palette_cb_priv priv = { vs, (uint8_t *)header };
|
||||
|
||||
old_offset = vs->output.offset;
|
||||
palette_iter(palette, write_palette, &priv);
|
||||
vnc_write(vs, header, sizeof(header));
|
||||
vnc_write(vs, header, palette_sz * sizeof(uint32_t));
|
||||
|
||||
if (vs->tight->pixel24) {
|
||||
tight_pack24(vs, vs->output.buffer + old_offset, colors, &offset);
|
||||
@ -1115,11 +1115,12 @@ static int send_palette_rect(VncState *vs, int x, int y,
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
uint16_t header[palette_size(palette)];
|
||||
size_t palette_sz = palette_size(palette);
|
||||
g_autofree uint16_t *header = g_new(uint16_t, palette_sz);
|
||||
struct palette_cb_priv priv = { vs, (uint8_t *)header };
|
||||
|
||||
palette_iter(palette, write_palette, &priv);
|
||||
vnc_write(vs, header, sizeof(header));
|
||||
vnc_write(vs, header, palette_sz * sizeof(uint16_t));
|
||||
tight_encode_indexed_rect16(vs->tight->tight.buffer, w * h, palette);
|
||||
break;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user