From 610d69fae0188ca3d3ecb9407813cc1b7b0d7151 Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Tue, 19 Mar 2024 10:13:06 +0000 Subject: [PATCH] Implement ext-screencopy-v1 --- include/screencopy-interface.h | 4 +- meson.build | 1 + protocols/ext-image-source-v1.xml | 77 ++++++ protocols/ext-screencopy-v1.xml | 324 ++++++++++++++++++++++ protocols/meson.build | 2 + src/ext-screencopy.c | 430 ++++++++++++++++++++++++++++++ src/main.c | 93 +++++-- src/screencopy-interface.c | 20 +- 8 files changed, 916 insertions(+), 35 deletions(-) create mode 100644 protocols/ext-image-source-v1.xml create mode 100644 protocols/ext-screencopy-v1.xml create mode 100644 src/ext-screencopy.c diff --git a/include/screencopy-interface.h b/include/screencopy-interface.h index dd9de8c..60744ba 100644 --- a/include/screencopy-interface.h +++ b/include/screencopy-interface.h @@ -55,8 +55,8 @@ struct screencopy { void* userdata; }; -struct screencopy* screencopy_create(struct screencopy_impl* impl, - struct wl_output* output, bool render_cursor); +struct screencopy* screencopy_create(struct wl_output* output, + bool render_cursor); struct screencopy* screencopy_create_cursor(struct screencopy_impl* impl, struct wl_output* output); void screencopy_destroy(struct screencopy* self); diff --git a/meson.build b/meson.build index a2dee4f..7dfcefd 100644 --- a/meson.build +++ b/meson.build @@ -87,6 +87,7 @@ sources = [ 'src/strlcpy.c', 'src/shm.c', 'src/screencopy.c', + 'src/ext-screencopy.c', 'src/screencopy-interface.c', 'src/data-control.c', 'src/output.c', diff --git a/protocols/ext-image-source-v1.xml b/protocols/ext-image-source-v1.xml new file mode 100644 index 0000000..5d9813c --- /dev/null +++ b/protocols/ext-image-source-v1.xml @@ -0,0 +1,77 @@ + + + + Copyright © 2022 Andri Yngvason + + 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. + + + + This protocol serves as an intermediary between screen capturing protocols + and potential image sources such as outputs and toplevels. + + This protocol may be extended to support more image sources in the future, + thereby adding those image sources to other protocols that use the image + source object without having to modify those protocols. + + + + + A manager for creating image source objects for standard objects. Sources + may also be created from elsewhere. + + + + + Creates a source object for an output. Images captured from this source + will show the same image as the output. Some elements may be omitted, + such as cursors and overlays that have been marked as transparent to + capturing. + + + + + + + + Destroys the manager. This request may be sent at any time by the client + and objects created by the manager will remain valid after its + destruction. + + + + + + + The image source object is an opaque descriptor for a capturable resource. + This resource may be any sort of entity from which an image may be + derived. + + This interface may not be extended. + + + + + Destroys the image source. This request may be sent at any time by the + client. + + + + diff --git a/protocols/ext-screencopy-v1.xml b/protocols/ext-screencopy-v1.xml new file mode 100644 index 0000000..622dd7a --- /dev/null +++ b/protocols/ext-screencopy-v1.xml @@ -0,0 +1,324 @@ + + + + Copyright © 2021-2022 Andri Yngvason + + 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. + + + + This protocol allows clients to ask the compositor to capture screen + contents to user submitted buffers. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible changes + may be added together with the corresponding interface version bump. + Backward incompatible changes are done by bumping the version number in + the protocol and interface names and resetting the interface version. + Once the protocol is to be declared stable, the 'z' prefix and the + version number in the protocol and interface names are removed and the + interface version number is reset. + + + + + This object is a manager which offers requests to start capturing from a + source. + + + + + + + + + + + + + + Create a capturing session for an image source. + + If the "render_cursors" flag is set, cursors shall be composited onto + the captured frame. The cursor should not be composited onto the frame + if this flag is not set. + + + + + + + + + Create a cursor capturing session for an image source. + + The options argument has no effect and should be set to 0. This is + intended for any future flags that might be added. + + + + + + + + + + + + This is the frame capturing interface. It keeps track of changes between + frames. + + After a screencopy session is created, buffer constraint events will be + emitted from the compositor to tell the client which buffer types and + formats are supported for reading from the session. + + When the client knows all the buffer attributes, it can create a buffer, + attach it to the screencopy session using the "attach_buffer" request, + set the buffer damage using the "damage_buffer" request and then call + the "commit" request. + + After copying, a series of events will be generated, ending with the + "ready" event, which means that the buffer is ready to be used and a + buffer may be committed to the session again. The buffer may be re-used + after the "ready" event. The "wl_buffer.release" event is not used. + + The compositor should present frames to the client at a steady rate, + unless otherwise instructed (see "on_damage" option). + + A client wishing to capture all frames without missing any of them, should + commit a new buffer immediately after receiving the "ready" event. + + The "failed" event may be sent at any time. When this happens, the client + must destroy the session. Depending on the failure reason, the client can + create a new session to replace it. + + + + + + + + + + + + + + + + Provides the format that must be used for shm buffers. + + This may be emitted multiple times, in which case the client may choose + any given format. + + + + + + + Provides the format that must be used for dma buffers. + + This may be emitted multiple times, in which case the client may choose + any given format. + + + + + + + Provides the dimensions of the source image in buffer pixel coordinates. + + The client must submit buffers that are large enough for the source + image. + + + + + + + + This event is sent once when all constraint events have been sent. + + + + + + Attach a buffer to the session. + + The buffer must satisfy the constraints given by the + "buffer_constraints_shm" and "buffer_constraints_dmabuf" events. + + If an over-sized buffer is committed, the compositor will place the + image into the top left corner of the buffer. + + + + + + + Apply damage to the buffer which is to be committed next. + + This is for optimisation purposes. The compositor may use this + information to reduce copying. + + The client must submit damage if it's using multiple buffers. Otherwise, + the server might not copy into damaged regions of the buffer. + + These coordinates originate in the upper left corner of the buffer. + + + + + + + + + + Commit the screencopy session. + + The frame will be copied to the attached buffer on next output commit. A + ready event is generated when the buffer is ready. + + If the "on_damage" flag is set, the compositor should skip sending new + frames to the client until there is damage. + + + + + + + Destroys the session. This request can be sent at any time by the + client. + + + + + + This event is sent before the ready event and holds the output transform + of the source buffer. + + + + + + + This event is sent before the ready event. It may be generated multiple + times for each commit. + + A capture session's initial damage encompasses the whole captured area. + + The arguments describe a box around an area that has changed since the + last ready event. + + These coordinates originate in the upper left corner of the buffer. + + + + + + + + + + This event indicates that the attempted frame copy has failed. + + After receiving this event, the client must destroy the object. + + + + + + + This event indicates the time at which the frame is presented to the + output in system monotonic time. + + The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples, + each component being an unsigned 32-bit value. Whole seconds are in + tv_sec which is a 64-bit value combined from tv_sec_hi and tv_sec_lo, + and the additional fractional part in tv_nsec as nanoseconds. Hence, + for valid timestamps tv_nsec must be in [0, 999999999]. + + + + + + + + + Called as soon as the frame is copied, indicating it is available + for reading. + + The buffer may be re-used by the client after the "ready" event. + + + + + + + + Gets the screencopy session for this cursor session. + + + + + + + Sent when a cursor enters the captured area. It shall be generated + before the "position" and "hotspot" events when and only when a cursor + enters the area. + + + + + + Sent when a cursor leaves the captured area. No "position" or "hotspot" + event is generated for the cursor. + + + + + + Cursors outside the image source do not get captured and no event will + be generated for them. + + The given position is the position of the cursor's hotspot and it is + relative to the main buffer's top left corner in transformed buffer + pixel coordinates. + + The hotspot coordinates are relative to the cursor buffers upper left + corner. + + + + + + + + The given coordinates are the hotspot's offset from the origin in + buffer coordinates. + + + + + + diff --git a/protocols/meson.build b/protocols/meson.build index d230dca..973e59f 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -16,6 +16,8 @@ wayland_scanner_client = generator( client_protocols = [ 'wlr-export-dmabuf-unstable-v1.xml', 'wlr-screencopy-unstable-v1.xml', + 'ext-screencopy-v1.xml', + 'ext-image-source-v1.xml', 'wlr-virtual-pointer-unstable-v1.xml', 'virtual-keyboard-unstable-v1.xml', 'xdg-output-unstable-v1.xml', diff --git a/src/ext-screencopy.c b/src/ext-screencopy.c new file mode 100644 index 0000000..5ccbc7e --- /dev/null +++ b/src/ext-screencopy.c @@ -0,0 +1,430 @@ +/* + * Copyright (c) 2022 - 2023 Andri Yngvason + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "screencopy-interface.h" +#include "ext-screencopy-v1.h" +#include "ext-image-source-v1.h" +#include "buffer.h" +#include "shm.h" +#include "time-util.h" +#include "usdt.h" +#include "pixels.h" +#include "config.h" +#include "logging.h" + +extern struct ext_image_source_manager_v1* ext_image_source_manager; +extern struct ext_screencopy_manager_v1* ext_screencopy_manager; + +struct ext_screencopy { + struct screencopy parent; + struct wl_output* wl_output; + struct ext_screencopy_session_v1* session; + struct ext_screencopy_cursor_session_v1* cursor; + bool render_cursors; + struct wv_buffer_pool* pool; + struct wv_buffer* buffer; + bool have_buffer_info; + bool should_start; + bool shall_be_immediate; + bool capture_cursor; + + uint32_t width, height; + uint32_t wl_shm_stride, wl_shm_format; + + bool have_wl_shm; + bool have_linux_dmabuf; + uint32_t dmabuf_format; +}; + +struct screencopy_impl ext_screencopy_impl; + +static struct ext_screencopy_session_v1_listener session_listener; +static struct ext_screencopy_cursor_session_v1_listener cursor_listener; + +static int ext_screencopy_init_session(struct ext_screencopy* self) +{ + if (self->session) + ext_screencopy_session_v1_destroy(self->session); + + struct ext_image_source_v1* source; + source = ext_image_source_manager_v1_create_output_source( + ext_image_source_manager, self->wl_output); + if (!source) + return -1; + + enum ext_screencopy_manager_v1_options options = 0; + if (self->render_cursors) + options |= EXT_SCREENCOPY_MANAGER_V1_OPTIONS_RENDER_CURSORS; + + if (self->capture_cursor) { + self->cursor = ext_screencopy_manager_v1_capture_cursor( + ext_screencopy_manager, source, NULL, + EXT_SCREENCOPY_MANAGER_V1_INPUT_TYPE_POINTER, + options); + ext_image_source_v1_destroy(source); + if (!self->cursor) + return -1; + + self->session = + ext_screencopy_cursor_session_v1_get_screencopy_session( + self->cursor); + } else { + self->session = ext_screencopy_manager_v1_capture( + ext_screencopy_manager, source, options); + ext_image_source_v1_destroy(source); + } + if (!self->session) + return -1; + + ext_screencopy_session_v1_add_listener(self->session, + &session_listener, self); + + if (self->capture_cursor) { + ext_screencopy_cursor_session_v1_add_listener(self->cursor, + &cursor_listener, self); + } + + return 0; +} + +// TODO: Throttle capturing to max_fps +static void ext_screencopy_schedule_capture(struct ext_screencopy* self, + bool immediate) +{ + self->buffer = wv_buffer_pool_acquire(self->pool); + self->buffer->domain = self->capture_cursor ? WV_BUFFER_DOMAIN_CURSOR : + WV_BUFFER_DOMAIN_OUTPUT; + + ext_screencopy_session_v1_attach_buffer(self->session, + self->buffer->wl_buffer); + + int n_rects = 0; + struct pixman_box16* rects = + pixman_region_rectangles(&self->buffer->buffer_damage, &n_rects); + + for (int i = 0; i < n_rects; ++i) { + uint32_t x = rects[i].x1; + uint32_t y = rects[i].y1; + uint32_t width = rects[i].x2 - x; + uint32_t height = rects[i].y2 - y; + + ext_screencopy_session_v1_damage_buffer(self->session, x, y, + width, height); + } + + uint32_t flags = 0; + + if (!immediate) + flags |= EXT_SCREENCOPY_SESSION_V1_OPTIONS_ON_DAMAGE; + + ext_screencopy_session_v1_commit(self->session, flags); + + nvnc_log(NVNC_LOG_DEBUG, "Committed buffer%s: %p\n", immediate ? " immediately" : "", + self->buffer); +} + +static void session_handle_format_shm(void *data, + struct ext_screencopy_session_v1 *session, + uint32_t format) +{ + struct ext_screencopy* self = data; + + self->have_wl_shm = true; + self->wl_shm_format = format; +} + +static void session_handle_format_drm(void *data, + struct ext_screencopy_session_v1 *session, + uint32_t format) +{ + struct ext_screencopy* self = data; + +#ifdef ENABLE_SCREENCOPY_DMABUF + self->have_linux_dmabuf = true; + self->dmabuf_format = format; +#endif +} + +static void session_handle_dimensions(void *data, + struct ext_screencopy_session_v1 *session, uint32_t width, + uint32_t height) +{ + struct ext_screencopy* self = data; + + self->width = width; + self->height = height; + self->wl_shm_stride = width * 4; +} + +static void session_handle_constraints_done(void *data, + struct ext_screencopy_session_v1 *session) +{ + struct ext_screencopy* self = data; + uint32_t width, height, stride, format; + enum wv_buffer_type type = WV_BUFFER_UNSPEC; + + width = self->width; + height = self->height; + +#ifdef ENABLE_SCREENCOPY_DMABUF + if (self->have_linux_dmabuf && self->parent.enable_linux_dmabuf) { + format = self->dmabuf_format; + stride = 0; + type = WV_BUFFER_DMABUF; + } else +#endif + { + format = self->wl_shm_format; + stride = self->wl_shm_stride; + type = WV_BUFFER_SHM; + } + + wv_buffer_pool_resize(self->pool, type, width, height, stride, format); + + if (self->should_start) { + ext_screencopy_schedule_capture(self, self->shall_be_immediate); + + self->should_start = false; + self->shall_be_immediate = false; + } + + self->have_buffer_info = true; + + nvnc_log(NVNC_LOG_DEBUG, "Init done\n"); +} + +static void session_handle_transform(void *data, + struct ext_screencopy_session_v1 *session, + int32_t transform) +{ + struct ext_screencopy* self = data; + + assert(self->buffer); + + // TODO: Tell main.c not to override this transform + nvnc_fb_set_transform(self->buffer->nvnc_fb, transform); +} + +static void session_handle_ready(void *data, + struct ext_screencopy_session_v1 *session) +{ + struct ext_screencopy* self = data; + + nvnc_log(NVNC_LOG_DEBUG, "Ready!\n"); + + assert(self->buffer); + + enum wv_buffer_domain domain = self->capture_cursor ? + WV_BUFFER_DOMAIN_CURSOR : WV_BUFFER_DOMAIN_OUTPUT; + wv_buffer_registry_damage_all(&self->buffer->frame_damage, domain); + pixman_region_clear(&self->buffer->buffer_damage); + + struct wv_buffer* buffer = self->buffer; + self->buffer = NULL; + + self->parent.on_done(SCREENCOPY_DONE, buffer, self->parent.userdata); +} + +static void session_handle_failed(void *data, + struct ext_screencopy_session_v1 *session, + enum ext_screencopy_session_v1_failure_reason reason) +{ + struct ext_screencopy* self = data; + + nvnc_log(NVNC_LOG_DEBUG, "Failed!\n"); + + assert(self->buffer); + + wv_buffer_pool_release(self->pool, self->buffer); + self->buffer = NULL; + + if (reason == EXT_SCREENCOPY_SESSION_V1_FAILURE_REASON_INVALID_BUFFER) { + ext_screencopy_init_session(self); + } + + self->parent.on_done(SCREENCOPY_FAILED, NULL, self->parent.userdata); +} + +static void session_handle_damage(void *data, + struct ext_screencopy_session_v1 *session, + uint32_t x, uint32_t y, uint32_t width, uint32_t height) +{ + struct ext_screencopy* self = data; + + wv_buffer_damage_rect(self->buffer, x, y, width, height); +} + +static void session_handle_presentation_time(void *data, + struct ext_screencopy_session_v1 *session, + uint32_t sec_hi, uint32_t sec_lo, uint32_t nsec) +{ + // TODO +} + +static struct ext_screencopy_session_v1_listener session_listener = { + .format_shm = session_handle_format_shm, + .format_drm = session_handle_format_drm, + .dimensions = session_handle_dimensions, + .constraints_done = session_handle_constraints_done, + .damage = session_handle_damage, + .presentation_time = session_handle_presentation_time, + .transform = session_handle_transform, + .ready = session_handle_ready, + .failed = session_handle_failed, +}; + +static void cursor_handle_enter(void* data, + struct ext_screencopy_cursor_session_v1* cursor) +{ + struct ext_screencopy* self = data; + if (self->parent.cursor_enter) + self->parent.cursor_enter(self->parent.userdata); +} + +static void cursor_handle_leave(void* data, + struct ext_screencopy_cursor_session_v1* cursor) +{ + struct ext_screencopy* self = data; + if (self->parent.cursor_leave) + self->parent.cursor_leave(self->parent.userdata); +} + +static void cursor_handle_position(void* data, + struct ext_screencopy_cursor_session_v1* cursor, int x, int y) +{ + // Don't care +} + +static void cursor_handle_hotspot(void* data, + struct ext_screencopy_cursor_session_v1* cursor, int x, int y) +{ + struct ext_screencopy* self = data; + if (self->parent.cursor_hotspot) + self->parent.cursor_hotspot(x, y, self->parent.userdata); +} + +static struct ext_screencopy_cursor_session_v1_listener cursor_listener = { + .enter = cursor_handle_enter, + .leave = cursor_handle_leave, + .position = cursor_handle_position, + .hotspot = cursor_handle_hotspot, +}; + +static int ext_screencopy_start(struct screencopy* ptr, bool immediate) +{ + struct ext_screencopy* self = (struct ext_screencopy*)ptr; + + if (!self->have_buffer_info) { + self->should_start = true; + self->shall_be_immediate = immediate; + } else { + ext_screencopy_schedule_capture(self, immediate); + } + + return 0; +} + +static void ext_screencopy_stop(struct screencopy* self) +{ + // Nothing to stop? +} + +static struct screencopy* ext_screencopy_create(struct wl_output* output, + bool render_cursor) +{ + struct ext_screencopy* self = calloc(1, sizeof(*self)); + if (!self) + return NULL; + + self->parent.impl = &ext_screencopy_impl; + self->parent.rate_limit = 30; + + self->wl_output = output; + self->render_cursors = render_cursor; + + self->pool = wv_buffer_pool_create(0, 0, 0, 0, 0); + if (!self->pool) + goto failure; + + if (ext_screencopy_init_session(self) < 0) + goto session_failure; + + return (struct screencopy*)self; + +session_failure: + wv_buffer_pool_destroy(self->pool); +failure: + free(self); + return NULL; +} + +static struct screencopy* ext_screencopy_create_cursor(struct wl_output* output) +{ + struct ext_screencopy* self = calloc(1, sizeof(*self)); + if (!self) + return NULL; + + self->parent.impl = &ext_screencopy_impl; + self->parent.rate_limit = 30; + + self->wl_output = output; + self->capture_cursor = true; + + self->pool = wv_buffer_pool_create(0, 0, 0, 0, 0); + if (!self->pool) + goto failure; + + if (ext_screencopy_init_session(self) < 0) + goto session_failure; + + return (struct screencopy*)self; + +session_failure: + wv_buffer_pool_destroy(self->pool); +failure: + free(self); + return NULL; +} + +void ext_screencopy_destroy(struct screencopy* ptr) +{ + struct ext_screencopy* self = (struct ext_screencopy*)ptr; + + if (self->session) + ext_screencopy_session_v1_destroy(self->session); + if (self->buffer) + wv_buffer_pool_release(self->pool, self->buffer); + free(self); +} + +struct screencopy_impl ext_screencopy_impl = { + .create = ext_screencopy_create, + .create_cursor = ext_screencopy_create_cursor, + .destroy = ext_screencopy_destroy, + .start = ext_screencopy_start, + .stop = ext_screencopy_stop, +}; diff --git a/src/main.c b/src/main.c index d1ca7ef..2de5e64 100644 --- a/src/main.c +++ b/src/main.c @@ -35,6 +35,8 @@ #include #include "wlr-screencopy-unstable-v1.h" +#include "ext-screencopy-v1.h" +#include "ext-image-source-v1.h" #include "wlr-virtual-pointer-unstable-v1.h" #include "virtual-keyboard-unstable-v1.h" #include "xdg-output-unstable-v1.h" @@ -128,6 +130,8 @@ struct wayvnc { bool start_detached; bool overlay_cursor; + int max_rate; + bool enable_gpu_features; struct wayvnc_client* master_layout_client; }; @@ -168,8 +172,10 @@ struct gbm_device* gbm_device = NULL; struct zxdg_output_manager_v1* xdg_output_manager = NULL; struct zwlr_output_power_manager_v1* wlr_output_power_manager = NULL; struct zwlr_screencopy_manager_v1* screencopy_manager = NULL; +struct ext_image_source_manager_v1* ext_image_source_manager = NULL; +struct ext_screencopy_manager_v1* ext_screencopy_manager = NULL; -extern struct screencopy_impl wlr_screencopy_impl; +extern struct screencopy_impl wlr_screencopy_impl, ext_screencopy_impl; static bool registry_add_input(void* data, struct wl_registry* registry, uint32_t id, const char* interface, @@ -288,6 +294,23 @@ static void registry_add(void* data, struct wl_registry* registry, return; } +#if 1 + if (strcmp(interface, ext_screencopy_manager_v1_interface.name) == 0) { + ext_screencopy_manager = + wl_registry_bind(registry, id, + &ext_screencopy_manager_v1_interface, + MIN(1, version)); + return; + } + + if (strcmp(interface, ext_image_source_manager_v1_interface.name) == 0) { + ext_image_source_manager = wl_registry_bind(registry, id, + &ext_image_source_manager_v1_interface, + MIN(1, version)); + return; + } +#endif + if (strcmp(interface, wl_shm_interface.name) == 0) { wl_shm = wl_registry_bind(registry, id, &wl_shm_interface, 1); return; @@ -423,8 +446,6 @@ static void wayland_detach(struct wayvnc* self) } self->selected_output = NULL; - //TODO: Make output inert: - //self->screencopy->wl_output = NULL; output_list_destroy(&self->outputs); seat_list_destroy(&self->seats); @@ -465,15 +486,24 @@ static void wayland_detach(struct wayvnc* self) zwlr_data_control_manager_v1_destroy(self->data_control_manager); self->data_control_manager = NULL; - if (screencopy_manager) - zwlr_screencopy_manager_v1_destroy(screencopy_manager); - if (self->performance_ticker) { aml_stop(aml_get_default(), self->performance_ticker); aml_unref(self->performance_ticker); } self->performance_ticker = NULL; + if (screencopy_manager) + zwlr_screencopy_manager_v1_destroy(screencopy_manager); + screencopy_manager = NULL; + + if (ext_image_source_manager) + ext_image_source_manager_v1_destroy(ext_image_source_manager); + ext_image_source_manager = NULL; + + if (ext_screencopy_manager) + ext_screencopy_manager_v1_destroy(ext_screencopy_manager); + ext_screencopy_manager = NULL; + if (self->capture_retry_timer) aml_unref(self->capture_retry_timer); self->capture_retry_timer = NULL; @@ -569,7 +599,7 @@ static int init_wayland(struct wayvnc* self, const char* display) goto failure; } - if (!screencopy_manager) { + if (!screencopy_manager && !ext_screencopy_manager) { nvnc_log(NVNC_LOG_ERROR, "Screencopy protocol not supported by compositor. Exiting. Refer to FAQ section in man page."); goto failure; } @@ -1093,6 +1123,8 @@ void wayvnc_process_frame(struct wayvnc* self, struct wv_buffer* buffer) { // TODO: Back buffer used to be set to NULL here, what's that about? + nvnc_log(NVNC_LOG_DEBUG, "Passing on buffer: %p", buffer); + self->n_frames_captured++; self->damage_area_sum += calculate_region_area(&buffer->frame_damage); @@ -1525,16 +1557,32 @@ void log_selected_output(struct wayvnc* self) } } -void set_selected_output(struct wayvnc* self, struct output* output) { +bool configure_screencopy(struct wayvnc* self) +{ + screencopy_destroy(self->screencopy); + self->screencopy = screencopy_create(self->selected_output->wl_output, + self->overlay_cursor); + if (!self->screencopy) { + nvnc_log(NVNC_LOG_ERROR, "screencopy is not supported by compositor"); + return false; + } + + self->screencopy->rate_limit = self->max_rate; + self->screencopy->enable_linux_dmabuf = self->enable_gpu_features; + + return true; +} + +void set_selected_output(struct wayvnc* self, struct output* output) +{ if (self->selected_output) { self->selected_output->on_dimension_change = NULL; } self->selected_output = output; - // TODO: Change screencopy output: - // self->screencopy.wl_output = output->wl_output; output->on_dimension_change = on_output_dimension_change; output->on_power_change = on_output_power_change; output->userdata = self; + if (self->ctl) ctl_server_event_capture_changed(self->ctl, output->name); log_selected_output(self); @@ -1549,6 +1597,7 @@ void switch_to_output(struct wayvnc* self, struct output* output) } screencopy_stop(self->screencopy); set_selected_output(self, output); + configure_screencopy(self); reinitialise_pointers(self); if (self->nr_clients > 0) wayvnc_start_capture_immediate(self); @@ -1644,10 +1693,8 @@ static bool wayland_attach(struct wayvnc* self, const char* display, wayland_detach(self); return false; } - self->screencopy = screencopy_create(&wlr_screencopy_impl, - self->selected_output->wl_output, self->overlay_cursor); - set_selected_output(self, out); + configure_screencopy(self); struct nvnc_client* nvnc_client; for (nvnc_client = nvnc_client_first(self->nvnc); nvnc_client; @@ -1810,6 +1857,8 @@ int main(int argc, char* argv[]) self.start_detached = start_detached; self.overlay_cursor = overlay_cursor; + self.max_rate = max_rate; + self.enable_gpu_features = enable_gpu_features; keyboard_options = option_parser_get_value(&option_parser, "keyboard"); if (keyboard_options) @@ -1932,21 +1981,8 @@ int main(int argc, char* argv[]) if (init_nvnc(&self, address, port, socket_type) < 0) goto nvnc_failure; - if (!start_detached) { - if (screencopy_manager) { - self.screencopy = screencopy_create(&wlr_screencopy_impl, - self.selected_output->wl_output, - overlay_cursor); - if (!self.screencopy) - goto screencopy_failure; - - self.screencopy->rate_limit = max_rate; - self.screencopy->enable_linux_dmabuf = enable_gpu_features; - } else { - nvnc_log(NVNC_LOG_ERROR, "screencopy is not supported by compositor"); - goto capture_failure; - } - } + if (!start_detached && !configure_screencopy(&self)) + goto screencopy_failure; self.screencopy->on_done = on_capture_done; self.screencopy->userdata = &self; @@ -2009,7 +2045,6 @@ int main(int argc, char* argv[]) return 0; ctl_server_failure: -capture_failure: screencopy_failure: nvnc_display_unref(self.nvnc_display); nvnc_close(self.nvnc); diff --git a/src/screencopy-interface.c b/src/screencopy-interface.c index 53131b1..9a64df6 100644 --- a/src/screencopy-interface.c +++ b/src/screencopy-interface.c @@ -18,10 +18,21 @@ #include -struct screencopy* screencopy_create(struct screencopy_impl* impl, - struct wl_output* output, bool render_cursor) +extern struct zwlr_screencopy_manager_v1* screencopy_manager; +extern struct ext_image_source_manager_v1* ext_image_source_manager; +extern struct ext_screencopy_manager_v1* ext_screencopy_manager; + +extern struct screencopy_impl wlr_screencopy_impl; +extern struct screencopy_impl ext_screencopy_impl; + +struct screencopy* screencopy_create(struct wl_output* output, + bool render_cursor) { - return impl->create(output, render_cursor); + if (ext_screencopy_manager && ext_image_source_manager) + return ext_screencopy_impl.create(output, render_cursor); + if (screencopy_manager) + return wlr_screencopy_impl.create(output, render_cursor); + return NULL; } struct screencopy* screencopy_create_cursor(struct screencopy_impl* impl, @@ -32,7 +43,8 @@ struct screencopy* screencopy_create_cursor(struct screencopy_impl* impl, void screencopy_destroy(struct screencopy* self) { - self->impl->destroy(self); + if (self) + self->impl->destroy(self); } int screencopy_start(struct screencopy* self, bool immediate)