Implement ext-screencopy-v1
parent
d7e256942f
commit
610d69fae0
|
@ -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);
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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',
|
||||
|
|
|
@ -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,
|
||||
};
|
93
src/main.c
93
src/main.c
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue