diff options
-rw-r--r-- | clients/meson.build | 13 | ||||
-rw-r--r-- | clients/screenshooter.c | 620 | ||||
-rw-r--r-- | clients/screenshot.c | 673 | ||||
-rw-r--r-- | meson.build | 45 | ||||
-rw-r--r-- | shared/pixel-formats.h | 373 | ||||
-rw-r--r-- | shared/process-util.c | 3 | ||||
-rw-r--r-- | shared/process-util.h | 1 | ||||
-rw-r--r-- | src/compositor.c | 784 | ||||
-rw-r--r-- | src/desktop.c | 32 | ||||
-rw-r--r-- | src/ivi-compositor.h | 15 | ||||
-rw-r--r-- | src/layout.c | 167 | ||||
-rw-r--r-- | src/screenshooter.c | 137 | ||||
-rw-r--r-- | src/shell.c | 113 | ||||
-rw-r--r-- | src/xwayland.c | 398 |
14 files changed, 2052 insertions, 1322 deletions
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 <stdint.h> -#include <errno.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <fcntl.h> -#include <unistd.h> -#include <limits.h> -#include <sys/param.h> -#include <sys/mman.h> -#include <getopt.h> -#include <cairo.h> - -#include <wayland-client.h> -#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 <stdint.h> +#include <stdbool.h> +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <limits.h> +#include <sys/param.h> +#include <sys/mman.h> +#include <pixman.h> +#include <cairo.h> +#include <getopt.h> +#include <assert.h> + +#include <wayland-client.h> +#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 0817c9d..aa811ad 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project('agl-compositor', 'c','cpp', - version: '0.0.23', + version: '0.0.24', default_options: [ 'warning_level=3', 'c_std=gnu99', @@ -11,7 +11,7 @@ project('agl-compositor', ) config_h = configuration_data() -libweston_version = 'libweston-10' +libweston_version = 'libweston-13' pkgconfig = import('pkgconfig') fs = import('fs') @@ -22,6 +22,7 @@ add_project_arguments( cc.get_supported_arguments([ '-Wno-unused-parameter', '-Wno-pedantic', + '-Wno-deprecated-declarations' ]), language: 'c' ) @@ -42,7 +43,7 @@ foreach func: optional_libc_funcs endforeach dep_libsystemd = dependency('libsystemd', required: false) -dep_scanner = dependency('wayland-scanner') +dep_scanner = dependency('wayland-scanner', native: true) prog_scanner = find_program(dep_scanner.get_pkgconfig_variable('wayland_scanner')) dep_wp = dependency('wayland-protocols', version: '>= 1.18') dir_wp_base = dep_wp.get_pkgconfig_variable('pkgdatadir') @@ -62,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' ] @@ -123,9 +127,11 @@ deps_libweston = [ dependency('wayland-server'), dependency('weston'), libweston_dep, - dependency('libweston-desktop-10'), ] +weston_module_dir = libweston_dep.get_pkgconfig_variable('libdir') +config_h.set_quoted('WESTON_MODULEDIR', join_paths(weston_module_dir, 'weston')) +config_h.set_quoted('LIBWESTON_MODULEDIR', join_paths(weston_module_dir, libweston_version)) srcs_agl_compositor = [ 'src/compositor.c', @@ -133,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, ] @@ -168,17 +171,17 @@ if libweston_dep.found() if not meson.is_cross_build() if not prefix_path.contains('/usr') dir_path_x11_backend = join_paths(prefix_path, 'include', libweston_version, 'libweston', 'backend-x11.h') - dir_path_headless_backend = join_paths(prefix_path, 'include', libweston_version, 'libweston', 'backend-headless.h') + dir_path_wayland_backend = join_paths(prefix_path, 'include', libweston_version, 'libweston', 'backend-wayland.h') dir_path_rdp_backend = join_paths(prefix_path, 'include', libweston_version, 'libweston', 'backend-rdp.h') else dir_path_x11_backend = join_paths(libweston_version, 'libweston', 'backend-x11.h') - dir_path_headless_backend = join_paths(libweston_version, 'libweston', 'backend-headless.h') - dir_path_rdp = join_paths(libweston_version, 'libweston', 'backend-rdp.h') + dir_path_wayland_backend = join_paths(libweston_version, 'libweston', 'backend-wayland.h') + dir_path_rdp_backend = join_paths(libweston_version, 'libweston', 'backend-rdp.h') endif else message('Building with cross environment') dir_path_x11_backend = join_paths(libweston_version, 'libweston', 'backend-x11.h') - dir_path_headless_backend = join_paths(libweston_version, 'libweston', 'backend-headless.h') + dir_path_wayland_backend = join_paths(libweston_version, 'libweston', 'backend-wayland.h') dir_path_rdp_backend = join_paths(libweston_version, 'libweston', 'backend-rdp.h') endif @@ -188,9 +191,9 @@ if libweston_dep.found() message('Building with X11 backend') endif - if cc.has_header(dir_path_headless_backend) - config_h.set('HAVE_BACKEND_HEADLESS', 1) - message('Building with headless backend') + if cc.has_header(dir_path_wayland_backend) + config_h.set('HAVE_BACKEND_WAYLAND', 1) + message('Building with Wayland backend') endif if cc.has_header(dir_path_rdp_backend) @@ -217,6 +220,8 @@ if get_option('xwayland') config_h.set('BUILD_XWAYLAND', '1') srcs_agl_compositor += 'src/xwayland.c' + srcs_agl_compositor += 'shared/xalloc.c' + srcs_agl_compositor += 'shared/process-util.c' config_h.set_quoted('XSERVER_PATH', get_option('xwayland-path')) endif 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 <daniels@collabora.com> + */ + +#include <inttypes.h> +#include <stdbool.h> +#include <pixman.h> + +/** + * 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/shared/process-util.c b/shared/process-util.c index e36c647..fe895d2 100644 --- a/shared/process-util.c +++ b/shared/process-util.c @@ -68,7 +68,8 @@ fdstr_close_all(struct fdstr *s) unsigned i; for (i = 0; i < ARRAY_LENGTH(s->fds); i++) { - close(s->fds[i]); + if (s->fds[i] >= 0) + close(s->fds[i]); s->fds[i] = -1; } } diff --git a/shared/process-util.h b/shared/process-util.h index 05543f6..aa35c77 100644 --- a/shared/process-util.h +++ b/shared/process-util.h @@ -59,6 +59,7 @@ fdstr_clear_cloexec_fd1(struct fdstr *s); void fdstr_close_all(struct fdstr *s); +#define FDSTR_INIT ((struct fdstr){ { 0 }, { -1, -1 }}) /** * A container for environment variables and/or process arguments, designed to diff --git a/src/compositor.c b/src/compositor.c index 2c8c386..078157a 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -1,5 +1,5 @@ /* - * Copyright © 2012-2021 Collabora, Ltd. + * Copyright © 2012-2024 Collabora, Ltd. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -42,9 +42,6 @@ #ifdef HAVE_BACKEND_RDP #include <libweston/backend-rdp.h> #endif -#ifdef HAVE_BACKEND_HEADLESS -#include <libweston/backend-headless.h> -#endif #ifdef HAVE_BACKEND_X11 #include <libweston/backend-x11.h> #endif @@ -57,12 +54,16 @@ #include "shared/os-compatibility.h" #include "shared/helpers.h" +#include "config.h" #include "agl-shell-server-protocol.h" #ifdef HAVE_REMOTING #include "remote.h" #endif +#define WINDOWED_DEFAULT_WIDTH 1024 +#define WINDOWED_DEFAULT_HEIGHT 768 + static int cached_tm_mday = -1; static struct weston_log_scope *log_scope; @@ -72,6 +73,16 @@ to_ivi_compositor(struct weston_compositor *ec) return weston_compositor_get_user_data(ec); } +void +ivi_process_destroy(struct wet_process *process, int status, bool call_cleanup) +{ + wl_list_remove(&process->link); + if (call_cleanup && process->cleanup) + process->cleanup(process, status, process->cleanup_data); + free(process->path); + free(process); +} + struct ivi_output_config * ivi_init_parsed_options(struct weston_compositor *compositor) { @@ -92,6 +103,11 @@ 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 @@ -100,93 +116,59 @@ sigint_helper(int sig) raise(SIGUSR2); } -void -ivi_layout_save(struct ivi_compositor *ivi, struct ivi_output *output) -{ - struct ivi_output *new_output; - ivi->need_ivi_output_relayout = true; - - new_output = zalloc(sizeof(*new_output)); - - new_output->ivi = ivi; - new_output->background = output->background; - - new_output->top = output->top; - new_output->bottom = output->bottom; - new_output->left = output->left; - new_output->right = output->right; - - new_output->active = output->active; - new_output->previous_active = output->previous_active; - new_output->name = strdup(output->name); - if (output->app_ids) - new_output->app_ids = strdup(output->app_ids); - - new_output->area = output->area; - new_output->area_saved = output->area_saved; - new_output->area_activation = output->area_activation; - - weston_log("saving output layout for output %s\n", new_output->name); - - wl_list_insert(&ivi->saved_outputs, &new_output->link); -} - -void -ivi_layout_restore(struct ivi_compositor *ivi, struct ivi_output *n_output) -{ - struct ivi_output *output = NULL; - struct ivi_output *iter_output; - - if (!ivi->need_ivi_output_relayout) - return; - - ivi->need_ivi_output_relayout = false; - - wl_list_for_each(iter_output, &ivi->saved_outputs, link) { - if (strcmp(n_output->name, iter_output->name) == 0) { - output = iter_output; - break; +struct { + char *name; + enum weston_renderer_type renderer; +} renderer_name_map[] = { + { "auto", WESTON_RENDERER_AUTO }, + { "gl", WESTON_RENDERER_GL }, + { "noop", WESTON_RENDERER_NOOP }, + { "pixman", WESTON_RENDERER_PIXMAN }, +}; + +struct { + char *short_name; + char *long_name; + enum weston_compositor_backend backend; +} backend_name_map[] = { + { "drm", "drm-backend.so", WESTON_BACKEND_DRM }, + { "rdp", "rdp-backend.so", WESTON_BACKEND_RDP }, + { "wayland", "wayland-backend.so", WESTON_BACKEND_WAYLAND }, + { "x11", "x11-backend.so", WESTON_BACKEND_X11 }, +}; + +bool +get_backend_from_string(const char *name, enum weston_compositor_backend *backend) +{ + size_t i; + + for (i = 0; i < ARRAY_LENGTH(backend_name_map); i++) { + if (strcmp(name, backend_name_map[i].short_name) == 0 || + strcmp(name, backend_name_map[i].long_name) == 0) { + *backend = backend_name_map[i].backend; + return true; } } - if (!output) - return; - - weston_log("restoring output layout for output %s\n", output->name); - n_output->background = output->background; - - n_output->top = output->top; - n_output->bottom = output->bottom; - n_output->left = output->left; - n_output->right = output->right; - - n_output->active = output->active; - n_output->previous_active = output->previous_active; - if (output->app_ids) - n_output->app_ids = strdup(output->app_ids); - - n_output->area = output->area; - n_output->area_saved = output->area_saved; - n_output->area_activation = output->area_activation; - - free(output->app_ids); - free(output->name); - wl_list_remove(&output->link); - free(output); + return false; } -void -ivi_layout_destroy_saved_outputs(struct ivi_compositor *ivi) +bool +get_renderer_from_string(const char *name, enum weston_renderer_type *renderer) { - struct ivi_output *output, *output_next; + size_t i; - wl_list_for_each_safe(output, output_next, &ivi->saved_outputs, link) { - free(output->app_ids); - free(output->name); + if (!name) + name = "auto"; - wl_list_remove(&output->link); - free(output); + for (i = 0; i < ARRAY_LENGTH(renderer_name_map); i++) { + if (strcmp(name, renderer_name_map[i].name) == 0) { + *renderer = renderer_name_map[i].renderer; + return true; + } } + + return false; } static void @@ -199,7 +181,8 @@ handle_output_destroy(struct wl_listener *listener, void *data) if (output->fullscreen_view.fs && output->fullscreen_view.fs->view) { - weston_surface_destroy(output->fullscreen_view.fs->view->surface); + weston_surface_unref(output->fullscreen_view.fs->view->surface); + weston_buffer_destroy_solid(output->fullscreen_view.buffer_ref); output->fullscreen_view.fs->view = NULL; } @@ -216,6 +199,9 @@ to_ivi_output(struct weston_output *o) struct ivi_output *output; listener = weston_output_get_destroy_listener(o, handle_output_destroy); + if (!listener) + return NULL; + output = wl_container_of(listener, output, output_destroy); return output; @@ -264,16 +250,23 @@ ivi_ensure_output(struct ivi_compositor *ivi, char *name, output->name = name; output->config = config; - if (ivi->simple_output_configure) { - output->output = - weston_compositor_create_output_with_head(ivi->compositor, - head); - if (!output->output) { - free(output->name); - free(output); - return NULL; - } + output->output = + weston_compositor_create_output(ivi->compositor, head, head->name); + if (!output->output) { + free(output->name); + free(output); + return NULL; + } + + /* simple_output_configure might assume we have an ivi_output created + * by this point, which we do but we can only link it to a + * weston_output through the destroy listener, so install it earlier + * before actually running the callback handler */ + output->output_destroy.notify = handle_output_destroy; + weston_output_add_destroy_listener(output->output, + &output->output_destroy); + if (ivi->simple_output_configure) { int ret = ivi->simple_output_configure(output->output); if (ret < 0) { weston_log("Configuring output \"%s\" failed.\n", @@ -290,23 +283,12 @@ ivi_ensure_output(struct ivi_compositor *ivi, char *name, ivi->init_failed = true; return NULL; } - - } else { - output->output = - weston_compositor_create_output(ivi->compositor, name); - if (!output->output) { - free(output->name); - free(output); - return NULL; - } } - output->output_destroy.notify = handle_output_destroy; - weston_output_add_destroy_listener(output->output, - &output->output_destroy); wl_list_insert(&ivi->outputs, &output->link); ivi_output_configure_app_id(output); + return output; } @@ -358,63 +340,14 @@ add_head_destroyed_listener(struct weston_head *head) } static int -drm_configure_output(struct ivi_output *output) +ivi_configure_windowed_output_from_config(struct ivi_output *output, + struct ivi_output_config *defaults) { struct ivi_compositor *ivi = output->ivi; struct weston_config_section *section = output->config; - enum weston_drm_backend_output_mode mode = - WESTON_DRM_BACKEND_OUTPUT_PREFERRED; - char *modeline = NULL; - char *gbm_format = NULL; - char *seat = NULL; - - if (section) { - char *m; - weston_config_section_get_string(section, "mode", &m, "preferred"); - - /* This should have been handled earlier */ - assert(strcmp(m, "off") != 0); - - if (ivi->cmdline.use_current_mode || strcmp(m, "current") == 0) { - mode = WESTON_DRM_BACKEND_OUTPUT_CURRENT; - } else if (strcmp(m, "preferred") != 0) { - modeline = m; - m = NULL; - } - free(m); - - weston_config_section_get_string(section, "gbm-format", - &gbm_format, NULL); - - weston_config_section_get_string(section, "seat", &seat, ""); - } - - if (ivi->drm_api->set_mode(output->output, mode, modeline) < 0) { - weston_log("Cannot configure output using weston_drm_output_api.\n"); - free(modeline); - return -1; - } - free(modeline); - - ivi->drm_api->set_gbm_format(output->output, gbm_format); - free(gbm_format); - - ivi->drm_api->set_seat(output->output, seat); - free(seat); - - return 0; -} - -#define WINDOWED_DEFAULT_WIDTH 1024 -#define WINDOWED_DEFAULT_HEIGHT 768 - -static int -windowed_configure_output(struct ivi_output *output) -{ - struct ivi_compositor *ivi = output->ivi; - struct weston_config_section *section = output->config; - int width = WINDOWED_DEFAULT_WIDTH; - int height = WINDOWED_DEFAULT_HEIGHT; + int width; + int height; + int scale; if (section) { char *mode; @@ -423,18 +356,26 @@ windowed_configure_output(struct ivi_output *output) if (!mode || sscanf(mode, "%dx%d", &width, &height) != 2) { weston_log("Invalid mode for output %s. Using defaults.\n", output->name); - width = WINDOWED_DEFAULT_WIDTH; - height = WINDOWED_DEFAULT_HEIGHT; + width = defaults->width; + height = defaults->height; } free(mode); + } else { + width = defaults->width; + height = defaults->height; } + scale = defaults->scale; + if (ivi->cmdline.width) width = ivi->cmdline.width; if (ivi->cmdline.height) height = ivi->cmdline.height; if (ivi->cmdline.scale) - weston_output_set_scale(output->output, ivi->cmdline.scale); + scale = ivi->cmdline.scale; + + weston_output_set_scale(output->output, scale); + weston_output_set_transform(output->output, defaults->transform); if (ivi->window_api->output_set_size(output->output, width, height) < 0) { weston_log("Cannot configure output '%s' using weston_windowed_output_api.\n", @@ -442,7 +383,9 @@ windowed_configure_output(struct ivi_output *output) return -1; } - weston_log("Configured windowed_output_api to %dx%d\n", width, height); + + weston_log("Configured windowed_output_api to %dx%d, scale %d\n", + width, height, scale); return 0; } @@ -489,19 +432,17 @@ parse_activation_area(const char *geometry, struct ivi_output *output) } static int -configure_output(struct ivi_output *output) +drm_configure_output(struct ivi_output *output) { struct ivi_compositor *ivi = output->ivi; struct weston_config_section *section = output->config; int32_t scale = 1; uint32_t transform = WL_OUTPUT_TRANSFORM_NORMAL; - - /* - * This can happen with the wayland backend with 'sprawl'. The config - * is hard-coded, so we don't need to do anything. - */ - if (!ivi->drm_api && !ivi->window_api) - return 0; + enum weston_drm_backend_output_mode mode = + WESTON_DRM_BACKEND_OUTPUT_PREFERRED; + char *modeline = NULL; + char *gbm_format = NULL; + char *seat = NULL; if (section) { char *t; @@ -521,10 +462,41 @@ configure_output(struct ivi_output *output) weston_output_set_scale(output->output, scale); weston_output_set_transform(output->output, transform); - if (ivi->drm_api) - return drm_configure_output(output); - else - return windowed_configure_output(output); + if (section) { + char *m; + weston_config_section_get_string(section, "mode", &m, "preferred"); + + /* This should have been handled earlier */ + assert(strcmp(m, "off") != 0); + + if (ivi->cmdline.use_current_mode || strcmp(m, "current") == 0) { + mode = WESTON_DRM_BACKEND_OUTPUT_CURRENT; + } else if (strcmp(m, "preferred") != 0) { + modeline = m; + m = NULL; + } + free(m); + + weston_config_section_get_string(section, "gbm-format", + &gbm_format, NULL); + + weston_config_section_get_string(section, "seat", &seat, ""); + } + + if (ivi->drm_api->set_mode(output->output, mode, modeline) < 0) { + weston_log("Cannot configure output using weston_drm_output_api.\n"); + free(modeline); + return -1; + } + free(modeline); + + ivi->drm_api->set_gbm_format(output->output, gbm_format); + free(gbm_format); + + ivi->drm_api->set_seat(output->output, seat); + free(seat); + + return 0; } /* @@ -539,7 +511,7 @@ try_attach_heads(struct ivi_output *output) { size_t fail_len = 0; - for (size_t i = 0; i < output->add_len; ++i) { + for (size_t i = 1; i < output->add_len; i++) { if (weston_output_attach_head(output->output, output->add[i]) < 0) { struct weston_head *tmp = output->add[i]; memmove(&output->add[fail_len + 1], output->add[fail_len], @@ -550,6 +522,39 @@ try_attach_heads(struct ivi_output *output) return fail_len; } +/* Place output exactly to the right of the most recently enabled output. + * + * Historically, we haven't given much thought to output placement, + * simply adding outputs in a horizontal line as they're enabled. This + * function simply sets an output's x coordinate to the right of the + * most recently enabled output, and its y to zero. + * + * If you're adding new calls to this function, you're also not giving + * much thought to output placement, so please consider carefully if + * it's really doing what you want. + * + * You especially don't want to use this for any code that won't + * immediately enable the passed output. + */ +static void +weston_output_lazy_align(struct weston_output *output) +{ + struct weston_compositor *c; + struct weston_output *peer; + int next_x = 0; + + /* Put this output to the right of the most recently enabled output */ + c = output->compositor; + if (!wl_list_empty(&c->output_list)) { + peer = container_of(c->output_list.prev, + struct weston_output, link); + next_x = peer->pos.c.x + peer->width; + } + output->pos.c.x = next_x; + output->pos.c.y = 0; +} + + /* * Like try_attach_heads, this reorganizes the output's add array into a failed * and successful section. @@ -561,6 +566,8 @@ try_enable_output(struct ivi_output *output, size_t i) for (; i < output->add_len; ++i) { struct weston_head *head; + weston_output_lazy_align(output->output); + if (weston_output_enable(output->output) == 0) break; @@ -583,7 +590,7 @@ try_attach_enable_heads(struct ivi_output *output) fail_len = try_attach_heads(output); - if (configure_output(output) < 0) + if (drm_configure_output(output) < 0) return -1; fail_len = try_enable_output(output, fail_len); @@ -614,7 +621,7 @@ process_output(struct ivi_output *output) } static void -head_disable(struct ivi_compositor *ivi, struct weston_head *head) +drm_head_disable(struct ivi_compositor *ivi, struct weston_head *head) { struct weston_output *output; struct ivi_output *ivi_output; @@ -684,7 +691,7 @@ find_controlling_output_config(struct weston_config *config, } static void -head_prepare_enable(struct ivi_compositor *ivi, struct weston_head *head) +drm_head_prepare_enable(struct ivi_compositor *ivi, struct weston_head *head) { const char *name = weston_head_get_name(head); struct weston_config_section *section; @@ -723,7 +730,7 @@ head_prepare_enable(struct ivi_compositor *ivi, struct weston_head *head) } static void -heads_changed(struct wl_listener *listener, void *arg) +drm_heads_changed(struct wl_listener *listener, void *arg) { struct weston_compositor *compositor = arg; struct weston_head *head = NULL; @@ -737,9 +744,9 @@ heads_changed(struct wl_listener *listener, void *arg) bool non_desktop = weston_head_is_non_desktop(head); if (connected && !enabled && !non_desktop) - head_prepare_enable(ivi, head); + drm_head_prepare_enable(ivi, head); else if (!connected && enabled) - head_disable(ivi, head); + drm_head_disable(ivi, head); else if (enabled && changed) weston_log("Detected a monitor change on head '%s', " "not bothering to do anything about it.\n", @@ -759,6 +766,90 @@ heads_changed(struct wl_listener *listener, void *arg) } } +static void +simple_head_enable(struct ivi_compositor *ivi, struct weston_head *head) +{ + struct ivi_output *output; + struct weston_config_section *section; + char *output_name = NULL; + const char *name = weston_head_get_name(head); + + section = find_controlling_output_config(ivi->config, name); + if (section) { + char *mode; + + weston_config_section_get_string(section, "mode", &mode, NULL); + if (mode && strcmp(mode, "off") == 0) { + free(mode); + return; + } + free(mode); + + weston_config_section_get_string(section, "name", + &output_name, NULL); + } else { + output_name = strdup(name); + } + + if (!output_name) + return; + + output = ivi_ensure_output(ivi, output_name, section, head); + if (!output) { + weston_log("Failed to create output %s\n", output_name); + return; + } + + add_head_destroyed_listener(head); +} + + +static void +simple_head_disable(struct weston_head *head) +{ + struct weston_output *output; + struct wl_listener *listener; + + listener = weston_head_get_destroy_listener(head, handle_head_destroy); + wl_list_empty(&listener->link); + + output = weston_head_get_output(head); + assert(output); + weston_output_destroy(output); +} + + +static void +simple_heads_changed(struct wl_listener *listener, void *arg) +{ + struct weston_compositor *compositor = arg; + struct ivi_compositor *ivi = to_ivi_compositor(compositor); + struct weston_head *head = NULL; + bool connected; + bool enabled; + bool changed; + bool non_desktop; + + while ((head = weston_compositor_iterate_heads(ivi->compositor, head))) { + connected = weston_head_is_connected(head); + enabled = weston_head_is_enabled(head); + changed = weston_head_is_device_changed(head); + non_desktop = weston_head_is_non_desktop(head); + + if (connected && !enabled && !non_desktop) { + simple_head_enable(ivi, head); + } else if (!connected && enabled) { + simple_head_disable(head); + } else if (enabled && changed) { + weston_log("Detected a monitor change on head '%s', " + "not bothering to do anything about it.\n", + weston_head_get_name(head)); + } + weston_head_reset_device_changed(head); + } +} + + #ifdef HAVE_REMOTING static int drm_backend_remoted_output_configure(struct weston_output *output, @@ -942,7 +1033,8 @@ load_remoting_plugin(struct ivi_compositor *ivi, struct weston_config *config) int (*module_init)(struct weston_compositor *wc); module_init = weston_load_module("remoting-plugin.so", - "weston_module_init"); + "weston_module_init", + LIBWESTON_MODULEDIR); if (!module_init) return -1; @@ -963,7 +1055,8 @@ load_remoting_plugin(struct weston_compositor *compositor, struct weston_config #endif static int -load_drm_backend(struct ivi_compositor *ivi, int *argc, char *argv[]) +load_drm_backend(struct ivi_compositor *ivi, int *argc, char *argv[], + enum weston_renderer_type renderer) { struct weston_drm_backend_config config = { .base = { @@ -973,21 +1066,25 @@ load_drm_backend(struct ivi_compositor *ivi, int *argc, char *argv[]) }; struct weston_config_section *section; int use_current_mode = 0; - int use_pixman = 0; + bool force_pixman = false; bool use_shadow; bool without_input = false; - int ret; const struct weston_option options[] = { { WESTON_OPTION_STRING, "seat", 0, &config.seat_id }, { WESTON_OPTION_STRING, "drm-device", 0, &config.specific_device }, { WESTON_OPTION_BOOLEAN, "current-mode", 0, &use_current_mode }, - { WESTON_OPTION_BOOLEAN, "use-pixman", 0, &use_pixman }, + { WESTON_OPTION_BOOLEAN, "use-pixman", 0, &force_pixman }, { WESTON_OPTION_BOOLEAN, "continue-without-input", false, &without_input } }; parse_options(options, ARRAY_LENGTH(options), argc, argv); - config.use_pixman = use_pixman; + + if (force_pixman) + config.renderer = WESTON_RENDERER_PIXMAN; + else + config.renderer = WESTON_RENDERER_AUTO; + ivi->cmdline.use_current_mode = use_current_mode; section = weston_config_get_section(ivi->config, "core", NULL, NULL); @@ -1001,29 +1098,35 @@ load_drm_backend(struct ivi_compositor *ivi, int *argc, char *argv[]) if (without_input) ivi->compositor->require_input = !without_input; - ret = weston_compositor_load_backend(ivi->compositor, WESTON_BACKEND_DRM, - &config.base); - if (ret < 0) - return ret; + ivi->heads_changed.notify = drm_heads_changed; + weston_compositor_add_heads_changed_listener(ivi->compositor, + &ivi->heads_changed); + + if (!weston_compositor_load_backend(ivi->compositor, WESTON_BACKEND_DRM, + &config.base)) { + weston_log("Failed to load DRM backend\n"); + return -1; + } ivi->drm_api = weston_drm_output_get_api(ivi->compositor); if (!ivi->drm_api) { weston_log("Cannot use drm output api.\n"); - ret = -1; goto error; } load_remoting_plugin(ivi, ivi->config); + return 0; + error: free(config.gbm_format); free(config.seat_id); - return ret; + return -1; } static void windowed_parse_common_options(struct ivi_compositor *ivi, int *argc, char *argv[], - bool *use_pixman, bool *fullscreen, int *output_count) + bool *force_pixman, bool *fullscreen, int *output_count) { struct weston_config_section *section; bool pixman; @@ -1033,17 +1136,17 @@ windowed_parse_common_options(struct ivi_compositor *ivi, int *argc, char *argv[ { WESTON_OPTION_INTEGER, "width", 0, &ivi->cmdline.width }, { WESTON_OPTION_INTEGER, "height", 0, &ivi->cmdline.height }, { WESTON_OPTION_INTEGER, "scale", 0, &ivi->cmdline.scale }, - { WESTON_OPTION_BOOLEAN, "use-pixman", 0, &pixman }, + { WESTON_OPTION_BOOLEAN, "use-pixman", 0, &force_pixman }, { WESTON_OPTION_BOOLEAN, "fullscreen", 0, &fs }, { WESTON_OPTION_INTEGER, "output-count", 0, output_count }, }; section = weston_config_get_section(ivi->config, "core", NULL, NULL); - weston_config_section_get_bool(section, "use-pixman", &pixman, 0); + weston_config_section_get_bool(section, "use-pixman", &pixman, false); *output_count = 1; parse_options(options, ARRAY_LENGTH(options), argc, argv); - *use_pixman = pixman; + *force_pixman = pixman; *fullscreen = fs; } @@ -1074,7 +1177,7 @@ windowed_create_outputs(struct ivi_compositor *ivi, int output_count, continue; } - if (ivi->window_api->create_head(ivi->compositor, output_name) < 0) { + if (ivi->window_api->create_head(ivi->backend, output_name) < 0) { free(output_name); return -1; } @@ -1087,7 +1190,7 @@ windowed_create_outputs(struct ivi_compositor *ivi, int output_count, if (asprintf(&default_output, "%s%d", name_prefix, i) < 0) return -1; - if (ivi->window_api->create_head(ivi->compositor, default_output) < 0) { + if (ivi->window_api->create_head(ivi->backend, default_output) < 0) { free(default_output); return -1; } @@ -1098,8 +1201,30 @@ windowed_create_outputs(struct ivi_compositor *ivi, int output_count, return 0; } + +static int +wayland_backend_output_configure(struct weston_output *output) +{ + struct ivi_output *ivi_output = to_ivi_output(output); + + struct ivi_output_config defaults = { + .width = WINDOWED_DEFAULT_WIDTH, + .height = WINDOWED_DEFAULT_HEIGHT, + .scale = 1, + .transform = WL_OUTPUT_TRANSFORM_NORMAL + }; + + if (!ivi_output) { + weston_log("Failed to configure and enable Wayland output. No ivi-output available!\n"); + return -1; + } + + return ivi_configure_windowed_output_from_config(to_ivi_output(output), &defaults); +} + static int -load_wayland_backend(struct ivi_compositor *ivi, int *argc, char *argv[]) +load_wayland_backend(struct ivi_compositor *ivi, int *argc, char *argv[], + enum weston_renderer_type renderer) { struct weston_wayland_backend_config config = { .base = { @@ -1110,14 +1235,14 @@ load_wayland_backend(struct ivi_compositor *ivi, int *argc, char *argv[]) struct weston_config_section *section; int sprawl = 0; int output_count; - int ret; + bool force_pixman = false; const struct weston_option options[] = { { WESTON_OPTION_STRING, "display", 0, &config.display_name }, { WESTON_OPTION_STRING, "sprawl", 0, &sprawl }, }; - windowed_parse_common_options(ivi, argc, argv, &config.use_pixman, + windowed_parse_common_options(ivi, argc, argv, &force_pixman, &config.fullscreen, &output_count); parse_options(options, ARRAY_LENGTH(options), argc, argv); @@ -1129,14 +1254,23 @@ load_wayland_backend(struct ivi_compositor *ivi, int *argc, char *argv[]) weston_config_section_get_int(section, "cursor-size", &config.cursor_size, 32); - ret = weston_compositor_load_backend(ivi->compositor, WESTON_BACKEND_WAYLAND, - &config.base); + ivi->simple_output_configure = wayland_backend_output_configure; + ivi->heads_changed.notify = simple_heads_changed; + weston_compositor_add_heads_changed_listener(ivi->compositor, + &ivi->heads_changed); + + ivi->backend = weston_compositor_load_backend(ivi->compositor, + WESTON_BACKEND_WAYLAND, + &config.base); + if (!ivi->backend) { + weston_log("Failed to create Wayland backend!\n"); + } free(config.cursor_theme); free(config.display_name); - if (ret < 0) - return ret; + if (!ivi->backend) + return -1; ivi->window_api = weston_windowed_output_get_api(ivi->compositor); @@ -1155,7 +1289,29 @@ load_wayland_backend(struct ivi_compositor *ivi, int *argc, char *argv[]) #ifdef HAVE_BACKEND_X11 static int -load_x11_backend(struct ivi_compositor *ivi, int *argc, char *argv[]) +x11_backend_output_configure(struct weston_output *output) +{ + struct ivi_output *ivi_output = to_ivi_output(output); + + struct ivi_output_config defaults = { + .width = WINDOWED_DEFAULT_WIDTH, + .height = WINDOWED_DEFAULT_HEIGHT, + .scale = 1, + .transform = WL_OUTPUT_TRANSFORM_NORMAL + }; + + if (!ivi_output) { + weston_log("Failed to configure and enable X11 output. No ivi-output available!\n"); + return -1; + } + + + return ivi_configure_windowed_output_from_config(ivi_output, &defaults); +} + +static int +load_x11_backend(struct ivi_compositor *ivi, int *argc, char *argv[], + enum weston_renderer_type renderer) { struct weston_x11_backend_config config = { .base = { @@ -1165,91 +1321,48 @@ load_x11_backend(struct ivi_compositor *ivi, int *argc, char *argv[]) }; int no_input = 0; int output_count; - int ret; + bool force_pixman = false; const struct weston_option options[] = { { WESTON_OPTION_BOOLEAN, "no-input", 0, &no_input }, }; - windowed_parse_common_options(ivi, argc, argv, &config.use_pixman, + windowed_parse_common_options(ivi, argc, argv, &force_pixman, &config.fullscreen, &output_count); parse_options(options, ARRAY_LENGTH(options), argc, argv); + if (force_pixman) + config.renderer = WESTON_RENDERER_PIXMAN; + else + config.renderer = WESTON_RENDERER_AUTO; config.no_input = no_input; - ret = weston_compositor_load_backend(ivi->compositor, WESTON_BACKEND_X11, - &config.base); + ivi->simple_output_configure = x11_backend_output_configure; - if (ret < 0) - return ret; + ivi->heads_changed.notify = simple_heads_changed; + weston_compositor_add_heads_changed_listener(ivi->compositor, + &ivi->heads_changed); - ivi->window_api = weston_windowed_output_get_api(ivi->compositor); - if (!ivi->window_api) { - weston_log("Cannot use weston_windowed_output_api.\n"); + ivi->backend = weston_compositor_load_backend(ivi->compositor, + WESTON_BACKEND_X11, + &config.base); + if (!ivi->backend) { + weston_log("Failed to create X11 backend!\n"); return -1; } - return windowed_create_outputs(ivi, output_count, "X", "screen"); -} -#else -static int -load_x11_backend(struct ivi_compositor *ivi, int *argc, char *argv[]) -{ - return -1; -} -#endif - -#ifdef HAVE_BACKEND_HEADLESS -static int -load_headless_backend(struct ivi_compositor *ivi, int *argc, char **argv) -{ - struct weston_headless_backend_config config = {}; - int ret = 0; - - bool use_pixman; - bool fullscreen; - bool use_gl; - int output_count; - - struct weston_compositor *c = ivi->compositor; - - const struct weston_option options[] = { - { WESTON_OPTION_BOOLEAN, "use-pixman", 0, &use_pixman }, - { WESTON_OPTION_BOOLEAN, "use-gl", 0, &use_gl }, - }; - - windowed_parse_common_options(ivi, argc, argv, &use_pixman, - &fullscreen, &output_count); - - parse_options(options, ARRAY_LENGTH(options), argc, argv); - config.use_pixman = use_pixman; - config.use_gl = use_gl; - - config.base.struct_version = WESTON_HEADLESS_BACKEND_CONFIG_VERSION; - config.base.struct_size = sizeof(struct weston_headless_backend_config); - - /* load the actual headless-backend and configure it */ - ret = weston_compositor_load_backend(c, WESTON_BACKEND_HEADLESS, - &config.base); - if (ret < 0) - return ret; - - ivi->window_api = weston_windowed_output_get_api(c); + ivi->window_api = weston_windowed_output_get_api(ivi->compositor); if (!ivi->window_api) { weston_log("Cannot use weston_windowed_output_api.\n"); return -1; } - if (ivi->window_api->create_head(c, "headless") < 0) { - weston_log("Cannot create headless back-end\n"); - return -1; - } - - return 0; + return windowed_create_outputs(ivi, output_count, "X", "screen"); } #else static int -load_headless_backend(struct ivi_compositor *ivi, int *argc, char **argv) +load_x11_backend(struct ivi_compositor *ivi, int *argc, char **argv, + enum weston_renderer_type renderer) { return -1; } @@ -1262,14 +1375,19 @@ weston_rdp_backend_config_init(struct weston_rdp_backend_config *config) config->base.struct_version = WESTON_RDP_BACKEND_CONFIG_VERSION; config->base.struct_size = sizeof(struct weston_rdp_backend_config); + config->renderer = WESTON_RENDERER_AUTO; config->bind_address = NULL; config->port = 3389; config->rdp_key = NULL; config->server_cert = NULL; config->server_key = NULL; config->env_socket = 0; - config->no_clients_resize = 1; + config->external_listener_fd = -1; + config->no_clients_resize = 0; config->force_no_compression = 0; + config->remotefx_codec = true; + config->refresh_rate = RDP_DEFAULT_FREQ; + } static int @@ -1284,6 +1402,7 @@ rdp_backend_output_configure(struct weston_output *output) struct weston_config_section *section; uint32_t transform = WL_OUTPUT_TRANSFORM_NORMAL; char *transform_string; + struct weston_mode new_mode = {}; assert(parsed_options); @@ -1320,23 +1439,25 @@ rdp_backend_output_configure(struct weston_output *output) return -1; } - weston_output_set_transform(output, transform); - if (api->output_set_size(output, width, height) < 0) { - weston_log("Cannot configure output \"%s\" using weston_rdp_output_api.\n", - output->name); - return -1; - } + new_mode.width = width; + new_mode.height = height; + + weston_log("Setting modeline to %dx%d\n", width, height); + + api->output_set_mode(output, &new_mode); + weston_output_set_transform(output, transform); return 0; } static int -load_rdp_backend(struct ivi_compositor *ivi, int *argc, char **argv) +load_rdp_backend(struct ivi_compositor *ivi, int *argc, char **argv, + enum weston_renderer_type renderer) { struct weston_rdp_backend_config config = {}; - int ret = 0; struct weston_config_section *section; + bool no_remotefx_codec = false; struct ivi_output_config *parsed_options = ivi_init_parsed_options(ivi->compositor); if (!parsed_options) @@ -1346,21 +1467,28 @@ load_rdp_backend(struct ivi_compositor *ivi, int *argc, char **argv) const struct weston_option rdp_options[] = { { WESTON_OPTION_BOOLEAN, "env-socket", 0, &config.env_socket }, + { WESTON_OPTION_INTEGER, "external-listener-fd", 0, &config.external_listener_fd }, { WESTON_OPTION_INTEGER, "width", 0, &parsed_options->width }, { WESTON_OPTION_INTEGER, "height", 0, &parsed_options->height }, - { WESTON_OPTION_INTEGER, "transform", 0, &parsed_options->transform }, - { WESTON_OPTION_INTEGER, "scale", 0, &parsed_options->scale }, { WESTON_OPTION_STRING, "address", 0, &config.bind_address }, { WESTON_OPTION_INTEGER, "port", 0, &config.port }, { WESTON_OPTION_BOOLEAN, "no-clients-resize", 0, &config.no_clients_resize }, { WESTON_OPTION_STRING, "rdp4-key", 0, &config.rdp_key }, { WESTON_OPTION_STRING, "rdp-tls-cert", 0, &config.server_cert }, { WESTON_OPTION_STRING, "rdp-tls-key", 0, &config.server_key }, + { WESTON_OPTION_INTEGER, "scale", 0, &parsed_options->scale }, { WESTON_OPTION_BOOLEAN, "force-no-compression", 0, &config.force_no_compression }, + { WESTON_OPTION_BOOLEAN, "no-remotefx-codec", 0, &no_remotefx_codec }, }; + config.remotefx_codec = !no_remotefx_codec; + config.renderer = renderer; + section = weston_config_get_section(ivi->config, "rdp", NULL, NULL); + weston_config_section_get_int(section, "refresh-rate", + &config.refresh_rate, RDP_DEFAULT_FREQ); + weston_config_section_get_string(section, "tls-cert", &config.server_cert, config.server_cert); @@ -1369,20 +1497,31 @@ load_rdp_backend(struct ivi_compositor *ivi, int *argc, char **argv) parse_options(rdp_options, ARRAY_LENGTH(rdp_options), argc, argv); + weston_log("No clients resize: %d\n", config.no_clients_resize); ivi->simple_output_configure = rdp_backend_output_configure; - ret = weston_compositor_load_backend(ivi->compositor, WESTON_BACKEND_RDP, &config.base); + + ivi->heads_changed.notify = simple_heads_changed; + weston_compositor_add_heads_changed_listener(ivi->compositor, + &ivi->heads_changed); + + if (!weston_compositor_load_backend(ivi->compositor, + WESTON_BACKEND_RDP, &config.base)) { + weston_log("Failed to create RDP backend\n"); + return -1; + } free(config.bind_address); free(config.rdp_key); free(config.server_cert); free(config.server_key); - return ret; + return 0; } #else static int -load_rdp_backend(struct ivi_compositor *ivi, int *argc, char **argv) +load_rdp_backend(struct ivi_compositor *ivi, int *argc, char **argv, + enum weston_renderer_type renderer) { return -1; } @@ -1390,23 +1529,36 @@ load_rdp_backend(struct ivi_compositor *ivi, int *argc, char **argv) static int -load_backend(struct ivi_compositor *ivi, const char *backend, - int *argc, char *argv[]) -{ - if (strcmp(backend, "drm-backend.so") == 0) { - return load_drm_backend(ivi, argc, argv); - } else if (strcmp(backend, "wayland-backend.so") == 0) { - return load_wayland_backend(ivi, argc, argv); - } else if (strcmp(backend, "x11-backend.so") == 0) { - return load_x11_backend(ivi, argc, argv); - } else if (strcmp(backend, "headless-backend.so") == 0) { - return load_headless_backend(ivi, argc, argv); - } else if (strcmp(backend, "rdp-backend.so") == 0) { - return load_rdp_backend(ivi, argc, argv); - } - - weston_log("fatal: unknown backend '%s'.\n", backend); - return -1; +load_backend(struct ivi_compositor *ivi, int *argc, char **argv, + const char *backend_name, const char *renderer_name) +{ + enum weston_compositor_backend backend; + enum weston_renderer_type renderer; + + if (!get_backend_from_string(backend_name, &backend)) { + weston_log("Error: unknown backend \"%s\"\n", backend_name); + return -1; + } + + if (!get_renderer_from_string(renderer_name, &renderer)) { + weston_log("Error: unknown renderer \"%s\"\n", renderer_name); + return -1; + } + + switch (backend) { + case WESTON_BACKEND_DRM: + return load_drm_backend(ivi, argc, argv, renderer); + case WESTON_BACKEND_RDP: + return load_rdp_backend(ivi, argc, argv, renderer); + case WESTON_BACKEND_WAYLAND: + return load_wayland_backend(ivi, argc, argv, renderer); + case WESTON_BACKEND_X11: + return load_x11_backend(ivi, argc, argv, renderer); + default: + assert(!"unknown backend type in load_backend()"); + } + + return 0; } static int @@ -1430,7 +1582,7 @@ load_modules(struct ivi_compositor *ivi, const char *modules, } else if (strstr(buffer, "systemd-notify.so")) { weston_log("systemd-notify plug-in already loaded!\n"); } else { - module_init = weston_load_module(buffer, "wet_module_init"); + module_init = weston_load_module(buffer, "wet_module_init", WESTON_MODULEDIR); if (!module_init) return -1; @@ -1693,7 +1845,7 @@ log_timestamp(char *buf, size_t len) strftime(timestr, sizeof(timestr), "%H:%M:%S", brokendown_time); /* if datestr is empty it prints only timestr*/ - snprintf(buf, len, "%s[%s.%03li]", datestr, + snprintf(buf, len, "%s[%s.%03"PRIi64"]", datestr, timestr, (tv.tv_usec / 1000)); return buf; @@ -1802,6 +1954,7 @@ usage(int error_code) "\t\t\t\twayland-backend.so\n" "\t\t\t\tx11-backend.so\n" "\t\t\t\theadless-backend.so\n" + " -r, --renderer=NAME\tName of renderer to use: auto, gl, noop, pixman\n" " -S, --socket=NAME\tName of socket to listen on\n" " --log=FILE\t\tLog to the given file\n" " -c, --config=FILE\tConfig file to load, defaults to agl-compositor.ini\n" @@ -1833,12 +1986,12 @@ copy_command_line(int argc, char * const argv[]) } #if !defined(BUILD_XWAYLAND) -int +void * wet_load_xwayland(struct weston_compositor *comp) { weston_log("Attempted to load xwayland library but compositor " "was *not* built with xwayland support!\n"); - return -1; + return NULL; } #endif @@ -1897,8 +2050,11 @@ int wet_main(int argc, char *argv[], const struct weston_testsuite_data *test_da int ret = EXIT_FAILURE; bool xwayland = false; struct sigaction action; + char *renderer = NULL; + struct wet_process *process, *process_tmp; const struct weston_option core_options[] = { + { WESTON_OPTION_STRING, "renderer", 'r', &renderer }, { WESTON_OPTION_STRING, "backend", 'B', &backend }, { WESTON_OPTION_STRING, "socket", 'S', &socket_name }, { WESTON_OPTION_STRING, "log", 0, &log }, @@ -2011,16 +2167,16 @@ int wet_main(int argc, char *argv[], const struct weston_testsuite_data *test_da if (compositor_init_config(&ivi) < 0) goto error_compositor; - if (load_backend(&ivi, backend, &argc, argv) < 0) { + if (load_backend(&ivi, &argc, argv, backend, renderer) < 0) { weston_log("fatal: failed to create compositor backend.\n"); goto error_compositor; } - ivi.heads_changed.notify = heads_changed; - weston_compositor_add_heads_changed_listener(ivi.compositor, - &ivi.heads_changed); + if (weston_compositor_backends_loaded(ivi.compositor) < 0) + goto error_compositor; weston_compositor_flush_heads_changed(ivi.compositor); + if (ivi_desktop_init(&ivi) < 0) goto error_compositor; @@ -2043,7 +2199,7 @@ int wet_main(int argc, char *argv[], const struct weston_testsuite_data *test_da goto error_compositor; if (xwayland) { - if (wet_load_xwayland(ivi.compositor) < 0) + if (!wet_load_xwayland(ivi.compositor)) goto error_compositor; } @@ -2088,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); @@ -2114,6 +2273,9 @@ error_compositor: ivi_policy_destroy(ivi.policy); + wl_list_for_each_safe(process, process_tmp, &ivi.child_process_list, link) + ivi_process_destroy(process, 0, false); + error_signals: for (size_t i = 0; i < ARRAY_LENGTH(signals); ++i) if (signals[i]) diff --git a/src/desktop.c b/src/desktop.c index 7875eb9..d3c1d4e 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -29,7 +29,7 @@ #include "shared/helpers.h" #include <libweston/libweston.h> -#include <libweston-desktop/libweston-desktop.h> +#include <libweston/desktop.h> #ifdef BUILD_XWAYLAND #include <libweston/xwayland-api.h> #endif @@ -37,6 +37,20 @@ #include "agl-shell-desktop-server-protocol.h" static void +ivi_layout_destroy_saved_outputs(struct ivi_compositor *ivi) +{ + struct ivi_output *output, *output_next; + + wl_list_for_each_safe(output, output_next, &ivi->saved_outputs, link) { + free(output->app_ids); + free(output->name); + + wl_list_remove(&output->link); + free(output); + } +} + +static void desktop_advertise_app(struct wl_listener *listener, void *data) { struct ivi_surface *surface; @@ -318,7 +332,7 @@ desktop_surface_removed(struct weston_desktop_surface *dsurface, void *userdata) output->active->view->is_mapped = false; output->active->view->surface->is_mapped = false; - weston_layer_entry_remove(&output->active->view->layer_link); + weston_view_move_to_layer(output->active->view, NULL); output->active = NULL; } @@ -392,7 +406,7 @@ skip_output_asignment: static void desktop_committed(struct weston_desktop_surface *dsurface, - int32_t sx, int32_t sy, void *userdata) + struct weston_coord_surface buf_offset, void *userdata) { struct ivi_compositor *ivi = userdata; struct ivi_surface *surface = @@ -470,7 +484,7 @@ desktop_committed(struct weston_desktop_surface *dsurface, static void desktop_show_window_menu(struct weston_desktop_surface *dsurface, - struct weston_seat *seat, int32_t x, int32_t y, + struct weston_seat *seat, struct weston_coord_surface offset, void *userdata) { /* not supported */ @@ -522,13 +536,13 @@ desktop_minimized_requested(struct weston_desktop_surface *dsurface, static void desktop_set_xwayland_position(struct weston_desktop_surface *dsurface, - int32_t x, int32_t y, void *userdata) + struct weston_coord_global pos, void *userdata) { struct ivi_surface *ivisurf = weston_desktop_surface_get_user_data(dsurface); - ivisurf->xwayland.x = x; - ivisurf->xwayland.y = y; + ivisurf->xwayland.x = pos.c.x; + ivisurf->xwayland.y = pos.c.y; ivisurf->xwayland.is_set = true; } @@ -588,8 +602,8 @@ transform_handler(struct wl_listener *listener, void *data) if (!weston_view_is_mapped(ivisurf->view)) return; - x = ivisurf->view->geometry.x; - y = ivisurf->view->geometry.y; + x = ivisurf->view->geometry.pos_offset.x; + y = ivisurf->view->geometry.pos_offset.y; api->send_position(surface, x, y); #endif diff --git a/src/ivi-compositor.h b/src/ivi-compositor.h index 62e70e1..695cf95 100644 --- a/src/ivi-compositor.h +++ b/src/ivi-compositor.h @@ -29,10 +29,11 @@ #include <stdbool.h> #include "config.h" +#include <weston.h> #include <libweston/backend-drm.h> #include <libweston/libweston.h> #include <libweston/windowed-output-api.h> -#include <libweston-desktop/libweston-desktop.h> +#include <libweston/desktop.h> #include "remote.h" @@ -60,6 +61,7 @@ struct ivi_output_config { struct ivi_compositor { struct weston_compositor *compositor; + struct weston_backend *backend; struct weston_config *config; struct ivi_output_config *parsed_options; @@ -71,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. @@ -169,6 +173,7 @@ struct ivi_output { struct fullscreen_view { struct ivi_surface *fs; struct wl_listener fs_destroy; + struct weston_buffer_reference *buffer_ref; } fullscreen_view; struct wl_listener output_destroy; @@ -530,7 +535,10 @@ void shell_send_app_state(struct ivi_compositor *ivi, const char *app_id, enum agl_shell_app_state state); void -ivi_layout_destroy_saved_outputs(struct ivi_compositor *ivi); +ivi_layout_restore(struct ivi_compositor *ivi, struct ivi_output *n_output); + +void +ivi_layout_save(struct ivi_compositor *ivi, struct ivi_output *output); struct weston_output * get_default_output(struct weston_compositor *compositor); @@ -561,4 +569,7 @@ _ivi_set_shell_surface_split(struct ivi_surface *surface, struct ivi_output *out struct ivi_output_config * ivi_init_parsed_options(struct weston_compositor *compositor); +void +ivi_process_destroy(struct wet_process *process, int status, bool call_cleanup); + #endif diff --git a/src/layout.c b/src/layout.c index a06b1e5..48c478e 100644 --- a/src/layout.c +++ b/src/layout.c @@ -1,5 +1,5 @@ /* - * Copyright © 2019 Collabora, Ltd. + * Copyright © 2019, 2024 Collabora, Ltd. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -32,7 +32,7 @@ #include <libweston/config-parser.h> #include <libweston/libweston.h> -#include <libweston-desktop/libweston-desktop.h> +#include <libweston/desktop.h> #include "agl-shell-desktop-server-protocol.h" @@ -54,6 +54,81 @@ static const char *ivi_roles_as_string[] = { bool ivi_surf_in_hidden_layer(struct ivi_compositor *ivi, struct ivi_surface *surface); +void +ivi_layout_save(struct ivi_compositor *ivi, struct ivi_output *output) +{ + struct ivi_output *new_output; + ivi->need_ivi_output_relayout = true; + + new_output = zalloc(sizeof(*new_output)); + + new_output->ivi = ivi; + new_output->background = output->background; + + new_output->top = output->top; + new_output->bottom = output->bottom; + new_output->left = output->left; + new_output->right = output->right; + + new_output->active = output->active; + new_output->previous_active = output->previous_active; + new_output->name = strdup(output->name); + if (output->app_ids) + new_output->app_ids = strdup(output->app_ids); + + new_output->area = output->area; + new_output->area_saved = output->area_saved; + new_output->area_activation = output->area_activation; + + weston_log("saving output layout for output %s\n", new_output->name); + + wl_list_insert(&ivi->saved_outputs, &new_output->link); +} + +void +ivi_layout_restore(struct ivi_compositor *ivi, struct ivi_output *n_output) +{ + struct ivi_output *output = NULL; + struct ivi_output *iter_output; + + if (!ivi->need_ivi_output_relayout) + return; + + ivi->need_ivi_output_relayout = false; + + wl_list_for_each(iter_output, &ivi->saved_outputs, link) { + if (strcmp(n_output->name, iter_output->name) == 0) { + output = iter_output; + break; + } + } + + if (!output) + return; + + weston_log("restoring output layout for output %s\n", output->name); + n_output->background = output->background; + + n_output->top = output->top; + n_output->bottom = output->bottom; + n_output->left = output->left; + n_output->right = output->right; + + n_output->active = output->active; + n_output->previous_active = output->previous_active; + if (output->app_ids) + n_output->app_ids = strdup(output->app_ids); + + n_output->area = output->area; + n_output->area_saved = output->area_saved; + n_output->area_activation = output->area_activation; + + free(output->app_ids); + free(output->name); + wl_list_remove(&output->link); + free(output); +} + const char * ivi_layout_get_surface_role_name(struct ivi_surface *surf) { @@ -69,6 +144,8 @@ ivi_background_init(struct ivi_compositor *ivi, struct ivi_output *output) struct weston_output *woutput = output->output; struct ivi_surface *bg = output->background; struct weston_view *view; + struct weston_surface *wsurface = + weston_desktop_surface_get_surface(bg->dsurface); if (!bg) { weston_log("WARNING: Output does not have a background\n"); @@ -78,17 +155,15 @@ ivi_background_init(struct ivi_compositor *ivi, struct ivi_output *output) assert(bg->role == IVI_SURFACE_ROLE_BACKGROUND); view = bg->view; + weston_surface_map(wsurface); weston_view_set_output(view, woutput); - weston_view_set_position(view, woutput->x, woutput->y); + weston_view_set_position(view, woutput->pos); - weston_log("(background) position view %p, x %d, y %d, on output %s\n", view, - woutput->x, woutput->y, output->name); + weston_view_move_to_layer(view, &ivi->background.view_list); + weston_log("(background) position view %p, x %f, y %f, on output %s\n", view, + woutput->pos.c.x, woutput->pos.c.y, output->name); - view->is_mapped = true; - view->surface->is_mapped = true; - - weston_layer_entry_insert(&ivi->background.view_list, &view->layer_link); } static void @@ -99,8 +174,8 @@ ivi_panel_init(struct ivi_compositor *ivi, struct ivi_output *output, struct weston_desktop_surface *dsurface; struct weston_view *view; struct weston_geometry geom; - int x = woutput->x; - int y = woutput->y; + struct weston_coord_global pos = woutput->pos; + struct weston_surface *wsurface; if (!panel) return; @@ -109,6 +184,7 @@ ivi_panel_init(struct ivi_compositor *ivi, struct ivi_output *output, dsurface = panel->dsurface; view = panel->view; geom = weston_desktop_surface_get_geometry(dsurface); + wsurface = weston_desktop_surface_get_surface(panel->dsurface); weston_log("(panel) geom.width %d, geom.height %d, geom.x %d, geom.y %d\n", geom.width, geom.height, geom.x, geom.y); @@ -119,7 +195,7 @@ ivi_panel_init(struct ivi_compositor *ivi, struct ivi_output *output, output->area.height -= geom.height; break; case AGL_SHELL_EDGE_BOTTOM: - y += woutput->height - geom.height; + pos.c.y += woutput->height - geom.height; output->area.height -= geom.height; break; case AGL_SHELL_EDGE_LEFT: @@ -127,27 +203,25 @@ ivi_panel_init(struct ivi_compositor *ivi, struct ivi_output *output, output->area.width -= geom.width; break; case AGL_SHELL_EDGE_RIGHT: - x += woutput->width - geom.width; + pos.c.x += woutput->width - geom.width; output->area.width -= geom.width; break; } - x -= geom.x; - y -= geom.y; + pos.c.x -= geom.x; + pos.c.y -= geom.y; weston_view_set_output(view, woutput); - weston_view_set_position(view, x, y); + weston_view_set_position(view, pos); - weston_log("(panel) edge %d position view %p, x %d, y %d\n", - panel->panel.edge, view, x, y); + weston_surface_map(wsurface); + weston_view_move_to_layer(view, &ivi->panel.view_list); - view->is_mapped = true; - view->surface->is_mapped = true; + weston_log("(panel) edge %d position view %p, x %f, y %f\n", + panel->panel.edge, view, pos.c.x, pos.c.y); weston_log("panel type %d inited on output %s\n", panel->panel.edge, output->name); - - weston_layer_entry_insert(&ivi->panel.view_list, &view->layer_link); } /* @@ -247,6 +321,7 @@ ivi_layout_activate_complete(struct ivi_output *output, struct ivi_shell_seat *ivi_seat = get_ivi_shell_seat(wseat); const char *app_id = weston_desktop_surface_get_app_id(surf->dsurface); bool update_previous = true; + struct weston_coord_global pos; if (weston_view_is_mapped(view)) { weston_layer_entry_remove(&view->layer_link); @@ -280,10 +355,11 @@ ivi_layout_activate_complete(struct ivi_output *output, /* drop any previous masks set on this view */ weston_view_set_mask_infinite(view); - if (surf->role != IVI_SURFACE_ROLE_BACKGROUND) - weston_view_set_position(view, - woutput->x + output->area.x, - woutput->y + output->area.y); + if (surf->role != IVI_SURFACE_ROLE_BACKGROUND) { + pos.c.x = woutput->pos.c.x + output->area.x; + pos.c.y = woutput->pos.c.y + output->area.y; + weston_view_set_position(view, pos); + } /* reset any previous orientation */ if (surf->orientation != AGL_SHELL_TILE_ORIENTATION_NONE && @@ -720,7 +796,7 @@ ivi_layout_fullscreen_committed(struct ivi_surface *surface) weston_layer_entry_remove(&view->layer_link); weston_view_set_output(view, woutput); - weston_view_set_position(view, woutput->x, woutput->y); + weston_view_set_position(view, woutput->pos); weston_layer_entry_insert(&ivi->fullscreen.view_list, &view->layer_link); wsurface->is_mapped = true; @@ -752,15 +828,17 @@ ivi_layout_desktop_resize(struct ivi_surface *surface, struct weston_desktop_surface *dsurf = surface->dsurface; struct weston_view *view = surface->view; - int x = area.x; - int y = area.y; + struct weston_coord_global pos; int width = area.width; int height = area.height; + pos.c.x = area.x; + pos.c.y = area.y; + weston_desktop_surface_set_size(dsurf, width, height); - weston_view_set_position(view, x, y); + weston_view_set_position(view, pos); weston_view_geometry_dirty(view); weston_surface_damage(view->surface); @@ -786,12 +864,9 @@ ivi_layout_split_committed(struct ivi_surface *surface) struct weston_view *view = surface->view; struct weston_geometry geom; - int x, y; + struct weston_coord_global pos = woutput->pos; int width, height; - x = woutput->x; - y = woutput->y; - if (policy && policy->api.surface_activate_by_default && !policy->api.surface_activate_by_default(surface, surface->ivi) && !surface->mapped) @@ -813,25 +888,25 @@ ivi_layout_split_committed(struct ivi_surface *surface) case IVI_SURFACE_ROLE_SPLIT_V: geom.width = (output->area.width / 2); - x += woutput->width - geom.width; + pos.c.x += woutput->width - geom.width; output->area.width -= geom.width; - width = woutput->width - x; + width = woutput->width - pos.c.x; height = output->area.height; - y = output->area.y; + pos.c.y = output->area.y; break; case IVI_SURFACE_ROLE_SPLIT_H: geom.height = (output->area.height / 2); - y = output->area.y; + pos.c.y = output->area.y; output->area.y += geom.height; output->area.height -= geom.height; width = output->area.width; height = output->area.height; - x = output->area.x; + pos.c.x = output->area.x; break; default: @@ -847,7 +922,7 @@ ivi_layout_split_committed(struct ivi_surface *surface) ivi_layout_desktop_resize(output->active, output->area); weston_view_set_output(view, woutput); - weston_view_set_position(view, x, y); + weston_view_set_position(view, pos); weston_layer_entry_insert(&ivi->normal.view_list, &view->layer_link); weston_view_geometry_dirty(view); @@ -868,10 +943,10 @@ ivi_layout_split_committed(struct ivi_surface *surface) static void ivi_compute_popup_position(const struct weston_output *output, struct weston_view *view, - int initial_x, int initial_y, int *new_x, int *new_y) + int initial_x, int initial_y, double *new_x, double *new_y) { - *new_x = output->x + initial_x; - *new_y = output->y + initial_y; + *new_x = output->pos.c.x + initial_x; + *new_y = output->pos.c.y + initial_y; } @@ -904,7 +979,7 @@ ivi_layout_popup_committed(struct ivi_surface *surface) weston_desktop_surface_get_surface(dsurface); const char *app_id = weston_desktop_surface_get_app_id(dsurface); - int new_x, new_y; + struct weston_coord_global pos; struct ivi_output *output = surface->popup.output; struct weston_output *woutput = output->output; @@ -931,8 +1006,8 @@ ivi_layout_popup_committed(struct ivi_surface *surface) weston_view_set_output(view, woutput); ivi_compute_popup_position(woutput, view, - surface->popup.x, surface->popup.y, &new_x, &new_y); - weston_view_set_position(view, new_x, new_y); + surface->popup.x, surface->popup.y, &pos.c.x, &pos.c.y); + weston_view_set_position(view, pos); weston_view_update_transform(view); /* only clip the pop-up dialog window if we have a valid diff --git a/src/screenshooter.c b/src/screenshooter.c deleted file mode 100644 index ef3d32e..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 <libweston/libweston.h> -#include "agl-screenshooter-server-protocol.h" -#include <libweston/weston-log.h> - -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(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"); -} diff --git a/src/shell.c b/src/shell.c index 16d924c..c29d89e 100644 --- a/src/shell.c +++ b/src/shell.c @@ -1,5 +1,5 @@ /* - * Copyright © 2019, 2022 Collabora, Ltd. + * Copyright © 2019, 2022, 2024 Collabora, Ltd. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -778,7 +778,7 @@ ivi_shell_finalize(struct ivi_compositor *ivi) wl_list_for_each(output, &ivi->outputs, link) { if (output->fullscreen_view.fs && output->fullscreen_view.fs->view) { - weston_surface_destroy(output->fullscreen_view.fs->view->surface); + weston_surface_unref(output->fullscreen_view.fs->view->surface); output->fullscreen_view.fs->view = NULL; } } @@ -803,9 +803,9 @@ ivi_shell_advertise_xdg_surfaces(struct ivi_compositor *ivi, struct wl_resource static struct wl_client * client_launch(struct weston_compositor *compositor, - struct weston_process *proc, + struct wet_process *proc, const char *path, - weston_process_cleanup_func_t cleanup) + wet_process_cleanup_func_t cleanup) { struct wl_client *client = NULL; struct custom_env child_env; @@ -910,14 +910,14 @@ client_launch(struct weston_compositor *compositor, } struct process_info { - struct weston_process proc; + struct wet_process proc; char *path; }; int sigchld_handler(int signal_number, void *data) { - struct weston_process *p; + struct wet_process *p; struct ivi_compositor *ivi = data; int status; pid_t pid; @@ -935,7 +935,7 @@ sigchld_handler(int signal_number, void *data) wl_list_remove(&p->link); wl_list_init(&p->link); - p->cleanup(p, status); + p->cleanup(p, status, NULL); } if (pid < 0 && errno != ECHILD) @@ -946,7 +946,7 @@ sigchld_handler(int signal_number, void *data) static void -process_handle_sigchld(struct weston_process *process, int status) +process_handle_sigchld(struct wet_process *process, int status, void *data) { struct process_info *pinfo = container_of(process, struct process_info, proc); @@ -1022,41 +1022,72 @@ destroy_black_curtain_view(struct wl_listener *listener, void *data) } +int +curtain_get_label(struct weston_surface *surface, char *buf, size_t len) +{ + return snprintf(buf, len, "%s", "black curtain"); +} + +static void +curtain_surface_committed(struct weston_surface *es, struct weston_coord_surface new_origin) +{ + +} + + static void create_black_curtain_view(struct ivi_output *output) { struct weston_surface *surface = NULL; struct weston_view *view; struct ivi_compositor *ivi = output->ivi; - struct weston_compositor *wc= ivi->compositor; + struct weston_compositor *ec = ivi->compositor; struct weston_output *woutput = output->output; + struct weston_buffer_reference *buffer_ref; if (!woutput) return; - surface = weston_surface_create(wc); + surface = weston_surface_create(ec); if (!surface) return; + view = weston_view_create(surface); - if (!view) { - weston_surface_destroy(surface); - return; - } + if (!view) + goto err_surface; + + buffer_ref = weston_buffer_create_solid_rgba(ec, 0.0, 0.0, 0.0, 1.0); + if (buffer_ref == NULL) + goto err_view; - weston_surface_set_color(surface, 0.0, 0.0, 0.0, 1); + surface->committed = curtain_surface_committed; + surface->committed_private = NULL; weston_surface_set_size(surface, woutput->width, woutput->height); - weston_view_set_position(view, woutput->x, woutput->y); + + weston_surface_attach_solid(surface, buffer_ref, + woutput->width, woutput->height); + + weston_surface_set_label_func(surface, curtain_get_label); + weston_view_set_position(view, woutput->pos); output->fullscreen_view.fs = zalloc(sizeof(struct ivi_surface)); - if (!output->fullscreen_view.fs) { - weston_surface_destroy(surface); - return; - } + if (!output->fullscreen_view.fs) + goto err_view; + output->fullscreen_view.fs->view = view; + output->fullscreen_view.buffer_ref = buffer_ref; - output->fullscreen_view.fs_destroy.notify = destroy_black_curtain_view; + output->fullscreen_view.fs_destroy.notify = + destroy_black_curtain_view; wl_signal_add(&woutput->destroy_signal, &output->fullscreen_view.fs_destroy); + + return; + +err_view: + weston_view_destroy(view); +err_surface: + weston_surface_unref(surface); } bool @@ -1072,6 +1103,7 @@ void remove_black_curtain(struct ivi_output *output) { struct weston_view *view; + struct weston_surface *wsurface; if ((!output && !output->fullscreen_view.fs && @@ -1082,17 +1114,13 @@ remove_black_curtain(struct ivi_output *output) } view = output->fullscreen_view.fs->view; + wsurface = view->surface; assert(view->is_mapped == true || view->surface->is_mapped == true); - view->is_mapped = false; - view->surface->is_mapped = false; - - weston_layer_entry_remove(&view->layer_link); - weston_view_update_transform(view); - - weston_view_damage_below(view); + weston_surface_unmap(wsurface); + weston_view_move_to_layer(view, NULL); weston_log("Removed black curtain from output %s\n", output->output->name); } @@ -1100,29 +1128,23 @@ void insert_black_curtain(struct ivi_output *output) { struct weston_view *view; + struct weston_surface *wsurface; if ((!output && !output->fullscreen_view.fs && - !output->fullscreen_view.fs->view) || !output->output || - !output->fullscreen_view.fs) { + !output->fullscreen_view.fs->view) || + !output->output || !output->fullscreen_view.fs) { weston_log("Output %s doesn't have a surface installed!\n", output->name); return; } view = output->fullscreen_view.fs->view; + wsurface = view->surface; if (view->is_mapped || view->surface->is_mapped) return; - weston_layer_entry_remove(&view->layer_link); - weston_layer_entry_insert(&output->ivi->fullscreen.view_list, - &view->layer_link); - - view->is_mapped = true; - view->surface->is_mapped = true; - - weston_view_update_transform(view); - weston_view_damage_below(view); - + weston_surface_map(wsurface); + weston_view_move_to_layer(view, &output->ivi->fullscreen.view_list); weston_log("Added black curtain to output %s\n", output->output->name); } @@ -1733,13 +1755,16 @@ static void shell_set_app_position(struct wl_client *client, struct wl_resource *res, const char *app_id, int32_t x, int32_t y) { + struct weston_coord_global pos; struct ivi_compositor *ivi = wl_resource_get_user_data(res); struct ivi_surface *surf = ivi_find_app(ivi, app_id); if (!surf || !app_id || surf->role != IVI_SURFACE_ROLE_POPUP) return; - weston_view_set_position(surf->view, x, y); + pos.c.x = x; + pos.c.y = y; + weston_view_set_position(surf->view, pos); weston_compositor_schedule_repaint(ivi->compositor); } @@ -1832,6 +1857,7 @@ _ivi_set_shell_surface_split(struct ivi_surface *surface, struct ivi_output *iou struct weston_view *ev = surface->view; struct weston_geometry geom = {}; struct ivi_output *output = NULL; + struct weston_coord_global pos; int new_width, new_height; int x, y; @@ -1936,7 +1962,10 @@ _ivi_set_shell_surface_split(struct ivi_surface *surface, struct ivi_output *iou ivi_shell_activate_surface(surface, ivi_seat, WESTON_ACTIVATE_FLAG_NONE); } - weston_view_set_position(surface->view, x, y); + pos.c.x = x; + pos.c.y = y; + + weston_view_set_position(surface->view, pos); weston_desktop_surface_set_size(surface->dsurface, new_width, new_height); weston_desktop_surface_set_orientation(surface->dsurface, orientation); surface->orientation = orientation; diff --git a/src/xwayland.c b/src/xwayland.c index 8ac5edc..3bea411 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -31,194 +31,338 @@ #include <string.h> #include <errno.h> #include <sys/socket.h> +#include <fcntl.h> #include <libweston/libweston.h> #include <weston.h> #include <libweston/xwayland-api.h> #include "shared/helpers.h" +#include "shared/process-util.h" +#include "shared/xalloc.h" + +#ifdef HAVE_XWAYLAND_LISTENFD +# define LISTEN_STR "-listenfd" +#else +# define LISTEN_STR "-listen" +#endif struct wet_xwayland { - struct ivi_compositor *ivi_compositor; + struct weston_compositor *compositor; const struct weston_xwayland_api *api; struct weston_xwayland *xwayland; - struct wl_event_source *sigusr1_source; - struct wl_client *client; + struct wl_event_source *display_fd_source; int wm_fd; - struct weston_process process; + struct wet_process *process; }; static int -handle_sigusr1(int signal_number, void *data) +handle_display_fd(int fd, uint32_t mask, void *data) { struct wet_xwayland *wxw = data; + char buf[64]; + ssize_t n; + + /* xwayland exited before being ready, don't finish initialization, + * the process watcher will cleanup */ + if (!(mask & WL_EVENT_READABLE)) + goto out; + + /* Xwayland writes to the pipe twice, so if we close it too early + * it's possible the second write will fail and Xwayland shuts down. + * Make sure we read until end of line marker to avoid this. */ + n = read(fd, buf, sizeof buf); + if (n < 0 && errno != EAGAIN) { + weston_log("read from Xwayland display_fd failed: %s\n", + strerror(errno)); + goto out; + } + /* Returning 1 here means recheck and call us again if required. */ + if (n <= 0 || (n > 0 && buf[n - 1] != '\n')) + return 1; + + wxw->api->xserver_loaded(wxw->xwayland, wxw->wm_fd); + +out: + wl_event_source_remove(wxw->display_fd_source); + close(fd); + + return 0; +} + + +static void +xserver_cleanup(struct wet_process *process, int status, void *data) +{ + struct wet_xwayland *wxw = data; + + /* We only have one Xwayland process active, so make sure it's the + * right one */ + assert(process == wxw->process); + + wxw->api->xserver_exited(wxw->xwayland); + wxw->process = NULL; +} +static void +cleanup_for_child_process() { + sigset_t allsigs; - /* We'd be safer if we actually had the struct - * signalfd_siginfo from the signalfd data and could verify - * this came from Xwayland.*/ - wxw->api->xserver_loaded(wxw->xwayland, wxw->client, wxw->wm_fd); - wl_event_source_remove(wxw->sigusr1_source); + /* Put the client in a new session so it won't catch signals + * intended for the parent. Sharing a session can be + * confusing when launching weston under gdb, as the ctrl-c + * intended for gdb will pass to the child, and weston + * will cleanly shut down when the child exits. + */ + setsid(); - return 1; + /* do not give our signal mask to the new process */ + sigfillset(&allsigs); + sigprocmask(SIG_UNBLOCK, &allsigs, NULL); } -static pid_t +static struct wet_process * +client_launch(struct weston_compositor *compositor, + struct custom_env *child_env, + int *no_cloexec_fds, + size_t num_no_cloexec_fds, + wet_process_cleanup_func_t cleanup, + void *cleanup_data) +{ + struct ivi_compositor *ivi = to_ivi_compositor(compositor); + struct wet_process *proc = NULL; + const char *fail_cloexec = "Couldn't unset CLOEXEC on child FDs"; + const char *fail_seteuid = "Couldn't call seteuid"; + char *fail_exec; + char * const *argp; + char * const *envp; + pid_t pid; + int err; + size_t i; + size_t written __attribute__((unused)); + + argp = custom_env_get_argp(child_env); + envp = custom_env_get_envp(child_env); + + weston_log("launching '%s'\n", argp[0]); + str_printf(&fail_exec, "Error: Couldn't launch client '%s'\n", argp[0]); + + pid = fork(); + switch (pid) { + case 0: + cleanup_for_child_process(); + + /* Launch clients as the user. Do not launch clients with wrong euid. */ + if (seteuid(getuid()) == -1) { + written = write(STDERR_FILENO, fail_seteuid, + strlen(fail_seteuid)); + _exit(EXIT_FAILURE); + } + + for (i = 0; i < num_no_cloexec_fds; i++) { + err = os_fd_clear_cloexec(no_cloexec_fds[i]); + if (err < 0) { + written = write(STDERR_FILENO, fail_cloexec, + strlen(fail_cloexec)); + _exit(EXIT_FAILURE); + } + } + + execve(argp[0], argp, envp); + + if (fail_exec) + written = write(STDERR_FILENO, fail_exec, + strlen(fail_exec)); + _exit(EXIT_FAILURE); + + default: + proc = xzalloc(sizeof(*proc)); + proc->pid = pid; + proc->cleanup = cleanup; + proc->cleanup_data = cleanup_data; + proc->path = strdup(argp[0]); + wl_list_insert(&ivi->child_process_list, &proc->link); + break; + + case -1: + weston_log("weston_client_launch: " + "fork failed while launching '%s': %s\n", argp[0], + strerror(errno)); + break; + } + + custom_env_fini(child_env); + free(fail_exec); + return proc; +} + +static struct weston_config * +ivi_get_config(struct weston_compositor *ec) +{ + struct ivi_compositor *ivi = to_ivi_compositor(ec); + + return ivi->config; +} + + +static struct wl_client * spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd) { struct wet_xwayland *wxw = user_data; - pid_t pid; - char s[12], abstract_fd_str[12], unix_fd_str[12], wm_fd_str[12]; - int sv[2], wm[2], fd; + struct fdstr wayland_socket = FDSTR_INIT; + struct fdstr x11_abstract_socket = FDSTR_INIT; + struct fdstr x11_unix_socket = FDSTR_INIT; + struct fdstr x11_wm_socket = FDSTR_INIT; + struct fdstr display_pipe = FDSTR_INIT; + char *xserver = NULL; - struct weston_config *config = wxw->ivi_compositor->config; + struct weston_config *config = ivi_get_config(wxw->compositor); struct weston_config_section *section; + struct wl_client *client; + struct wl_event_loop *loop; + struct custom_env child_env; + int no_cloexec_fds[5]; + size_t num_no_cloexec_fds = 0; + size_t written __attribute__ ((unused)); - if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) { + if (os_socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0, wayland_socket.fds) < 0) { weston_log("wl connection socketpair failed\n"); - return 1; + goto err; } + fdstr_update_str1(&wayland_socket); + no_cloexec_fds[num_no_cloexec_fds++] = wayland_socket.fds[1]; - if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, wm) < 0) { + if (os_socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0, x11_wm_socket.fds) < 0) { weston_log("X wm connection socketpair failed\n"); - return 1; + goto err; } + fdstr_update_str1(&x11_wm_socket); + no_cloexec_fds[num_no_cloexec_fds++] = x11_wm_socket.fds[1]; - pid = fork(); - switch (pid) { - case 0: - /* SOCK_CLOEXEC closes both ends, so we need to unset - * the flag on the client fd. */ - fd = dup(sv[1]); - if (fd < 0) - goto fail; - snprintf(s, sizeof s, "%d", fd); - setenv("WAYLAND_SOCKET", s, 1); - - fd = dup(abstract_fd); - if (fd < 0) - goto fail; - snprintf(abstract_fd_str, sizeof abstract_fd_str, "%d", fd); - fd = dup(unix_fd); - if (fd < 0) - goto fail; - snprintf(unix_fd_str, sizeof unix_fd_str, "%d", fd); - fd = dup(wm[1]); - if (fd < 0) - goto fail; - snprintf(wm_fd_str, sizeof wm_fd_str, "%d", fd); - - section = weston_config_get_section(config, - "xwayland", NULL, NULL); - weston_config_section_get_string(section, "path", - &xserver, XSERVER_PATH); - - /* Ignore SIGUSR1 in the child, which will make the X - * server send SIGUSR1 to the parent (weston) when - * it's done with initialization. During - * initialization the X server will round trip and - * block on the wayland compositor, so avoid making - * blocking requests (like xcb_connect_to_fd) until - * it's done with that. */ - signal(SIGUSR1, SIG_IGN); - - if (execl(xserver, - xserver, - display, - "-rootless", -#ifdef HAVE_XWAYLAND_LISTENFD - "-listenfd", abstract_fd_str, - "-listenfd", unix_fd_str, -#else - "-listen", abstract_fd_str, - "-listen", unix_fd_str, -#endif - "-wm", wm_fd_str, - "-terminate", - NULL) < 0) - weston_log("exec of '%s %s -rootless " -#ifdef HAVE_XWAYLAND_LISTENFD - "-listenfd %s -listenfd %s " -#else - "-listen %s -listen %s " -#endif - "-wm %s -terminate' failed: %s\n", - xserver, display, - abstract_fd_str, unix_fd_str, wm_fd_str, - strerror(errno)); - fail: - _exit(EXIT_FAILURE); + if (pipe2(display_pipe.fds, O_CLOEXEC) < 0) { + weston_log("pipe creation for displayfd failed\n"); + goto err; + } + fdstr_update_str1(&display_pipe); + no_cloexec_fds[num_no_cloexec_fds++] = display_pipe.fds[1]; - default: - close(sv[1]); - wxw->client = wl_client_create(wxw->ivi_compositor->compositor->wl_display, sv[0]); + fdstr_set_fd1(&x11_abstract_socket, abstract_fd); + no_cloexec_fds[num_no_cloexec_fds++] = abstract_fd; - close(wm[1]); - wxw->wm_fd = wm[0]; + fdstr_set_fd1(&x11_unix_socket, unix_fd); + no_cloexec_fds[num_no_cloexec_fds++] = unix_fd; - wxw->process.pid = pid; - wl_list_insert(&wxw->ivi_compositor->child_process_list, - &wxw->process.link); - break; + assert(num_no_cloexec_fds <= ARRAY_LENGTH(no_cloexec_fds)); - case -1: - weston_log("Failed to fork to spawn xserver process\n"); - break; + section = weston_config_get_section(config, "xwayland", NULL, NULL); + weston_config_section_get_string(section, "path", + &xserver, XSERVER_PATH); + custom_env_init_from_environ(&child_env); + custom_env_set_env_var(&child_env, "WAYLAND_SOCKET", wayland_socket.str1); + + custom_env_add_arg(&child_env, xserver); + custom_env_add_arg(&child_env, display); + custom_env_add_arg(&child_env, "-rootless"); + custom_env_add_arg(&child_env, LISTEN_STR); + custom_env_add_arg(&child_env, x11_abstract_socket.str1); + custom_env_add_arg(&child_env, LISTEN_STR); + custom_env_add_arg(&child_env, x11_unix_socket.str1); + custom_env_add_arg(&child_env, "-displayfd"); + custom_env_add_arg(&child_env, display_pipe.str1); + custom_env_add_arg(&child_env, "-wm"); + custom_env_add_arg(&child_env, x11_wm_socket.str1); + custom_env_add_arg(&child_env, "-terminate"); + + wxw->process = client_launch(wxw->compositor, &child_env, + no_cloexec_fds, num_no_cloexec_fds, + xserver_cleanup, wxw); + if (!wxw->process) { + weston_log("Couldn't start Xwayland\n"); + goto err; + } + + client = wl_client_create(wxw->compositor->wl_display, + wayland_socket.fds[0]); + if (!client) { + weston_log("Couldn't create client for Xwayland\n"); + goto err_proc; } - return pid; + wxw->wm_fd = x11_wm_socket.fds[0]; + + /* Now we can no longer fail, close the child end of our sockets */ + close(wayland_socket.fds[1]); + close(x11_wm_socket.fds[1]); + close(display_pipe.fds[1]); + + /* During initialization the X server will round trip + * and block on the wayland compositor, so avoid making + * blocking requests (like xcb_connect_to_fd) until + * it's done with that. */ + loop = wl_display_get_event_loop(wxw->compositor->wl_display); + wxw->display_fd_source = + wl_event_loop_add_fd(loop, display_pipe.fds[0], + WL_EVENT_READABLE, + handle_display_fd, wxw); + + free(xserver); + + return client; + + +err_proc: + wl_list_remove(&wxw->process->link); +err: + free(xserver); + fdstr_close_all(&display_pipe); + fdstr_close_all(&x11_wm_socket); + fdstr_close_all(&wayland_socket); + free(wxw->process); + return NULL; } -static void -xserver_cleanup(struct weston_process *process, int status) +void +wet_xwayland_destroy(struct weston_compositor *compositor, void *data) { - struct wet_xwayland *wxw = - container_of(process, struct wet_xwayland, process); - struct wl_event_loop *loop = - wl_display_get_event_loop(wxw->ivi_compositor->compositor->wl_display); - - wxw->api->xserver_exited(wxw->xwayland, status); - wxw->sigusr1_source = wl_event_loop_add_signal(loop, SIGUSR1, - handle_sigusr1, wxw); - wxw->client = NULL; + struct wet_xwayland *wxw = data; + + /* Calling this will call the process cleanup, in turn cleaning up the + * client and the core Xwayland state */ + if (wxw->process) + ivi_process_destroy(wxw->process, 0, true); + + free(wxw); } -int -wet_load_xwayland(struct weston_compositor *comp) +void * +wet_load_xwayland(struct weston_compositor *compositor) { const struct weston_xwayland_api *api; struct weston_xwayland *xwayland; struct wet_xwayland *wxw; - struct wl_event_loop *loop; - struct ivi_compositor *ivi = to_ivi_compositor(comp); - if (weston_compositor_load_xwayland(comp) < 0) - return -1; + if (weston_compositor_load_xwayland(compositor) < 0) + return NULL; - api = weston_xwayland_get_api(comp); + api = weston_xwayland_get_api(compositor); if (!api) { weston_log("Failed to get the xwayland module API.\n"); - return -1; + return NULL; } - xwayland = api->get(comp); + xwayland = api->get(compositor); if (!xwayland) { weston_log("Failed to get the xwayland object.\n"); - return -1; + return NULL; } wxw = zalloc(sizeof *wxw); if (!wxw) - return -1; + return NULL; - wxw->ivi_compositor = ivi; + wxw->compositor = compositor; wxw->api = api; wxw->xwayland = xwayland; - wxw->process.cleanup = xserver_cleanup; if (api->listen(xwayland, wxw, spawn_xserver) < 0) - return -1; + return NULL; - loop = wl_display_get_event_loop(comp->wl_display); - wxw->sigusr1_source = wl_event_loop_add_signal(loop, SIGUSR1, - handle_sigusr1, wxw); - - return 0; + return wxw; } |