qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH v2] Added parameter to take screenshot with screendump as PNG


From: Daniel P . Berrangé
Subject: Re: [PATCH v2] Added parameter to take screenshot with screendump as PNG
Date: Thu, 24 Feb 2022 16:18:14 +0000
User-agent: Mutt/2.1.5 (2021-12-30)

On Thu, Feb 24, 2022 at 11:59:08AM +0000, Kshitij Suri wrote:
> Currently screendump only supports PPM format, which is un-compressed and not
> standard. Added a "format" parameter to qemu monitor screendump capabilites
> to support PNG image capture using libpng. The param was added in QAPI schema
> of screendump present in ui.json along with png_save() function which converts
> pixman_image to PNG. HMP command equivalent was also modified to support the
> feature.
> 
> Example usage:
> { "execute": "screendump", "arguments": { "filename": "/tmp/image", 
> "format":"png" } }
> 
> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/718
> 
> Signed-off-by: Kshitij Suri <kshitij.suri@nutanix.com>
> ---
> diff to v1:
>   - Removed repeated alpha conversion operation.
>   - Modified logic to mirror png conversion in vnc-enc-tight.c file.
>   - Added a new CONFIG_PNG parameter for libpng support.
>   - Changed input format to enum instead of string.
> 
>  hmp-commands.hx    | 11 +++---
>  meson.build        | 13 +++++--
>  meson_options.txt  |  2 +
>  monitor/hmp-cmds.c |  8 +++-
>  qapi/ui.json       | 24 ++++++++++--
>  ui/console.c       | 97 ++++++++++++++++++++++++++++++++++++++++++++--
>  6 files changed, 139 insertions(+), 16 deletions(-)
> 
> diff --git a/hmp-commands.hx b/hmp-commands.hx
> index 70a9136ac2..e43c9954b5 100644
> --- a/hmp-commands.hx
> +++ b/hmp-commands.hx
> @@ -244,17 +244,18 @@ ERST
>  
>      {
>          .name       = "screendump",
> -        .args_type  = "filename:F,device:s?,head:i?",
> -        .params     = "filename [device [head]]",
> -        .help       = "save screen from head 'head' of display device 
> 'device' "
> -                      "into PPM image 'filename'",
> +        .args_type  = "filename:F,device:s?,head:i?,format:f?",
> +        .params     = "filename [device [head]] [format]",
> +        .help       = "save screen from head 'head' of display device 
> 'device'"
> +                      "in specified format 'format' as image 'filename'."
> +                      "Currently only 'png' and 'ppm' formats are 
> supported.",
>          .cmd        = hmp_screendump,
>          .coroutine  = true,
>      },
>  
>  SRST
>  ``screendump`` *filename*
> -  Save screen into PPM image *filename*.
> +  Save screen as an image *filename*.
>  ERST
>  
>      {
> diff --git a/meson.build b/meson.build
> index 8df40bfac4..fd550c01ec 100644
> --- a/meson.build
> +++ b/meson.build
> @@ -1112,13 +1112,16 @@ if gtkx11.found()
>    x11 = dependency('x11', method: 'pkg-config', required: gtkx11.found(),
>                     kwargs: static_kwargs)
>  endif
> -vnc = not_found
>  png = not_found
> +png = dependency('libpng', required: get_option('png'),
> +                   method: 'pkg-config', kwargs: static_kwargs)
> +vnc = not_found
> +vnc_png = not_found
>  jpeg = not_found
>  sasl = not_found
>  if get_option('vnc').allowed() and have_system
>    vnc = declare_dependency() # dummy dependency
> -  png = dependency('libpng', required: get_option('vnc_png'),
> +  vnc_png = dependency('libpng', required: get_option('vnc_png'),
>                     method: 'pkg-config', kwargs: static_kwargs)
>    jpeg = dependency('libjpeg', required: get_option('vnc_jpeg'),
>                      method: 'pkg-config', kwargs: static_kwargs)
> @@ -1537,9 +1540,10 @@ config_host_data.set('CONFIG_TPM', have_tpm)
>  config_host_data.set('CONFIG_USB_LIBUSB', libusb.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_PNG', png.found())
>  config_host_data.set('CONFIG_VNC', vnc.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_PNG', vnc_png.found())


I think we should be removing  CONFIG_VNC_PNG entirely - the VNC
code should just use  CONFIG_PNG.

I'd suggest splitting ths into two patches.  The first patch
updates meson.build to look for libpng unconditionally and
rename to CONFIG_PNG.  The second patch introduces the PNG
support for screendump.

>  config_host_data.set('CONFIG_VNC_SASL', sasl.found())
>  config_host_data.set('CONFIG_VIRTFS', have_virtfs)
>  config_host_data.set('CONFIG_VTE', vte.found())
> @@ -3579,11 +3583,12 @@ summary_info += {'curses support':    curses}
>  summary_info += {'virgl support':     virgl}
>  summary_info += {'curl support':      curl}
>  summary_info += {'Multipath support': mpathpersist}
> +summary_info += {'PNG support':       png}
>  summary_info += {'VNC support':       vnc}
>  if vnc.found()
>    summary_info += {'VNC SASL support':  sasl}
>    summary_info += {'VNC JPEG support':  jpeg}
> -  summary_info += {'VNC PNG support':   png}
> +  summary_info += {'VNC PNG support':   vnc_png}
>  endif
>  if targetos not in ['darwin', 'haiku', 'windows']
>    summary_info += {'OSS support':     oss}
> diff --git a/meson_options.txt b/meson_options.txt
> index 52b11cead4..88148dec6c 100644
> --- a/meson_options.txt
> +++ b/meson_options.txt
> @@ -177,6 +177,8 @@ option('vde', type : 'feature', value : 'auto',
>         description: 'vde network backend support')
>  option('virglrenderer', type : 'feature', value : 'auto',
>         description: 'virgl rendering support')
> +option('png', type : 'feature', value : 'auto',
> +       description: 'PNG support with libpng')
>  option('vnc', type : 'feature', value : 'auto',
>         description: 'VNC server')
>  option('vnc_jpeg', type : 'feature', value : 'auto',


> diff --git a/qapi/ui.json b/qapi/ui.json
> index 9354f4c467..8caeb2d3bb 100644
> --- a/qapi/ui.json
> +++ b/qapi/ui.json
> @@ -73,12 +73,27 @@
>  ##
>  { 'command': 'expire_password', 'data': {'protocol': 'str', 'time': 'str'} }
>  
> +##
> +# @ImageFormat:
> +#
> +# Available list of supported types.
> +#
> +# @png: PNG format
> +#
> +# @ppm: PPM format
> +#
> +# Since: 6.3
> +#
> +##
> +{ 'enum': 'ImageFormat',
> +  'data': ['ppm', 'png'] }
> +
>  ##
>  # @screendump:
>  #
> -# Write a PPM of the VGA screen to a file.
> +# Write a screenshot of the VGA screen 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
>  #          is missing, the primary display will be used. (Since 2.12)
> @@ -87,6 +102,9 @@
>  #        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)
>  #
> +# @format: image format for screendump is specified. Currently only PNG and
> +#             PPM are supported.

Mention that PPM is the default if omitted.

> diff --git a/ui/console.c b/ui/console.c
> index 40eebb6d2c..911092c908 100644
> --- a/ui/console.c
> +++ b/ui/console.c
> @@ -37,6 +37,9 @@
>  #include "exec/memory.h"
>  #include "io/channel-file.h"
>  #include "qom/object.h"
> +#ifdef CONFIG_PNG
> +#include "png.h"

System headers should use  <...> for includes

> +#endif

> +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_structp png_ptr;
> +    png_infop info_ptr;
> +    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(errp, "Failed to create file from file descriptor");
> +        return false;
> +    }

This leaks 'f', as do following error paths.

> +
> +    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");
> +        return false;
> +    }
> +
> +    info_ptr = png_create_info_struct(png_ptr);
> +
> +    if (!info_ptr) {
> +        error_setg(errp, "PNG creation failed. Unable to write info");
> +        return false;

This leaks 'png_ptr' too

> +    }
> +
> +    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(errp, "PNG creation failed. Unable to close file");
> +        return false;
> +    }
> +
> +    return true;
> +}

Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|




reply via email to

[Prev in Thread] Current Thread [Next in Thread]