mirror of https://gitee.com/openkylin/qemu.git
vnc: add display-update monitor command.
screendump: add png support. vmsvga: screen update fix. i386: sev setup for -bios loaded firmware -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEoDKM/7k6F6eZAf59TLbY7tPocTgFAmJpfYUACgkQTLbY7tPo cTg6ChAAn5EWtNwmVbfbVzRTu0kqdx7QXyK6FFgTgXrsrmBCWzJhHDraYa9cMOZU wBlU/Rutuv9ETvtRTRic3t0qcdRmvZjpHuA/3agBMJY7xpEQbQ8NwVdSRZTOZo0i hXzWEAnxviEv8F/W1TXwB5d2Q9sWlJ2yO8SvcxTfAEK7hOSFrWypp3XRKr5WBHjO DFTtwqTk9MRNsgsfnpEHNGDb30JPTqKZKRbDal15NDR9fQz+iCq3FIv/FpBaUfys v9GA2zNT324MvhR64xNblaujCn2XFOsFvDY4nGDrfbKGJch8ltAg5t4G1CCZqOn7 NIiwodC0508sAv9xUm+qvh+oHyf11EFdcAMWYimrExY2I51XOEDnJip/SAdogo5u h7LyLkZTEG5tyc+a4PGIcC216ecDDNytMnJM9nh9YK3p5UiBOgcHV2wWDdzJbsdS GRoP0fzF/MBQd985HBCF2vtQVk4AbQA7atZ8FKp1ZsHr3sFfs+vd0xyItsDMinBP k/eKOOKbHRgXcdIocw4PK16yURrMo5IUGCGGiG9waqYz+VDyHhtikBAzQvYYdnqN NaZttCcEieIk4XNd+wCfI0GQLtOY/AP1k8TV0rCaDTnO6nOxJ/uP64IaCzxzCT10 b8VRdCfYDGjd2C14XYKmTzBRPM4hVrf9bo7FtXVtmSksTG+eIao= =yaxh -----END PGP SIGNATURE----- Merge tag 'kraxel-20220427-pull-request' of git://git.kraxel.org/qemu into staging vnc: add display-update monitor command. screendump: add png support. vmsvga: screen update fix. i386: sev setup for -bios loaded firmware # -----BEGIN PGP SIGNATURE----- # # iQIzBAABCgAdFiEEoDKM/7k6F6eZAf59TLbY7tPocTgFAmJpfYUACgkQTLbY7tPo # cTg6ChAAn5EWtNwmVbfbVzRTu0kqdx7QXyK6FFgTgXrsrmBCWzJhHDraYa9cMOZU # wBlU/Rutuv9ETvtRTRic3t0qcdRmvZjpHuA/3agBMJY7xpEQbQ8NwVdSRZTOZo0i # hXzWEAnxviEv8F/W1TXwB5d2Q9sWlJ2yO8SvcxTfAEK7hOSFrWypp3XRKr5WBHjO # DFTtwqTk9MRNsgsfnpEHNGDb30JPTqKZKRbDal15NDR9fQz+iCq3FIv/FpBaUfys # v9GA2zNT324MvhR64xNblaujCn2XFOsFvDY4nGDrfbKGJch8ltAg5t4G1CCZqOn7 # NIiwodC0508sAv9xUm+qvh+oHyf11EFdcAMWYimrExY2I51XOEDnJip/SAdogo5u # h7LyLkZTEG5tyc+a4PGIcC216ecDDNytMnJM9nh9YK3p5UiBOgcHV2wWDdzJbsdS # GRoP0fzF/MBQd985HBCF2vtQVk4AbQA7atZ8FKp1ZsHr3sFfs+vd0xyItsDMinBP # k/eKOOKbHRgXcdIocw4PK16yURrMo5IUGCGGiG9waqYz+VDyHhtikBAzQvYYdnqN # NaZttCcEieIk4XNd+wCfI0GQLtOY/AP1k8TV0rCaDTnO6nOxJ/uP64IaCzxzCT10 # b8VRdCfYDGjd2C14XYKmTzBRPM4hVrf9bo7FtXVtmSksTG+eIao= # =yaxh # -----END PGP SIGNATURE----- # gpg: Signature made Wed 27 Apr 2022 10:29:41 AM PDT # gpg: using RSA key A0328CFFB93A17A79901FE7D4CB6D8EED3E87138 # gpg: Good signature from "Gerd Hoffmann (work) <kraxel@redhat.com>" [undefined] # gpg: aka "Gerd Hoffmann <gerd@kraxel.org>" [undefined] # gpg: aka "Gerd Hoffmann (private) <kraxel@gmail.com>" [undefined] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: A032 8CFF B93A 17A7 9901 FE7D 4CB6 D8EE D3E8 7138 * tag 'kraxel-20220427-pull-request' of git://git.kraxel.org/qemu: i386: firmware parsing and sev setup for -bios loaded firmware i386: factor out x86_firmware_configure() i386: move bios load error message avocado/vnc: add test_change_listen qapi/ui: add 'display-update' command for changing listen address ui/vnc: refactor arrays of addresses to SocketAddressList Added parameter to take screenshot with screendump as PNG Replacing CONFIG_VNC_PNG with CONFIG_PNG hw/display/vmware_vga: do not discard screen updates Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
commit
cf6f26d6f9
|
@ -366,7 +366,8 @@ documentation of ``query-hotpluggable-cpus`` for additional details.
|
||||||
``change`` (removed in 6.0)
|
``change`` (removed in 6.0)
|
||||||
'''''''''''''''''''''''''''
|
'''''''''''''''''''''''''''
|
||||||
|
|
||||||
Use ``blockdev-change-medium`` or ``change-vnc-password`` instead.
|
Use ``blockdev-change-medium`` or ``change-vnc-password`` or
|
||||||
|
``display-update`` instead.
|
||||||
|
|
||||||
``query-events`` (removed in 6.0)
|
``query-events`` (removed in 6.0)
|
||||||
'''''''''''''''''''''''''''''''''
|
'''''''''''''''''''''''''''''''''
|
||||||
|
|
|
@ -247,11 +247,12 @@ ERST
|
||||||
|
|
||||||
{
|
{
|
||||||
.name = "screendump",
|
.name = "screendump",
|
||||||
.args_type = "filename:F,device:s?,head:i?",
|
.args_type = "filename:F,format:-fs,device:s?,head:i?",
|
||||||
.params = "filename [device [head]]",
|
.params = "filename [-f format] [device [head]]",
|
||||||
.help = "save screen from head 'head' of display device 'device' "
|
.help = "save screen from head 'head' of display device 'device'"
|
||||||
"into PPM image 'filename'",
|
"in specified format 'format' as image 'filename'."
|
||||||
.cmd = hmp_screendump,
|
"Currently only 'png' and 'ppm' formats are supported.",
|
||||||
|
.cmd = hmp_screendump,
|
||||||
.coroutine = true,
|
.coroutine = true,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ vmware_setmode(uint32_t w, uint32_t h, uint32_t bpp) "%dx%d @ %d bpp"
|
||||||
vmware_verify_rect_less_than_zero(const char *name, const char *param, int x) "%s: %s was < 0 (%d)"
|
vmware_verify_rect_less_than_zero(const char *name, const char *param, int x) "%s: %s was < 0 (%d)"
|
||||||
vmware_verify_rect_greater_than_bound(const char *name, const char *param, int bound, int x) "%s: %s was > %d (%d)"
|
vmware_verify_rect_greater_than_bound(const char *name, const char *param, int bound, int x) "%s: %s was > %d (%d)"
|
||||||
vmware_verify_rect_surface_bound_exceeded(const char *name, const char *component, int bound, const char *param1, int value1, const char *param2, int value2) "%s: %s > %d (%s: %d, %s: %d)"
|
vmware_verify_rect_surface_bound_exceeded(const char *name, const char *component, int bound, const char *param1, int value1, const char *param2, int value2) "%s: %s > %d (%s: %d, %s: %d)"
|
||||||
|
vmware_update_rect_delayed_flush(void) "display update FIFO full - forcing flush"
|
||||||
|
|
||||||
# virtio-gpu-base.c
|
# virtio-gpu-base.c
|
||||||
virtio_gpu_features(bool virgl) "virgl %d"
|
virtio_gpu_features(bool virgl) "virgl %d"
|
||||||
|
|
|
@ -80,7 +80,7 @@ struct vmsvga_state_s {
|
||||||
struct vmsvga_rect_s {
|
struct vmsvga_rect_s {
|
||||||
int x, y, w, h;
|
int x, y, w, h;
|
||||||
} redraw_fifo[REDRAW_FIFO_LEN];
|
} redraw_fifo[REDRAW_FIFO_LEN];
|
||||||
int redraw_fifo_first, redraw_fifo_last;
|
int redraw_fifo_last;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define TYPE_VMWARE_SVGA "vmware-svga"
|
#define TYPE_VMWARE_SVGA "vmware-svga"
|
||||||
|
@ -380,33 +380,39 @@ static inline void vmsvga_update_rect(struct vmsvga_state_s *s,
|
||||||
dpy_gfx_update(s->vga.con, x, y, w, h);
|
dpy_gfx_update(s->vga.con, x, y, w, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void vmsvga_update_rect_delayed(struct vmsvga_state_s *s,
|
|
||||||
int x, int y, int w, int h)
|
|
||||||
{
|
|
||||||
struct vmsvga_rect_s *rect = &s->redraw_fifo[s->redraw_fifo_last++];
|
|
||||||
|
|
||||||
s->redraw_fifo_last &= REDRAW_FIFO_LEN - 1;
|
|
||||||
rect->x = x;
|
|
||||||
rect->y = y;
|
|
||||||
rect->w = w;
|
|
||||||
rect->h = h;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void vmsvga_update_rect_flush(struct vmsvga_state_s *s)
|
static inline void vmsvga_update_rect_flush(struct vmsvga_state_s *s)
|
||||||
{
|
{
|
||||||
struct vmsvga_rect_s *rect;
|
struct vmsvga_rect_s *rect;
|
||||||
|
|
||||||
if (s->invalidated) {
|
if (s->invalidated) {
|
||||||
s->redraw_fifo_first = s->redraw_fifo_last;
|
s->redraw_fifo_last = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* Overlapping region updates can be optimised out here - if someone
|
/* Overlapping region updates can be optimised out here - if someone
|
||||||
* knows a smart algorithm to do that, please share. */
|
* knows a smart algorithm to do that, please share. */
|
||||||
while (s->redraw_fifo_first != s->redraw_fifo_last) {
|
for (int i = 0; i < s->redraw_fifo_last; i++) {
|
||||||
rect = &s->redraw_fifo[s->redraw_fifo_first++];
|
rect = &s->redraw_fifo[i];
|
||||||
s->redraw_fifo_first &= REDRAW_FIFO_LEN - 1;
|
|
||||||
vmsvga_update_rect(s, rect->x, rect->y, rect->w, rect->h);
|
vmsvga_update_rect(s, rect->x, rect->y, rect->w, rect->h);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s->redraw_fifo_last = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void vmsvga_update_rect_delayed(struct vmsvga_state_s *s,
|
||||||
|
int x, int y, int w, int h)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (s->redraw_fifo_last >= REDRAW_FIFO_LEN) {
|
||||||
|
trace_vmware_update_rect_delayed_flush();
|
||||||
|
vmsvga_update_rect_flush(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct vmsvga_rect_s *rect = &s->redraw_fifo[s->redraw_fifo_last++];
|
||||||
|
|
||||||
|
rect->x = x;
|
||||||
|
rect->y = y;
|
||||||
|
rect->w = w;
|
||||||
|
rect->h = h;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HW_RECT_ACCEL
|
#ifdef HW_RECT_ACCEL
|
||||||
|
@ -1161,7 +1167,6 @@ static void vmsvga_reset(DeviceState *dev)
|
||||||
s->config = 0;
|
s->config = 0;
|
||||||
s->svgaid = SVGA_ID;
|
s->svgaid = SVGA_ID;
|
||||||
s->cursor.on = 0;
|
s->cursor.on = 0;
|
||||||
s->redraw_fifo_first = 0;
|
|
||||||
s->redraw_fifo_last = 0;
|
s->redraw_fifo_last = 0;
|
||||||
s->syncing = 0;
|
s->syncing = 0;
|
||||||
|
|
||||||
|
|
|
@ -147,7 +147,6 @@ static void pc_system_flash_map(PCMachineState *pcms,
|
||||||
MemoryRegion *flash_mem;
|
MemoryRegion *flash_mem;
|
||||||
void *flash_ptr;
|
void *flash_ptr;
|
||||||
int flash_size;
|
int flash_size;
|
||||||
int ret;
|
|
||||||
|
|
||||||
assert(PC_MACHINE_GET_CLASS(pcms)->pci_enabled);
|
assert(PC_MACHINE_GET_CLASS(pcms)->pci_enabled);
|
||||||
|
|
||||||
|
@ -195,19 +194,7 @@ static void pc_system_flash_map(PCMachineState *pcms,
|
||||||
if (sev_enabled()) {
|
if (sev_enabled()) {
|
||||||
flash_ptr = memory_region_get_ram_ptr(flash_mem);
|
flash_ptr = memory_region_get_ram_ptr(flash_mem);
|
||||||
flash_size = memory_region_size(flash_mem);
|
flash_size = memory_region_size(flash_mem);
|
||||||
/*
|
x86_firmware_configure(flash_ptr, flash_size);
|
||||||
* OVMF places a GUIDed structures in the flash, so
|
|
||||||
* search for them
|
|
||||||
*/
|
|
||||||
pc_system_parse_ovmf_flash(flash_ptr, flash_size);
|
|
||||||
|
|
||||||
ret = sev_es_save_reset_vector(flash_ptr, flash_size);
|
|
||||||
if (ret) {
|
|
||||||
error_report("failed to locate and/or save reset vector");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
sev_encrypt_flash(flash_ptr, flash_size, &error_fatal);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -259,3 +246,24 @@ void pc_system_firmware_init(PCMachineState *pcms,
|
||||||
|
|
||||||
pc_system_flash_cleanup_unused(pcms);
|
pc_system_flash_cleanup_unused(pcms);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void x86_firmware_configure(void *ptr, int size)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* OVMF places a GUIDed structures in the flash, so
|
||||||
|
* search for them
|
||||||
|
*/
|
||||||
|
pc_system_parse_ovmf_flash(ptr, size);
|
||||||
|
|
||||||
|
if (sev_enabled()) {
|
||||||
|
ret = sev_es_save_reset_vector(ptr, size);
|
||||||
|
if (ret) {
|
||||||
|
error_report("failed to locate and/or save reset vector");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
sev_encrypt_flash(ptr, size, &error_fatal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1115,14 +1115,25 @@ void x86_bios_rom_init(MachineState *ms, const char *default_firmware,
|
||||||
}
|
}
|
||||||
bios = g_malloc(sizeof(*bios));
|
bios = g_malloc(sizeof(*bios));
|
||||||
memory_region_init_ram(bios, NULL, "pc.bios", bios_size, &error_fatal);
|
memory_region_init_ram(bios, NULL, "pc.bios", bios_size, &error_fatal);
|
||||||
if (!isapc_ram_fw) {
|
if (sev_enabled()) {
|
||||||
memory_region_set_readonly(bios, true);
|
/*
|
||||||
}
|
* The concept of a "reset" simply doesn't exist for
|
||||||
ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size), -1);
|
* confidential computing guests, we have to destroy and
|
||||||
if (ret != 0) {
|
* re-launch them instead. So there is no need to register
|
||||||
bios_error:
|
* the firmware as rom to properly re-initialize on reset.
|
||||||
fprintf(stderr, "qemu: could not load PC BIOS '%s'\n", bios_name);
|
* Just go for a straight file load instead.
|
||||||
exit(1);
|
*/
|
||||||
|
void *ptr = memory_region_get_ram_ptr(bios);
|
||||||
|
load_image_size(filename, ptr, bios_size);
|
||||||
|
x86_firmware_configure(ptr, bios_size);
|
||||||
|
} else {
|
||||||
|
if (!isapc_ram_fw) {
|
||||||
|
memory_region_set_readonly(bios, true);
|
||||||
|
}
|
||||||
|
ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size), -1);
|
||||||
|
if (ret != 0) {
|
||||||
|
goto bios_error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
g_free(filename);
|
g_free(filename);
|
||||||
|
|
||||||
|
@ -1143,6 +1154,11 @@ void x86_bios_rom_init(MachineState *ms, const char *default_firmware,
|
||||||
memory_region_add_subregion(rom_memory,
|
memory_region_add_subregion(rom_memory,
|
||||||
(uint32_t)(-bios_size),
|
(uint32_t)(-bios_size),
|
||||||
bios);
|
bios);
|
||||||
|
return;
|
||||||
|
|
||||||
|
bios_error:
|
||||||
|
fprintf(stderr, "qemu: could not load PC BIOS '%s'\n", bios_name);
|
||||||
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool x86_machine_is_smm_enabled(const X86MachineState *x86ms)
|
bool x86_machine_is_smm_enabled(const X86MachineState *x86ms)
|
||||||
|
|
|
@ -140,4 +140,7 @@ void gsi_handler(void *opaque, int n, int level);
|
||||||
void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name);
|
void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name);
|
||||||
DeviceState *ioapic_init_secondary(GSIState *gsi_state);
|
DeviceState *ioapic_init_secondary(GSIState *gsi_state);
|
||||||
|
|
||||||
|
/* pc_sysfw.c */
|
||||||
|
void x86_firmware_configure(void *ptr, int size);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -518,6 +518,7 @@ int vnc_display_pw_expire(const char *id, time_t expires);
|
||||||
void vnc_parse(const char *str);
|
void vnc_parse(const char *str);
|
||||||
int vnc_init_func(void *opaque, QemuOpts *opts, Error **errp);
|
int vnc_init_func(void *opaque, QemuOpts *opts, Error **errp);
|
||||||
bool vnc_display_reload_certs(const char *id, Error **errp);
|
bool vnc_display_reload_certs(const char *id, Error **errp);
|
||||||
|
bool vnc_display_update(DisplayUpdateOptionsVNC *arg, Error **errp);
|
||||||
|
|
||||||
/* input.c */
|
/* input.c */
|
||||||
int index_from_key(const char *key, size_t key_length);
|
int index_from_key(const char *key, size_t key_length);
|
||||||
|
|
12
meson.build
12
meson.build
|
@ -1115,14 +1115,16 @@ if gtkx11.found()
|
||||||
x11 = dependency('x11', method: 'pkg-config', required: gtkx11.found(),
|
x11 = dependency('x11', method: 'pkg-config', required: gtkx11.found(),
|
||||||
kwargs: static_kwargs)
|
kwargs: static_kwargs)
|
||||||
endif
|
endif
|
||||||
vnc = not_found
|
|
||||||
png = not_found
|
png = not_found
|
||||||
|
if get_option('png').allowed() and have_system
|
||||||
|
png = dependency('libpng', required: get_option('png'),
|
||||||
|
method: 'pkg-config', kwargs: static_kwargs)
|
||||||
|
endif
|
||||||
|
vnc = not_found
|
||||||
jpeg = not_found
|
jpeg = not_found
|
||||||
sasl = not_found
|
sasl = not_found
|
||||||
if get_option('vnc').allowed() and have_system
|
if get_option('vnc').allowed() and have_system
|
||||||
vnc = declare_dependency() # dummy dependency
|
vnc = declare_dependency() # dummy dependency
|
||||||
png = dependency('libpng', required: get_option('vnc_png'),
|
|
||||||
method: 'pkg-config', kwargs: static_kwargs)
|
|
||||||
jpeg = dependency('libjpeg', required: get_option('vnc_jpeg'),
|
jpeg = dependency('libjpeg', required: get_option('vnc_jpeg'),
|
||||||
method: 'pkg-config', kwargs: static_kwargs)
|
method: 'pkg-config', kwargs: static_kwargs)
|
||||||
sasl = cc.find_library('sasl2', has_headers: ['sasl/sasl.h'],
|
sasl = cc.find_library('sasl2', has_headers: ['sasl/sasl.h'],
|
||||||
|
@ -1554,9 +1556,9 @@ config_host_data.set('CONFIG_TPM', have_tpm)
|
||||||
config_host_data.set('CONFIG_USB_LIBUSB', libusb.found())
|
config_host_data.set('CONFIG_USB_LIBUSB', libusb.found())
|
||||||
config_host_data.set('CONFIG_VDE', vde.found())
|
config_host_data.set('CONFIG_VDE', vde.found())
|
||||||
config_host_data.set('CONFIG_VHOST_USER_BLK_SERVER', have_vhost_user_blk_server)
|
config_host_data.set('CONFIG_VHOST_USER_BLK_SERVER', have_vhost_user_blk_server)
|
||||||
|
config_host_data.set('CONFIG_PNG', png.found())
|
||||||
config_host_data.set('CONFIG_VNC', vnc.found())
|
config_host_data.set('CONFIG_VNC', vnc.found())
|
||||||
config_host_data.set('CONFIG_VNC_JPEG', jpeg.found())
|
config_host_data.set('CONFIG_VNC_JPEG', jpeg.found())
|
||||||
config_host_data.set('CONFIG_VNC_PNG', png.found())
|
|
||||||
config_host_data.set('CONFIG_VNC_SASL', sasl.found())
|
config_host_data.set('CONFIG_VNC_SASL', sasl.found())
|
||||||
config_host_data.set('CONFIG_VIRTFS', have_virtfs)
|
config_host_data.set('CONFIG_VIRTFS', have_virtfs)
|
||||||
config_host_data.set('CONFIG_VTE', vte.found())
|
config_host_data.set('CONFIG_VTE', vte.found())
|
||||||
|
@ -3667,11 +3669,11 @@ summary_info += {'curses support': curses}
|
||||||
summary_info += {'virgl support': virgl}
|
summary_info += {'virgl support': virgl}
|
||||||
summary_info += {'curl support': curl}
|
summary_info += {'curl support': curl}
|
||||||
summary_info += {'Multipath support': mpathpersist}
|
summary_info += {'Multipath support': mpathpersist}
|
||||||
|
summary_info += {'PNG support': png}
|
||||||
summary_info += {'VNC support': vnc}
|
summary_info += {'VNC support': vnc}
|
||||||
if vnc.found()
|
if vnc.found()
|
||||||
summary_info += {'VNC SASL support': sasl}
|
summary_info += {'VNC SASL support': sasl}
|
||||||
summary_info += {'VNC JPEG support': jpeg}
|
summary_info += {'VNC JPEG support': jpeg}
|
||||||
summary_info += {'VNC PNG support': png}
|
|
||||||
endif
|
endif
|
||||||
if targetos not in ['darwin', 'haiku', 'windows']
|
if targetos not in ['darwin', 'haiku', 'windows']
|
||||||
summary_info += {'OSS support': oss}
|
summary_info += {'OSS support': oss}
|
||||||
|
|
|
@ -177,12 +177,12 @@ option('vde', type : 'feature', value : 'auto',
|
||||||
description: 'vde network backend support')
|
description: 'vde network backend support')
|
||||||
option('virglrenderer', type : 'feature', value : 'auto',
|
option('virglrenderer', type : 'feature', value : 'auto',
|
||||||
description: 'virgl rendering support')
|
description: 'virgl rendering support')
|
||||||
|
option('png', type : 'feature', value : 'auto',
|
||||||
|
description: 'PNG support with libpng')
|
||||||
option('vnc', type : 'feature', value : 'auto',
|
option('vnc', type : 'feature', value : 'auto',
|
||||||
description: 'VNC server')
|
description: 'VNC server')
|
||||||
option('vnc_jpeg', type : 'feature', value : 'auto',
|
option('vnc_jpeg', type : 'feature', value : 'auto',
|
||||||
description: 'JPEG lossy compression for VNC server')
|
description: 'JPEG lossy compression for VNC server')
|
||||||
option('vnc_png', type : 'feature', value : 'auto',
|
|
||||||
description: 'PNG compression for VNC server')
|
|
||||||
option('vnc_sasl', type : 'feature', value : 'auto',
|
option('vnc_sasl', type : 'feature', value : 'auto',
|
||||||
description: 'SASL authentication for VNC server')
|
description: 'SASL authentication for VNC server')
|
||||||
option('vte', type : 'feature', value : 'auto',
|
option('vte', type : 'feature', value : 'auto',
|
||||||
|
|
|
@ -1722,9 +1722,19 @@ hmp_screendump(Monitor *mon, const QDict *qdict)
|
||||||
const char *filename = qdict_get_str(qdict, "filename");
|
const char *filename = qdict_get_str(qdict, "filename");
|
||||||
const char *id = qdict_get_try_str(qdict, "device");
|
const char *id = qdict_get_try_str(qdict, "device");
|
||||||
int64_t head = qdict_get_try_int(qdict, "head", 0);
|
int64_t head = qdict_get_try_int(qdict, "head", 0);
|
||||||
|
const char *input_format = qdict_get_try_str(qdict, "format");
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
|
ImageFormat format;
|
||||||
|
|
||||||
qmp_screendump(filename, id != NULL, id, id != NULL, head, &err);
|
format = qapi_enum_parse(&ImageFormat_lookup, input_format,
|
||||||
|
IMAGE_FORMAT_PPM, &err);
|
||||||
|
if (err) {
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
qmp_screendump(filename, id != NULL, id, id != NULL, head,
|
||||||
|
input_format != NULL, format, &err);
|
||||||
|
end:
|
||||||
hmp_handle_error(mon, err);
|
hmp_handle_error(mon, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -346,6 +346,21 @@ void qmp_display_reload(DisplayReloadOptions *arg, Error **errp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void qmp_display_update(DisplayUpdateOptions *arg, Error **errp)
|
||||||
|
{
|
||||||
|
switch (arg->type) {
|
||||||
|
case DISPLAY_UPDATE_TYPE_VNC:
|
||||||
|
#ifdef CONFIG_VNC
|
||||||
|
vnc_display_update(&arg->u.vnc, errp);
|
||||||
|
#else
|
||||||
|
error_setg(errp, "vnc is invalid, missing 'CONFIG_VNC'");
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int qmp_x_query_rdma_foreach(Object *obj, void *opaque)
|
static int qmp_x_query_rdma_foreach(Object *obj, void *opaque)
|
||||||
{
|
{
|
||||||
RdmaProvider *rdma;
|
RdmaProvider *rdma;
|
||||||
|
|
89
qapi/ui.json
89
qapi/ui.json
|
@ -157,12 +157,27 @@
|
||||||
##
|
##
|
||||||
{ 'command': 'expire_password', 'boxed': true, 'data': 'ExpirePasswordOptions' }
|
{ 'command': 'expire_password', 'boxed': true, 'data': 'ExpirePasswordOptions' }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @ImageFormat:
|
||||||
|
#
|
||||||
|
# Supported image format types.
|
||||||
|
#
|
||||||
|
# @png: PNG format
|
||||||
|
#
|
||||||
|
# @ppm: PPM format
|
||||||
|
#
|
||||||
|
# Since: 7.1
|
||||||
|
#
|
||||||
|
##
|
||||||
|
{ 'enum': 'ImageFormat',
|
||||||
|
'data': ['ppm', 'png'] }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @screendump:
|
# @screendump:
|
||||||
#
|
#
|
||||||
# Write a PPM of the VGA screen to a file.
|
# Capture the contents of a screen and write it to a file.
|
||||||
#
|
#
|
||||||
# @filename: the path of a new PPM file to store the image
|
# @filename: the path of a new file to store the image
|
||||||
#
|
#
|
||||||
# @device: ID of the display device that should be dumped. If this parameter
|
# @device: ID of the display device that should be dumped. If this parameter
|
||||||
# is missing, the primary display will be used. (Since 2.12)
|
# is missing, the primary display will be used. (Since 2.12)
|
||||||
|
@ -171,6 +186,8 @@
|
||||||
# parameter is missing, head #0 will be used. Also note that the head
|
# parameter is missing, head #0 will be used. Also note that the head
|
||||||
# can only be specified in conjunction with the device ID. (Since 2.12)
|
# can only be specified in conjunction with the device ID. (Since 2.12)
|
||||||
#
|
#
|
||||||
|
# @format: image format for screendump. (default: ppm) (Since 7.1)
|
||||||
|
#
|
||||||
# Returns: Nothing on success
|
# Returns: Nothing on success
|
||||||
#
|
#
|
||||||
# Since: 0.14
|
# Since: 0.14
|
||||||
|
@ -183,7 +200,8 @@
|
||||||
#
|
#
|
||||||
##
|
##
|
||||||
{ 'command': 'screendump',
|
{ 'command': 'screendump',
|
||||||
'data': {'filename': 'str', '*device': 'str', '*head': 'int'},
|
'data': {'filename': 'str', '*device': 'str', '*head': 'int',
|
||||||
|
'*format': 'ImageFormat'},
|
||||||
'coroutine': true }
|
'coroutine': true }
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -1450,3 +1468,68 @@
|
||||||
{ 'command': 'display-reload',
|
{ 'command': 'display-reload',
|
||||||
'data': 'DisplayReloadOptions',
|
'data': 'DisplayReloadOptions',
|
||||||
'boxed' : true }
|
'boxed' : true }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @DisplayUpdateType:
|
||||||
|
#
|
||||||
|
# Available DisplayUpdate types.
|
||||||
|
#
|
||||||
|
# @vnc: VNC display
|
||||||
|
#
|
||||||
|
# Since: 7.1
|
||||||
|
#
|
||||||
|
##
|
||||||
|
{ 'enum': 'DisplayUpdateType',
|
||||||
|
'data': ['vnc'] }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @DisplayUpdateOptionsVNC:
|
||||||
|
#
|
||||||
|
# Specify the VNC reload options.
|
||||||
|
#
|
||||||
|
# @addresses: If specified, change set of addresses
|
||||||
|
# to listen for connections. Addresses configured
|
||||||
|
# for websockets are not touched.
|
||||||
|
#
|
||||||
|
# Since: 7.1
|
||||||
|
#
|
||||||
|
##
|
||||||
|
{ 'struct': 'DisplayUpdateOptionsVNC',
|
||||||
|
'data': { '*addresses': ['SocketAddress'] } }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @DisplayUpdateOptions:
|
||||||
|
#
|
||||||
|
# Options of the display configuration reload.
|
||||||
|
#
|
||||||
|
# @type: Specify the display type.
|
||||||
|
#
|
||||||
|
# Since: 7.1
|
||||||
|
#
|
||||||
|
##
|
||||||
|
{ 'union': 'DisplayUpdateOptions',
|
||||||
|
'base': {'type': 'DisplayUpdateType'},
|
||||||
|
'discriminator': 'type',
|
||||||
|
'data': { 'vnc': 'DisplayUpdateOptionsVNC' } }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @display-update:
|
||||||
|
#
|
||||||
|
# Update display configuration.
|
||||||
|
#
|
||||||
|
# Returns: Nothing on success.
|
||||||
|
#
|
||||||
|
# Since: 7.1
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
#
|
||||||
|
# -> { "execute": "display-update",
|
||||||
|
# "arguments": { "type": "vnc", "addresses":
|
||||||
|
# [ { "type": "inet", "host": "0.0.0.0",
|
||||||
|
# "port": "5901" } ] } }
|
||||||
|
# <- { "return": {} }
|
||||||
|
#
|
||||||
|
##
|
||||||
|
{ 'command': 'display-update',
|
||||||
|
'data': 'DisplayUpdateOptions',
|
||||||
|
'boxed' : true }
|
||||||
|
|
|
@ -142,7 +142,7 @@
|
||||||
--disable-virtiofsd \
|
--disable-virtiofsd \
|
||||||
--disable-vnc \
|
--disable-vnc \
|
||||||
--disable-vnc-jpeg \
|
--disable-vnc-jpeg \
|
||||||
--disable-vnc-png \
|
--disable-png \
|
||||||
--disable-vnc-sasl \
|
--disable-vnc-sasl \
|
||||||
--disable-vte \
|
--disable-vte \
|
||||||
--disable-vvfat \
|
--disable-vvfat \
|
||||||
|
@ -200,7 +200,7 @@
|
||||||
--enable-vhost-vdpa \
|
--enable-vhost-vdpa \
|
||||||
--enable-vhost-vsock \
|
--enable-vhost-vsock \
|
||||||
--enable-vnc \
|
--enable-vnc \
|
||||||
--enable-vnc-png \
|
--enable-png \
|
||||||
--enable-vnc-sasl \
|
--enable-vnc-sasl \
|
||||||
--enable-werror \
|
--enable-werror \
|
||||||
--enable-xkbcommon
|
--enable-xkbcommon
|
||||||
|
|
|
@ -394,7 +394,7 @@ echo "Configuring..."
|
||||||
--enable-opengl --enable-vte --enable-gnutls \
|
--enable-opengl --enable-vte --enable-gnutls \
|
||||||
--enable-nettle --enable-curses --enable-curl \
|
--enable-nettle --enable-curses --enable-curl \
|
||||||
--audio-drv-list=oss,alsa,sdl,pa --enable-virtfs \
|
--audio-drv-list=oss,alsa,sdl,pa --enable-virtfs \
|
||||||
--enable-vnc --enable-vnc-sasl --enable-vnc-jpeg --enable-vnc-png \
|
--enable-vnc --enable-vnc-sasl --enable-vnc-jpeg --enable-png \
|
||||||
--enable-xen --enable-brlapi \
|
--enable-xen --enable-brlapi \
|
||||||
--enable-linux-aio --enable-attr \
|
--enable-linux-aio --enable-attr \
|
||||||
--enable-cap-ng --enable-trace-backends=log --enable-spice --enable-rbd \
|
--enable-cap-ng --enable-trace-backends=log --enable-spice --enable-rbd \
|
||||||
|
|
|
@ -94,6 +94,7 @@ meson_options_help() {
|
||||||
printf "%s\n" ' oss OSS sound support'
|
printf "%s\n" ' oss OSS sound support'
|
||||||
printf "%s\n" ' pa PulseAudio sound support'
|
printf "%s\n" ' pa PulseAudio sound support'
|
||||||
printf "%s\n" ' parallels parallels image format support'
|
printf "%s\n" ' parallels parallels image format support'
|
||||||
|
printf "%s\n" ' png PNG support with libpng'
|
||||||
printf "%s\n" ' qcow1 qcow1 image format support'
|
printf "%s\n" ' qcow1 qcow1 image format support'
|
||||||
printf "%s\n" ' qed qed image format support'
|
printf "%s\n" ' qed qed image format support'
|
||||||
printf "%s\n" ' qga-vss build QGA VSS support (broken with MinGW)'
|
printf "%s\n" ' qga-vss build QGA VSS support (broken with MinGW)'
|
||||||
|
@ -123,7 +124,6 @@ meson_options_help() {
|
||||||
printf "%s\n" ' virtiofsd build virtiofs daemon (virtiofsd)'
|
printf "%s\n" ' virtiofsd build virtiofs daemon (virtiofsd)'
|
||||||
printf "%s\n" ' vnc VNC server'
|
printf "%s\n" ' vnc VNC server'
|
||||||
printf "%s\n" ' vnc-jpeg JPEG lossy compression for VNC server'
|
printf "%s\n" ' vnc-jpeg JPEG lossy compression for VNC server'
|
||||||
printf "%s\n" ' vnc-png PNG compression for VNC server'
|
|
||||||
printf "%s\n" ' vnc-sasl SASL authentication for VNC server'
|
printf "%s\n" ' vnc-sasl SASL authentication for VNC server'
|
||||||
printf "%s\n" ' vte vte support for the gtk UI'
|
printf "%s\n" ' vte vte support for the gtk UI'
|
||||||
printf "%s\n" ' vvfat vvfat image format support'
|
printf "%s\n" ' vvfat vvfat image format support'
|
||||||
|
@ -277,6 +277,8 @@ _meson_option_parse() {
|
||||||
--disable-pa) printf "%s" -Dpa=disabled ;;
|
--disable-pa) printf "%s" -Dpa=disabled ;;
|
||||||
--enable-parallels) printf "%s" -Dparallels=enabled ;;
|
--enable-parallels) printf "%s" -Dparallels=enabled ;;
|
||||||
--disable-parallels) printf "%s" -Dparallels=disabled ;;
|
--disable-parallels) printf "%s" -Dparallels=disabled ;;
|
||||||
|
--enable-png) printf "%s" -Dpng=enabled ;;
|
||||||
|
--disable-png) printf "%s" -Dpng=disabled ;;
|
||||||
--enable-profiler) printf "%s" -Dprofiler=true ;;
|
--enable-profiler) printf "%s" -Dprofiler=true ;;
|
||||||
--disable-profiler) printf "%s" -Dprofiler=false ;;
|
--disable-profiler) printf "%s" -Dprofiler=false ;;
|
||||||
--enable-qcow1) printf "%s" -Dqcow1=enabled ;;
|
--enable-qcow1) printf "%s" -Dqcow1=enabled ;;
|
||||||
|
@ -347,8 +349,6 @@ _meson_option_parse() {
|
||||||
--disable-vnc) printf "%s" -Dvnc=disabled ;;
|
--disable-vnc) printf "%s" -Dvnc=disabled ;;
|
||||||
--enable-vnc-jpeg) printf "%s" -Dvnc_jpeg=enabled ;;
|
--enable-vnc-jpeg) printf "%s" -Dvnc_jpeg=enabled ;;
|
||||||
--disable-vnc-jpeg) printf "%s" -Dvnc_jpeg=disabled ;;
|
--disable-vnc-jpeg) printf "%s" -Dvnc_jpeg=disabled ;;
|
||||||
--enable-vnc-png) printf "%s" -Dvnc_png=enabled ;;
|
|
||||||
--disable-vnc-png) printf "%s" -Dvnc_png=disabled ;;
|
|
||||||
--enable-vnc-sasl) printf "%s" -Dvnc_sasl=enabled ;;
|
--enable-vnc-sasl) printf "%s" -Dvnc_sasl=enabled ;;
|
||||||
--disable-vnc-sasl) printf "%s" -Dvnc_sasl=disabled ;;
|
--disable-vnc-sasl) printf "%s" -Dvnc_sasl=disabled ;;
|
||||||
--enable-vte) printf "%s" -Dvte=enabled ;;
|
--enable-vte) printf "%s" -Dvte=enabled ;;
|
||||||
|
|
|
@ -8,9 +8,48 @@
|
||||||
# This work is licensed under the terms of the GNU GPL, version 2 or
|
# This work is licensed under the terms of the GNU GPL, version 2 or
|
||||||
# later. See the COPYING file in the top-level directory.
|
# later. See the COPYING file in the top-level directory.
|
||||||
|
|
||||||
|
import socket
|
||||||
|
from typing import List
|
||||||
|
|
||||||
from avocado_qemu import QemuSystemTest
|
from avocado_qemu import QemuSystemTest
|
||||||
|
|
||||||
|
|
||||||
|
VNC_ADDR = '127.0.0.1'
|
||||||
|
VNC_PORT_START = 32768
|
||||||
|
VNC_PORT_END = VNC_PORT_START + 1024
|
||||||
|
|
||||||
|
|
||||||
|
def check_bind(port: int) -> bool:
|
||||||
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
||||||
|
try:
|
||||||
|
sock.bind((VNC_ADDR, port))
|
||||||
|
except OSError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def check_connect(port: int) -> bool:
|
||||||
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
||||||
|
try:
|
||||||
|
sock.connect((VNC_ADDR, port))
|
||||||
|
except ConnectionRefusedError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def find_free_ports(count: int) -> List[int]:
|
||||||
|
result = []
|
||||||
|
for port in range(VNC_PORT_START, VNC_PORT_END):
|
||||||
|
if check_bind(port):
|
||||||
|
result.append(port)
|
||||||
|
if len(result) >= count:
|
||||||
|
break
|
||||||
|
assert len(result) == count
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
class Vnc(QemuSystemTest):
|
class Vnc(QemuSystemTest):
|
||||||
"""
|
"""
|
||||||
:avocado: tags=vnc,quick
|
:avocado: tags=vnc,quick
|
||||||
|
@ -51,3 +90,27 @@ def test_change_password(self):
|
||||||
set_password_response = self.vm.qmp('change-vnc-password',
|
set_password_response = self.vm.qmp('change-vnc-password',
|
||||||
password='new_password')
|
password='new_password')
|
||||||
self.assertEqual(set_password_response['return'], {})
|
self.assertEqual(set_password_response['return'], {})
|
||||||
|
|
||||||
|
def test_change_listen(self):
|
||||||
|
a, b, c = find_free_ports(3)
|
||||||
|
self.assertFalse(check_connect(a))
|
||||||
|
self.assertFalse(check_connect(b))
|
||||||
|
self.assertFalse(check_connect(c))
|
||||||
|
|
||||||
|
self.vm.add_args('-nodefaults', '-S', '-vnc', f'{VNC_ADDR}:{a - 5900}')
|
||||||
|
self.vm.launch()
|
||||||
|
self.assertEqual(self.vm.qmp('query-vnc')['return']['service'], str(a))
|
||||||
|
self.assertTrue(check_connect(a))
|
||||||
|
self.assertFalse(check_connect(b))
|
||||||
|
self.assertFalse(check_connect(c))
|
||||||
|
|
||||||
|
res = self.vm.qmp('display-update', type='vnc',
|
||||||
|
addresses=[{'type': 'inet', 'host': VNC_ADDR,
|
||||||
|
'port': str(b)},
|
||||||
|
{'type': 'inet', 'host': VNC_ADDR,
|
||||||
|
'port': str(c)}])
|
||||||
|
self.assertEqual(res['return'], {})
|
||||||
|
self.assertEqual(self.vm.qmp('query-vnc')['return']['service'], str(b))
|
||||||
|
self.assertFalse(check_connect(a))
|
||||||
|
self.assertTrue(check_connect(b))
|
||||||
|
self.assertTrue(check_connect(c))
|
||||||
|
|
101
ui/console.c
101
ui/console.c
|
@ -37,6 +37,9 @@
|
||||||
#include "exec/memory.h"
|
#include "exec/memory.h"
|
||||||
#include "io/channel-file.h"
|
#include "io/channel-file.h"
|
||||||
#include "qom/object.h"
|
#include "qom/object.h"
|
||||||
|
#ifdef CONFIG_PNG
|
||||||
|
#include <png.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#define DEFAULT_BACKSCROLL 512
|
#define DEFAULT_BACKSCROLL 512
|
||||||
#define CONSOLE_CURSOR_PERIOD 500
|
#define CONSOLE_CURSOR_PERIOD 500
|
||||||
|
@ -291,6 +294,89 @@ void graphic_hw_invalidate(QemuConsole *con)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#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);
|
||||||
|
g_autofree png_struct *png_ptr = NULL;
|
||||||
|
g_autofree png_info *info_ptr = NULL;
|
||||||
|
g_autoptr(pixman_image_t) linebuf =
|
||||||
|
qemu_pixman_linebuf_create(PIXMAN_a8r8g8b8, 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_ALPHA, 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);
|
||||||
|
}
|
||||||
|
qemu_pixman_image_unref(linebuf);
|
||||||
|
|
||||||
|
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)
|
static bool ppm_save(int fd, pixman_image_t *image, Error **errp)
|
||||||
{
|
{
|
||||||
int width = pixman_image_get_width(image);
|
int width = pixman_image_get_width(image);
|
||||||
|
@ -329,7 +415,8 @@ static void graphic_hw_update_bh(void *con)
|
||||||
/* Safety: coroutine-only, concurrent-coroutine safe, main thread only */
|
/* Safety: coroutine-only, concurrent-coroutine safe, main thread only */
|
||||||
void coroutine_fn
|
void coroutine_fn
|
||||||
qmp_screendump(const char *filename, bool has_device, const char *device,
|
qmp_screendump(const char *filename, bool has_device, const char *device,
|
||||||
bool has_head, int64_t head, Error **errp)
|
bool has_head, int64_t head,
|
||||||
|
bool has_format, ImageFormat format, Error **errp)
|
||||||
{
|
{
|
||||||
g_autoptr(pixman_image_t) image = NULL;
|
g_autoptr(pixman_image_t) image = NULL;
|
||||||
QemuConsole *con;
|
QemuConsole *con;
|
||||||
|
@ -385,8 +472,16 @@ qmp_screendump(const char *filename, bool has_device, const char *device,
|
||||||
* yields and releases the BQL. It could produce corrupted dump, but
|
* yields and releases the BQL. It could produce corrupted dump, but
|
||||||
* it should be otherwise safe.
|
* it should be otherwise safe.
|
||||||
*/
|
*/
|
||||||
if (!ppm_save(fd, image, errp)) {
|
if (has_format && format == IMAGE_FORMAT_PNG) {
|
||||||
qemu_unlink(filename);
|
/* 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
INT32 definitions between jmorecfg.h (included by jpeglib.h) and
|
INT32 definitions between jmorecfg.h (included by jpeglib.h) and
|
||||||
Win32 basetsd.h (included by windows.h). */
|
Win32 basetsd.h (included by windows.h). */
|
||||||
|
|
||||||
#ifdef CONFIG_VNC_PNG
|
#ifdef CONFIG_PNG
|
||||||
/* The following define is needed by pngconf.h. Otherwise it won't compile,
|
/* The following define is needed by pngconf.h. Otherwise it won't compile,
|
||||||
because setjmp.h was already included by osdep.h. */
|
because setjmp.h was already included by osdep.h. */
|
||||||
#define PNG_SKIP_SETJMP_CHECK
|
#define PNG_SKIP_SETJMP_CHECK
|
||||||
|
@ -95,7 +95,7 @@ static const struct {
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_VNC_PNG
|
#ifdef CONFIG_PNG
|
||||||
static const struct {
|
static const struct {
|
||||||
int png_zlib_level, png_filters;
|
int png_zlib_level, png_filters;
|
||||||
} tight_png_conf[] = {
|
} tight_png_conf[] = {
|
||||||
|
@ -919,7 +919,7 @@ static int send_full_color_rect(VncState *vs, int x, int y, int w, int h)
|
||||||
int stream = 0;
|
int stream = 0;
|
||||||
ssize_t bytes;
|
ssize_t bytes;
|
||||||
|
|
||||||
#ifdef CONFIG_VNC_PNG
|
#ifdef CONFIG_PNG
|
||||||
if (tight_can_send_png_rect(vs, w, h)) {
|
if (tight_can_send_png_rect(vs, w, h)) {
|
||||||
return send_png_rect(vs, x, y, w, h, NULL);
|
return send_png_rect(vs, x, y, w, h, NULL);
|
||||||
}
|
}
|
||||||
|
@ -966,7 +966,7 @@ static int send_mono_rect(VncState *vs, int x, int y,
|
||||||
int stream = 1;
|
int stream = 1;
|
||||||
int level = tight_conf[vs->tight->compression].mono_zlib_level;
|
int level = tight_conf[vs->tight->compression].mono_zlib_level;
|
||||||
|
|
||||||
#ifdef CONFIG_VNC_PNG
|
#ifdef CONFIG_PNG
|
||||||
if (tight_can_send_png_rect(vs, w, h)) {
|
if (tight_can_send_png_rect(vs, w, h)) {
|
||||||
int ret;
|
int ret;
|
||||||
int bpp = vs->client_pf.bytes_per_pixel * 8;
|
int bpp = vs->client_pf.bytes_per_pixel * 8;
|
||||||
|
@ -1020,7 +1020,7 @@ static int send_mono_rect(VncState *vs, int x, int y,
|
||||||
struct palette_cb_priv {
|
struct palette_cb_priv {
|
||||||
VncState *vs;
|
VncState *vs;
|
||||||
uint8_t *header;
|
uint8_t *header;
|
||||||
#ifdef CONFIG_VNC_PNG
|
#ifdef CONFIG_PNG
|
||||||
png_colorp png_palette;
|
png_colorp png_palette;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
@ -1082,7 +1082,7 @@ static int send_palette_rect(VncState *vs, int x, int y,
|
||||||
int colors;
|
int colors;
|
||||||
ssize_t bytes;
|
ssize_t bytes;
|
||||||
|
|
||||||
#ifdef CONFIG_VNC_PNG
|
#ifdef CONFIG_PNG
|
||||||
if (tight_can_send_png_rect(vs, w, h)) {
|
if (tight_can_send_png_rect(vs, w, h)) {
|
||||||
return send_png_rect(vs, x, y, w, h, palette);
|
return send_png_rect(vs, x, y, w, h, palette);
|
||||||
}
|
}
|
||||||
|
@ -1233,7 +1233,7 @@ static int send_jpeg_rect(VncState *vs, int x, int y, int w, int h, int quality)
|
||||||
/*
|
/*
|
||||||
* PNG compression stuff.
|
* PNG compression stuff.
|
||||||
*/
|
*/
|
||||||
#ifdef CONFIG_VNC_PNG
|
#ifdef CONFIG_PNG
|
||||||
static void write_png_palette(int idx, uint32_t pix, void *opaque)
|
static void write_png_palette(int idx, uint32_t pix, void *opaque)
|
||||||
{
|
{
|
||||||
struct palette_cb_priv *priv = opaque;
|
struct palette_cb_priv *priv = opaque;
|
||||||
|
@ -1379,7 +1379,7 @@ static int send_png_rect(VncState *vs, int x, int y, int w, int h,
|
||||||
buffer_reset(&vs->tight->png);
|
buffer_reset(&vs->tight->png);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_VNC_PNG */
|
#endif /* CONFIG_PNG */
|
||||||
|
|
||||||
static void vnc_tight_start(VncState *vs)
|
static void vnc_tight_start(VncState *vs)
|
||||||
{
|
{
|
||||||
|
@ -1706,7 +1706,7 @@ void vnc_tight_clear(VncState *vs)
|
||||||
#ifdef CONFIG_VNC_JPEG
|
#ifdef CONFIG_VNC_JPEG
|
||||||
buffer_free(&vs->tight->jpeg);
|
buffer_free(&vs->tight->jpeg);
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_VNC_PNG
|
#ifdef CONFIG_PNG
|
||||||
buffer_free(&vs->tight->png);
|
buffer_free(&vs->tight->png);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
156
ui/vnc.c
156
ui/vnc.c
|
@ -2165,7 +2165,7 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
|
||||||
vs->features |= VNC_FEATURE_TIGHT_MASK;
|
vs->features |= VNC_FEATURE_TIGHT_MASK;
|
||||||
vs->vnc_encoding = enc;
|
vs->vnc_encoding = enc;
|
||||||
break;
|
break;
|
||||||
#ifdef CONFIG_VNC_PNG
|
#ifdef CONFIG_PNG
|
||||||
case VNC_ENCODING_TIGHT_PNG:
|
case VNC_ENCODING_TIGHT_PNG:
|
||||||
vs->features |= VNC_FEATURE_TIGHT_PNG_MASK;
|
vs->features |= VNC_FEATURE_TIGHT_PNG_MASK;
|
||||||
vs->vnc_encoding = enc;
|
vs->vnc_encoding = enc;
|
||||||
|
@ -3256,7 +3256,7 @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc,
|
||||||
#ifdef CONFIG_VNC_JPEG
|
#ifdef CONFIG_VNC_JPEG
|
||||||
buffer_init(&vs->tight->jpeg, "vnc-tight-jpeg/%p", sioc);
|
buffer_init(&vs->tight->jpeg, "vnc-tight-jpeg/%p", sioc);
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_VNC_PNG
|
#ifdef CONFIG_PNG
|
||||||
buffer_init(&vs->tight->png, "vnc-tight-png/%p", sioc);
|
buffer_init(&vs->tight->png, "vnc-tight-png/%p", sioc);
|
||||||
#endif
|
#endif
|
||||||
buffer_init(&vs->zlib.zlib, "vnc-zlib/%p", sioc);
|
buffer_init(&vs->zlib.zlib, "vnc-zlib/%p", sioc);
|
||||||
|
@ -3820,30 +3820,19 @@ static int vnc_display_get_address(const char *addrstr,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vnc_free_addresses(SocketAddress ***retsaddr,
|
|
||||||
size_t *retnsaddr)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
for (i = 0; i < *retnsaddr; i++) {
|
|
||||||
qapi_free_SocketAddress((*retsaddr)[i]);
|
|
||||||
}
|
|
||||||
g_free(*retsaddr);
|
|
||||||
|
|
||||||
*retsaddr = NULL;
|
|
||||||
*retnsaddr = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int vnc_display_get_addresses(QemuOpts *opts,
|
static int vnc_display_get_addresses(QemuOpts *opts,
|
||||||
bool reverse,
|
bool reverse,
|
||||||
SocketAddress ***retsaddr,
|
SocketAddressList **saddr_list_ret,
|
||||||
size_t *retnsaddr,
|
SocketAddressList **wsaddr_list_ret,
|
||||||
SocketAddress ***retwsaddr,
|
|
||||||
size_t *retnwsaddr,
|
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
SocketAddress *saddr = NULL;
|
SocketAddress *saddr = NULL;
|
||||||
SocketAddress *wsaddr = NULL;
|
SocketAddress *wsaddr = NULL;
|
||||||
|
g_autoptr(SocketAddressList) saddr_list = NULL;
|
||||||
|
SocketAddressList **saddr_tail = &saddr_list;
|
||||||
|
SocketAddress *single_saddr = NULL;
|
||||||
|
g_autoptr(SocketAddressList) wsaddr_list = NULL;
|
||||||
|
SocketAddressList **wsaddr_tail = &wsaddr_list;
|
||||||
QemuOptsIter addriter;
|
QemuOptsIter addriter;
|
||||||
const char *addr;
|
const char *addr;
|
||||||
int to = qemu_opt_get_number(opts, "to", 0);
|
int to = qemu_opt_get_number(opts, "to", 0);
|
||||||
|
@ -3852,23 +3841,16 @@ static int vnc_display_get_addresses(QemuOpts *opts,
|
||||||
bool ipv4 = qemu_opt_get_bool(opts, "ipv4", false);
|
bool ipv4 = qemu_opt_get_bool(opts, "ipv4", false);
|
||||||
bool ipv6 = qemu_opt_get_bool(opts, "ipv6", false);
|
bool ipv6 = qemu_opt_get_bool(opts, "ipv6", false);
|
||||||
int displaynum = -1;
|
int displaynum = -1;
|
||||||
int ret = -1;
|
|
||||||
|
|
||||||
*retsaddr = NULL;
|
|
||||||
*retnsaddr = 0;
|
|
||||||
*retwsaddr = NULL;
|
|
||||||
*retnwsaddr = 0;
|
|
||||||
|
|
||||||
addr = qemu_opt_get(opts, "vnc");
|
addr = qemu_opt_get(opts, "vnc");
|
||||||
if (addr == NULL || g_str_equal(addr, "none")) {
|
if (addr == NULL || g_str_equal(addr, "none")) {
|
||||||
ret = 0;
|
return 0;
|
||||||
goto cleanup;
|
|
||||||
}
|
}
|
||||||
if (qemu_opt_get(opts, "websocket") &&
|
if (qemu_opt_get(opts, "websocket") &&
|
||||||
!qcrypto_hash_supports(QCRYPTO_HASH_ALG_SHA1)) {
|
!qcrypto_hash_supports(QCRYPTO_HASH_ALG_SHA1)) {
|
||||||
error_setg(errp,
|
error_setg(errp,
|
||||||
"SHA1 hash support is required for websockets");
|
"SHA1 hash support is required for websockets");
|
||||||
goto cleanup;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu_opt_iter_init(&addriter, opts, "vnc");
|
qemu_opt_iter_init(&addriter, opts, "vnc");
|
||||||
|
@ -3879,7 +3861,7 @@ static int vnc_display_get_addresses(QemuOpts *opts,
|
||||||
ipv4, ipv6,
|
ipv4, ipv6,
|
||||||
&saddr, errp);
|
&saddr, errp);
|
||||||
if (rv < 0) {
|
if (rv < 0) {
|
||||||
goto cleanup;
|
return -1;
|
||||||
}
|
}
|
||||||
/* Historical compat - first listen address can be used
|
/* Historical compat - first listen address can be used
|
||||||
* to set the default websocket port
|
* to set the default websocket port
|
||||||
|
@ -3887,13 +3869,16 @@ static int vnc_display_get_addresses(QemuOpts *opts,
|
||||||
if (displaynum == -1) {
|
if (displaynum == -1) {
|
||||||
displaynum = rv;
|
displaynum = rv;
|
||||||
}
|
}
|
||||||
*retsaddr = g_renew(SocketAddress *, *retsaddr, *retnsaddr + 1);
|
QAPI_LIST_APPEND(saddr_tail, saddr);
|
||||||
(*retsaddr)[(*retnsaddr)++] = saddr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we had multiple primary displays, we don't do defaults
|
if (saddr_list && !saddr_list->next) {
|
||||||
* for websocket, and require explicit config instead. */
|
single_saddr = saddr_list->value;
|
||||||
if (*retnsaddr > 1) {
|
} else {
|
||||||
|
/*
|
||||||
|
* If we had multiple primary displays, we don't do defaults
|
||||||
|
* for websocket, and require explicit config instead.
|
||||||
|
*/
|
||||||
displaynum = -1;
|
displaynum = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3903,57 +3888,50 @@ static int vnc_display_get_addresses(QemuOpts *opts,
|
||||||
has_ipv4, has_ipv6,
|
has_ipv4, has_ipv6,
|
||||||
ipv4, ipv6,
|
ipv4, ipv6,
|
||||||
&wsaddr, errp) < 0) {
|
&wsaddr, errp) < 0) {
|
||||||
goto cleanup;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Historical compat - if only a single listen address was
|
/* Historical compat - if only a single listen address was
|
||||||
* provided, then this is used to set the default listen
|
* provided, then this is used to set the default listen
|
||||||
* address for websocket too
|
* address for websocket too
|
||||||
*/
|
*/
|
||||||
if (*retnsaddr == 1 &&
|
if (single_saddr &&
|
||||||
(*retsaddr)[0]->type == SOCKET_ADDRESS_TYPE_INET &&
|
single_saddr->type == SOCKET_ADDRESS_TYPE_INET &&
|
||||||
wsaddr->type == SOCKET_ADDRESS_TYPE_INET &&
|
wsaddr->type == SOCKET_ADDRESS_TYPE_INET &&
|
||||||
g_str_equal(wsaddr->u.inet.host, "") &&
|
g_str_equal(wsaddr->u.inet.host, "") &&
|
||||||
!g_str_equal((*retsaddr)[0]->u.inet.host, "")) {
|
!g_str_equal(single_saddr->u.inet.host, "")) {
|
||||||
g_free(wsaddr->u.inet.host);
|
g_free(wsaddr->u.inet.host);
|
||||||
wsaddr->u.inet.host = g_strdup((*retsaddr)[0]->u.inet.host);
|
wsaddr->u.inet.host = g_strdup(single_saddr->u.inet.host);
|
||||||
}
|
}
|
||||||
|
|
||||||
*retwsaddr = g_renew(SocketAddress *, *retwsaddr, *retnwsaddr + 1);
|
QAPI_LIST_APPEND(wsaddr_tail, wsaddr);
|
||||||
(*retwsaddr)[(*retnwsaddr)++] = wsaddr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = 0;
|
*saddr_list_ret = g_steal_pointer(&saddr_list);
|
||||||
cleanup:
|
*wsaddr_list_ret = g_steal_pointer(&wsaddr_list);
|
||||||
if (ret < 0) {
|
return 0;
|
||||||
vnc_free_addresses(retsaddr, retnsaddr);
|
|
||||||
vnc_free_addresses(retwsaddr, retnwsaddr);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vnc_display_connect(VncDisplay *vd,
|
static int vnc_display_connect(VncDisplay *vd,
|
||||||
SocketAddress **saddr,
|
SocketAddressList *saddr_list,
|
||||||
size_t nsaddr,
|
SocketAddressList *wsaddr_list,
|
||||||
SocketAddress **wsaddr,
|
|
||||||
size_t nwsaddr,
|
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
/* connect to viewer */
|
/* connect to viewer */
|
||||||
QIOChannelSocket *sioc = NULL;
|
QIOChannelSocket *sioc = NULL;
|
||||||
if (nwsaddr != 0) {
|
if (wsaddr_list) {
|
||||||
error_setg(errp, "Cannot use websockets in reverse mode");
|
error_setg(errp, "Cannot use websockets in reverse mode");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (nsaddr != 1) {
|
if (!saddr_list || saddr_list->next) {
|
||||||
error_setg(errp, "Expected a single address in reverse mode");
|
error_setg(errp, "Expected a single address in reverse mode");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
/* TODO SOCKET_ADDRESS_TYPE_FD when fd has AF_UNIX */
|
/* TODO SOCKET_ADDRESS_TYPE_FD when fd has AF_UNIX */
|
||||||
vd->is_unix = saddr[0]->type == SOCKET_ADDRESS_TYPE_UNIX;
|
vd->is_unix = saddr_list->value->type == SOCKET_ADDRESS_TYPE_UNIX;
|
||||||
sioc = qio_channel_socket_new();
|
sioc = qio_channel_socket_new();
|
||||||
qio_channel_set_name(QIO_CHANNEL(sioc), "vnc-reverse");
|
qio_channel_set_name(QIO_CHANNEL(sioc), "vnc-reverse");
|
||||||
if (qio_channel_socket_connect_sync(sioc, saddr[0], errp) < 0) {
|
if (qio_channel_socket_connect_sync(sioc, saddr_list->value, errp) < 0) {
|
||||||
object_unref(OBJECT(sioc));
|
object_unref(OBJECT(sioc));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -3964,20 +3942,18 @@ static int vnc_display_connect(VncDisplay *vd,
|
||||||
|
|
||||||
|
|
||||||
static int vnc_display_listen(VncDisplay *vd,
|
static int vnc_display_listen(VncDisplay *vd,
|
||||||
SocketAddress **saddr,
|
SocketAddressList *saddr_list,
|
||||||
size_t nsaddr,
|
SocketAddressList *wsaddr_list,
|
||||||
SocketAddress **wsaddr,
|
|
||||||
size_t nwsaddr,
|
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
size_t i;
|
SocketAddressList *el;
|
||||||
|
|
||||||
if (nsaddr) {
|
if (saddr_list) {
|
||||||
vd->listener = qio_net_listener_new();
|
vd->listener = qio_net_listener_new();
|
||||||
qio_net_listener_set_name(vd->listener, "vnc-listen");
|
qio_net_listener_set_name(vd->listener, "vnc-listen");
|
||||||
for (i = 0; i < nsaddr; i++) {
|
for (el = saddr_list; el; el = el->next) {
|
||||||
if (qio_net_listener_open_sync(vd->listener,
|
if (qio_net_listener_open_sync(vd->listener,
|
||||||
saddr[i], 1,
|
el->value, 1,
|
||||||
errp) < 0) {
|
errp) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -3987,12 +3963,12 @@ static int vnc_display_listen(VncDisplay *vd,
|
||||||
vnc_listen_io, vd, NULL);
|
vnc_listen_io, vd, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nwsaddr) {
|
if (wsaddr_list) {
|
||||||
vd->wslistener = qio_net_listener_new();
|
vd->wslistener = qio_net_listener_new();
|
||||||
qio_net_listener_set_name(vd->wslistener, "vnc-ws-listen");
|
qio_net_listener_set_name(vd->wslistener, "vnc-ws-listen");
|
||||||
for (i = 0; i < nwsaddr; i++) {
|
for (el = wsaddr_list; el; el = el->next) {
|
||||||
if (qio_net_listener_open_sync(vd->wslistener,
|
if (qio_net_listener_open_sync(vd->wslistener,
|
||||||
wsaddr[i], 1,
|
el->value, 1,
|
||||||
errp) < 0) {
|
errp) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -4005,13 +3981,36 @@ static int vnc_display_listen(VncDisplay *vd,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool vnc_display_update(DisplayUpdateOptionsVNC *arg, Error **errp)
|
||||||
|
{
|
||||||
|
VncDisplay *vd = vnc_display_find(NULL);
|
||||||
|
|
||||||
|
if (!vd) {
|
||||||
|
error_setg(errp, "Can not find vnc display");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg->has_addresses) {
|
||||||
|
if (vd->listener) {
|
||||||
|
qio_net_listener_disconnect(vd->listener);
|
||||||
|
object_unref(OBJECT(vd->listener));
|
||||||
|
vd->listener = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vnc_display_listen(vd, arg->addresses, NULL, errp) < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void vnc_display_open(const char *id, Error **errp)
|
void vnc_display_open(const char *id, Error **errp)
|
||||||
{
|
{
|
||||||
VncDisplay *vd = vnc_display_find(id);
|
VncDisplay *vd = vnc_display_find(id);
|
||||||
QemuOpts *opts = qemu_opts_find(&qemu_vnc_opts, id);
|
QemuOpts *opts = qemu_opts_find(&qemu_vnc_opts, id);
|
||||||
SocketAddress **saddr = NULL, **wsaddr = NULL;
|
g_autoptr(SocketAddressList) saddr_list = NULL;
|
||||||
size_t nsaddr, nwsaddr;
|
g_autoptr(SocketAddressList) wsaddr_list = NULL;
|
||||||
const char *share, *device_id;
|
const char *share, *device_id;
|
||||||
QemuConsole *con;
|
QemuConsole *con;
|
||||||
bool password = false;
|
bool password = false;
|
||||||
|
@ -4036,8 +4035,8 @@ void vnc_display_open(const char *id, Error **errp)
|
||||||
}
|
}
|
||||||
|
|
||||||
reverse = qemu_opt_get_bool(opts, "reverse", false);
|
reverse = qemu_opt_get_bool(opts, "reverse", false);
|
||||||
if (vnc_display_get_addresses(opts, reverse, &saddr, &nsaddr,
|
if (vnc_display_get_addresses(opts, reverse, &saddr_list, &wsaddr_list,
|
||||||
&wsaddr, &nwsaddr, errp) < 0) {
|
errp) < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4212,16 +4211,16 @@ void vnc_display_open(const char *id, Error **errp)
|
||||||
}
|
}
|
||||||
qkbd_state_set_delay(vd->kbd, key_delay_ms);
|
qkbd_state_set_delay(vd->kbd, key_delay_ms);
|
||||||
|
|
||||||
if (saddr == NULL) {
|
if (saddr_list == NULL) {
|
||||||
goto cleanup;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reverse) {
|
if (reverse) {
|
||||||
if (vnc_display_connect(vd, saddr, nsaddr, wsaddr, nwsaddr, errp) < 0) {
|
if (vnc_display_connect(vd, saddr_list, wsaddr_list, errp) < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (vnc_display_listen(vd, saddr, nsaddr, wsaddr, nwsaddr, errp) < 0) {
|
if (vnc_display_listen(vd, saddr_list, wsaddr_list, errp) < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4230,14 +4229,11 @@ void vnc_display_open(const char *id, Error **errp)
|
||||||
vnc_display_print_local_addr(vd);
|
vnc_display_print_local_addr(vd);
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup:
|
/* Success */
|
||||||
vnc_free_addresses(&saddr, &nsaddr);
|
|
||||||
vnc_free_addresses(&wsaddr, &nwsaddr);
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
vnc_display_close(vd);
|
vnc_display_close(vd);
|
||||||
goto cleanup;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void vnc_display_add_client(const char *id, int csock, bool skipauth)
|
void vnc_display_add_client(const char *id, int csock, bool skipauth)
|
||||||
|
|
Loading…
Reference in New Issue