From 4035f2379225331fba7df1a6f07c71ed472bf286 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 12 Feb 2024 20:14:56 +0100 Subject: [PATCH 01/11] ext-image-capture-source-v1: add base interface --- .../wlr_ext_image_capture_source_v1.h | 30 +++++++++ .../types/wlr_ext_image_capture_source_v1.h | 60 +++++++++++++++++ protocol/meson.build | 3 +- types/ext_image_capture_source_v1/base.c | 67 +++++++++++++++++++ types/meson.build | 1 + 5 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 include/wlr/interfaces/wlr_ext_image_capture_source_v1.h create mode 100644 include/wlr/types/wlr_ext_image_capture_source_v1.h create mode 100644 types/ext_image_capture_source_v1/base.c diff --git a/include/wlr/interfaces/wlr_ext_image_capture_source_v1.h b/include/wlr/interfaces/wlr_ext_image_capture_source_v1.h new file mode 100644 index 00000000..239e515c --- /dev/null +++ b/include/wlr/interfaces/wlr_ext_image_capture_source_v1.h @@ -0,0 +1,30 @@ +/* + * This an unstable interface of wlroots. No guarantees are made regarding the + * future consistency of this API. + */ +#ifndef WLR_USE_UNSTABLE +#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" +#endif + +#ifndef WLR_INTERFACES_WLR_EXT_IMAGE_CAPTURE_SOURCE_V1_H +#define WLR_INTERFACES_WLR_EXT_IMAGE_CAPTURE_SOURCE_V1_H + +#include +#include + +struct wlr_ext_image_copy_capture_frame_v1; + +struct wlr_ext_image_capture_source_v1_interface { + void (*schedule_frame)(struct wlr_ext_image_capture_source_v1 *source); + void (*copy_frame)(struct wlr_ext_image_capture_source_v1 *source, + struct wlr_ext_image_copy_capture_frame_v1 *dst_frame, + struct wlr_ext_image_capture_source_v1_frame_event *frame_event); +}; + +void wlr_ext_image_capture_source_v1_init(struct wlr_ext_image_capture_source_v1 *source, + const struct wlr_ext_image_capture_source_v1_interface *impl); +void wlr_ext_image_capture_source_v1_finish(struct wlr_ext_image_capture_source_v1 *source); +bool wlr_ext_image_capture_source_v1_create_resource(struct wlr_ext_image_capture_source_v1 *source, + struct wl_client *client, uint32_t new_id); + +#endif diff --git a/include/wlr/types/wlr_ext_image_capture_source_v1.h b/include/wlr/types/wlr_ext_image_capture_source_v1.h new file mode 100644 index 00000000..5405bf01 --- /dev/null +++ b/include/wlr/types/wlr_ext_image_capture_source_v1.h @@ -0,0 +1,60 @@ +/* + * This an unstable interface of wlroots. No guarantees are made regarding the + * future consistency of this API. + */ +#ifndef WLR_USE_UNSTABLE +#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" +#endif + +#ifndef WLR_TYPES_WLR_EXT_IMAGE_CAPTURE_SOURCE_V1_H +#define WLR_TYPES_WLR_EXT_IMAGE_CAPTURE_SOURCE_V1_H + +#include +#include +#include + +/** + * A screen capture source. + * + * When the size, device or formats change, the constraints_update event is + * emitted. + * + * The device and formats advertised are suitable for copying into a + * struct wlr_buffer. + */ +struct wlr_ext_image_capture_source_v1 { + const struct wlr_ext_image_capture_source_v1_interface *impl; + struct wl_list resources; // wl_resource_get_link() + + uint32_t width, height; + + uint32_t *shm_formats; + size_t shm_formats_len; + + dev_t dmabuf_device; + struct wlr_drm_format_set dmabuf_formats; + + struct { + struct wl_signal constraints_update; + struct wl_signal frame; // struct wlr_ext_image_capture_source_v1_frame_event + struct wl_signal destroy; + } events; +}; + +/** + * Event indicating that the source has produced a new frame. + */ +struct wlr_ext_image_capture_source_v1_frame_event { + const pixman_region32_t *damage; +}; + +/** + * Obtain a struct wlr_ext_image_capture_source_v1 from an ext_image_capture_source_v1 + * resource. + * + * Asserts that the resource has the correct type. Returns NULL if the resource + * is inert. + */ +struct wlr_ext_image_capture_source_v1 *wlr_ext_image_capture_source_v1_from_resource(struct wl_resource *resource); + +#endif diff --git a/protocol/meson.build b/protocol/meson.build index a4476918..b8b22003 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -1,5 +1,5 @@ wayland_protos = dependency('wayland-protocols', - version: '>=1.35', + version: '>=1.37', fallback: 'wayland-protocols', default_options: ['tests=false'], ) @@ -26,6 +26,7 @@ protocols = { 'drm-lease-v1': wl_protocol_dir / 'staging/drm-lease/drm-lease-v1.xml', 'ext-foreign-toplevel-list-v1': wl_protocol_dir / 'staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml', 'ext-idle-notify-v1': wl_protocol_dir / 'staging/ext-idle-notify/ext-idle-notify-v1.xml', + 'ext-image-capture-source-v1': wl_protocol_dir / 'staging/ext-image-capture-source/ext-image-capture-source-v1.xml', 'ext-session-lock-v1': wl_protocol_dir / 'staging/ext-session-lock/ext-session-lock-v1.xml', 'fractional-scale-v1': wl_protocol_dir / 'staging/fractional-scale/fractional-scale-v1.xml', 'linux-drm-syncobj-v1': wl_protocol_dir / 'staging/linux-drm-syncobj/linux-drm-syncobj-v1.xml', diff --git a/types/ext_image_capture_source_v1/base.c b/types/ext_image_capture_source_v1/base.c new file mode 100644 index 00000000..1b45da75 --- /dev/null +++ b/types/ext_image_capture_source_v1/base.c @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include "ext-image-capture-source-v1-protocol.h" + +static void source_handle_destroy(struct wl_client *client, + struct wl_resource *source_resource) { + wl_resource_destroy(source_resource); +} + +static const struct ext_image_capture_source_v1_interface source_impl = { + .destroy = source_handle_destroy, +}; + +struct wlr_ext_image_capture_source_v1 *wlr_ext_image_capture_source_v1_from_resource(struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &ext_image_capture_source_v1_interface, &source_impl)); + return wl_resource_get_user_data(resource); +} + +static void source_handle_resource_destroy(struct wl_resource *resource) { + wl_list_remove(wl_resource_get_link(resource)); +} + +void wlr_ext_image_capture_source_v1_init(struct wlr_ext_image_capture_source_v1 *source, + const struct wlr_ext_image_capture_source_v1_interface *impl) { + *source = (struct wlr_ext_image_capture_source_v1){ + .impl = impl, + }; + wl_list_init(&source->resources); + wl_signal_init(&source->events.destroy); + wl_signal_init(&source->events.constraints_update); + wl_signal_init(&source->events.frame); +} + +void wlr_ext_image_capture_source_v1_finish(struct wlr_ext_image_capture_source_v1 *source) { + wl_signal_emit_mutable(&source->events.destroy, NULL); + + struct wl_resource *resource, *resource_tmp; + wl_resource_for_each_safe(resource, resource_tmp, &source->resources) { + wl_resource_set_user_data(resource, NULL); + wl_list_remove(wl_resource_get_link(resource)); + wl_list_init(wl_resource_get_link(resource)); + } + + free(source->shm_formats); + wlr_drm_format_set_finish(&source->dmabuf_formats); +} + +bool wlr_ext_image_capture_source_v1_create_resource(struct wlr_ext_image_capture_source_v1 *source, + struct wl_client *client, uint32_t new_id) { + struct wl_resource *resource = wl_resource_create(client, + &ext_image_capture_source_v1_interface, 1, new_id); + if (resource == NULL) { + wl_client_post_no_memory(client); + return false; + } + wl_resource_set_implementation(resource, &source_impl, source, + source_handle_resource_destroy); + if (source != NULL) { + wl_list_insert(&source->resources, wl_resource_get_link(resource)); + } else { + wl_list_init(wl_resource_get_link(resource)); + } + return true; +} diff --git a/types/meson.build b/types/meson.build index ec70d4b7..899959da 100644 --- a/types/meson.build +++ b/types/meson.build @@ -3,6 +3,7 @@ wlr_files += files( 'data_device/wlr_data_offer.c', 'data_device/wlr_data_source.c', 'data_device/wlr_drag.c', + 'ext_image_capture_source_v1/base.c', 'output/cursor.c', 'output/output.c', 'output/render.c', From a05965afe5121095f5a2e23b80121a2cf48a8677 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 8 Apr 2024 19:59:44 +0200 Subject: [PATCH 02/11] ext-image-capture-source-v1: add source pointer cursors --- .../wlr_ext_image_capture_source_v1.h | 7 +++++++ .../types/wlr_ext_image_capture_source_v1.h | 20 +++++++++++++++++++ types/ext_image_capture_source_v1/base.c | 11 ++++++++++ 3 files changed, 38 insertions(+) diff --git a/include/wlr/interfaces/wlr_ext_image_capture_source_v1.h b/include/wlr/interfaces/wlr_ext_image_capture_source_v1.h index 239e515c..49737ac3 100644 --- a/include/wlr/interfaces/wlr_ext_image_capture_source_v1.h +++ b/include/wlr/interfaces/wlr_ext_image_capture_source_v1.h @@ -13,12 +13,15 @@ #include struct wlr_ext_image_copy_capture_frame_v1; +struct wlr_seat; struct wlr_ext_image_capture_source_v1_interface { void (*schedule_frame)(struct wlr_ext_image_capture_source_v1 *source); void (*copy_frame)(struct wlr_ext_image_capture_source_v1 *source, struct wlr_ext_image_copy_capture_frame_v1 *dst_frame, struct wlr_ext_image_capture_source_v1_frame_event *frame_event); + struct wlr_ext_image_capture_source_v1_cursor *(*get_pointer_cursor)( + struct wlr_ext_image_capture_source_v1 *source, struct wlr_seat *seat); }; void wlr_ext_image_capture_source_v1_init(struct wlr_ext_image_capture_source_v1 *source, @@ -27,4 +30,8 @@ void wlr_ext_image_capture_source_v1_finish(struct wlr_ext_image_capture_source_ bool wlr_ext_image_capture_source_v1_create_resource(struct wlr_ext_image_capture_source_v1 *source, struct wl_client *client, uint32_t new_id); +void wlr_ext_image_capture_source_v1_cursor_init(struct wlr_ext_image_capture_source_v1_cursor *source_cursor, + const struct wlr_ext_image_capture_source_v1_interface *impl); +void wlr_ext_image_capture_source_v1_cursor_finish(struct wlr_ext_image_capture_source_v1_cursor *source_cursor); + #endif diff --git a/include/wlr/types/wlr_ext_image_capture_source_v1.h b/include/wlr/types/wlr_ext_image_capture_source_v1.h index 5405bf01..4247dd29 100644 --- a/include/wlr/types/wlr_ext_image_capture_source_v1.h +++ b/include/wlr/types/wlr_ext_image_capture_source_v1.h @@ -48,6 +48,26 @@ struct wlr_ext_image_capture_source_v1_frame_event { const pixman_region32_t *damage; }; +/** + * A cursor capture source. + * + * Provides additional cursor-specific functionality on top of + * struct wlr_ext_image_capture_source_v1. + */ +struct wlr_ext_image_capture_source_v1_cursor { + struct wlr_ext_image_capture_source_v1 base; + + bool entered; + int32_t x, y; + struct { + int32_t x, y; + } hotspot; + + struct { + struct wl_signal update; + } events; +}; + /** * Obtain a struct wlr_ext_image_capture_source_v1 from an ext_image_capture_source_v1 * resource. diff --git a/types/ext_image_capture_source_v1/base.c b/types/ext_image_capture_source_v1/base.c index 1b45da75..9c15ea0b 100644 --- a/types/ext_image_capture_source_v1/base.c +++ b/types/ext_image_capture_source_v1/base.c @@ -65,3 +65,14 @@ bool wlr_ext_image_capture_source_v1_create_resource(struct wlr_ext_image_captur } return true; } + +void wlr_ext_image_capture_source_v1_cursor_init(struct wlr_ext_image_capture_source_v1_cursor *source_cursor, + const struct wlr_ext_image_capture_source_v1_interface *impl) { + *source_cursor = (struct wlr_ext_image_capture_source_v1_cursor){0}; + wlr_ext_image_capture_source_v1_init(&source_cursor->base, impl); + wl_signal_init(&source_cursor->events.update); +} + +void wlr_ext_image_capture_source_v1_cursor_finish(struct wlr_ext_image_capture_source_v1_cursor *source_cursor) { + wlr_ext_image_capture_source_v1_finish(&source_cursor->base); +} From 18c5445d84c8e0c16aee7bca7c9322e51c940a90 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 18 Apr 2024 18:19:53 +0200 Subject: [PATCH 03/11] ext-image-copy-capture-v1: new protocol implementation Co-authored-by: Andri Yngvason --- .../wlr/types/wlr_ext_image_copy_capture_v1.h | 47 ++ protocol/meson.build | 1 + types/meson.build | 1 + types/wlr_ext_image_copy_capture_v1.c | 588 ++++++++++++++++++ 4 files changed, 637 insertions(+) create mode 100644 include/wlr/types/wlr_ext_image_copy_capture_v1.h create mode 100644 types/wlr_ext_image_copy_capture_v1.c diff --git a/include/wlr/types/wlr_ext_image_copy_capture_v1.h b/include/wlr/types/wlr_ext_image_copy_capture_v1.h new file mode 100644 index 00000000..9742dd1f --- /dev/null +++ b/include/wlr/types/wlr_ext_image_copy_capture_v1.h @@ -0,0 +1,47 @@ +/* + * This an unstable interface of wlroots. No guarantees are made regarding the + * future consistency of this API. + */ +#ifndef WLR_USE_UNSTABLE +#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" +#endif + +#ifndef WLR_TYPES_WLR_EXT_IMAGE_COPY_CAPTURE_V1_H +#define WLR_TYPES_WLR_EXT_IMAGE_COPY_CAPTURE_V1_H + +#include +#include +#include "ext-image-copy-capture-v1-protocol.h" + +struct wlr_ext_image_copy_capture_manager_v1 { + struct wl_global *global; + + // private state + + struct wl_listener display_destroy; +}; + +struct wlr_ext_image_copy_capture_frame_v1 { + struct wl_resource *resource; + bool capturing; + struct wlr_buffer *buffer; + pixman_region32_t buffer_damage; + + struct { + struct wl_signal destroy; + } events; + + // private state + + struct wlr_ext_image_copy_capture_session_v1 *session; +}; + +struct wlr_ext_image_copy_capture_manager_v1 *wlr_ext_image_copy_capture_manager_v1_create( + struct wl_display *display, uint32_t version); + +void wlr_ext_image_copy_capture_frame_v1_ready(struct wlr_ext_image_copy_capture_frame_v1 *frame, + enum wl_output_transform transform, const struct timespec *presentation_time); +void wlr_ext_image_copy_capture_frame_v1_fail(struct wlr_ext_image_copy_capture_frame_v1 *frame, + enum ext_image_copy_capture_frame_v1_failure_reason reason); + +#endif diff --git a/protocol/meson.build b/protocol/meson.build index b8b22003..1aac4da4 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -27,6 +27,7 @@ protocols = { 'ext-foreign-toplevel-list-v1': wl_protocol_dir / 'staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml', 'ext-idle-notify-v1': wl_protocol_dir / 'staging/ext-idle-notify/ext-idle-notify-v1.xml', 'ext-image-capture-source-v1': wl_protocol_dir / 'staging/ext-image-capture-source/ext-image-capture-source-v1.xml', + 'ext-image-copy-capture-v1': wl_protocol_dir / 'staging/ext-image-copy-capture/ext-image-copy-capture-v1.xml', 'ext-session-lock-v1': wl_protocol_dir / 'staging/ext-session-lock/ext-session-lock-v1.xml', 'fractional-scale-v1': wl_protocol_dir / 'staging/fractional-scale/fractional-scale-v1.xml', 'linux-drm-syncobj-v1': wl_protocol_dir / 'staging/linux-drm-syncobj/linux-drm-syncobj-v1.xml', diff --git a/types/meson.build b/types/meson.build index 899959da..2315e84a 100644 --- a/types/meson.build +++ b/types/meson.build @@ -44,6 +44,7 @@ wlr_files += files( 'wlr_drm.c', 'wlr_export_dmabuf_v1.c', 'wlr_foreign_toplevel_management_v1.c', + 'wlr_ext_image_copy_capture_v1.c', 'wlr_ext_foreign_toplevel_list_v1.c', 'wlr_fullscreen_shell_v1.c', 'wlr_gamma_control_v1.c', diff --git a/types/wlr_ext_image_copy_capture_v1.c b/types/wlr_ext_image_copy_capture_v1.c new file mode 100644 index 00000000..c0f5909e --- /dev/null +++ b/types/wlr_ext_image_copy_capture_v1.c @@ -0,0 +1,588 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define IMAGE_COPY_CAPTURE_MANAGER_V1_VERSION 1 + +struct wlr_ext_image_copy_capture_session_v1 { + struct wl_resource *resource; + struct wlr_ext_image_capture_source_v1 *source; + struct wlr_ext_image_copy_capture_frame_v1 *frame; + + struct wl_listener source_destroy; + struct wl_listener source_constraints_update; + struct wl_listener source_frame; + + pixman_region32_t damage; +}; + +struct wlr_ext_image_copy_capture_cursor_session_v1 { + struct wl_resource *resource; + struct wlr_ext_image_capture_source_v1_cursor *source; + bool capture_session_created; + + struct { + bool entered; + int32_t x, y; + struct { + int32_t x, y; + } hotspot; + } prev; + + struct wl_listener source_destroy; + struct wl_listener source_update; +}; + +static const struct ext_image_copy_capture_frame_v1_interface frame_impl; +static const struct ext_image_copy_capture_session_v1_interface session_impl; +static const struct ext_image_copy_capture_cursor_session_v1_interface cursor_session_impl; + +static struct wlr_ext_image_copy_capture_frame_v1 *frame_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &ext_image_copy_capture_frame_v1_interface, &frame_impl)); + return wl_resource_get_user_data(resource); +} + +static struct wlr_ext_image_copy_capture_session_v1 *session_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &ext_image_copy_capture_session_v1_interface, &session_impl)); + return wl_resource_get_user_data(resource); +} + +static struct wlr_ext_image_copy_capture_cursor_session_v1 *cursor_session_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &ext_image_copy_capture_cursor_session_v1_interface, &cursor_session_impl)); + return wl_resource_get_user_data(resource); +} + +static void frame_destroy(struct wlr_ext_image_copy_capture_frame_v1 *frame) { + if (frame == NULL) { + return; + } + wl_signal_emit_mutable(&frame->events.destroy, NULL); + wl_resource_set_user_data(frame->resource, NULL); + wlr_buffer_unlock(frame->buffer); + pixman_region32_fini(&frame->buffer_damage); + if (frame->session->frame == frame) { + frame->session->frame = NULL; + } + free(frame); +} + +static void frame_handle_resource_destroy(struct wl_resource *resource) { + frame_destroy(wl_resource_get_user_data(resource)); +} + +void wlr_ext_image_copy_capture_frame_v1_ready(struct wlr_ext_image_copy_capture_frame_v1 *frame, + enum wl_output_transform transform, + const struct timespec *presentation_time) { + assert(frame->capturing); + + int rects_len = 0; + const pixman_box32_t *rects = + pixman_region32_rectangles(&frame->session->damage, &rects_len); + for (int i = 0; i < rects_len; i++) { + const pixman_box32_t *rect = &rects[i]; + ext_image_copy_capture_frame_v1_send_damage(frame->resource, + rect->x1, rect->y1, rect->x2 - rect->x1, rect->y2 - rect->y1); + } + + ext_image_copy_capture_frame_v1_send_transform(frame->resource, transform); + ext_image_copy_capture_frame_v1_send_presentation_time(frame->resource, + presentation_time->tv_sec >> 32, presentation_time->tv_sec, + presentation_time->tv_nsec); + ext_image_copy_capture_frame_v1_send_ready(frame->resource); + frame_destroy(frame); +} + +void wlr_ext_image_copy_capture_frame_v1_fail(struct wlr_ext_image_copy_capture_frame_v1 *frame, + enum ext_image_copy_capture_frame_v1_failure_reason reason) { + ext_image_copy_capture_frame_v1_send_failed(frame->resource, reason); + frame_destroy(frame); +} + +static void frame_handle_destroy(struct wl_client *client, + struct wl_resource *frame_resource) { + wl_resource_destroy(frame_resource); +} + +static void frame_handle_attach_buffer(struct wl_client *client, + struct wl_resource *frame_resource, struct wl_resource *buffer_resource) { + struct wlr_ext_image_copy_capture_frame_v1 *frame = frame_from_resource(frame_resource); + if (frame == NULL) { + return; + } + + if (frame->capturing) { + wl_resource_post_error(frame->resource, + EXT_IMAGE_COPY_CAPTURE_FRAME_V1_ERROR_ALREADY_CAPTURED, + "attach_buffer sent after capture"); + return; + } + + struct wlr_buffer *buffer = wlr_buffer_try_from_resource(buffer_resource); + if (buffer == NULL) { + wl_resource_post_no_memory(frame_resource); + return; + } + + wlr_buffer_unlock(frame->buffer); + frame->buffer = buffer; +} + +static void frame_handle_damage_buffer(struct wl_client *client, + struct wl_resource *frame_resource, int32_t x, int32_t y, + int32_t width, int32_t height) { + struct wlr_ext_image_copy_capture_frame_v1 *frame = frame_from_resource(frame_resource); + if (frame == NULL) { + return; + } + + if (frame->capturing) { + wl_resource_post_error(frame->resource, + EXT_IMAGE_COPY_CAPTURE_FRAME_V1_ERROR_ALREADY_CAPTURED, + "damage_buffer sent after capture"); + return; + } + + if (x < 0 || y < 0 || width <= 0 || height <= 0) { + wl_resource_post_error(frame->resource, + EXT_IMAGE_COPY_CAPTURE_FRAME_V1_ERROR_INVALID_BUFFER_DAMAGE, + "Invalid buffer damage coordinates"); + return; + } + + pixman_region32_union_rect(&frame->buffer_damage, &frame->buffer_damage, + x, y, width, height); +} + +static void frame_handle_capture(struct wl_client *client, + struct wl_resource *frame_resource) { + struct wlr_ext_image_copy_capture_frame_v1 *frame = frame_from_resource(frame_resource); + if (frame == NULL) { + return; + } + + if (frame->capturing) { + wl_resource_post_error(frame->resource, + EXT_IMAGE_COPY_CAPTURE_FRAME_V1_ERROR_ALREADY_CAPTURED, + "capture sent twice"); + return; + } + + if (frame->buffer == NULL) { + wl_resource_post_error(frame->resource, + EXT_IMAGE_COPY_CAPTURE_FRAME_V1_ERROR_NO_BUFFER, + "No buffer attached"); + return; + } + + frame->capturing = true; + + bool need_frame = pixman_region32_not_empty(&frame->session->damage); + struct wlr_ext_image_capture_source_v1 *source = frame->session->source; + if (need_frame && source->impl->schedule_frame) { + source->impl->schedule_frame(source); + } +} + +static const struct ext_image_copy_capture_frame_v1_interface frame_impl = { + .destroy = frame_handle_destroy, + .attach_buffer = frame_handle_attach_buffer, + .damage_buffer = frame_handle_damage_buffer, + .capture = frame_handle_capture, +}; + +static void session_handle_destroy(struct wl_client *client, + struct wl_resource *session_resource) { + wl_resource_destroy(session_resource); +} + +static void session_handle_create_frame(struct wl_client *client, + struct wl_resource *session_resource, uint32_t new_id) { + struct wlr_ext_image_copy_capture_session_v1 *session = session_from_resource(session_resource); + + if (session->frame != NULL) { + wl_resource_post_error(session_resource, + EXT_IMAGE_COPY_CAPTURE_SESSION_V1_ERROR_DUPLICATE_FRAME, + "session already has a frame object"); + return; + } + + uint32_t version = wl_resource_get_version(session_resource); + struct wl_resource *frame_resource = wl_resource_create(client, + &ext_image_copy_capture_frame_v1_interface, version, new_id); + if (frame_resource == NULL) { + wl_resource_post_no_memory(frame_resource); + return; + } + wl_resource_set_implementation(frame_resource, &frame_impl, NULL, + frame_handle_resource_destroy); + + if (session == NULL) { + ext_image_copy_capture_frame_v1_send_failed(frame_resource, + EXT_IMAGE_COPY_CAPTURE_FRAME_V1_FAILURE_REASON_STOPPED); + return; + } + + struct wlr_ext_image_copy_capture_frame_v1 *frame = calloc(1, sizeof(*frame)); + if (frame == NULL) { + wl_resource_post_no_memory(session_resource); + return; + } + + frame->resource = frame_resource; + frame->session = session; + pixman_region32_init(&frame->buffer_damage); + wl_signal_init(&frame->events.destroy); + + wl_resource_set_user_data(frame_resource, frame); + + session->frame = frame; +} + +static const struct ext_image_copy_capture_session_v1_interface session_impl = { + .destroy = session_handle_destroy, + .create_frame = session_handle_create_frame, +}; + +static void session_send_constraints(struct wlr_ext_image_copy_capture_session_v1 *session) { + struct wlr_ext_image_capture_source_v1 *source = session->source; + + ext_image_copy_capture_session_v1_send_buffer_size(session->resource, + source->width, source->height); + + for (size_t i = 0; i < source->shm_formats_len; i++) { + ext_image_copy_capture_session_v1_send_shm_format(session->resource, + source->shm_formats[i]); + } + + if (source->dmabuf_formats.len > 0) { + struct wl_array dev_id_array = { + .data = &source->dmabuf_device, + .size = sizeof(source->dmabuf_device), + }; + ext_image_copy_capture_session_v1_send_dmabuf_device(session->resource, + &dev_id_array); + } + for (size_t i = 0; i < source->dmabuf_formats.len; i++) { + struct wlr_drm_format *fmt = &source->dmabuf_formats.formats[i]; + struct wl_array modifiers_array = { + .data = fmt->modifiers, + .size = fmt->len * sizeof(fmt->modifiers[0]), + }; + ext_image_copy_capture_session_v1_send_dmabuf_format(session->resource, + fmt->format, &modifiers_array); + } + + ext_image_copy_capture_session_v1_send_done(session->resource); +} + +static void session_destroy(struct wlr_ext_image_copy_capture_session_v1 *session) { + if (session == NULL) { + return; + } + + if (session->frame != NULL) { + wlr_ext_image_copy_capture_frame_v1_fail(session->frame, + EXT_IMAGE_COPY_CAPTURE_FRAME_V1_FAILURE_REASON_STOPPED); + } + + ext_image_copy_capture_session_v1_send_stopped(session->resource); + wl_resource_set_user_data(session->resource, NULL); + + pixman_region32_fini(&session->damage); + wl_list_remove(&session->source_destroy.link); + wl_list_remove(&session->source_constraints_update.link); + wl_list_remove(&session->source_frame.link); + free(session); +} + +static void session_handle_source_destroy(struct wl_listener *listener, void *data) { + struct wlr_ext_image_copy_capture_session_v1 *session = wl_container_of(listener, session, source_destroy); + session_destroy(session); +} + +static void session_handle_source_constraints_update(struct wl_listener *listener, + void *data) { + struct wlr_ext_image_copy_capture_session_v1 *session = + wl_container_of(listener, session, source_constraints_update); + session_send_constraints(session); +} + +static void session_handle_source_frame(struct wl_listener *listener, void *data) { + struct wlr_ext_image_copy_capture_session_v1 *session = wl_container_of(listener, session, source_frame); + struct wlr_ext_image_capture_source_v1_frame_event *event = data; + + pixman_region32_union(&session->damage, &session->damage, event->damage); + + struct wlr_ext_image_copy_capture_frame_v1 *frame = session->frame; + if (frame != NULL && frame->capturing && + pixman_region32_not_empty(&session->damage)) { + pixman_region32_union(&frame->buffer_damage, + &frame->buffer_damage, &session->damage); + + struct wlr_ext_image_capture_source_v1 *source = frame->session->source; + source->impl->copy_frame(source, frame, event); + + // TODO: don't clear on copy error + pixman_region32_clear(&session->damage); + } +} + +static void session_handle_resource_destroy(struct wl_resource *resource) { + session_destroy(session_from_resource(resource)); +} + +static void session_create(struct wl_resource *parent_resource, uint32_t new_id, + struct wlr_ext_image_capture_source_v1 *source, uint32_t options) { + struct wl_client *client = wl_resource_get_client(parent_resource); + uint32_t version = wl_resource_get_version(parent_resource); + struct wl_resource *session_resource = wl_resource_create(client, + &ext_image_copy_capture_session_v1_interface, version, new_id); + if (session_resource == NULL) { + wl_resource_post_no_memory(parent_resource); + return; + } + wl_resource_set_implementation(session_resource, &session_impl, NULL, + session_handle_resource_destroy); + + if (source == NULL) { + ext_image_copy_capture_session_v1_send_stopped(session_resource); + return; + } + + struct wlr_ext_image_copy_capture_session_v1 *session = calloc(1, sizeof(*session)); + if (session == NULL) { + wl_resource_post_no_memory(parent_resource); + return; + } + + session->resource = session_resource; + session->source = source; + pixman_region32_init_rect(&session->damage, 0, 0, source->width, + source->height); + + session->source_destroy.notify = session_handle_source_destroy; + wl_signal_add(&source->events.destroy, &session->source_destroy); + + session->source_constraints_update.notify = session_handle_source_constraints_update; + wl_signal_add(&source->events.constraints_update, &session->source_constraints_update); + + session->source_frame.notify = session_handle_source_frame; + wl_signal_add(&source->events.frame, &session->source_frame); + + wl_resource_set_user_data(session_resource, session); + session_send_constraints(session); +} + +static void cursor_session_destroy(struct wlr_ext_image_copy_capture_cursor_session_v1 *cursor_session) { + if (cursor_session == NULL) { + return; + } + if (cursor_session->source->entered) { + ext_image_copy_capture_cursor_session_v1_send_leave(cursor_session->resource); + } + wl_resource_set_user_data(cursor_session->resource, NULL); + wl_list_remove(&cursor_session->source_destroy.link); + wl_list_remove(&cursor_session->source_update.link); + free(cursor_session); +} + +static void cursor_session_handle_destroy(struct wl_client *client, + struct wl_resource *cursor_session_resource) { + wl_resource_destroy(cursor_session_resource); +} + +static void cursor_session_handle_get_capture_session(struct wl_client *client, + struct wl_resource *cursor_session_resource, uint32_t new_id) { + struct wlr_ext_image_copy_capture_cursor_session_v1 *cursor_session = + cursor_session_from_resource(cursor_session_resource); + + if (cursor_session != NULL && cursor_session->capture_session_created) { + wl_resource_post_error(cursor_session_resource, + EXT_IMAGE_COPY_CAPTURE_CURSOR_SESSION_V1_ERROR_DUPLICATE_SESSION, + "get_capture_session sent twice"); + return; + } + + struct wlr_ext_image_capture_source_v1 *source = NULL; + if (cursor_session != NULL) { + source = &cursor_session->source->base; + } + + session_create(cursor_session_resource, new_id, source, 0); +} + +static const struct ext_image_copy_capture_cursor_session_v1_interface cursor_session_impl = { + .destroy = cursor_session_handle_destroy, + .get_capture_session = cursor_session_handle_get_capture_session, +}; + +static void cursor_session_handle_resource_destroy(struct wl_resource *resource) { + struct wlr_ext_image_copy_capture_cursor_session_v1 *cursor_session = + cursor_session_from_resource(resource); + cursor_session_destroy(cursor_session); +} + +static void cursor_session_handle_source_destroy(struct wl_listener *listener, + void *data) { + struct wlr_ext_image_copy_capture_cursor_session_v1 *cursor_session = + wl_container_of(listener, cursor_session, source_destroy); + cursor_session_destroy(cursor_session); +} + +static void cursor_session_update( + struct wlr_ext_image_copy_capture_cursor_session_v1 *cursor_session) { + struct wlr_ext_image_capture_source_v1_cursor *cursor_source = cursor_session->source; + + if (cursor_source->entered && !cursor_session->prev.entered) { + ext_image_copy_capture_cursor_session_v1_send_enter(cursor_session->resource); + } + if (!cursor_source->entered && cursor_session->prev.entered) { + ext_image_copy_capture_cursor_session_v1_send_leave(cursor_session->resource); + } + + if (cursor_source->x != cursor_session->prev.x || + cursor_source->y != cursor_session->prev.y) { + ext_image_copy_capture_cursor_session_v1_send_position(cursor_session->resource, + cursor_source->x, cursor_source->y); + } + + if (cursor_source->hotspot.x != cursor_session->prev.hotspot.x || + cursor_source->hotspot.y != cursor_session->prev.hotspot.y) { + ext_image_copy_capture_cursor_session_v1_send_hotspot(cursor_session->resource, + cursor_source->hotspot.x, cursor_source->hotspot.y); + } + + cursor_session->prev.entered = cursor_source->entered; + cursor_session->prev.x = cursor_source->x; + cursor_session->prev.y = cursor_source->y; + cursor_session->prev.hotspot.y = cursor_source->hotspot.y; + cursor_session->prev.hotspot.y = cursor_source->hotspot.y; +} + +static void cursor_session_handle_source_update(struct wl_listener *listener, + void *data) { + struct wlr_ext_image_copy_capture_cursor_session_v1 *cursor_session = + wl_container_of(listener, cursor_session, source_update); + cursor_session_update(cursor_session); +} + +static void manager_handle_create_session(struct wl_client *client, + struct wl_resource *manager_resource, uint32_t new_id, + struct wl_resource *source_resource, uint32_t options) { + struct wlr_ext_image_capture_source_v1 *source = + wlr_ext_image_capture_source_v1_from_resource(source_resource); + session_create(manager_resource, new_id, source, options); +} + +static void manager_handle_create_pointer_cursor_session(struct wl_client *client, + struct wl_resource *manager_resource, uint32_t new_id, + struct wl_resource *source_resource, struct wl_resource *pointer_resource) { + struct wlr_ext_image_capture_source_v1 *source = wlr_ext_image_capture_source_v1_from_resource(source_resource); + struct wlr_seat_client *seat_client = wlr_seat_client_from_pointer_resource(pointer_resource); + + struct wlr_seat *seat = NULL; + if (seat_client != NULL) { + seat = seat_client->seat; + } + + struct wlr_ext_image_capture_source_v1_cursor *source_cursor = NULL; + if (source != NULL && seat != NULL && source->impl->get_pointer_cursor) { + source_cursor = source->impl->get_pointer_cursor(source, seat); + } + + uint32_t version = wl_resource_get_version(manager_resource); + struct wl_resource *cursor_session_resource = wl_resource_create(client, + &ext_image_copy_capture_cursor_session_v1_interface, version, new_id); + if (cursor_session_resource == NULL) { + wl_resource_post_no_memory(manager_resource); + return; + } + wl_resource_set_implementation(cursor_session_resource, &cursor_session_impl, NULL, + cursor_session_handle_resource_destroy); + + if (source_cursor == NULL) { + return; // leave inert + } + + struct wlr_ext_image_copy_capture_cursor_session_v1 *cursor_session = calloc(1, sizeof(*cursor_session)); + if (cursor_session == NULL) { + wl_resource_post_no_memory(manager_resource); + return; + } + + cursor_session->resource = cursor_session_resource; + cursor_session->source = source_cursor; + + cursor_session->source_destroy.notify = cursor_session_handle_source_destroy; + wl_signal_add(&source_cursor->base.events.destroy, &cursor_session->source_destroy); + + cursor_session->source_update.notify = cursor_session_handle_source_update; + wl_signal_add(&source_cursor->events.update, &cursor_session->source_update); + + wl_resource_set_user_data(cursor_session_resource, cursor_session); + + cursor_session_update(cursor_session); +} + +static void manager_handle_destroy(struct wl_client *client, + struct wl_resource *manager_resource) { + wl_resource_destroy(manager_resource); +} + +static const struct ext_image_copy_capture_manager_v1_interface manager_impl = { + .create_session = manager_handle_create_session, + .create_pointer_cursor_session = manager_handle_create_pointer_cursor_session, + .destroy = manager_handle_destroy, +}; + +static void manager_bind(struct wl_client *client, void *data, + uint32_t version, uint32_t id) { + struct wl_resource *resource = wl_resource_create(client, + &ext_image_copy_capture_manager_v1_interface, version, id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &manager_impl, NULL, NULL); +} + +static void manager_handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_ext_image_copy_capture_manager_v1 *manager = + wl_container_of(listener, manager, display_destroy); + wl_list_remove(&manager->display_destroy.link); + wl_global_destroy(manager->global); + free(manager); +} + +struct wlr_ext_image_copy_capture_manager_v1 *wlr_ext_image_copy_capture_manager_v1_create( + struct wl_display *display, uint32_t version) { + assert(version <= IMAGE_COPY_CAPTURE_MANAGER_V1_VERSION); + + struct wlr_ext_image_copy_capture_manager_v1 *manager = calloc(1, sizeof(*manager)); + if (manager == NULL) { + return NULL; + } + + manager->global = wl_global_create(display, + &ext_image_copy_capture_manager_v1_interface, version, manager, manager_bind); + if (manager->global == NULL) { + free(manager); + return NULL; + } + + manager->display_destroy.notify = manager_handle_display_destroy; + wl_display_add_destroy_listener(display, &manager->display_destroy); + + return manager; +} From 90ed3298e4ebacd7f2de1dbd7b78fc2acb404df7 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 18 Apr 2024 18:20:29 +0200 Subject: [PATCH 04/11] ext-image-copy-capture-v1: add wlr_ext_image_copy_capture_frame_v1_copy_buffer() --- .../wlr/types/wlr_ext_image_copy_capture_v1.h | 2 + types/wlr_ext_image_copy_capture_v1.c | 89 +++++++++++++++++++ 2 files changed, 91 insertions(+) diff --git a/include/wlr/types/wlr_ext_image_copy_capture_v1.h b/include/wlr/types/wlr_ext_image_copy_capture_v1.h index 9742dd1f..68f0579f 100644 --- a/include/wlr/types/wlr_ext_image_copy_capture_v1.h +++ b/include/wlr/types/wlr_ext_image_copy_capture_v1.h @@ -41,6 +41,8 @@ struct wlr_ext_image_copy_capture_manager_v1 *wlr_ext_image_copy_capture_manager void wlr_ext_image_copy_capture_frame_v1_ready(struct wlr_ext_image_copy_capture_frame_v1 *frame, enum wl_output_transform transform, const struct timespec *presentation_time); +bool wlr_ext_image_copy_capture_frame_v1_copy_buffer(struct wlr_ext_image_copy_capture_frame_v1 *frame, + struct wlr_buffer *src, struct wlr_renderer *renderer); void wlr_ext_image_copy_capture_frame_v1_fail(struct wlr_ext_image_copy_capture_frame_v1 *frame, enum ext_image_copy_capture_frame_v1_failure_reason reason); diff --git a/types/wlr_ext_image_copy_capture_v1.c b/types/wlr_ext_image_copy_capture_v1.c index c0f5909e..6dc8ca9b 100644 --- a/types/wlr_ext_image_copy_capture_v1.c +++ b/types/wlr_ext_image_copy_capture_v1.c @@ -103,6 +103,95 @@ void wlr_ext_image_copy_capture_frame_v1_ready(struct wlr_ext_image_copy_capture frame_destroy(frame); } +static bool copy_dmabuf(struct wlr_buffer *dst, + struct wlr_buffer *src, struct wlr_renderer *renderer, + const pixman_region32_t *clip) { + struct wlr_texture *texture = wlr_texture_from_buffer(renderer, src); + if (texture == NULL) { + return false; + } + + bool ok = false; + struct wlr_render_pass *pass = wlr_renderer_begin_buffer_pass(renderer, dst, NULL); + if (!pass) { + goto out; + } + + wlr_render_pass_add_texture(pass, &(struct wlr_render_texture_options) { + .texture = texture, + .clip = clip, + .blend_mode = WLR_RENDER_BLEND_MODE_NONE, + }); + + ok = wlr_render_pass_submit(pass); + +out: + wlr_texture_destroy(texture); + return ok; +} + +static bool copy_shm(void *data, uint32_t format, size_t stride, + struct wlr_buffer *src, struct wlr_renderer *renderer) { + // TODO: bypass renderer if source buffer supports data ptr access + struct wlr_texture *texture = wlr_texture_from_buffer(renderer, src); + if (!texture) { + return false; + } + + // TODO: only copy damaged region + bool ok = wlr_texture_read_pixels(texture, &(struct wlr_texture_read_pixels_options){ + .data = data, + .format = format, + .stride = stride, + }); + + wlr_texture_destroy(texture); + + return ok; +} + +bool wlr_ext_image_copy_capture_frame_v1_copy_buffer(struct wlr_ext_image_copy_capture_frame_v1 *frame, + struct wlr_buffer *src, struct wlr_renderer *renderer) { + struct wlr_buffer *dst = frame->buffer; + + if (src->width != dst->width || src->height != dst->height) { + wlr_ext_image_copy_capture_frame_v1_fail(frame, + EXT_IMAGE_COPY_CAPTURE_FRAME_V1_FAILURE_REASON_BUFFER_CONSTRAINTS); + return false; + } + + bool ok = false; + enum ext_image_copy_capture_frame_v1_failure_reason failure_reason = + EXT_IMAGE_COPY_CAPTURE_FRAME_V1_FAILURE_REASON_UNKNOWN; + struct wlr_dmabuf_attributes dmabuf; + void *data; + uint32_t format; + size_t stride; + if (wlr_buffer_get_dmabuf(dst, &dmabuf)) { + if (frame->session->source->dmabuf_formats.len == 0) { + ok = false; + failure_reason = EXT_IMAGE_COPY_CAPTURE_FRAME_V1_FAILURE_REASON_BUFFER_CONSTRAINTS; + } else { + ok = copy_dmabuf(dst, src, renderer, &frame->buffer_damage); + } + } else if (wlr_buffer_begin_data_ptr_access(dst, + WLR_BUFFER_DATA_PTR_ACCESS_WRITE, &data, &format, &stride)) { + if (frame->session->source->shm_formats_len == 0) { + ok = false; + failure_reason = EXT_IMAGE_COPY_CAPTURE_FRAME_V1_FAILURE_REASON_BUFFER_CONSTRAINTS; + } else { + ok = copy_shm(data, format, stride, src, renderer); + } + wlr_buffer_end_data_ptr_access(dst); + } + if (!ok) { + wlr_ext_image_copy_capture_frame_v1_fail(frame, failure_reason); + return false; + } + + return true; +} + void wlr_ext_image_copy_capture_frame_v1_fail(struct wlr_ext_image_copy_capture_frame_v1 *frame, enum ext_image_copy_capture_frame_v1_failure_reason reason) { ext_image_copy_capture_frame_v1_send_failed(frame->resource, reason); From e3460030e64510c81dc977cd00679a093fa325c7 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 18 Apr 2024 18:39:28 +0200 Subject: [PATCH 05/11] ext-image-capture-source-v1: add wlr_ext_image_capture_source_v1_set_constraints_from_swapchain() --- .../wlr_ext_image_capture_source_v1.h | 5 ++ types/ext_image_capture_source_v1/base.c | 63 +++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/include/wlr/interfaces/wlr_ext_image_capture_source_v1.h b/include/wlr/interfaces/wlr_ext_image_capture_source_v1.h index 49737ac3..09ec188e 100644 --- a/include/wlr/interfaces/wlr_ext_image_capture_source_v1.h +++ b/include/wlr/interfaces/wlr_ext_image_capture_source_v1.h @@ -13,6 +13,8 @@ #include struct wlr_ext_image_copy_capture_frame_v1; +struct wlr_swapchain; +struct wlr_renderer; struct wlr_seat; struct wlr_ext_image_capture_source_v1_interface { @@ -29,6 +31,9 @@ void wlr_ext_image_capture_source_v1_init(struct wlr_ext_image_capture_source_v1 void wlr_ext_image_capture_source_v1_finish(struct wlr_ext_image_capture_source_v1 *source); bool wlr_ext_image_capture_source_v1_create_resource(struct wlr_ext_image_capture_source_v1 *source, struct wl_client *client, uint32_t new_id); +void wlr_ext_image_capture_source_v1_set_constraints_from_swapchain( + struct wlr_ext_image_capture_source_v1 *source, + struct wlr_swapchain *swapchain, struct wlr_renderer *renderer); void wlr_ext_image_capture_source_v1_cursor_init(struct wlr_ext_image_capture_source_v1_cursor *source_cursor, const struct wlr_ext_image_capture_source_v1_interface *impl); diff --git a/types/ext_image_capture_source_v1/base.c b/types/ext_image_capture_source_v1/base.c index 9c15ea0b..e4b03f85 100644 --- a/types/ext_image_capture_source_v1/base.c +++ b/types/ext_image_capture_source_v1/base.c @@ -1,6 +1,12 @@ #include +#include #include +#include #include +#include +#include +#include +#include #include #include "ext-image-capture-source-v1-protocol.h" @@ -66,6 +72,63 @@ bool wlr_ext_image_capture_source_v1_create_resource(struct wlr_ext_image_captur return true; } +static uint32_t get_swapchain_shm_format(struct wlr_swapchain *swapchain, + struct wlr_renderer *renderer) { + struct wlr_buffer *buffer = wlr_swapchain_acquire(swapchain); + if (buffer == NULL) { + return DRM_FORMAT_INVALID; + } + + struct wlr_texture *texture = wlr_texture_from_buffer(renderer, buffer); + wlr_buffer_unlock(buffer); + if (texture == NULL) { + return DRM_FORMAT_INVALID; + } + + uint32_t format = wlr_texture_preferred_read_format(texture); + wlr_texture_destroy(texture); + + return format; +} + +void wlr_ext_image_capture_source_v1_set_constraints_from_swapchain(struct wlr_ext_image_capture_source_v1 *source, + struct wlr_swapchain *swapchain, struct wlr_renderer *renderer) { + source->width = swapchain->width; + source->height = swapchain->height; + + uint32_t shm_format = get_swapchain_shm_format(swapchain, renderer); + if (shm_format != DRM_FORMAT_INVALID) { + uint32_t *shm_formats = calloc(1, sizeof(shm_formats[0])); + if (shm_formats == NULL) { + return; + } + shm_formats[0] = shm_format; + + source->shm_formats_len = 1; + free(source->shm_formats); + source->shm_formats = shm_formats; + } + + int drm_fd = wlr_renderer_get_drm_fd(renderer); + if (swapchain->allocator != NULL && + (swapchain->allocator->buffer_caps & WLR_BUFFER_CAP_DMABUF) && + drm_fd >= 0) { + struct stat dev_stat; + if (fstat(drm_fd, &dev_stat) != 0) { + return; + } + + source->dmabuf_device = dev_stat.st_rdev; + source->dmabuf_formats.len = 0; + for (size_t i = 0; i < swapchain->format.len; i++) { + wlr_drm_format_set_add(&source->dmabuf_formats, + swapchain->format.format, swapchain->format.modifiers[i]); + } + } + + wl_signal_emit_mutable(&source->events.constraints_update, NULL); +} + void wlr_ext_image_capture_source_v1_cursor_init(struct wlr_ext_image_capture_source_v1_cursor *source_cursor, const struct wlr_ext_image_capture_source_v1_interface *impl) { *source_cursor = (struct wlr_ext_image_capture_source_v1_cursor){0}; From 145ad0ce468841c4b420b4c002ed2c72df955e29 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 12 Feb 2024 20:16:28 +0100 Subject: [PATCH 06/11] ext-image-capture-source-v1: add output source --- .../types/wlr_ext_image_capture_source_v1.h | 14 ++ types/ext_image_capture_source_v1/output.c | 198 ++++++++++++++++++ types/meson.build | 1 + 3 files changed, 213 insertions(+) create mode 100644 types/ext_image_capture_source_v1/output.c diff --git a/include/wlr/types/wlr_ext_image_capture_source_v1.h b/include/wlr/types/wlr_ext_image_capture_source_v1.h index 4247dd29..2fd43502 100644 --- a/include/wlr/types/wlr_ext_image_capture_source_v1.h +++ b/include/wlr/types/wlr_ext_image_capture_source_v1.h @@ -68,6 +68,17 @@ struct wlr_ext_image_capture_source_v1_cursor { } events; }; +/** + * Interface exposing one screen capture source per output. + */ +struct wlr_ext_output_image_capture_source_manager_v1 { + struct wl_global *global; + + // private state + + struct wl_listener display_destroy; +}; + /** * Obtain a struct wlr_ext_image_capture_source_v1 from an ext_image_capture_source_v1 * resource. @@ -77,4 +88,7 @@ struct wlr_ext_image_capture_source_v1_cursor { */ struct wlr_ext_image_capture_source_v1 *wlr_ext_image_capture_source_v1_from_resource(struct wl_resource *resource); +struct wlr_ext_output_image_capture_source_manager_v1 *wlr_ext_output_image_capture_source_manager_v1_create( + struct wl_display *display, uint32_t version); + #endif diff --git a/types/ext_image_capture_source_v1/output.c b/types/ext_image_capture_source_v1/output.c new file mode 100644 index 00000000..f547ae31 --- /dev/null +++ b/types/ext_image_capture_source_v1/output.c @@ -0,0 +1,198 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "render/wlr_renderer.h" +#include "ext-image-capture-source-v1-protocol.h" + +#define OUTPUT_IMAGE_SOURCE_MANAGER_V1_VERSION 1 + +struct wlr_ext_output_image_capture_source_v1 { + struct wlr_ext_image_capture_source_v1 base; + struct wlr_addon addon; + + struct wlr_output *output; + + struct wl_listener output_commit; +}; + +struct wlr_ext_output_image_capture_source_v1_frame_event { + struct wlr_ext_image_capture_source_v1_frame_event base; + struct wlr_buffer *buffer; + struct timespec *when; +}; + +static void output_source_schedule_frame(struct wlr_ext_image_capture_source_v1 *base) { + struct wlr_ext_output_image_capture_source_v1 *source = wl_container_of(base, source, base); + wlr_output_update_needs_frame(source->output); +} + +static void output_source_copy_frame(struct wlr_ext_image_capture_source_v1 *base, + struct wlr_ext_image_copy_capture_frame_v1 *frame, + struct wlr_ext_image_capture_source_v1_frame_event *base_event) { + struct wlr_ext_output_image_capture_source_v1 *source = wl_container_of(base, source, base); + struct wlr_ext_output_image_capture_source_v1_frame_event *event = + wl_container_of(base_event, event, base); + + if (wlr_ext_image_copy_capture_frame_v1_copy_buffer(frame, + event->buffer, source->output->renderer)) { + wlr_ext_image_copy_capture_frame_v1_ready(frame, + source->output->transform, event->when); + } +} + +static const struct wlr_ext_image_capture_source_v1_interface output_source_impl = { + .schedule_frame = output_source_schedule_frame, + .copy_frame = output_source_copy_frame, +}; + +static void source_update_buffer_constraints(struct wlr_ext_output_image_capture_source_v1 *source) { + struct wlr_output *output = source->output; + + if (!wlr_output_configure_primary_swapchain(output, NULL, &output->swapchain)) { + return; + } + + wlr_ext_image_capture_source_v1_set_constraints_from_swapchain(&source->base, + output->swapchain, output->renderer); +} + +static void source_handle_output_commit(struct wl_listener *listener, + void *data) { + struct wlr_ext_output_image_capture_source_v1 *source = wl_container_of(listener, source, output_commit); + struct wlr_output_event_commit *event = data; + + if (event->state->committed & (WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_RENDER_FORMAT)) { + source_update_buffer_constraints(source); + } + + if (event->state->committed & WLR_OUTPUT_STATE_BUFFER) { + struct wlr_buffer *buffer = event->state->buffer; + + pixman_region32_t full_damage; + pixman_region32_init_rect(&full_damage, 0, 0, buffer->width, buffer->height); + + const pixman_region32_t *damage; + if (event->state->committed & WLR_OUTPUT_STATE_DAMAGE) { + damage = &event->state->damage; + } else { + damage = &full_damage; + } + + struct wlr_ext_output_image_capture_source_v1_frame_event frame_event = { + .base = { + .damage = damage, + }, + .buffer = buffer, + .when = event->when, // TODO: predict next presentation time instead + }; + wl_signal_emit_mutable(&source->base.events.frame, &frame_event); + + pixman_region32_fini(&full_damage); + } +} + +static void output_addon_destroy(struct wlr_addon *addon) { + struct wlr_ext_output_image_capture_source_v1 *source = wl_container_of(addon, source, addon); + wlr_ext_image_capture_source_v1_finish(&source->base); + wl_list_remove(&source->output_commit.link); + wlr_addon_finish(&source->addon); + free(source); +} + +static const struct wlr_addon_interface output_addon_impl = { + .name = "wlr_ext_output_image_capture_source_v1", + .destroy = output_addon_destroy, +}; + +static void output_manager_handle_create_source(struct wl_client *client, + struct wl_resource *manager_resource, uint32_t new_id, + struct wl_resource *output_resource) { + struct wlr_output *output = wlr_output_from_resource(output_resource); + if (output == NULL) { + wlr_ext_image_capture_source_v1_create_resource(NULL, client, new_id); + return; + } + + struct wlr_ext_output_image_capture_source_v1 *source; + struct wlr_addon *addon = wlr_addon_find(&output->addons, NULL, &output_addon_impl); + if (addon != NULL) { + source = wl_container_of(addon, source, addon); + } else { + source = calloc(1, sizeof(*source)); + if (source == NULL) { + wl_resource_post_no_memory(manager_resource); + return; + } + + wlr_ext_image_capture_source_v1_init(&source->base, &output_source_impl); + wlr_addon_init(&source->addon, &output->addons, NULL, &output_addon_impl); + source->output = output; + + source->output_commit.notify = source_handle_output_commit; + wl_signal_add(&output->events.commit, &source->output_commit); + + source_update_buffer_constraints(source); + } + + if (!wlr_ext_image_capture_source_v1_create_resource(&source->base, client, new_id)) { + return; + } +} + +static void output_manager_handle_destroy(struct wl_client *client, + struct wl_resource *manager_resource) { + wl_resource_destroy(manager_resource); +} + +static const struct ext_output_image_capture_source_manager_v1_interface output_manager_impl = { + .create_source = output_manager_handle_create_source, + .destroy = output_manager_handle_destroy, +}; + +static void output_manager_bind(struct wl_client *client, void *data, + uint32_t version, uint32_t id) { + struct wlr_ext_output_image_capture_source_manager_v1 *manager = data; + + struct wl_resource *resource = wl_resource_create(client, + &ext_output_image_capture_source_manager_v1_interface, version, id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &output_manager_impl, manager, NULL); +} + +static void output_manager_handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_ext_output_image_capture_source_manager_v1 *manager = + wl_container_of(listener, manager, display_destroy); + wl_list_remove(&manager->display_destroy.link); + wl_global_destroy(manager->global); + free(manager); +} + +struct wlr_ext_output_image_capture_source_manager_v1 *wlr_ext_output_image_capture_source_manager_v1_create( + struct wl_display *display, uint32_t version) { + assert(version <= OUTPUT_IMAGE_SOURCE_MANAGER_V1_VERSION); + + struct wlr_ext_output_image_capture_source_manager_v1 *manager = calloc(1, sizeof(*manager)); + if (manager == NULL) { + return NULL; + } + + manager->global = wl_global_create(display, + &ext_output_image_capture_source_manager_v1_interface, version, manager, output_manager_bind); + if (manager->global == NULL) { + free(manager); + return NULL; + } + + manager->display_destroy.notify = output_manager_handle_display_destroy; + wl_display_add_destroy_listener(display, &manager->display_destroy); + + return manager; +} diff --git a/types/meson.build b/types/meson.build index 2315e84a..6a00af0e 100644 --- a/types/meson.build +++ b/types/meson.build @@ -4,6 +4,7 @@ wlr_files += files( 'data_device/wlr_data_source.c', 'data_device/wlr_drag.c', 'ext_image_capture_source_v1/base.c', + 'ext_image_capture_source_v1/output.c', 'output/cursor.c', 'output/output.c', 'output/render.c', From 55077701943a5a508de9f5522e401e2fda6f3174 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 26 Apr 2024 18:41:58 +0200 Subject: [PATCH 07/11] output: require commit after hardware cursor update Up until now only the DRM backend required an output commit after updating the cursor. Unify this for all backends, because: - Screen capture can now catch cursor updates listening for output commits - In the future we want to make the cursor a regular wlr_output_layer, which would need an output commit to be updated anyways --- backend/drm/drm.c | 2 -- types/output/cursor.c | 17 +++++++++++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 1a1af309..e838c246 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -1129,7 +1129,6 @@ static bool drm_connector_set_cursor(struct wlr_output *output, conn->cursor_height = buffer->height; } - wlr_output_update_needs_frame(output); return true; } @@ -1159,7 +1158,6 @@ static bool drm_connector_move_cursor(struct wlr_output *output, conn->cursor_x = box.x; conn->cursor_y = box.y; - wlr_output_update_needs_frame(output); return true; } diff --git a/types/output/cursor.c b/types/output/cursor.c index 7bc8d0d6..f8f5eb64 100644 --- a/types/output/cursor.c +++ b/types/output/cursor.c @@ -23,6 +23,8 @@ static bool output_set_hardware_cursor(struct wlr_output *output, return false; } + wlr_output_update_needs_frame(output); + wlr_buffer_unlock(output->cursor_front_buffer); output->cursor_front_buffer = NULL; @@ -33,6 +35,15 @@ static bool output_set_hardware_cursor(struct wlr_output *output, return true; } +static bool output_move_hardware_cursor(struct wlr_output *output, int x, int y) { + assert(output->impl->move_cursor); + if (!output->impl->move_cursor(output, x, y)) { + return false; + } + wlr_output_update_needs_frame(output); + return true; +} + static void output_cursor_damage_whole(struct wlr_output_cursor *cursor); static void output_disable_hardware_cursor(struct wlr_output *output) { @@ -302,8 +313,7 @@ static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) { // If the cursor was hidden or was a software cursor, the hardware // cursor position is outdated - output->impl->move_cursor(cursor->output, - (int)cursor->x, (int)cursor->y); + output_move_hardware_cursor(cursor->output, (int)cursor->x, (int)cursor->y); struct wlr_buffer *buffer = NULL; if (texture != NULL) { @@ -455,8 +465,7 @@ bool wlr_output_cursor_move(struct wlr_output_cursor *cursor, return true; } - assert(cursor->output->impl->move_cursor); - return cursor->output->impl->move_cursor(cursor->output, (int)x, (int)y); + return output_move_hardware_cursor(cursor->output, (int)x, (int)y); } struct wlr_output_cursor *wlr_output_cursor_create(struct wlr_output *output) { From 1b0bcf8f768e50726324ca5b29ff78d07581cffe Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 18 Apr 2024 17:56:50 +0200 Subject: [PATCH 08/11] ext-image-capture-source-v1: add output cursor source --- types/ext_image_capture_source_v1/output.c | 154 +++++++++++++++++++++ 1 file changed, 154 insertions(+) diff --git a/types/ext_image_capture_source_v1/output.c b/types/ext_image_capture_source_v1/output.c index f547ae31..24e77d21 100644 --- a/types/ext_image_capture_source_v1/output.c +++ b/types/ext_image_capture_source_v1/output.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -11,6 +12,17 @@ #define OUTPUT_IMAGE_SOURCE_MANAGER_V1_VERSION 1 +struct output_cursor_source { + struct wlr_ext_image_capture_source_v1_cursor base; + + struct wlr_output *output; + struct wlr_buffer *prev_buffer; + bool initialized; + + struct wl_listener output_commit; + struct wl_listener prev_buffer_release; +}; + struct wlr_ext_output_image_capture_source_v1 { struct wlr_ext_image_capture_source_v1 base; struct wlr_addon addon; @@ -18,6 +30,8 @@ struct wlr_ext_output_image_capture_source_v1 { struct wlr_output *output; struct wl_listener output_commit; + + struct output_cursor_source cursor; }; struct wlr_ext_output_image_capture_source_v1_frame_event { @@ -45,9 +59,17 @@ static void output_source_copy_frame(struct wlr_ext_image_capture_source_v1 *bas } } +static struct wlr_ext_image_capture_source_v1_cursor *output_source_get_pointer_cursor( + struct wlr_ext_image_capture_source_v1 *base, struct wlr_seat *seat) { + // TODO: handle seat + struct wlr_ext_output_image_capture_source_v1 *source = wl_container_of(base, source, base); + return &source->cursor.base; +} + static const struct wlr_ext_image_capture_source_v1_interface output_source_impl = { .schedule_frame = output_source_schedule_frame, .copy_frame = output_source_copy_frame, + .get_pointer_cursor = output_source_get_pointer_cursor, }; static void source_update_buffer_constraints(struct wlr_ext_output_image_capture_source_v1 *source) { @@ -96,9 +118,14 @@ static void source_handle_output_commit(struct wl_listener *listener, } } +static void output_cursor_source_init(struct output_cursor_source *cursor_source, + struct wlr_output *output); +static void output_cursor_source_finish(struct output_cursor_source *cursor_source); + static void output_addon_destroy(struct wlr_addon *addon) { struct wlr_ext_output_image_capture_source_v1 *source = wl_container_of(addon, source, addon); wlr_ext_image_capture_source_v1_finish(&source->base); + output_cursor_source_finish(&source->cursor); wl_list_remove(&source->output_commit.link); wlr_addon_finish(&source->addon); free(source); @@ -137,6 +164,8 @@ static void output_manager_handle_create_source(struct wl_client *client, wl_signal_add(&output->events.commit, &source->output_commit); source_update_buffer_constraints(source); + + output_cursor_source_init(&source->cursor, output); } if (!wlr_ext_image_capture_source_v1_create_resource(&source->base, client, new_id)) { @@ -196,3 +225,128 @@ struct wlr_ext_output_image_capture_source_manager_v1 *wlr_ext_output_image_capt return manager; } + +static void output_cursor_source_schedule_frame(struct wlr_ext_image_capture_source_v1 *base) { + struct output_cursor_source *cursor_source = wl_container_of(base, cursor_source, base); + wlr_output_update_needs_frame(cursor_source->output); +} + +static void output_cursor_source_copy_frame(struct wlr_ext_image_capture_source_v1 *base, + struct wlr_ext_image_copy_capture_frame_v1 *frame, + struct wlr_ext_image_capture_source_v1_frame_event *base_event) { + struct output_cursor_source *cursor_source = wl_container_of(base, cursor_source, base); + + struct wlr_buffer *src_buffer = cursor_source->output->cursor_front_buffer; + if (src_buffer == NULL) { + wlr_ext_image_copy_capture_frame_v1_fail(frame, EXT_IMAGE_COPY_CAPTURE_FRAME_V1_FAILURE_REASON_STOPPED); + return; + } + + if (!wlr_ext_image_copy_capture_frame_v1_copy_buffer(frame, + src_buffer, cursor_source->output->renderer)) { + return; + } + + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + wlr_ext_image_copy_capture_frame_v1_ready(frame, WL_OUTPUT_TRANSFORM_NORMAL, &now); +} + +static const struct wlr_ext_image_capture_source_v1_interface output_cursor_source_impl = { + .schedule_frame = output_cursor_source_schedule_frame, + .copy_frame = output_cursor_source_copy_frame, +}; + +static void output_cursor_source_update(struct output_cursor_source *cursor_source) { + struct wlr_output *output = cursor_source->output; + + if (output->cursor_swapchain != NULL && !cursor_source->initialized) { + wlr_ext_image_capture_source_v1_set_constraints_from_swapchain(&cursor_source->base.base, + output->cursor_swapchain, output->renderer); + cursor_source->initialized = true; + } + + struct wlr_output_cursor *output_cursor = output->hardware_cursor; + if (output_cursor == NULL || !output_cursor->visible) { + cursor_source->base.entered = false; + wl_signal_emit_mutable(&cursor_source->base.events.update, NULL); + return; + } + + if (output->cursor_swapchain != NULL && + ((int)cursor_source->base.base.width != output->cursor_swapchain->width || + (int)cursor_source->base.base.height != output->cursor_swapchain->height)) { + cursor_source->base.base.width = output->cursor_swapchain->width; + cursor_source->base.base.height = output->cursor_swapchain->height; + wl_signal_emit_mutable(&cursor_source->base.base.events.constraints_update, NULL); + } + + cursor_source->base.entered = true; + cursor_source->base.x = round(output_cursor->x); + cursor_source->base.y = round(output_cursor->y); + cursor_source->base.hotspot.x = output_cursor->hotspot_x; + cursor_source->base.hotspot.y = output_cursor->hotspot_y; + wl_signal_emit_mutable(&cursor_source->base.events.update, NULL); +} + +static void output_cursor_source_handle_prev_buffer_release(struct wl_listener *listener, + void *data) { + struct output_cursor_source *cursor_source = wl_container_of(listener, cursor_source, prev_buffer_release); + wl_list_remove(&cursor_source->prev_buffer_release.link); + wl_list_init(&cursor_source->prev_buffer_release.link); + cursor_source->prev_buffer = NULL; +} + +static void output_cursor_source_handle_output_commit(struct wl_listener *listener, + void *data) { + struct output_cursor_source *cursor_source = wl_container_of(listener, cursor_source, output_commit); + struct wlr_output_event_commit *event = data; + + output_cursor_source_update(cursor_source); + + struct wlr_buffer *buffer = cursor_source->output->cursor_front_buffer; + if (buffer != NULL && buffer != cursor_source->prev_buffer) { + pixman_region32_t full_damage; + pixman_region32_init_rect(&full_damage, 0, 0, buffer->width, buffer->height); + + struct wlr_ext_output_image_capture_source_v1_frame_event frame_event = { + .base = { + .damage = &full_damage, + }, + .buffer = buffer, + .when = event->when, // TODO: predict next presentation time instead + }; + wl_signal_emit_mutable(&cursor_source->base.base.events.frame, &frame_event); + + pixman_region32_fini(&full_damage); + + assert(buffer->n_locks > 0); + cursor_source->prev_buffer = buffer; + wl_list_remove(&cursor_source->prev_buffer_release.link); + cursor_source->prev_buffer_release.notify = output_cursor_source_handle_prev_buffer_release; + wl_signal_add(&buffer->events.release, &cursor_source->prev_buffer_release); + } +} + +static void output_cursor_source_init(struct output_cursor_source *cursor_source, + struct wlr_output *output) { + wlr_ext_image_capture_source_v1_cursor_init(&cursor_source->base, &output_cursor_source_impl); + + // Caller is responsible for destroying the output cursor source when the + // output is destroyed + cursor_source->output = output; + + cursor_source->output_commit.notify = output_cursor_source_handle_output_commit; + wl_signal_add(&output->events.commit, &cursor_source->output_commit); + + wl_list_init(&cursor_source->prev_buffer_release.link); + + output_cursor_source_update(cursor_source); +} + +static void output_cursor_source_finish(struct output_cursor_source *cursor_source) { + wlr_ext_image_capture_source_v1_cursor_finish(&cursor_source->base); + wl_list_remove(&cursor_source->output_commit.link); + wl_list_remove(&cursor_source->prev_buffer_release.link); +} From d52f38c4031c749989c6826b3132667650f1927e Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 26 Apr 2024 18:48:19 +0200 Subject: [PATCH 09/11] backend/headless: accept hardware cursors When running headless, remoting programs (e.g. VNC servers) might want to capture outputs without the cursor, and send the cursor image separately. --- backend/headless/output.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/backend/headless/output.c b/backend/headless/output.c index 5aaf1bd8..1f81f0b9 100644 --- a/backend/headless/output.c +++ b/backend/headless/output.c @@ -79,6 +79,15 @@ static bool output_commit(struct wlr_output *wlr_output, return true; } +static bool output_set_cursor(struct wlr_output *wlr_output, + struct wlr_buffer *buffer, int hotspot_x, int hotspot_y) { + return true; +} + +static bool output_move_cursor(struct wlr_output *wlr_output, int x, int y) { + return true; +} + static void output_destroy(struct wlr_output *wlr_output) { struct wlr_headless_output *output = headless_output_from_output(wlr_output); @@ -90,6 +99,8 @@ static void output_destroy(struct wlr_output *wlr_output) { static const struct wlr_output_impl output_impl = { .destroy = output_destroy, .commit = output_commit, + .set_cursor = output_set_cursor, + .move_cursor = output_move_cursor, }; bool wlr_output_is_headless(struct wlr_output *wlr_output) { From 324e894bf0d0e54e2dda0a84980a11acd224d2bb Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 7 Jul 2024 22:14:53 +0200 Subject: [PATCH 10/11] ext-image-capture-source-v1: add start/stop hooks This allows the source to change its behavior when actively captured. --- .../wlr_ext_image_capture_source_v1.h | 3 ++ types/ext_image_capture_source_v1/output.c | 31 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/include/wlr/interfaces/wlr_ext_image_capture_source_v1.h b/include/wlr/interfaces/wlr_ext_image_capture_source_v1.h index 09ec188e..2aa7653f 100644 --- a/include/wlr/interfaces/wlr_ext_image_capture_source_v1.h +++ b/include/wlr/interfaces/wlr_ext_image_capture_source_v1.h @@ -18,6 +18,9 @@ struct wlr_renderer; struct wlr_seat; struct wlr_ext_image_capture_source_v1_interface { + // TODO: drop with_cursors flag + void (*start)(struct wlr_ext_image_capture_source_v1 *source, bool with_cursors); + void (*stop)(struct wlr_ext_image_capture_source_v1 *source); void (*schedule_frame)(struct wlr_ext_image_capture_source_v1 *source); void (*copy_frame)(struct wlr_ext_image_capture_source_v1 *source, struct wlr_ext_image_copy_capture_frame_v1 *dst_frame, diff --git a/types/ext_image_capture_source_v1/output.c b/types/ext_image_capture_source_v1/output.c index 24e77d21..17b69578 100644 --- a/types/ext_image_capture_source_v1/output.c +++ b/types/ext_image_capture_source_v1/output.c @@ -32,6 +32,9 @@ struct wlr_ext_output_image_capture_source_v1 { struct wl_listener output_commit; struct output_cursor_source cursor; + + size_t num_started; + bool software_cursors_locked; }; struct wlr_ext_output_image_capture_source_v1_frame_event { @@ -40,6 +43,32 @@ struct wlr_ext_output_image_capture_source_v1_frame_event { struct timespec *when; }; +static void output_source_start(struct wlr_ext_image_capture_source_v1 *base, + bool with_cursors) { + struct wlr_ext_output_image_capture_source_v1 *source = wl_container_of(base, source, base); + source->num_started++; + if (source->num_started > 1) { + return; + } + wlr_output_lock_attach_render(source->output, true); + if (with_cursors) { + wlr_output_lock_software_cursors(source->output, true); + } + source->software_cursors_locked = with_cursors; +} + +static void output_source_stop(struct wlr_ext_image_capture_source_v1 *base) { + struct wlr_ext_output_image_capture_source_v1 *source = wl_container_of(base, source, base); + assert(source->num_started > 0); + if (source->num_started > 0) { + return; + } + wlr_output_lock_attach_render(source->output, false); + if (source->software_cursors_locked) { + wlr_output_lock_software_cursors(source->output, false); + } +} + static void output_source_schedule_frame(struct wlr_ext_image_capture_source_v1 *base) { struct wlr_ext_output_image_capture_source_v1 *source = wl_container_of(base, source, base); wlr_output_update_needs_frame(source->output); @@ -67,6 +96,8 @@ static struct wlr_ext_image_capture_source_v1_cursor *output_source_get_pointer_ } static const struct wlr_ext_image_capture_source_v1_interface output_source_impl = { + .start = output_source_start, + .stop = output_source_stop, .schedule_frame = output_source_schedule_frame, .copy_frame = output_source_copy_frame, .get_pointer_cursor = output_source_get_pointer_cursor, From 0f0154fed4ffb39e7e9d15d739b63b51c3d114e7 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 7 Jul 2024 22:15:50 +0200 Subject: [PATCH 11/11] ext-image-copy-capture-v1: implement PAINT_CURSORS flag This is unreliable because this is first come, first served: the first capture stream decides whether or not cursors will be included. Moreover, if the output lacks hw cursor support, cursors will always be included. But it's the best we're going to get with automatic wlr_output sources (and has bug parity with wlr-screencopy-unstable-v1). --- types/wlr_ext_image_copy_capture_v1.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/types/wlr_ext_image_copy_capture_v1.c b/types/wlr_ext_image_copy_capture_v1.c index 6dc8ca9b..321c6f99 100644 --- a/types/wlr_ext_image_copy_capture_v1.c +++ b/types/wlr_ext_image_copy_capture_v1.c @@ -385,6 +385,10 @@ static void session_destroy(struct wlr_ext_image_copy_capture_session_v1 *sessio EXT_IMAGE_COPY_CAPTURE_FRAME_V1_FAILURE_REASON_STOPPED); } + if (session->source->impl->stop) { + session->source->impl->stop(session->source); + } + ext_image_copy_capture_session_v1_send_stopped(session->resource); wl_resource_set_user_data(session->resource, NULL); @@ -455,6 +459,10 @@ static void session_create(struct wl_resource *parent_resource, uint32_t new_id, return; } + if (source->impl->start) { + source->impl->start(source, options & EXT_IMAGE_COPY_CAPTURE_MANAGER_V1_OPTIONS_PAINT_CURSORS); + } + session->resource = session_resource; session->source = source; pixman_region32_init_rect(&session->damage, 0, 0, source->width,