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:
Stefan Hajnoczi 2023-09-06 11:16:00 -04:00
commit c152379422
17 changed files with 776 additions and 692 deletions

View File

@ -320,7 +320,4 @@ GSource *qemu_chr_timeout_add_ms(Chardev *chr, guint ms,
void suspend_mux_open(void); void suspend_mux_open(void);
void resume_mux_open(void); void resume_mux_open(void);
/* console.c */
void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, Error **errp);
#endif #endif

View File

@ -12,6 +12,27 @@
# include "ui/shader.h" # include "ui/shader.h"
#endif #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 */ /* keyboard/mouse support */
#define MOUSE_EVENT_LBUTTON 0x01 #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_PAGEUP 0xe406
#define QEMU_KEY_CTRL_PAGEDOWN 0xe407 #define QEMU_KEY_CTRL_PAGEDOWN 0xe407
void kbd_put_keysym_console(QemuConsole *s, int keysym); void kbd_put_keysym_console(QemuTextConsole *s, int keysym);
bool kbd_put_qcode_console(QemuConsole *s, int qcode, bool ctrl); bool kbd_put_qcode_console(QemuTextConsole *s, int qcode, bool ctrl);
void kbd_put_string_console(QemuConsole *s, const char *str, int len); void kbd_put_string_console(QemuTextConsole *s, const char *str, int len);
void kbd_put_keysym(int keysym); void kbd_put_keysym(int keysym);
/* Touch devices */ /* Touch devices */
@ -112,10 +133,6 @@ void console_handle_touch_event(QemuConsole *con,
Error **errp); Error **errp);
/* consoles */ /* consoles */
#define TYPE_QEMU_CONSOLE "qemu-console"
OBJECT_DECLARE_TYPE(QemuConsole, QemuConsoleClass, QEMU_CONSOLE)
struct QemuConsoleClass { struct QemuConsoleClass {
ObjectClass parent_class; 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(DeviceState *dev, uint32_t head);
QemuConsole *qemu_console_lookup_by_device_name(const char *device_id, QemuConsole *qemu_console_lookup_by_device_name(const char *device_id,
uint32_t head, Error **errp); uint32_t head, Error **errp);
QemuConsole *qemu_console_lookup_unused(void);
QEMUCursor *qemu_console_get_cursor(QemuConsole *con); QEMUCursor *qemu_console_get_cursor(QemuConsole *con);
bool qemu_console_is_visible(QemuConsole *con); bool qemu_console_is_visible(QemuConsole *con);
bool qemu_console_is_graphic(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 console_select(unsigned int index);
void qemu_console_resize(QemuConsole *con, int width, int height); void qemu_console_resize(QemuConsole *con, int width, int height);
DisplaySurface *qemu_console_surface(QemuConsole *con); 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 */ /* console-gl.c */
#ifdef CONFIG_OPENGL #ifdef CONFIG_OPENGL

View File

@ -47,6 +47,12 @@
# define PIXMAN_LE_x8r8g8b8 PIXMAN_x8r8g8b8 # define PIXMAN_LE_x8r8g8b8 PIXMAN_x8r8g8b8
#endif #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 { typedef struct PixelFormat {
@ -72,13 +78,10 @@ pixman_image_t *qemu_pixman_linebuf_create(pixman_format_code_t format,
int width); int width);
void qemu_pixman_linebuf_fill(pixman_image_t *linebuf, pixman_image_t *fb, void qemu_pixman_linebuf_fill(pixman_image_t *linebuf, pixman_image_t *fb,
int width, int x, int y); 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 *qemu_pixman_mirror_create(pixman_format_code_t format,
pixman_image_t *image); pixman_image_t *image);
void qemu_pixman_image_unref(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, pixman_image_t *qemu_pixman_glyph_from_vgafont(int height, const uint8_t *font,
unsigned int ch); unsigned int ch);
void qemu_pixman_glyph_render(pixman_image_t *glyph, void qemu_pixman_glyph_render(pixman_image_t *glyph,

View File

@ -390,6 +390,10 @@
# #
# @rows: console height, in chars # @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 # Since: 1.5
## ##
{ 'struct': 'ChardevVC', { 'struct': 'ChardevVC',

File diff suppressed because it is too large Load Diff

View File

@ -150,6 +150,8 @@ dbus_display_console_dispose(GObject *object)
DBusDisplayConsole *ddc = DBUS_DISPLAY_CONSOLE(object); DBusDisplayConsole *ddc = DBUS_DISPLAY_CONSOLE(object);
unregister_displaychangelistener(&ddc->dcl); 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_kbd);
g_clear_object(&ddc->iface); g_clear_object(&ddc->iface);
g_clear_pointer(&ddc->listeners, g_hash_table_unref); g_clear_pointer(&ddc->listeners, g_hash_table_unref);

View File

@ -26,6 +26,9 @@
#include "qapi/error.h" #include "qapi/error.h"
#include "sysemu/sysemu.h" #include "sysemu/sysemu.h"
#include "dbus.h" #include "dbus.h"
#ifdef CONFIG_OPENGL
#include <pixman.h>
#endif
#ifdef G_OS_UNIX #ifdef G_OS_UNIX
#include <gio/gunixfdlist.h> #include <gio/gunixfdlist.h>
#endif #endif
@ -59,12 +62,15 @@ struct _DBusDisplayListener {
QemuDBusDisplay1Listener *proxy; QemuDBusDisplay1Listener *proxy;
#ifdef CONFIG_OPENGL
/* Keep track of the damage region */
pixman_region32_t gl_damage;
#endif
DisplayChangeListener dcl; DisplayChangeListener dcl;
DisplaySurface *ds; DisplaySurface *ds;
enum share_kind ds_share; enum share_kind ds_share;
int gl_updates;
bool ds_mapped; bool ds_mapped;
bool can_share_map; bool can_share_map;
@ -539,11 +545,16 @@ static void dbus_gl_refresh(DisplayChangeListener *dcl)
return; return;
} }
if (ddl->gl_updates) { int n_rects = pixman_region32_n_rects(&ddl->gl_damage);
dbus_call_update_gl(dcl, 0, 0,
surface_width(ddl->ds), surface_height(ddl->ds)); for (int i = 0; i < n_rects; i++) {
ddl->gl_updates = 0; 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 */ #endif /* OPENGL */
@ -558,7 +569,10 @@ static void dbus_gl_gfx_update(DisplayChangeListener *dcl,
{ {
DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, 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 #endif
@ -738,6 +752,7 @@ dbus_display_listener_dispose(GObject *object)
g_clear_object(&ddl->d3d11_proxy); g_clear_object(&ddl->d3d11_proxy);
g_clear_pointer(&ddl->peer_process, CloseHandle); g_clear_pointer(&ddl->peer_process, CloseHandle);
#ifdef CONFIG_OPENGL #ifdef CONFIG_OPENGL
pixman_region32_fini(&ddl->gl_damage);
egl_fb_destroy(&ddl->fb); egl_fb_destroy(&ddl->fb);
#endif #endif
#endif #endif
@ -772,6 +787,9 @@ dbus_display_listener_class_init(DBusDisplayListenerClass *klass)
static void static void
dbus_display_listener_init(DBusDisplayListener *ddl) dbus_display_listener_init(DBusDisplayListener *ddl)
{ {
#ifdef CONFIG_OPENGL
pixman_region32_init(&ddl->gl_damage);
#endif
} }
const char * const char *

View File

@ -1187,7 +1187,7 @@ static gboolean gd_text_key_down(GtkWidget *widget,
GdkEventKey *key, void *opaque) GdkEventKey *key, void *opaque)
{ {
VirtualConsole *vc = 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) { if (key->keyval == GDK_KEY_Delete) {
kbd_put_qcode_console(con, Q_KEY_CODE_DELETE, false); 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); ChardevClass *cc = CHARDEV_CLASS(oc);
cc->parse = qemu_chr_parse_vc;
cc->open = gd_vc_open; cc->open = gd_vc_open;
cc->chr_write = gd_vc_chr_write; cc->chr_write = gd_vc_chr_write;
cc->chr_accept_input = gd_vc_chr_accept_input; cc->chr_accept_input = gd_vc_chr_accept_input;
@ -2360,7 +2359,7 @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts)
{ {
VirtualConsole *vc; VirtualConsole *vc;
GtkDisplayState *s = g_malloc0(sizeof(*s)); GtkDisplayState *s;
GdkDisplay *window_display; GdkDisplay *window_display;
GtkIconTheme *theme; GtkIconTheme *theme;
char *dir; char *dir;
@ -2370,6 +2369,7 @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts)
exit(1); exit(1);
} }
assert(opts->type == DISPLAY_TYPE_GTK); assert(opts->type == DISPLAY_TYPE_GTK);
s = g_malloc0(sizeof(*s));
s->opts = opts; s->opts = opts;
theme = gtk_icon_theme_get_default(); theme = gtk_icon_theme_get_default();

View File

@ -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); 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 *qemu_pixman_mirror_create(pixman_format_code_t format,
pixman_image_t *image) pixman_image_t *image)
{ {
@ -226,17 +218,6 @@ void qemu_pixman_image_unref(pixman_image_t *image)
pixman_image_unref(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, pixman_image_t *qemu_pixman_glyph_from_vgafont(int height, const uint8_t *font,
unsigned int ch) unsigned int ch)
{ {

View File

@ -43,15 +43,16 @@ void sdl2_process_key(struct sdl2_console *scon,
ev->type == SDL_KEYDOWN ? "down" : "up"); ev->type == SDL_KEYDOWN ? "down" : "up");
qkbd_state_key_event(scon->kbd, qcode, ev->type == SDL_KEYDOWN); 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); bool ctrl = qkbd_state_modifier_get(scon->kbd, QKBD_MOD_CTRL);
if (ev->type == SDL_KEYDOWN) { if (ev->type == SDL_KEYDOWN) {
switch (qcode) { switch (qcode) {
case Q_KEY_CODE_RET: case Q_KEY_CODE_RET:
kbd_put_keysym_console(con, '\n'); kbd_put_keysym_console(s, '\n');
break; break;
default: default:
kbd_put_qcode_console(con, qcode, ctrl); kbd_put_qcode_console(s, qcode, ctrl);
break; break;
} }
} }

View File

@ -483,10 +483,9 @@ static void handle_textinput(SDL_Event *ev)
return; return;
} }
if (qemu_console_is_graphic(con)) { if (QEMU_IS_TEXT_CONSOLE(con)) {
return; 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) static void handle_mousemotion(SDL_Event *ev)

View File

@ -96,6 +96,11 @@ static void vc_chr_set_echo(Chardev *chr, bool echo)
/* TODO: set echo for frontends QMP and qtest */ /* 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) static void char_vc_class_init(ObjectClass *oc, void *data)
{ {
VCChardevClass *vc = CHARDEV_VC_CLASS(oc); 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; vc->parent_open = cc->open;
cc->parse = qemu_chr_parse_vc; cc->parse = vc_chr_parse;
cc->open = vc_chr_open; cc->open = vc_chr_open;
cc->chr_set_echo = vc_chr_set_echo; cc->chr_set_echo = vc_chr_set_echo;
} }

View File

@ -189,7 +189,7 @@ static void qemu_spice_create_update(SimpleSpiceDisplay *ssd)
{ {
static const int blksize = 32; static const int blksize = 32;
int blocks = DIV_ROUND_UP(surface_width(ssd->ds), blksize); 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 y, yoff1, yoff2, x, xoff, blk, bw;
int bpp = surface_bytes_per_pixel(ssd->ds); int bpp = surface_bytes_per_pixel(ssd->ds);
uint8_t *guest, *mirror; uint8_t *guest, *mirror;
@ -198,6 +198,7 @@ static void qemu_spice_create_update(SimpleSpiceDisplay *ssd)
return; return;
}; };
dirty_top = g_new(int, blocks);
for (blk = 0; blk < blocks; blk++) { for (blk = 0; blk < blocks; blk++) {
dirty_top[blk] = -1; dirty_top[blk] = -1;
} }

View File

@ -14,13 +14,20 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "io/channel-file.h"
#include "monitor/qmp-helpers.h" #include "monitor/qmp-helpers.h"
#include "qapi/qapi-commands-ui.h" #include "qapi/qapi-commands-ui.h"
#include "qapi/qmp/qerror.h" #include "qapi/qmp/qerror.h"
#include "qemu/coroutine.h"
#include "qemu/cutils.h" #include "qemu/cutils.h"
#include "trace.h"
#include "ui/console.h" #include "ui/console.h"
#include "ui/dbus-display.h" #include "ui/dbus-display.h"
#include "ui/qemu-spice.h" #include "ui/qemu-spice.h"
#ifdef CONFIG_PNG
#include <png.h>
#endif
void qmp_set_password(SetPasswordOptions *opts, Error **errp) 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'"); 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);
}
}
}

View File

@ -870,8 +870,11 @@ static void vdagent_disconnect(VDAgentChardev *vd)
static void vdagent_chr_set_fe_open(struct Chardev *chr, int fe_open) static void vdagent_chr_set_fe_open(struct Chardev *chr, int fe_open)
{ {
VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(chr);
if (!fe_open) { if (!fe_open) {
trace_vdagent_close(); trace_vdagent_close();
vdagent_disconnect(vd);
/* To reset_serial, we CLOSED our side. Make sure the other end knows we /* To reset_serial, we CLOSED our side. Make sure the other end knows we
* are ready again. */ * are ready again. */
qemu_chr_be_event(chr, CHR_EVENT_OPENED); 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); migrate_del_blocker(vd->migration_blocker);
vdagent_disconnect(vd); vdagent_disconnect(vd);
if (vd->mouse_hs) {
qemu_input_handler_unregister(vd->mouse_hs);
}
buffer_free(&vd->outbuf); buffer_free(&vd->outbuf);
error_free(vd->migration_blocker); error_free(vd->migration_blocker);
} }

View File

@ -7,6 +7,8 @@
#define NAME BPP #define NAME BPP
#endif #endif
#define MAX_BYTES_PER_PIXEL 4
static void CONCAT(send_hextile_tile_, NAME)(VncState *vs, static void CONCAT(send_hextile_tile_, NAME)(VncState *vs,
int x, int y, int w, int h, int x, int y, int w, int h,
void *last_bg_, void *last_bg_,
@ -25,10 +27,13 @@ static void CONCAT(send_hextile_tile_, NAME)(VncState *vs,
int bg_count = 0; int bg_count = 0;
int fg_count = 0; int fg_count = 0;
int flags = 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_data = 0;
int n_subtiles = 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 (j = 0; j < h; j++) {
for (i = 0; i < w; i++) { for (i = 0; i < w; i++) {
switch (n_colors) { switch (n_colors) {
@ -205,6 +210,7 @@ static void CONCAT(send_hextile_tile_, NAME)(VncState *vs,
} }
} }
#undef MAX_BYTES_PER_PIXEL
#undef NAME #undef NAME
#undef pixel_t #undef pixel_t
#undef CONCAT_I #undef CONCAT_I

View File

@ -1097,13 +1097,13 @@ static int send_palette_rect(VncState *vs, int x, int y,
switch (vs->client_pf.bytes_per_pixel) { switch (vs->client_pf.bytes_per_pixel) {
case 4: case 4:
{ {
size_t old_offset, offset; size_t old_offset, offset, palette_sz = palette_size(palette);
uint32_t header[palette_size(palette)]; g_autofree uint32_t *header = g_new(uint32_t, palette_sz);
struct palette_cb_priv priv = { vs, (uint8_t *)header }; struct palette_cb_priv priv = { vs, (uint8_t *)header };
old_offset = vs->output.offset; old_offset = vs->output.offset;
palette_iter(palette, write_palette, &priv); 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) { if (vs->tight->pixel24) {
tight_pack24(vs, vs->output.buffer + old_offset, colors, &offset); 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: 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 }; struct palette_cb_priv priv = { vs, (uint8_t *)header };
palette_iter(palette, write_palette, &priv); 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); tight_encode_indexed_rect16(vs->tight->tight.buffer, w * h, palette);
break; break;
} }