From: Marius Vlad Date: Thu, 18 Apr 2024 15:34:47 +0000 (+0300) Subject: clients/screenshot: Add support for weston output capture X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?p=src%2Fagl-compositor.git;a=commitdiff_plain;h=6366348eccec778f02061070d1334ea897d4f514 clients/screenshot: Add support for weston output capture libweston now provides a protocol which we can use for doing screenshots, not needing to provide one ourselves. This imports a simple client used in Weston but it has some changes to match our current args. Bug-AGL: SPEC-5095 Signed-off-by: Marius Vlad Change-Id: I00c9e976975447c74668fbfe1045b9177c623064 --- diff --git a/clients/meson.build b/clients/meson.build index 08b2c08..e636894 100644 --- a/clients/meson.build +++ b/clients/meson.build @@ -1,20 +1,19 @@ dep_wayland_client = dependency('wayland-client', version: '>= 1.17.0') +dep_pixman = dependency('pixman-1', version: '>= 0.25.2') clients = [ { 'basename': 'agl-screenshooter', 'sources': [ - 'screenshooter.c', + 'screenshot.c', '../shared/file-util.c', '../shared/os-compatibility.c', '../shared/xalloc.c', - agl_screenshooter_client_protocol_h, - agl_screenshooter_protocol_c, - xdg_output_unstable_v1_client_protocol_h, - xdg_output_unstable_v1_protocol_c, + weston_output_capture_protocol_c, + weston_output_capture_client_protocol_h, ], - 'deps_objs' : [ dep_wayland_client ], - 'deps': [ 'cairo' ], + 'deps_objs' : [ dep_wayland_client, dep_pixman ], + 'deps': [ 'cairo', ], }, ] diff --git a/clients/screenshooter.c b/clients/screenshooter.c deleted file mode 100644 index 0066ed5..0000000 --- a/clients/screenshooter.c +++ /dev/null @@ -1,620 +0,0 @@ -/* - * Copyright © 2020 Collabora, Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "shared/helpers.h" -#include "shared/xalloc.h" -#include "shared/file-util.h" -#include "shared/string-helpers.h" -#include "shared/os-compatibility.h" -#include "agl-screenshooter-client-protocol.h" -#include "xdg-output-unstable-v1-client-protocol.h" - -struct screenshooter_data; - -struct screenshooter_output { - struct wl_output *output; - struct wl_buffer *buffer; - - int width, height, offset_x, offset_y, scale; - void *data; - struct screenshooter_data *sh_data; - - struct wl_list link; /** screenshooter_data::output_list */ -}; - -struct xdg_output_v1_info { - struct zxdg_output_v1 *xdg_output; - struct screenshooter_output *output; - - char *name, *description; - struct wl_list link; /** screenshooter_data::xdg_output_list */ -}; - - -struct buffer_size { - int width, height; - - int min_x, min_y; - int max_x, max_y; -}; - -struct screenshooter_data { - struct wl_display *display; - struct wl_shm *shm; - struct wl_list output_list; /** screenshooter_output::link */ - struct wl_list xdg_output_list; /** xdg_output_v1_info::link */ - - struct zxdg_output_manager_v1 *xdg_output_manager; - struct agl_screenshooter *screenshooter; - int buffer_copy_done; -}; - -static int opts = 0x0; - -#define OPT_SCREENSHOT_OUTPUT 1 -#define OPT_SHOW_ALL_OUTPUTS 2 -#define OPT_SCREENSHOT_ALL_OUTPUTS 3 - -static void -display_handle_geometry(void *data, - struct wl_output *wl_output, - int x, - int y, - int physical_width, - int physical_height, - int subpixel, - const char *make, - const char *model, - int transform) -{ - struct screenshooter_output *output; - - output = wl_output_get_user_data(wl_output); - - if (wl_output == output->output) { - output->offset_x = x; - output->offset_y = y; - } -} - -static void -display_handle_mode(void *data, - struct wl_output *wl_output, - uint32_t flags, - int width, - int height, - int refresh) -{ - struct screenshooter_output *output; - - output = wl_output_get_user_data(wl_output); - - if (wl_output == output->output && (flags & WL_OUTPUT_MODE_CURRENT)) { - output->width = width; - output->height = height; - } -} - -static void -display_handle_done(void *data, struct wl_output *wl_output) -{ - -} - -static void -display_handle_scale(void *data, struct wl_output *wl_output, - int32_t scale) -{ - struct screenshooter_output *output = data; - output->scale = scale; -} - - -static const struct wl_output_listener output_listener = { - display_handle_geometry, - display_handle_mode, - display_handle_done, - display_handle_scale, - NULL, - NULL -}; - -static void -handle_xdg_output_v1_logical_position(void *data, struct zxdg_output_v1 *output, - int32_t x, int32_t y) -{ -} - -static void -handle_xdg_output_v1_logical_size(void *data, struct zxdg_output_v1 *output, - int32_t width, int32_t height) -{ -} - -static void -handle_xdg_output_v1_done(void *data, struct zxdg_output_v1 *output) -{ - /* Don't bother waiting for this; there's no good reason a - * compositor will wait more than one roundtrip before sending - * these initial events. */ -} - -static void -handle_xdg_output_v1_name(void *data, struct zxdg_output_v1 *output, - const char *name) -{ - struct xdg_output_v1_info *xdg_output = data; - xdg_output->name = strdup(name); -} - -static void -handle_xdg_output_v1_description(void *data, struct zxdg_output_v1 *output, - const char *description) -{ - struct xdg_output_v1_info *xdg_output = data; - xdg_output->description = strdup(description); -} - -static const struct zxdg_output_v1_listener xdg_output_v1_listener = { - .logical_position = handle_xdg_output_v1_logical_position, - .logical_size = handle_xdg_output_v1_logical_size, - .done = handle_xdg_output_v1_done, - .name = handle_xdg_output_v1_name, - .description = handle_xdg_output_v1_description, -}; - -static void -add_xdg_output_v1_info(struct screenshooter_data *shooter_data, - struct screenshooter_output *output) -{ - struct xdg_output_v1_info *xdg_output = zalloc(sizeof(*xdg_output)); - if (!xdg_output) - return; - - wl_list_insert(&shooter_data->xdg_output_list, &xdg_output->link); - - xdg_output->xdg_output = - zxdg_output_manager_v1_get_xdg_output(shooter_data->xdg_output_manager, - output->output); - - zxdg_output_v1_add_listener(xdg_output->xdg_output, - &xdg_output_v1_listener, xdg_output); - xdg_output->output = output; -} - -static void -screenshot_done(void *data, struct agl_screenshooter *screenshooter, uint32_t status) -{ - struct screenshooter_data *sh_data = data; - sh_data->buffer_copy_done = 1; -} - -static const struct agl_screenshooter_listener screenshooter_listener = { - screenshot_done -}; - -static void -handle_global(void *data, struct wl_registry *registry, - uint32_t name, const char *interface, uint32_t version) -{ - struct screenshooter_output *output; - struct screenshooter_data *sh_data = data; - - if (strcmp(interface, "wl_output") == 0) { - output = zalloc(sizeof(*output)); - if (!output) - return; - - output->output = wl_registry_bind(registry, name, - &wl_output_interface, 1); - output->sh_data = sh_data; - wl_list_insert(&sh_data->output_list, &output->link); - wl_output_add_listener(output->output, &output_listener, output); - } else if (strcmp(interface, "wl_shm") == 0) { - sh_data->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); - } else if (strcmp(interface, "agl_screenshooter") == 0) { - sh_data->screenshooter = wl_registry_bind(registry, name, - &agl_screenshooter_interface, 1); - - agl_screenshooter_add_listener(sh_data->screenshooter, - &screenshooter_listener, sh_data); - } else if (strcmp(interface, "zxdg_output_manager_v1") == 0) { - sh_data->xdg_output_manager = wl_registry_bind(registry, name, - &zxdg_output_manager_v1_interface, version); - } -} - -static void -handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) -{ - /* XXX: unimplemented */ -} - -static const struct wl_registry_listener registry_listener = { - handle_global, - handle_global_remove -}; - -static struct wl_buffer * -screenshot_create_shm_buffer(int width, int height, void **data_out, - struct wl_shm *shm) -{ - struct wl_shm_pool *pool; - struct wl_buffer *buffer; - int fd, size, stride; - void *data; - - stride = width * 4; - size = stride * height; - - fd = os_create_anonymous_file(size); - if (fd < 0) { - fprintf(stderr, "creating a buffer file for %d B failed: %s\n", - size, strerror(errno)); - return NULL; - } - - data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (data == MAP_FAILED) { - fprintf(stderr, "mmap failed: %s\n", strerror(errno)); - close(fd); - return NULL; - } - - pool = wl_shm_create_pool(shm, fd, size); - close(fd); - buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride, - WL_SHM_FORMAT_XRGB8888); - wl_shm_pool_destroy(pool); - - *data_out = data; - - return buffer; -} - -static void -screenshot_write_png_per_output(const struct buffer_size *buff_size, - struct screenshooter_output *sh_output, - const char *fn) -{ - int output_stride, buffer_stride, i; - cairo_surface_t *surface; - void *data, *d, *s; - FILE *fp; - char filepath[PATH_MAX]; - char *filename_to_write; - - buffer_stride = buff_size->width * 4; - data = xmalloc(buffer_stride * buff_size->height); - if (!data) - return; - - output_stride = sh_output->width * 4; - s = sh_output->data; - d = data + (sh_output->offset_y - buff_size->min_y) * buffer_stride + - (sh_output->offset_x - buff_size->min_x) * 4; - - for (i = 0; i < sh_output->height; i++) { - memcpy(d, s, output_stride); - d += buffer_stride; - s += output_stride; - } - - surface = cairo_image_surface_create_for_data(data, - CAIRO_FORMAT_ARGB32, - buff_size->width, - buff_size->height, - buffer_stride); - - if (fn) - str_printf(&filename_to_write, "agl-screenshot-%s-", fn); - else - str_printf(&filename_to_write, "agl-screenshot-"); - - fp = file_create_dated(getenv("XDG_PICTURES_DIR"), filename_to_write, - ".png", filepath, sizeof(filepath)); - if (fp) { - fclose(fp); - cairo_surface_write_to_png(surface, filepath); - } - - cairo_surface_destroy(surface); - free(filename_to_write); - free(data); -} - -static void -screenshot_set_buffer_size_per_output(struct buffer_size *buff_size, - struct screenshooter_output *output) -{ - buff_size->min_x = MIN(buff_size->min_x, output->offset_x); - buff_size->min_y = MIN(buff_size->min_y, output->offset_y); - buff_size->max_x = MAX(buff_size->max_x, output->offset_x + output->width); - buff_size->max_y = MAX(buff_size->max_y, output->offset_y + output->height); - - buff_size->width = buff_size->max_x - buff_size->min_x; - buff_size->height = buff_size->max_y - buff_size->min_y; -} - -static void -screenshot_compute_output_offset(int *pos, struct screenshooter_output *sh_output) -{ - sh_output->offset_x = *pos; - *pos += sh_output->width; -} - -static struct screenshooter_output * -agl_shooter_search_for_output(const char *output_name, - struct screenshooter_data *sh_data) -{ - struct screenshooter_output *found_output = NULL; - struct xdg_output_v1_info *output; - - if (!output_name) - return found_output; - - wl_list_for_each(output, &sh_data->xdg_output_list, link) { - if (output->name && strcmp(output->name, output_name) == 0) { - found_output = output->output; - break; - } - } - - return found_output; -} - -static char * -agl_shooter_search_get_output_name(struct screenshooter_output *sh_output) -{ - struct xdg_output_v1_info *output; - struct screenshooter_data *sh_data; - - if (!sh_output) - return NULL; - - sh_data = sh_output->sh_data; - - wl_list_for_each(output, &sh_data->xdg_output_list, link) { - if (output->output == sh_output) { - return output->name; - } - } - - return NULL; -} - -static void -agl_shooter_display_all_outputs(struct screenshooter_data *sh_data) -{ - struct xdg_output_v1_info *xdg_output; - wl_list_for_each(xdg_output, &sh_data->xdg_output_list, link) { - fprintf(stdout, "Output '%s', desc: '%s'\n", xdg_output->name, - xdg_output->description); - } -} - - -static void -agl_shooter_screenshot_all_outputs(struct screenshooter_data *sh_data) -{ - struct xdg_output_v1_info *xdg_output; - - wl_list_for_each(xdg_output, &sh_data->xdg_output_list, link) { - struct buffer_size buff_size = {}; - int pos = 0; - struct screenshooter_output *output = xdg_output->output; - - screenshot_compute_output_offset(&pos, output); - screenshot_set_buffer_size_per_output(&buff_size, output); - - output->buffer = - screenshot_create_shm_buffer(output->width, - output->height, - &output->data, - sh_data->shm); - - agl_screenshooter_take_shot(sh_data->screenshooter, - output->output, - output->buffer); - - sh_data->buffer_copy_done = 0; - while (!sh_data->buffer_copy_done) - wl_display_roundtrip(sh_data->display); - - screenshot_write_png_per_output(&buff_size, output, xdg_output->name); - } -} - -static void -agl_shooter_screenshot_output(struct screenshooter_output *sh_output) -{ - int pos = 0; - struct buffer_size buff_size = {}; - struct screenshooter_data *sh_data = sh_output->sh_data; - char *output_name; - - screenshot_compute_output_offset(&pos, sh_output); - screenshot_set_buffer_size_per_output(&buff_size, sh_output); - - sh_output->buffer = - screenshot_create_shm_buffer(sh_output->width, - sh_output->height, - &sh_output->data, sh_data->shm); - - agl_screenshooter_take_shot(sh_data->screenshooter, - sh_output->output, - sh_output->buffer); - - sh_data->buffer_copy_done = 0; - while (!sh_data->buffer_copy_done) - wl_display_roundtrip(sh_data->display); - - output_name = agl_shooter_search_get_output_name(sh_output); - assert(output_name); - screenshot_write_png_per_output(&buff_size, sh_output, output_name); -} - -static void -agl_shooter_destroy_xdg_output_manager(struct screenshooter_data *sh_data) -{ - struct xdg_output_v1_info *xdg_output; - - wl_list_for_each(xdg_output, &sh_data->xdg_output_list, link) { - free(xdg_output->name); - free(xdg_output->description); - zxdg_output_v1_destroy(xdg_output->xdg_output); - } - - zxdg_output_manager_v1_destroy(sh_data->xdg_output_manager); -} - -static void -print_usage_and_exit(void) -{ - fprintf(stderr, "./agl-screenshooter [-o OUTPUT_NAME] [-l] [-a]\n"); - - fprintf(stderr, "\t-o OUTPUT_NAME -- take a screenshot of the output " - "specified by OUTPUT_NAME\n"); - fprintf(stderr, "\t-a -- take a screenshot of all the outputs found\n"); - fprintf(stderr, "\t-l -- list all the outputs found\n"); - exit(EXIT_FAILURE); -} - -int main(int argc, char *argv[]) -{ - struct wl_display *display; - struct wl_registry *registry; - - struct screenshooter_data sh_data = {}; - struct screenshooter_output *sh_output = NULL; - int c, option_index; - - char *output_name = NULL; - - static struct option long_options[] = { - {"output", required_argument, 0, 'o' }, - {"list", required_argument, 0, 'l' }, - {"all", required_argument, 0, 'a' }, - {"help", no_argument , 0, 'h' }, - {0, 0, 0, 0} - }; - - while ((c = getopt_long(argc, argv, "o:lah", - long_options, &option_index)) != -1) { - switch (c) { - case 'o': - output_name = optarg; - opts |= (1 << OPT_SCREENSHOT_OUTPUT); - break; - case 'l': - opts |= (1 << OPT_SHOW_ALL_OUTPUTS); - break; - case 'a': - opts |= (1 << OPT_SCREENSHOT_ALL_OUTPUTS); - break; - default: - print_usage_and_exit(); - } - } - - display = wl_display_connect(NULL); - if (display == NULL) { - fprintf(stderr, "failed to create display: %s\n", - strerror(errno)); - return EXIT_FAILURE; - } - - wl_list_init(&sh_data.output_list); - wl_list_init(&sh_data.xdg_output_list); - sh_data.display = display; - - registry = wl_display_get_registry(display); - wl_registry_add_listener(registry, ®istry_listener, &sh_data); - - wl_display_dispatch(display); - wl_display_roundtrip(display); - - - if (sh_data.screenshooter == NULL) { - fprintf(stderr, "Compositor doesn't support screenshooter\n"); - return EXIT_FAILURE; - } - - wl_list_for_each(sh_output, &sh_data.output_list, link) - add_xdg_output_v1_info(&sh_data, sh_output); - - /* do another round-trip for xdg_output */ - wl_display_roundtrip(sh_data.display); - - if (opts & (1 << OPT_SHOW_ALL_OUTPUTS)) { - agl_shooter_display_all_outputs(&sh_data); - agl_shooter_destroy_xdg_output_manager(&sh_data); - return EXIT_SUCCESS; - } - - if (opts & (1 << OPT_SCREENSHOT_ALL_OUTPUTS)) { - agl_shooter_screenshot_all_outputs(&sh_data); - agl_shooter_destroy_xdg_output_manager(&sh_data); - return EXIT_SUCCESS; - } - - sh_output = NULL; - if (output_name) - sh_output = agl_shooter_search_for_output(output_name, &sh_data); - - if (!sh_output && (opts & (1 << OPT_SCREENSHOT_OUTPUT))) { - fprintf(stderr, "Could not find an output matching '%s'\n", - output_name); - agl_shooter_destroy_xdg_output_manager(&sh_data); - return EXIT_FAILURE; - } - - /* if we're still here just pick the first one available - * and use that. Still useful in case we are run without - * any args whatsoever */ - if (!sh_output) - sh_output = container_of(sh_data.output_list.next, - struct screenshooter_output, link); - - /* take a screenshot only of that specific output */ - agl_shooter_screenshot_output(sh_output); - agl_shooter_destroy_xdg_output_manager(&sh_data); - - return 0; -} diff --git a/clients/screenshot.c b/clients/screenshot.c new file mode 100644 index 0000000..214d065 --- /dev/null +++ b/clients/screenshot.c @@ -0,0 +1,673 @@ +/* + * Copyright 2024 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "weston-output-capture-client-protocol.h" +#include "shared/os-compatibility.h" +#include "shared/xalloc.h" +#include "shared/file-util.h" +#include "shared/pixel-formats.h" +#include "shared/helpers.h" +#include "shared/string-helpers.h" + +static int opts = 0x0; + +#define OPT_SCREENSHOT_OUTPUT 1 +#define OPT_SHOW_ALL_OUTPUTS 2 +#define OPT_SCREENSHOT_ALL_OUTPUTS 3 + +struct screenshooter_app { + struct wl_display *display; + struct wl_registry *registry; + struct wl_shm *shm; + struct weston_capture_v1 *capture_factory; + + struct wl_list output_list; /* struct screenshooter_output::link */ + + bool retry; + bool failed; + int waitcount; +}; + +struct screenshooter_buffer { + size_t len; + void *data; + struct wl_buffer *wl_buffer; + pixman_image_t *image; +}; + +struct screenshooter_output { + struct screenshooter_app *app; + struct wl_list link; /* struct screenshooter_app::output_list */ + + struct wl_output *wl_output; + int offset_x, offset_y; + char *name; + + struct weston_capture_source_v1 *source; + + int buffer_width; + int buffer_height; + const struct pixel_format_info *fmt; + struct screenshooter_buffer *buffer; +}; + +struct buffer_size { + int width, height; + + int min_x, min_y; + int max_x, max_y; +}; + +static struct screenshooter_buffer * +screenshot_create_shm_buffer(struct screenshooter_app *app, + size_t width, size_t height, + const struct pixel_format_info *fmt) +{ + struct screenshooter_buffer *buffer; + struct wl_shm_pool *pool; + int fd; + size_t bytes_pp; + size_t stride; + + assert(width > 0); + assert(height > 0); + assert(fmt && fmt->bpp > 0); + assert(fmt->pixman_format); + + buffer = xzalloc(sizeof *buffer); + + bytes_pp = fmt->bpp / 8; + stride = width * bytes_pp; + buffer->len = stride * height; + + assert(width == stride / bytes_pp); + assert(height == buffer->len / stride); + + fd = os_create_anonymous_file(buffer->len); + if (fd < 0) { + fprintf(stderr, "creating a buffer file for %zd B failed: %s\n", + buffer->len, strerror(errno)); + free(buffer); + return NULL; + } + + buffer->data = mmap(NULL, buffer->len, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, 0); + if (buffer->data == MAP_FAILED) { + fprintf(stderr, "mmap failed: %s\n", strerror(errno)); + close(fd); + free(buffer); + return NULL; + } + + pool = wl_shm_create_pool(app->shm, fd, buffer->len); + close(fd); + buffer->wl_buffer = + wl_shm_pool_create_buffer(pool, 0, width, height, stride, + pixel_format_get_shm_format(fmt)); + wl_shm_pool_destroy(pool); + + buffer->image = pixman_image_create_bits(fmt->pixman_format, + width, height, + buffer->data, stride); + if (!buffer->image) { + fprintf(stderr, "Failed to create buffer image!\n"); + close(fd); + free(buffer); + return NULL; + } + + return buffer; +} + +static void +screenshooter_buffer_destroy(struct screenshooter_buffer *buffer) +{ + if (!buffer) + return; + + pixman_image_unref(buffer->image); + munmap(buffer->data, buffer->len); + wl_buffer_destroy(buffer->wl_buffer); + free(buffer); +} + +static void +capture_source_handle_format(void *data, + struct weston_capture_source_v1 *proxy, + uint32_t drm_format) +{ + struct screenshooter_output *output = data; + + assert(output->source == proxy); + + output->fmt = pixel_format_get_info(drm_format); +} + +static void +capture_source_handle_size(void *data, + struct weston_capture_source_v1 *proxy, + int32_t width, int32_t height) +{ + struct screenshooter_output *output = data; + + assert(width > 0); + assert(height > 0); + + output->buffer_width = width; + output->buffer_height = height; +} + +static void +capture_source_handle_complete(void *data, + struct weston_capture_source_v1 *proxy) +{ + struct screenshooter_output *output = data; + + output->app->waitcount--; +} + +static void +capture_source_handle_retry(void *data, + struct weston_capture_source_v1 *proxy) +{ + struct screenshooter_output *output = data; + + output->app->waitcount--; + output->app->retry = true; +} + +static void +capture_source_handle_failed(void *data, + struct weston_capture_source_v1 *proxy, + const char *msg) +{ + struct screenshooter_output *output = data; + + output->app->waitcount--; + output->app->failed = true; + + if (msg) + fprintf(stderr, "Output capture error: %s\n", msg); +} + +static const struct weston_capture_source_v1_listener capture_source_handlers = { + .format = capture_source_handle_format, + .size = capture_source_handle_size, + .complete = capture_source_handle_complete, + .retry = capture_source_handle_retry, + .failed = capture_source_handle_failed, +}; + +static void +display_handle_geometry(void *data, + struct wl_output *wl_output, + int x, int y, + int physical_width, + int physical_height, + int subpixel, + const char *make, + const char *model, + int32_t transform) +{ +} + +static void +display_handle_mode(void *data, + struct wl_output *wl_output, + uint32_t flags, + int width, + int height, + int refresh) +{ +} + +static void +display_handle_scale(void *data, + struct wl_output *wl_output, + int scale) +{ +} + +static void +display_handle_name(void *data, struct wl_output *wl_output, const char *name) +{ + struct screenshooter_output *output = data; + output->name = strdup(name); +} + +static void +display_handle_description(void *data, struct wl_output *wl_output, const char *desc) +{ +} + +static void +display_handle_done(void *data, + struct wl_output *wl_output) +{ +} + + +static const struct wl_output_listener output_listener = { + display_handle_geometry, + display_handle_mode, + display_handle_done, + display_handle_scale, + display_handle_name, + display_handle_description, +}; + +static void +create_output(struct screenshooter_app *app, uint32_t output_name, uint32_t version) +{ + struct screenshooter_output *output; + + version = MIN(version, 4); + output = xzalloc(sizeof *output); + output->app = app; + output->wl_output = wl_registry_bind(app->registry, output_name, + &wl_output_interface, version); + if (!output->wl_output) { + fprintf(stderr, "Failed to get bind output!\n"); + exit(EXIT_FAILURE); + } + + wl_output_add_listener(output->wl_output, &output_listener, output); + + output->source = weston_capture_v1_create(app->capture_factory, + output->wl_output, + WESTON_CAPTURE_V1_SOURCE_FRAMEBUFFER); + if (!output->source) { + fprintf(stderr, "Failed to get a capture source!\n"); + exit(EXIT_FAILURE); + } + weston_capture_source_v1_add_listener(output->source, + &capture_source_handlers, output); + + wl_list_insert(&app->output_list, &output->link); +} + +static void +destroy_output(struct screenshooter_output *output) +{ + weston_capture_source_v1_destroy(output->source); + + if (wl_output_get_version(output->wl_output) >= WL_OUTPUT_RELEASE_SINCE_VERSION) + wl_output_release(output->wl_output); + else + wl_output_destroy(output->wl_output); + + screenshooter_buffer_destroy(output->buffer); + wl_list_remove(&output->link); + free(output->name); + free(output); +} + +static void +handle_global(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version) +{ + struct screenshooter_app *app = data; + + if (strcmp(interface, wl_output_interface.name) == 0) { + create_output(app, name, version); + } else if (strcmp(interface, wl_shm_interface.name) == 0) { + app->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); + /* + * Not listening for format advertisements, + * weston_capture_source_v1.format event tells us what to use. + */ + } else if (strcmp(interface, weston_capture_v1_interface.name) == 0) { + app->capture_factory = wl_registry_bind(registry, name, + &weston_capture_v1_interface, + 1); + } +} + +static void +handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) +{ + /* Dynamic output removals will just fail the respective shot. */ +} + +static const struct wl_registry_listener registry_listener = { + handle_global, + handle_global_remove +}; + +static void +screenshooter_output_capture(struct screenshooter_output *output) +{ + screenshooter_buffer_destroy(output->buffer); + output->buffer = screenshot_create_shm_buffer(output->app, + output->buffer_width, + output->buffer_height, + output->fmt); + if (!output->buffer) { + fprintf(stderr, "Failed to create output buffer\n"); + exit(EXIT_FAILURE); + } + + weston_capture_source_v1_capture(output->source, + output->buffer->wl_buffer); + output->app->waitcount++; +} + +static void +screenshot_write_png_per_output(const struct buffer_size *buff_size, + struct screenshooter_output *output, + const char *fn) +{ + pixman_image_t *shot; + cairo_surface_t *surface; + FILE *fp; + char filepath[PATH_MAX]; + char *filename_to_write; + + shot = pixman_image_create_bits(PIXMAN_a8r8g8b8, + buff_size->width, buff_size->height, + NULL, 0); + if (!shot) { + fprintf(stderr, "Failed to create shot\n"); + exit(EXIT_FAILURE); + } + + + pixman_image_composite32(PIXMAN_OP_SRC, + output->buffer->image, /* src */ + NULL, /* mask */ + shot, /* dest */ + 0, 0, /* src x,y */ + 0, 0, /* mask x,y */ + output->offset_x, output->offset_y, /* dst x,y */ + output->buffer_width, output->buffer_height); + + surface = cairo_image_surface_create_for_data((void *)pixman_image_get_data(shot), + CAIRO_FORMAT_ARGB32, + pixman_image_get_width(shot), + pixman_image_get_height(shot), + pixman_image_get_stride(shot)); + if (fn) + str_printf(&filename_to_write, "agl-screenshot-%s-", fn); + else + str_printf(&filename_to_write, "agl-screenshot-"); + + fp = file_create_dated(getenv("XDG_PICTURES_DIR"), filename_to_write, + ".png", filepath, sizeof(filepath)); + if (fp) { + fclose(fp); + cairo_surface_write_to_png(surface, filepath); + } + cairo_surface_destroy(surface); + pixman_image_unref(shot); +} + +static void +screenshot_set_buffer_size_per_output(struct buffer_size *buff_size, + struct screenshooter_output *output) +{ + buff_size->min_x = MIN(buff_size->min_x, output->offset_x); + buff_size->min_y = MIN(buff_size->min_y, output->offset_y); + buff_size->max_x = MAX(buff_size->max_x, output->offset_x + output->buffer_width); + buff_size->max_y = MAX(buff_size->max_y, output->offset_y + output->buffer_height); + + buff_size->width = buff_size->max_x - buff_size->min_x; + buff_size->height = buff_size->max_y - buff_size->min_y; +} + +static void +screenshot_compute_output_offset(int *pos, struct screenshooter_output *sh_output) +{ + sh_output->offset_x = *pos; + *pos += sh_output->buffer_width; +} + +static struct screenshooter_output * +agl_shooter_search_for_output(const char *output_name, + struct screenshooter_app *app) +{ + struct screenshooter_output *found_output = NULL; + struct screenshooter_output *output; + + if (!output_name) + return found_output; + + wl_list_for_each(output, &app->output_list, link) { + if (output->name && strcmp(output->name, output_name) == 0) { + found_output = output; + break; + } + } + + return found_output; +} + +static char * +agl_shooter_search_get_output_name(struct screenshooter_output *sh_output) +{ + struct screenshooter_app *app; + struct screenshooter_output *output; + + if (!sh_output) + return NULL; + + app = sh_output->app; + + wl_list_for_each(output, &app->output_list, link) { + if (output == sh_output) { + return output->name; + } + } + + return NULL; +} + +static void +agl_shooter_display_all_outputs(struct screenshooter_app *app) +{ + struct screenshooter_output *output; + + wl_list_for_each(output, &app->output_list, link) { + fprintf(stdout, "Output '%s'\n", output->name); + } +} + +static void +agl_shooter_screenshot_output(struct screenshooter_output *output, int *pos) +{ + struct buffer_size buff_size = {}; + struct screenshooter_app *app = output->app; + char *output_name; + + do { + app->retry = false; + screenshooter_output_capture(output); + + while (app->waitcount > 0 && !app->failed) { + if (wl_display_dispatch(app->display) < 0) + app->failed = true; + assert(app->waitcount >= 0); + } + } while (app->retry && !app->failed); + + if (!app->failed) { + screenshot_compute_output_offset(pos, output); + screenshot_set_buffer_size_per_output(&buff_size, output); + + output_name = agl_shooter_search_get_output_name(output); + assert(output_name); + screenshot_write_png_per_output(&buff_size, output, output_name); + } else { + fprintf(stderr, "Error: screenshot or protocol failure\n"); + } +} + + +static void +agl_shooter_screenshot_all_outputs(struct screenshooter_app *app) +{ + struct screenshooter_output *output; + int pos = 0; + + wl_list_for_each(output, &app->output_list, link) + agl_shooter_screenshot_output(output, &pos); +} + +static void +print_usage_and_exit(void) +{ + fprintf(stderr, "./agl-screenshooter [-o OUTPUT_NAME] [-l] [-a]\n"); + + fprintf(stderr, "\t-o OUTPUT_NAME -- take a screenshot of the output " + "specified by OUTPUT_NAME\n"); + fprintf(stderr, "\t-a -- take a screenshot of all the outputs found\n"); + fprintf(stderr, "\t-l -- list all the outputs found\n"); + exit(EXIT_FAILURE); +} + +int +main(int argc, char *argv[]) +{ + struct wl_display *display; + struct screenshooter_output *output; + struct screenshooter_output *sh_output = NULL; + struct screenshooter_output *tmp_output; + struct screenshooter_app app = {}; + + int c, option_index; + char *output_name = NULL; + int pos = 0; + + wl_list_init(&app.output_list); + + static struct option long_options[] = { + {"output", required_argument, 0, 'o' }, + {"list", required_argument, 0, 'l' }, + {"all", required_argument, 0, 'a' }, + {"help", no_argument , 0, 'h' }, + {0, 0, 0, 0} + }; + + while ((c = getopt_long(argc, argv, "o:lah", + long_options, &option_index)) != -1) { + switch (c) { + case 'o': + output_name = optarg; + opts |= (1 << OPT_SCREENSHOT_OUTPUT); + break; + case 'l': + opts |= (1 << OPT_SHOW_ALL_OUTPUTS); + break; + case 'a': + opts |= (1 << OPT_SCREENSHOT_ALL_OUTPUTS); + break; + default: + print_usage_and_exit(); + } + } + + display = wl_display_connect(NULL); + if (display == NULL) { + fprintf(stderr, "failed to create display: %s\n", + strerror(errno)); + return -1; + } + + app.display = display; + app.registry = wl_display_get_registry(display); + wl_registry_add_listener(app.registry, ®istry_listener, &app); + + /* Process wl_registry advertisements */ + wl_display_roundtrip(display); + + if (!app.shm) { + fprintf(stderr, "Error: display does not support wl_shm\n"); + return -1; + } + if (!app.capture_factory) { + fprintf(stderr, "Error: display does not support weston_capture_v1\n"); + return -1; + } + + /* Process initial events for wl_output and weston_capture_source_v1 */ + wl_display_roundtrip(display); + + if (opts & (1 << OPT_SHOW_ALL_OUTPUTS)) { + agl_shooter_display_all_outputs(&app); + return EXIT_SUCCESS; + } + + if (opts & (1 << OPT_SCREENSHOT_ALL_OUTPUTS)) { + agl_shooter_screenshot_all_outputs(&app); + return EXIT_SUCCESS; + } + + sh_output = NULL; + if (output_name) + sh_output = agl_shooter_search_for_output(output_name, &app); + + if (!sh_output && (opts & (1 << OPT_SCREENSHOT_OUTPUT))) { + fprintf(stderr, "Could not find an output matching '%s'\n", + output_name); + return EXIT_FAILURE; + } + + /* if we're still here just pick the first one available + * and use that. Still useful in case we are run without + * any args whatsoever */ + if (!sh_output) + sh_output = container_of(app.output_list.next, + struct screenshooter_output, link); + + /* take a screenshot only of that specific output */ + agl_shooter_screenshot_output(sh_output, &pos); + + wl_list_for_each_safe(output, tmp_output, &app.output_list, link) + destroy_output(output); + + weston_capture_v1_destroy(app.capture_factory); + wl_shm_destroy(app.shm); + wl_registry_destroy(app.registry); + wl_display_disconnect(display); + + return 0; +} diff --git a/meson.build b/meson.build index 9b942f0..aa811ad 100644 --- a/meson.build +++ b/meson.build @@ -63,31 +63,34 @@ foreach depname : depnames deps_remoting += dep endforeach - agl_shell_xml = files('protocol/agl-shell.xml') agl_shell_desktop_xml = files('protocol/agl-shell-desktop.xml') -agl_screenshooter = files('protocol/agl-screenshooter.xml') xdg_shell_xml = join_paths(dir_wp_base, 'stable', 'xdg-shell', 'xdg-shell.xml') +dep_libweston_protocols = dependency('libweston-13-protocols', version: '>= 13') +dir_protocol_libweston = dep_libweston_protocols.get_pkgconfig_variable('pkgdatadir') protocols = [ { 'name': 'agl-shell', 'source': 'internal' }, { 'name': 'agl-shell-desktop', 'source': 'internal' }, - { 'name': 'agl-screenshooter', 'source': 'internal' }, { 'name': 'xdg-shell', 'source': 'wp-stable' }, { 'name': 'xdg-output', 'source': 'unstable', 'version': 'v1' }, + { 'name': 'weston-output-capture', 'source': 'libweston-protocols' }, ] foreach proto: protocols proto_name = proto['name'] if proto['source'] == 'internal' base_file = proto_name - xml_path = join_paths('protocol', '@0@.xml'.format(base_file)) + xml_path = join_paths('protocol', '@0@.xml'.format(base_file)) + elif proto['source'] == 'libweston-protocols' + base_file = proto_name + xml_path = join_paths(dir_protocol_libweston, '@0@.xml'.format(base_file)) elif proto['source'] == 'wp-stable' base_file = proto_name - xml_path = join_paths(dir_wp_base, 'stable', proto_name, '@0@.xml'.format(base_file)) + xml_path = join_paths(dir_wp_base, 'stable', proto_name, '@0@.xml'.format(base_file)) else base_file = '@0@-unstable-@1@'.format(proto_name, proto['version']) - xml_path = join_paths(dir_wp_base, 'unstable', proto_name, '@0@.xml'.format(base_file)) + xml_path = join_paths(dir_wp_base, 'unstable', proto_name, '@0@.xml'.format(base_file)) endif foreach output_type: [ 'client-header', 'server-header', 'private-code' ] @@ -136,17 +139,14 @@ srcs_agl_compositor = [ 'src/layout.c', 'src/policy.c', 'src/shell.c', - 'src/screenshooter.c', 'src/input.c', 'shared/option-parser.c', 'shared/os-compatibility.c', 'shared/process-util.c', agl_shell_server_protocol_h, agl_shell_desktop_server_protocol_h, - agl_screenshooter_server_protocol_h, agl_shell_protocol_c, agl_shell_desktop_protocol_c, - agl_screenshooter_protocol_c, xdg_shell_protocol_c, ] diff --git a/shared/pixel-formats.h b/shared/pixel-formats.h new file mode 100644 index 0000000..8df5b45 --- /dev/null +++ b/shared/pixel-formats.h @@ -0,0 +1,373 @@ +/* + * Copyright © 2016, 2019 Collabora, Ltd. + * Copyright (c) 2018 DisplayLink (UK) Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Daniel Stone + */ + +#include +#include +#include + +/** + * Contains information about pixel formats, mapping format codes from + * wl_shm and drm_fourcc.h (which are deliberately identical, but for the + * special cases of WL_SHM_ARGB8888 and WL_SHM_XRGB8888) into various + * sets of information. Helper functions are provided for dealing with these + * raw structures. + */ +struct pixel_format_info { + /** DRM/wl_shm format code */ + uint32_t format; + + /** The DRM format name without the DRM_FORMAT_ prefix. */ + const char *drm_format_name; + + /** If true, is only for internal use and should not be advertised to + * clients to allow them to create buffers of this format. */ + bool hide_from_clients; + + /** If non-zero, number of planes in base (non-modified) format. */ + int num_planes; + + /** If format contains alpha channel, opaque equivalent of format, + * i.e. alpha channel replaced with X. */ + uint32_t opaque_substitute; + + /** How the format should be sampled, expressed in terms of tokens + * from the EGL_WL_bind_wayland_display extension. If not set, + * assumed to be either RGB or RGBA, depending on whether or not + * the format contains an alpha channel. The samplers may still + * return alpha even for opaque formats; users must manually set + * the alpha channel to 1.0 (or ignore it) if the format is + * opaque. */ + uint32_t sampler_type; + + /** GL internal format; to be used when creating FBO renderbuffers */ + int gl_internalformat; + + /** GL format, if data can be natively/directly uploaded. Note that + * whilst DRM formats are little-endian unless explicitly specified, + * (i.e. DRM_FORMAT_ARGB8888 is stored BGRA as sequential bytes in + * memory), GL uses the sequential byte order, so that format maps to + * GL_BGRA_EXT plus GL_UNSIGNED_BYTE. To add to the confusion, the + * explicitly-sized types (e.g. GL_UNSIGNED_SHORT_5_5_5_1) read in + * machine-endian order, so for these types, the correspondence + * depends on endianness. */ + int gl_format; + + /** GL data type, if data can be natively/directly uploaded. */ + int gl_type; + + /** Pixman data type, if it agrees exactly with the wl_shm format */ + pixman_format_code_t pixman_format; + + /** If set, this format can be used with the legacy drmModeAddFB() + * function (not AddFB2), using this and the bpp member. */ + int addfb_legacy_depth; + + /** Number of bits required to store a single pixel, for + * single-planar formats. */ + int bpp; + + /** Horizontal subsampling; if non-zero, divide the width by this + * member to obtain the number of columns in the source buffer for + * secondary planes only. Stride is not affected by horizontal + * subsampling. */ + int hsub; + + /** Vertical subsampling; if non-zero, divide the height by this + * member to obtain the number of rows in the source buffer for + * secondary planes only. */ + int vsub; + + /* Ordering of chroma components. */ + enum { + ORDER_UV = 0, + ORDER_VU, + } chroma_order; + + /* If packed YUV (num_planes == 1), ordering of luma/chroma + * components. */ + enum { + ORDER_LUMA_CHROMA = 0, + ORDER_CHROMA_LUMA, + } luma_chroma_order; + + /** How many significant bits each channel has, or zero if N/A. */ + struct { + int r; + int g; + int b; + int a; + } bits; + + /** How channel bits are interpreted, fixed (uint) or floating-point */ + enum { + PIXEL_COMPONENT_TYPE_FIXED = 0, + PIXEL_COMPONENT_TYPE_FLOAT, + } component_type; +}; + +/** + * Get pixel format information for a DRM format code + * + * Given a DRM format code, return a pixel format info structure describing + * the properties of that format. + * + * @param format DRM format code to get info for + * @returns A pixel format structure (must not be freed), or NULL if the + * format could not be found + */ +const struct pixel_format_info * +pixel_format_get_info(uint32_t format); + +/** + * Get pixel format information for a SHM format code + * + * Given a SHM format code, return a DRM pixel format info structure describing + * the properties of that format. + * + * @param format SHM format code to get info for. + * @returns A pixel format structure (must not be freed), or NULL if the + * format could not be found. + */ +const struct pixel_format_info * +pixel_format_get_info_shm(uint32_t format); + +/** + * Get pixel format information by table index + * + * Given a 0-based index in the format table, return the corresponding + * DRM pixel format info structure. + * + * @param index Index of the pixel format in the table + * @returns A pixel format structure (must not be freed), or NULL if the + * index is out of range. + */ +const struct pixel_format_info * +pixel_format_get_info_by_index(unsigned int index); + +/** + * Return the size of the pixel format table + * + * @returns The number of entries in the pixel format table + */ +unsigned int +pixel_format_get_info_count(void); + +/** + * Get pixel format information for a named DRM format + * + * Given a DRM format name, return a pixel format info structure describing + * the properties of that format. + * + * The DRM format name is the preprocessor token name from drm_fourcc.h + * without the DRM_FORMAT_ prefix. The search is also case-insensitive. + * Both "xrgb8888" and "XRGB8888" searches will find DRM_FORMAT_XRGB8888 + * for example. + * + * @param drm_format_name DRM format name to get info for (not NULL) + * @returns A pixel format structure (must not be freed), or NULL if the + * name could not be found + */ +const struct pixel_format_info * +pixel_format_get_info_by_drm_name(const char *drm_format_name); + +/** + * Get pixel format information for a Pixman format code + * + * Given a Pixman format code, return a pixel format info structure describing + * the properties of that format. + * + * @param pixman_format Pixman format code to get info for + * @returns A pixel format structure (must not be freed), or NULL if the + * format could not be found + */ +const struct pixel_format_info * +pixel_format_get_info_by_pixman(pixman_format_code_t pixman_format); + +/** + * Get number of planes used by a pixel format + * + * Given a pixel format info structure, return the number of planes + * required for a buffer. Note that this is not necessarily identical to + * the number of samplers required to be bound, as two views into a single + * plane are sometimes required. + * + * @param format Pixel format info structure + * @returns Number of planes required for the format + */ +unsigned int +pixel_format_get_plane_count(const struct pixel_format_info *format); + +/** + * Determine if a pixel format is opaque or contains alpha + * + * Returns whether or not the pixel format is opaque, or contains a + * significant alpha channel. Note that the suggested EGL sampler type may + * still sample undefined data into the alpha channel; users must consider + * alpha as 1.0 if the format is opaque, and not rely on the sampler to + * return this when sampling from the alpha channel. + * + * @param format Pixel format info structure + * @returns True if the format is opaque, or false if it has significant alpha + */ +bool +pixel_format_is_opaque(const struct pixel_format_info *format); + +/** + * Get compatible opaque equivalent for a format + * + * Given a pixel format info structure, return a format which is wholly + * compatible with the input format, but opaque, ignoring the alpha channel. + * If an alpha format is provided, but the content is known to all be opaque, + * then this can be used as a substitute to avoid blending. + * + * If the input format is opaque, this function will return the input format. + * + * @param format Pixel format info structure + * @returns A pixel format info structure for the compatible opaque substitute + */ +const struct pixel_format_info * +pixel_format_get_opaque_substitute(const struct pixel_format_info *format); + +/** + * For an opaque format, get the equivalent format with alpha instead of an + * ignored channel + * + * This is the opposite lookup from pixel_format_get_opaque_substitute(). + * Finds the format whose opaque substitute is the given format. + * + * If the input format is not opaque or does not have ignored (X) bits, then + * the search cannot find a match. + * + * @param format DRM format code to search for + * @returns A pixel format info structure for the pixel format whose opaque + * substitute is the argument, or NULL if no match. + */ +const struct pixel_format_info * +pixel_format_get_info_by_opaque_substitute(uint32_t format); + +/** + * Return the horizontal subsampling factor for a given plane + * + * When horizontal subsampling is effective, a sampler bound to a secondary + * plane must bind the sampler with a smaller effective width. This function + * returns the subsampling factor to use for the given plane. + * + * @param format Pixel format info structure + * @param plane Zero-indexed plane number + * @returns Horizontal subsampling factor for the given plane + */ +unsigned int +pixel_format_hsub(const struct pixel_format_info *format, + unsigned int plane); + +/** + * Return the vertical subsampling factor for a given plane + * + * When vertical subsampling is effective, a sampler bound to a secondary + * plane must bind the sampler with a smaller effective height. This function + * returns the subsampling factor to use for the given plane. + * + * @param format Pixel format info structure + * @param plane Zero-indexed plane number + * @returns Vertical subsampling factor for the given plane + */ +unsigned int +pixel_format_vsub(const struct pixel_format_info *format, + unsigned int plane); + +/** + * Return the effective sampling width for a given plane + * + * When horizontal subsampling is effective, a sampler bound to a secondary + * plane must bind the sampler with a smaller effective width. This function + * returns the effective width to use for the sampler, i.e. dividing by hsub. + * + * If horizontal subsampling is not in effect, this will be equal to the + * width. + * + * @param format Pixel format info structure + * @param plane Zero-indexed plane number + * @param width Width of the buffer + * @returns Effective width for sampling + */ +unsigned int +pixel_format_width_for_plane(const struct pixel_format_info *format, + unsigned int plane, + unsigned int width); + +/** + * Return the effective sampling height for a given plane + * + * When vertical subsampling is in effect, a sampler bound to a secondary + * plane must bind the sampler with a smaller effective height. This function + * returns the effective height to use for the sampler, i.e. dividing by vsub. + * + * If vertical subsampling is not in effect, this will be equal to the height. + * + * @param format Pixel format info structure + * @param plane Zero-indexed plane number + * @param height Height of the buffer + * @returns Effective width for sampling + */ +unsigned int +pixel_format_height_for_plane(const struct pixel_format_info *format, + unsigned int plane, + unsigned int height); +/** + * Return a human-readable format modifier. Comprised from the modifier name, + * the vendor name, and the original encoded value in hexadecimal, using + * 'VENDOR_NAME_MODIFIER_NAME (modifier_encoded_value)' pattern. In case the + * modifier name (and the vendor name) isn't found, this returns the original + * encoded value, as a string value. + * + * @param modifier the modifier in question + * @returns a malloc'ed string, caller responsible for freeing after use. + */ +char * +pixel_format_get_modifier(uint64_t modifier); + +/** + * Return the wl_shm format code + * + * @param info Pixel format info structure + * @returns The wl_shm format code for this pixel format. + */ +uint32_t +pixel_format_get_shm_format(const struct pixel_format_info *info); + +/** + * Get pixel format array for an array of DRM format codes + * + * Given an array of DRM format codes, return an array of corresponding pixel + * format info pointers. + * + * @param formats Array of DRM format codes to get info for + * @param formats_count Number of entries in formats. + * @returns An array of pixel format info pointers, or NULL if any format could + * not be found. Must be freed by the caller. + */ +const struct pixel_format_info ** +pixel_format_get_array(const uint32_t *formats, unsigned int formats_count); diff --git a/src/compositor.c b/src/compositor.c index 1881702..bdff9ed 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -103,6 +103,13 @@ ivi_init_parsed_options(struct weston_compositor *compositor) return config; } +static void +screenshot_allow_all(struct wl_listener *l, struct weston_output_capture_attempt *att) +{ + att->authorized = true; +} + + static void sigint_helper(int sig) { @@ -2237,8 +2244,11 @@ int wet_main(int argc, char *argv[], const struct weston_testsuite_data *test_da ivi_launch_shell_client(&ivi, "shell-client-ext", &ivi.shell_client_ext.client); - if (debug) - ivi_screenshooter_create(&ivi); + if (debug) { + weston_compositor_add_screenshot_authority(ivi.compositor, + &ivi.screenshot_auth, + screenshot_allow_all); + } ivi_agl_systemd_notify(&ivi); wl_display_run(display); diff --git a/src/ivi-compositor.h b/src/ivi-compositor.h index 1303e44..695cf95 100644 --- a/src/ivi-compositor.h +++ b/src/ivi-compositor.h @@ -73,6 +73,8 @@ struct ivi_compositor { bool activate_by_default; bool keep_pending_surfaces; + struct wl_listener screenshot_auth; + /* * Options parsed from command line arugments. * Overrides what is found in the config file. diff --git a/src/screenshooter.c b/src/screenshooter.c deleted file mode 100644 index 7d3edaa..0000000 --- a/src/screenshooter.c +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright © 2020 Collabora, Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "ivi-compositor.h" -#include "shared/helpers.h" - -#include -#include "agl-screenshooter-server-protocol.h" -#include - -struct screenshooter { - struct ivi_compositor *ivi; - struct wl_global *global; - struct wl_client *client; - struct wl_listener destroy_listener; -}; - -static void -screenshooter_done(void *data, enum weston_screenshooter_outcome outcome) -{ - struct wl_resource *resource = data; - - if (outcome == WESTON_SCREENSHOOTER_NO_MEMORY) { - wl_resource_post_no_memory(resource); - return; - } - - agl_screenshooter_send_done(resource, outcome); -} - -static void -screenshooter_shoot(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *output_resource, - struct wl_resource *buffer_resource) -{ - struct weston_output *output = - weston_head_from_resource(output_resource)->output; - struct weston_buffer *buffer = - weston_buffer_from_resource(output->compositor, buffer_resource); - - if (buffer == NULL) { - wl_resource_post_no_memory(resource); - return; - } - - weston_screenshooter_shoot(output, buffer, screenshooter_done, resource); -} - -static void -screenshooter_destructor_destroy(struct wl_client *client, - struct wl_resource *global_resource) -{ - wl_resource_destroy(global_resource); -} - -struct agl_screenshooter_interface screenshooter_implementation = { - screenshooter_shoot, - screenshooter_destructor_destroy -}; - -static void -bind_shooter(struct wl_client *client, - void *data, uint32_t version, uint32_t id) -{ - struct screenshooter *shooter = data; - struct wl_resource *resource; - bool debug_enabled = true; - - resource = wl_resource_create(client, - &agl_screenshooter_interface, 1, id); - - if (!debug_enabled && !shooter->client) { - wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT, - "screenshooter failed: permission denied. "\ - "Debug must be enabled"); - return; - } - - wl_resource_set_implementation(resource, &screenshooter_implementation, - data, NULL); -} - -static void -screenshooter_destroy(struct wl_listener *listener, void *data) -{ - struct screenshooter *shooter = - container_of(listener, struct screenshooter, destroy_listener); - - wl_list_remove(&shooter->destroy_listener.link); - - wl_global_destroy(shooter->global); - free(shooter); -} - -void -ivi_screenshooter_create(struct ivi_compositor *ivi) -{ - struct weston_compositor *ec = ivi->compositor; - struct screenshooter *shooter; - - shooter = zalloc(sizeof(*shooter)); - if (shooter == NULL) - return; - - shooter->ivi = ivi; - shooter->global = wl_global_create(ec->wl_display, - &agl_screenshooter_interface, 1, - shooter, bind_shooter); - - shooter->destroy_listener.notify = screenshooter_destroy; - wl_signal_add(&ec->destroy_signal, &shooter->destroy_listener); - - weston_log("Screenshooter interface created\n"); -}