From 3700c78131e92620753ad4d77a1f9b3b40a567cb Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 14 Jul 2022 17:42:14 +0300 Subject: [PATCH] libweston: implement new screenshooting protocol base This implements the basics of the new screenshooting protocol. The actual pixel operations will be implemented separately in the renderers and DRM-backend. See the previous commit "protocol: new screenshooter protocol" for why. If DRM-backend needs more from weston_capture_task when it implements writeback screenshooting, it will be easy to add user_data or expose weston_capture_task::link for the backend to use. Those were not added yet because it is uncertain what is actually needed. The DRM-backend no-damage optimization requires special handling here as well. See also 7f1a113c895e5690e0a09a0a5079a74b6d65e442 . Signed-off-by: Pekka Paalanen --- include/libweston/libweston.h | 33 ++ libweston/backend-drm/drm.c | 2 + libweston/compositor.c | 12 + libweston/meson.build | 3 + libweston/output-capture.c | 696 ++++++++++++++++++++++++++++++++++ libweston/output-capture.h | 98 +++++ 6 files changed, 844 insertions(+) create mode 100644 libweston/output-capture.c create mode 100644 libweston/output-capture.h diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index f600246f..1a4dfe66 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -91,6 +91,7 @@ struct ro_anonymous_file; struct weston_color_profile; struct weston_color_transform; struct pixel_format_info; +struct weston_output_capture_info; enum weston_keyboard_modifier { MODIFIER_CTRL = (1 << 0), @@ -502,6 +503,7 @@ struct weston_output { int disable_planes; int destroying; struct wl_list feedback_list; + struct weston_output_capture_info *capture_info; uint32_t transform; int32_t native_scale; @@ -1328,6 +1330,12 @@ struct weston_compositor { struct weston_log_pacer unmapped_surface_or_view_pacer; struct weston_log_pacer presentation_clock_failure_pacer; + + /** Screenshooting global state, see output-capture.c */ + struct { + struct wl_global *weston_capture_v1; + struct wl_signal ask_auth; + } output_capture; }; struct weston_solid_buffer_values { @@ -2363,6 +2371,31 @@ struct weston_color_profile * weston_compositor_load_icc_file(struct weston_compositor *compositor, const char *path); +/** Describes who is trying to capture and which output */ +struct weston_output_capture_client { + struct wl_client *client; + struct weston_output *output; +}; + +/** Arguments asking to authorize a screenshot/capture + * + * \sa weston_compositor_add_screenshot_authority + */ +struct weston_output_capture_attempt { + const struct weston_output_capture_client *const who; + + /** Set to true to authorize the screenshot. */ + bool authorized; + /** Set to true to deny the screenshot. */ + bool denied; +}; + +void +weston_compositor_add_screenshot_authority(struct weston_compositor *compositor, + struct wl_listener *listener, + void (*auth)(struct wl_listener *l, + struct weston_output_capture_attempt *att)); + #ifdef __cplusplus } #endif diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index b5ea193c..f85dba2d 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -55,6 +55,7 @@ #include "shared/timespec-util.h" #include "shared/string-helpers.h" #include "shared/weston-drm-fourcc.h" +#include "output-capture.h" #include "pixman-renderer.h" #include "pixel-formats.h" #include "libbacklight.h" @@ -385,6 +386,7 @@ drm_output_render(struct drm_output_state *state, pixman_region32_t *damage) */ if (!pixman_region32_not_empty(damage) && wl_list_empty(&output->base.frame_signal.listener_list) && + !weston_output_has_capture_tasks(&output->base) && scanout_plane->state_cur->fb && (scanout_plane->state_cur->fb->type == BUFFER_GBM_SURFACE || scanout_plane->state_cur->fb->type == BUFFER_PIXMAN_DUMB)) { diff --git a/libweston/compositor.c b/libweston/compositor.c index 322b7f21..688845d4 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -79,6 +79,7 @@ #include "backend.h" #include "libweston-internal.h" #include "color.h" +#include "output-capture.h" #include "weston-log-internal.h" @@ -3076,6 +3077,8 @@ weston_output_repaint(struct weston_output *output) animation->frame(animation, output, &output->frame_time); } + weston_output_capture_info_repaint_done(output->capture_info); + TL_POINT(ec, "core_repaint_posted", TLP_OUTPUT(output), TLP_END); return r; @@ -6658,6 +6661,8 @@ weston_compositor_remove_output(struct weston_output *output) wl_list_for_each(head, &output->head_list, output_link) weston_head_remove_global(head); + weston_output_capture_info_destroy(&output->capture_info); + compositor->output_id_pool &= ~(1u << output->id); output->id = 0xffffffff; /* invalid */ } @@ -7149,6 +7154,9 @@ weston_output_enable(struct weston_output *output) if (!weston_output_set_color_outcome(output)) return -1; + output->capture_info = weston_output_capture_info_create(); + assert(output->capture_info); + /* Enable the output (set up the crtc or create a * window representing the output, set up the * renderer, etc) @@ -7156,6 +7164,7 @@ weston_output_enable(struct weston_output *output) if (output->enable(output) < 0) { weston_log("Enabling output \"%s\" failed.\n", output->name); weston_output_color_outcome_destroy(&output->color_outcome); + weston_output_capture_info_destroy(&output->capture_info); return -1; } @@ -8175,6 +8184,7 @@ weston_compositor_create(struct wl_display *display, wl_signal_init(&ec->heads_changed_signal); wl_signal_init(&ec->output_heads_changed_signal); wl_signal_init(&ec->session_signal); + wl_signal_init(&ec->output_capture.ask_auth); ec->session_active = true; ec->output_id_pool = 0; @@ -8214,6 +8224,8 @@ weston_compositor_create(struct wl_display *display, if (weston_input_init(ec) != 0) goto fail; + weston_compositor_install_capture_protocol(ec); + wl_list_init(&ec->view_list); wl_list_init(&ec->plane_list); wl_list_init(&ec->layer_list); diff --git a/libweston/meson.build b/libweston/meson.build index e36b6df2..42adb0a2 100644 --- a/libweston/meson.build +++ b/libweston/meson.build @@ -25,6 +25,7 @@ srcs_libweston = [ 'linux-sync-file.c', 'log.c', 'noop-renderer.c', + 'output-capture.c', 'pixel-formats.c', 'pixman-renderer.c', 'plugin-registry.c', @@ -70,6 +71,8 @@ srcs_libweston = [ weston_debug_server_protocol_h, weston_direct_display_protocol_c, weston_direct_display_server_protocol_h, + weston_output_capture_protocol_c, + weston_output_capture_server_protocol_h, ] subdir('desktop') diff --git a/libweston/output-capture.c b/libweston/output-capture.c new file mode 100644 index 00000000..56b603c2 --- /dev/null +++ b/libweston/output-capture.c @@ -0,0 +1,696 @@ +/* + * Copyright 2022 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" + +#include + +#include +#include +#include "libweston-internal.h" +#include "output-capture.h" +#include "pixel-formats.h" +#include "shared/helpers.h" +#include "shared/weston-drm-fourcc.h" +#include "shared/xalloc.h" +#include "weston-output-capture-server-protocol.h" + +/* Lifetimes + * + * weston_output_capture_info is created at weston_output enable, and + * destroyed at weston_output disable. It maintains lists of + * weston_capture_source and weston_capture_task. + * + * Protocol request weston_capture_v1.create creates weston_capture_source + * whose lifetime is equal to the weston_capture_source_v1 protocol object + * (wl_resource) lifetime. + * + * weston_capture_source is associated with a weston_output. When the + * weston_output is disabled, weston_capture_source is removed from the list + * in weston_output_capture_info and any pending task is retired as failed. + * Furhter capture attempts on the source will be immediately failed. + * + * Protocol request weston_capture_source_v1.capture creates + * weston_capture_task, if the weston_capture_source still has its output and + * no pending task. weston_capture_task becomes the pending task for the + * weston_capture_source, and is added to the list in + * weston_output_capture_info. Retiring weston_capture_task destroys it. + * + * Each weston_capture_task is associated with a wl_buffer (weston_buffer). + * If the buffer is destroyed, the task is retired as failed. + * + * Operation + * + * Each weston_capture_source has a "pixel source" property. Pixel source + * describes what the capture shall actually contain. See + * weston_capture_v1.create request in the protocol specification. + * One pixel source can be provided by at most one component at a time. + * + * Whenever a renderer or DRM-backend is repainting an output, they will + * use weston_output_pull_capture_task() at the appropriate stages to see + * if there are any capture tasks to be serviced for a specific pixel source. + * The renderer or DRM-backend must then retire the returned tasks by either + * failing or completing them. + * + * When an output repaint completes, no weston_capture_task shall remain in + * the list. Renderers or backends could stash them in their own lists though. + * + * In order to allow clients to allocate correctly sized and formatted buffers + * to hold captured images, weston_output_capture_info maintains the current + * size and format for each type of pixel source. Renderers and DRM-backend + * who provide pixel sources are also responsible for keeping the buffer + * requirements information up-to-date with + * weston_output_update_capture_info(). + */ + +/** Implementation of weston_capture_source_v1 protocol object */ +struct weston_capture_source { + struct wl_resource *resource; + + /* struct weston_output_capture_info::capture_source_list */ + struct wl_list link; + + enum weston_output_capture_source pixel_source; + + /* weston_output_capture_info_destroy() will reset this. */ + struct weston_output *output; + + struct weston_capture_task *pending; +}; + +/** A pending task to capture an output */ +struct weston_capture_task { + /* We get cleaned up through owner->pending pointing to us. */ + struct weston_capture_source *owner; + + /* struct weston_output_capture_info::pending_capture_list */ + struct wl_list link; + + struct weston_buffer *buffer; + struct wl_listener buffer_resource_destroy_listener; +}; + +/** Buffer requirements broadcasting for a pixel source */ +struct weston_output_capture_source_info { + enum weston_output_capture_source pixel_source; + + int width; + int height; + uint32_t drm_format; +}; + +/** Capture records for an output */ +struct weston_output_capture_info { + /* struct weston_capture_task::link */ + struct wl_list pending_capture_list; + + /* struct weston_capture_source::link */ + struct wl_list capture_source_list; + + struct weston_output_capture_source_info source_info[WESTON_OUTPUT_CAPTURE_SOURCE__COUNT]; +}; + +/** Create capture tracking information on weston_output enable */ +struct weston_output_capture_info * +weston_output_capture_info_create(void) +{ + struct weston_output_capture_info *ci; + unsigned i; + + ci = xzalloc(sizeof *ci); + + wl_list_init(&ci->pending_capture_list); + wl_list_init(&ci->capture_source_list); + + /* + * Initialize to no sources available by leaving + * width, height and drm_format as zero. + */ + for (i = 0; i < ARRAY_LENGTH(ci->source_info); i++) + ci->source_info[i].pixel_source = i; + + return ci; +} + +/** Clean up capture tracking information on weston_output disable */ +void +weston_output_capture_info_destroy(struct weston_output_capture_info **cip) +{ + struct weston_output_capture_info *ci = *cip; + struct weston_capture_source *csrc, *tmp; + + assert(ci); + + /* Unlink sources. They get destroyed by their wl_resource later. */ + wl_list_for_each_safe(csrc, tmp, &ci->capture_source_list, link) { + csrc->output = NULL; + + wl_list_remove(&csrc->link); + wl_list_init(&csrc->link); + + if (csrc->pending) + weston_capture_task_retire_failed(csrc->pending, "output removed"); + } + + assert(wl_list_empty(&ci->pending_capture_list)); + + free(ci); + *cip = NULL; +} + +/** Assert that all capture tasks were taken + * + * This is called at the end of a weston_output repaint cycle when the renderer + * and the backend have had their chance to service all pending capture tasks. + * The remaining tasks would not be serviced by anything, so make sure none + * linger. + */ +void +weston_output_capture_info_repaint_done(struct weston_output_capture_info *ci) +{ + assert(wl_list_empty(&ci->pending_capture_list)); +} + +static bool +source_info_is_available(const struct weston_output_capture_source_info *csi) +{ + return csi->width > 0 && csi->height > 0 && + csi->drm_format != DRM_FORMAT_INVALID; +} + +static void +capture_info_send_source_info(struct weston_output_capture_info *ci, + struct weston_output_capture_source_info *csi) +{ + struct weston_capture_source *csrc; + + wl_list_for_each(csrc, &ci->capture_source_list, link) { + if (csrc->pixel_source != csi->pixel_source) + continue; + + weston_capture_source_v1_send_format(csrc->resource, + csi->drm_format); + weston_capture_source_v1_send_size(csrc->resource, + csi->width, csi->height); + } +} + +static struct weston_output_capture_source_info * +capture_info_get_csi(struct weston_output_capture_info *ci, + enum weston_output_capture_source src) +{ + int srcidx = src; + + assert(ci); + assert(srcidx >= 0 && srcidx < (int)ARRAY_LENGTH(ci->source_info)); + + return &ci->source_info[srcidx]; +} + +/** Update capture requirements broadcast to clients + * + * This is called by renderers and DRM-backend to update the buffer + * requirements information that is delivered to clients wanting to capture + * the output. This is how clients know what size and format buffer they + * need to allocate for the given output and pixel source. + * + * \param output The output whose capture info to update. + * \param src The source type on the output. + * \param width The new buffer width. + * \param height The new buffer height. + * \param format The new pixel format. + * + * If any one of width, height or format is zero/NULL, the source becomes + * unavailable to clients. Otherwise the source becomes available. + * + * Initially all sources are unavailable. + */ +WL_EXPORT void +weston_output_update_capture_info(struct weston_output *output, + enum weston_output_capture_source src, + int width, int height, + const struct pixel_format_info *format) +{ + struct weston_output_capture_info *ci = output->capture_info; + struct weston_output_capture_source_info *csi; + + csi = capture_info_get_csi(ci, src); + + if (csi->width == width && + csi->height == height && + csi->drm_format == format->format) + return; + + csi->width = width; + csi->height = height; + csi->drm_format = format->format; + + if (source_info_is_available(csi)) { + capture_info_send_source_info(ci, csi); + } else { + struct weston_capture_task *ct, *tmp; + + /* + * This source just became unavailable, so fail all pending + * tasks using it. + */ + wl_list_for_each_safe(ct, tmp, + &ci->pending_capture_list, link) { + if (ct->owner->pixel_source != csi->pixel_source) + continue; + + weston_capture_task_retire_failed(ct, "source removed"); + } + } +} + +static bool +buffer_is_compatible(struct weston_buffer *buffer, + struct weston_output_capture_source_info *csi) +{ + return buffer->width == csi->width && + buffer->height == csi->height && + buffer->pixel_format->format == csi->drm_format && + buffer->format_modifier == DRM_FORMAT_MOD_LINEAR; +} + +static void +weston_capture_task_destroy(struct weston_capture_task *ct) +{ + if (ct->owner->pixel_source != WESTON_OUTPUT_CAPTURE_SOURCE_WRITEBACK && + ct->owner->output) + weston_output_disable_planes_decr(ct->owner->output); + + assert(ct->owner->pending == ct); + ct->owner->pending = NULL; + wl_list_remove(&ct->link); + wl_list_remove(&ct->buffer_resource_destroy_listener.link); + free(ct); +} + +static void +weston_capture_task_buffer_destroy_handler(struct wl_listener *l, void *data) +{ + struct weston_capture_task *ct = + wl_container_of(l, ct, buffer_resource_destroy_listener); + + /* + * Client destroyed the wl_buffer object. By protocol spec, this is + * undefined behaviour. Do the most sensible thing. + */ + weston_capture_task_retire_failed(ct, "wl_buffer destroyed"); +} + +static struct weston_capture_task * +weston_capture_task_create(struct weston_capture_source *csrc, + struct weston_buffer *buffer) +{ + struct weston_capture_task *ct; + + ct = xzalloc(sizeof *ct); + + ct->owner = csrc; + /* Owner will explicitly destroy us if the owner gets destroyed. */ + + ct->buffer = buffer; + ct->buffer_resource_destroy_listener.notify = weston_capture_task_buffer_destroy_handler; + wl_resource_add_destroy_listener(buffer->resource, + &ct->buffer_resource_destroy_listener); + + wl_list_insert(&csrc->output->capture_info->pending_capture_list, + &ct->link); + + if (ct->owner->pixel_source != WESTON_OUTPUT_CAPTURE_SOURCE_WRITEBACK) + weston_output_disable_planes_incr(ct->owner->output); + + return ct; +} + +static bool +capture_is_authorized(struct weston_capture_source *csrc) +{ + struct weston_compositor *compositor = csrc->output->compositor; + const struct weston_output_capture_client who = { + .client = wl_resource_get_client(csrc->resource), + .output = csrc->output, + }; + struct weston_output_capture_attempt att = { + .who = &who, + .authorized = false, + .denied = false, + }; + + wl_signal_emit(&compositor->output_capture.ask_auth, &att); + + return att.authorized && !att.denied; +} + +/** Fetch the next capture task + * + * This is used by renderers and DRM-backend to get the next capture task + * they want to service. Only tasks for the given pixel source will be + * returned. + * + * Width, height and drm_format are for ensuring that + * weston_output_update_capture_info() was up-to-date before this. + * + * \return A capture task, or NULL if no more tasks. + */ +WL_EXPORT struct weston_capture_task * +weston_output_pull_capture_task(struct weston_output *output, + enum weston_output_capture_source src, + int width, int height, + const struct pixel_format_info *format) +{ + struct weston_output_capture_info *ci = output->capture_info; + struct weston_output_capture_source_info *csi; + struct weston_capture_task *ct, *tmp; + + /* + * Make sure the capture provider (renderers, DRM-backend) called + * weston_output_update_capture_info() if something changed, so that + * the 'retry' event keeps its promise of size/format events been + * already sent. + */ + csi = capture_info_get_csi(ci, src); + assert(csi->width == width); + assert(csi->height == height); + assert(csi->drm_format == format->format); + + wl_list_for_each_safe(ct, tmp, &ci->pending_capture_list, link) { + assert(ct->owner->output == output); + + if (ct->owner->pixel_source != src) + continue; + + if (!capture_is_authorized(ct->owner)) { + weston_capture_task_retire_failed(ct, "unauthorized"); + continue; + } + + /* + * Tell the client to retry, if requirements changed after + * the task was filed. + */ + if (!buffer_is_compatible(ct->buffer, csi)) { + weston_capture_source_v1_send_retry(ct->owner->resource); + weston_capture_task_destroy(ct); + continue; + } + + /* pass ct ownership to the caller */ + wl_list_remove(&ct->link); + wl_list_init(&ct->link); + + return ct; + } + + return NULL; +} + +/** Check if any capture tasks are waiting on the output */ +WL_EXPORT bool +weston_output_has_capture_tasks(struct weston_output *output) +{ + struct weston_output_capture_info *ci = output->capture_info; + + return !wl_list_empty(&ci->pending_capture_list); +} + +/** Get the destination buffer */ +WL_EXPORT struct weston_buffer * +weston_capture_task_get_buffer(struct weston_capture_task *ct) +{ + return ct->buffer; +} + +/** Signal completion of the capture task + * + * Sends 'complete' protocol event to the client, and destroys the task. + */ +WL_EXPORT void +weston_capture_task_retire_complete(struct weston_capture_task *ct) +{ + weston_capture_source_v1_send_complete(ct->owner->resource); + weston_capture_task_destroy(ct); +} + +/** Signal failure of the capture task + * + * Sends 'failed' protocol event to the client, and destroys the task. + */ +WL_EXPORT void +weston_capture_task_retire_failed(struct weston_capture_task *ct, + const char *err_msg) +{ + weston_capture_source_v1_send_failed(ct->owner->resource, err_msg); + weston_capture_task_destroy(ct); +} + +static void +destroy_capture_source(struct wl_resource *csrc_resource) +{ + struct weston_capture_source *csrc; + + csrc = wl_resource_get_user_data(csrc_resource); + assert(csrc_resource == csrc->resource); + + if (csrc->pending) + weston_capture_task_destroy(csrc->pending); + + wl_list_remove(&csrc->link); + free(csrc); +} + +static void +weston_capture_source_v1_destroy(struct wl_client *client, + struct wl_resource *csrc_resource) +{ + wl_resource_destroy(csrc_resource); +} + +static void +weston_capture_source_v1_capture(struct wl_client *client, + struct wl_resource *csrc_resource, + struct wl_resource *buffer_resource) +{ + struct weston_output_capture_source_info *csi; + struct weston_capture_source *csrc; + struct weston_buffer *buffer; + + csrc = wl_resource_get_user_data(csrc_resource); + assert(csrc_resource == csrc->resource); + + /* A capture task already exists? */ + if (csrc->pending) { + wl_resource_post_error(csrc->resource, + WESTON_CAPTURE_SOURCE_V1_ERROR_SEQUENCE, + "capture attempted before previous capture retired"); + return; + } + + /* weston_output disabled after creating the source? */ + if (!csrc->output) { + weston_capture_source_v1_send_failed(csrc->resource, "output removed"); + return; + } + + /* Is the pixel source not available? */ + csi = capture_info_get_csi(csrc->output->capture_info, + csrc->pixel_source); + if (!source_info_is_available(csi)) { + weston_capture_source_v1_send_failed(csrc->resource, "source unavailable"); + return; + } + + buffer = weston_buffer_from_resource(csrc->output->compositor, + buffer_resource); + if (!buffer) { + wl_client_post_no_memory(client); + return; + } + + /* If the buffer not up-to-date with the size and format? */ + if (!buffer_is_compatible(buffer, csi)) { + weston_capture_source_v1_send_retry(csrc->resource); + return; + } + + csrc->pending = weston_capture_task_create(csrc, buffer); + weston_output_schedule_repaint(csrc->output); +} + +static const struct weston_capture_source_v1_interface weston_capture_source_v1_impl = { + .destroy = weston_capture_source_v1_destroy, + .capture = weston_capture_source_v1_capture, +}; + +static int32_t +pixel_source_from_proto(uint32_t v) +{ + switch (v) { + case WESTON_CAPTURE_V1_SOURCE_WRITEBACK: + return WESTON_OUTPUT_CAPTURE_SOURCE_WRITEBACK; + case WESTON_CAPTURE_V1_SOURCE_FRAMEBUFFER: + return WESTON_OUTPUT_CAPTURE_SOURCE_FRAMEBUFFER; + case WESTON_CAPTURE_V1_SOURCE_FULL_FRAMEBUFFER: + return WESTON_OUTPUT_CAPTURE_SOURCE_FULL_FRAMEBUFFER; + case WESTON_CAPTURE_V1_SOURCE_BLENDING: + return WESTON_OUTPUT_CAPTURE_SOURCE_BLENDING; + default: + return -1; + } +} + +static void +weston_capture_v1_create(struct wl_client *client, + struct wl_resource *capture_resource, + struct wl_resource *output_resource, + uint32_t source, + uint32_t capture_source_new_id) +{ + int32_t isrc = pixel_source_from_proto(source); + struct weston_capture_source *csrc; + struct weston_head *head; + + if (isrc < 0) { + wl_resource_post_error(capture_resource, + WESTON_CAPTURE_V1_ERROR_INVALID_SOURCE, + "%u is not a valid source", source); + return; + } + + csrc = zalloc(sizeof *csrc); + if (!csrc) { + wl_client_post_no_memory(client); + return; + } + + csrc->pixel_source = isrc; + wl_list_init(&csrc->link); + + csrc->resource = wl_resource_create(client, + &weston_capture_source_v1_interface, + wl_resource_get_version(capture_resource), + capture_source_new_id); + if (!csrc->resource) { + free(csrc); + wl_client_post_no_memory(client); + return; + } + + wl_resource_set_implementation(csrc->resource, + &weston_capture_source_v1_impl, + csrc, destroy_capture_source); + + head = weston_head_from_resource(output_resource); + if (head) { + struct weston_output *output = head->output; + struct weston_output_capture_info *ci = output->capture_info; + struct weston_output_capture_source_info *csi; + + csi = capture_info_get_csi(ci, csrc->pixel_source); + wl_list_insert(&ci->capture_source_list, &csrc->link); + + csrc->output = output; + + if (source_info_is_available(csi)) + capture_info_send_source_info(ci, csi); + } + /* + * if (!head) then weston_capture_source_v1_capture() will respond with + * the failed event. + */ +} + +static void +weston_capture_v1_destroy(struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static const struct weston_capture_v1_interface weston_capture_v1_impl = { + .destroy = weston_capture_v1_destroy, + .create = weston_capture_v1_create, +}; + +static void +bind_weston_capture(struct wl_client *client, + void *data, uint32_t version, uint32_t id) +{ + struct wl_resource *resource; + + /* Access control is done at capture request. */ + resource = wl_resource_create(client, &weston_capture_v1_interface, + version, id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + + wl_resource_set_implementation(resource, &weston_capture_v1_impl, + NULL, NULL); +} + +void +weston_compositor_install_capture_protocol(struct weston_compositor *compositor) +{ + compositor->output_capture.weston_capture_v1 = + wl_global_create(compositor->wl_display, + &weston_capture_v1_interface, + 1, NULL, bind_weston_capture); + abort_oom_if_null(compositor->output_capture.weston_capture_v1); +} + +/** Add a new authority that may authorize or deny screenshots + * + * \param compositor The compositor instance. + * \param listener The listener to populate, and which will be passed as the + * listener to the auth callback. + * \param auth The callback function which shall be called every time any + * client sends a request to capture an output. + * + * The callback function \c auth is called with argument \c att. If you + * want to authorize the screenshot after inspecting the fields in + * \c att->who , you must set \c att->authorized to true. If you want to + * deny the screenshot instead, set \c att->denied to true. Otherwise, + * do not change anything. + * + * Any screenshot is carried out only if after iterating through all + * authorities \c att->authorized is true and \c att->denied is false. + * Both default to false, which forbids screenshots without any authorities. + * + * You can remove an added authority by \c wl_list_remove(&listener->link) . + */ +WL_EXPORT void +weston_compositor_add_screenshot_authority(struct weston_compositor *compositor, + struct wl_listener *listener, + void (*auth)(struct wl_listener *l, + struct weston_output_capture_attempt *att)) +{ + listener->notify = (wl_notify_func_t)auth; + wl_signal_add(&compositor->output_capture.ask_auth, listener); +} diff --git a/libweston/output-capture.h b/libweston/output-capture.h new file mode 100644 index 00000000..0b29f615 --- /dev/null +++ b/libweston/output-capture.h @@ -0,0 +1,98 @@ +/* + * Copyright 2022 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. + */ + +#pragma once + +#include + +/** Copy of weston_capture_v1.source enum from protocol */ +enum weston_output_capture_source { + /** DRM KMS hardware writeback */ + WESTON_OUTPUT_CAPTURE_SOURCE_WRITEBACK = 0, + + /** framebuffer desktop area */ + WESTON_OUTPUT_CAPTURE_SOURCE_FRAMEBUFFER, + + /** complete framebuffer, including borders if any */ + WESTON_OUTPUT_CAPTURE_SOURCE_FULL_FRAMEBUFFER, + + /** blending buffer, potentially light-linear */ + WESTON_OUTPUT_CAPTURE_SOURCE_BLENDING, +}; +#define WESTON_OUTPUT_CAPTURE_SOURCE__COUNT (WESTON_OUTPUT_CAPTURE_SOURCE_BLENDING + 1) + +struct weston_output_capture_info; + +/* + * For weston_output core implementation: + */ + +struct weston_output_capture_info * +weston_output_capture_info_create(void); + +void +weston_output_capture_info_destroy(struct weston_output_capture_info **cip); + +void +weston_output_capture_info_repaint_done(struct weston_output_capture_info *ci); + +/* + * For actual capturing implementations (renderers, DRM-backend): + */ + +void +weston_output_update_capture_info(struct weston_output *output, + enum weston_output_capture_source src, + int width, int height, + const struct pixel_format_info *format); + +bool +weston_output_has_capture_tasks(struct weston_output *output); + +struct weston_capture_task; + +struct weston_capture_task * +weston_output_pull_capture_task(struct weston_output *output, + enum weston_output_capture_source src, + int width, int height, + const struct pixel_format_info *format); + +struct weston_buffer * +weston_capture_task_get_buffer(struct weston_capture_task *ct); + +void +weston_capture_task_retire_failed(struct weston_capture_task *ct, + const char *err_msg); + +void +weston_capture_task_retire_complete(struct weston_capture_task *ct); + + +/* + * entry point for weston_compositor + */ + +void +weston_compositor_install_capture_protocol(struct weston_compositor *compositor);