Implement ext-screencopy-v1

ext-screencopy-v1
Andri Yngvason 2024-03-19 10:13:06 +00:00
parent d7e256942f
commit 610d69fae0
8 changed files with 916 additions and 35 deletions

View File

@ -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);

View File

@ -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',

View File

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="ext_image_source_v1">
<copyright>
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.
</copyright>
<description summary="opaque image source objects">
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.
</description>
<interface name="ext_image_source_manager_v1" version="1">
<description summary="opaque image source manager">
A manager for creating image source objects for standard objects. Sources
may also be created from elsewhere.
</description>
<request name="create_output_source">
<description summary="create source object for output">
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.
</description>
<arg name="source" type="new_id" interface="ext_image_source_v1"/>
<arg name="output" type="object" interface="wl_output"/>
</request>
<request name="destroy" type="destructor">
<description summary="delete this object">
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.
</description>
</request>
</interface>
<interface name="ext_image_source_v1" version="1">
<description summary="opaque image source object">
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.
</description>
<request name="destroy" type="destructor">
<description summary="delete this object">
Destroys the image source. This request may be sent at any time by the
client.
</description>
</request>
</interface>
</protocol>

View File

@ -0,0 +1,324 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="ext_screencopy_v1">
<copyright>
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.
</copyright>
<description summary="screen content capturing on client buffers">
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.
</description>
<interface name="ext_screencopy_manager_v1" version="1">
<description summary="manager to inform clients and begin capturing">
This object is a manager which offers requests to start capturing from a
source.
</description>
<enum name="options" bitfield="true">
<entry name="render_cursors" value="1"/>
</enum>
<enum name="input_type">
<entry name="pointer" value="1"/>
<entry name="touch" value="2"/>
</enum>
<request name="capture">
<description summary="capture an image 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.
</description>
<arg name="session" type="new_id" interface="ext_screencopy_session_v1"/>
<arg name="source" type="object" interface="ext_image_source_v1"/>
<arg name="options" type="uint" enum="options"/>
</request>
<request name="capture_cursor">
<description summary="capture the cursor of an image source">
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.
</description>
<arg name="session" type="new_id" interface="ext_screencopy_cursor_session_v1"/>
<arg name="source" type="object" interface="ext_image_source_v1"/>
<arg name="seat" type="object" interface="wl_seat"/>
<arg name="input_type" type="uint" enum="input_type"/>
<arg name="options" type="uint"/>
</request>
</interface>
<interface name="ext_screencopy_session_v1" version="1">
<description summary="capturing session">
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.
</description>
<enum name="failure_reason">
<entry name="invalid_buffer" value="1"/>
<entry name="output_missing" value="2"/>
<entry name="output_disabled" value="3"/>
<entry name="unknown_input" value="4"/>
</enum>
<enum name="options" bitfield="true">
<entry name="on_damage" value="1"/>
</enum>
<event name="format_shm">
<description summary="shm buffer format">
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.
</description>
<arg name="format" type="uint" enum="wl_shm.format" summary="shm format"/>
</event>
<event name="format_drm">
<description summary="dma buffer constraints">
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.
</description>
<arg name="format" type="uint" summary="drm format"/>
</event>
<event name="dimensions">
<description summary="image source dimensions">
Provides the dimensions of the source image in buffer pixel coordinates.
The client must submit buffers that are large enough for the source
image.
</description>
<arg name="width" type="uint" summary="buffer width"/>
<arg name="height" type="uint" summary="buffer height"/>
</event>
<event name="constraints_done">
<description summary="session initialisation done">
This event is sent once when all constraint events have been sent.
</description>
</event>
<request name="attach_buffer">
<description summary="attach buffer to session">
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.
</description>
<arg name="buffer" type="object" interface="wl_buffer"/>
</request>
<request name="damage_buffer">
<description summary="damage 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.
</description>
<arg name="x" type="uint" summary="region x coordinates"/>
<arg name="y" type="uint" summary="region y coordinates"/>
<arg name="width" type="uint" summary="region width"/>
<arg name="height" type="uint" summary="region height"/>
</request>
<request name="commit">
<description summary="commit session">
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.
</description>
<arg name="options" type="uint" enum="options"/>
</request>
<request name="destroy" type="destructor">
<description summary="delete this object">
Destroys the session. This request can be sent at any time by the
client.
</description>
</request>
<event name="transform">
<description summary="carries the output transform">
This event is sent before the ready event and holds the output transform
of the source buffer.
</description>
<arg name="transform" type="int" enum="wl_output.transform"/>
</event>
<event name="damage">
<description summary="carries the coordinates of the damaged region">
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.
</description>
<arg name="x" type="uint" summary="damaged x coordinates"/>
<arg name="y" type="uint" summary="damaged y coordinates"/>
<arg name="width" type="uint" summary="current width"/>
<arg name="height" type="uint" summary="current height"/>
</event>
<event name="failed">
<description summary="commit failed">
This event indicates that the attempted frame copy has failed.
After receiving this event, the client must destroy the object.
</description>
<arg name="reason" type="uint" enum="failure_reason"/>
</event>
<event name="presentation_time">
<description summary="indicates the presentation time of the frame">
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].
</description>
<arg name="tv_sec_hi" type="uint"
summary="high 32 bits of the seconds part of the timestamp"/>
<arg name="tv_sec_lo" type="uint"
summary="low 32 bits of the seconds part of the timestamp"/>
<arg name="tv_nsec" type="uint"
summary="nanoseconds part of the timestamp"/>
</event>
<event name="ready">
<description summary="indicates frame is available for reading">
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.
</description>
</event>
</interface>
<interface name="ext_screencopy_cursor_session_v1" version="1">
<request name="get_screencopy_session">
<description summary="get screencopy session">
Gets the screencopy session for this cursor session.
</description>
<arg name="session" type="new_id" interface="ext_screencopy_session_v1"/>
</request>
<event name="enter">
<description summary="cursor entered captured are">
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.
</description>
</event>
<event name="leave">
<description summary="cursor left area">
Sent when a cursor leaves the captured area. No "position" or "hotspot"
event is generated for the cursor.
</description>
</event>
<event name="position">
<description summary="position changed">
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.
</description>
<arg name="x" type="int" summary="position x coordinates"/>
<arg name="y" type="int" summary="position y coordinates"/>
</event>
<event name="hotspot">
<description summary="hotspot changed">
The given coordinates are the hotspot's offset from the origin in
buffer coordinates.
</description>
<arg name="x" type="int" summary="hotspot x coordinates"/>
<arg name="y" type="int" summary="hotspot y coordinates"/>
</event>
</interface>
</protocol>

View File

@ -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',

View File

@ -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 <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
#include <inttypes.h>
#include <sys/mman.h>
#include <wayland-client.h>
#include <libdrm/drm_fourcc.h>
#include <aml.h>
#include <neatvnc.h>
#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,
};

View File

@ -35,6 +35,8 @@
#include <fcntl.h>
#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);

View File

@ -18,10 +18,21 @@
#include <unistd.h>
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)