Compare commits

..

4 Commits

Author SHA1 Message Date
Andri Yngvason 9a3bdbfdbe Align to newest protocol revision 2024-03-24 16:25:42 +00:00
Andri Yngvason 610d69fae0 Implement ext-screencopy-v1 2024-03-24 13:10:17 +00:00
Andri Yngvason d7e256942f Create abstract screencopy interface 2024-03-24 13:10:17 +00:00
Andri Yngvason 3652f1e6cf buffer: Create a global buffer registry
This is used for tracking buffer damage, as opposed to frame damage.
2024-03-24 10:41:47 +00:00
19 changed files with 1389 additions and 382 deletions

1
.gitignore vendored
View File

@ -7,4 +7,3 @@ perf.*
*.pem *.pem
.vimrc .vimrc
.cache .cache
.vscode

10
FAQ.md
View File

@ -19,16 +19,6 @@ bindsym $mod+Pause mode passthrough
This makes it so that when you press $mod+Pause, all keybindings, except the one This makes it so that when you press $mod+Pause, all keybindings, except the one
to switch back, are disabled. to switch back, are disabled.
Disable `floating_modifier` during the mode if it's set up in your config file
and you wish to be able to use the same functionality in the nested desktop:
```
mode passthrough {
bindsym $mod+Pause mode default; floating_modifier $mod normal
}
bindsym $mod+Pause mode passthrough; floating_modifier none
```
Replace `$mod normal` with different arguments if applicable.
**Q: Not all symbols show up when I'm typing. What can I do to fix this?** **Q: Not all symbols show up when I'm typing. What can I do to fix this?**
A: Try setting the keyboard layout in wayvnc to the one that most closely A: Try setting the keyboard layout in wayvnc to the one that most closely

View File

@ -1,2 +1 @@
github: any1
patreon: andriyngvason patreon: andriyngvason

View File

@ -1,7 +1,7 @@
# wayvnc # wayvnc
[![Build and Unit Test](https://github.com/any1/wayvnc/actions/workflows/build.yml/badge.svg)](https://github.com/any1/wayvnc/actions/workflows/build.yml) [![Build and Unit Test](https://github.com/any1/wayvnc/actions/workflows/build.yml/badge.svg)](https://github.com/any1/wayvnc/actions/workflows/build.yml)
[![builds.sr.ht status](https://builds.sr.ht/~andri/wayvnc/pulls/1.svg)](https://builds.sr.ht/~andri/wayvnc/pulls/1?) [![builds.sr.ht status](https://builds.sr.ht/~andri/wayvnc/commits/master.svg)](https://builds.sr.ht/~andri/wayvnc/commits/master?)
[![Packaging status](https://repology.org/badge/tiny-repos/wayvnc.svg)](https://repology.org/project/wayvnc/versions) [![Packaging status](https://repology.org/badge/tiny-repos/wayvnc.svg)](https://repology.org/project/wayvnc/versions)
## Introduction ## Introduction

View File

@ -36,9 +36,16 @@ enum wv_buffer_type {
#endif #endif
}; };
enum wv_buffer_domain {
WV_BUFFER_DOMAIN_UNSPEC = 0,
WV_BUFFER_DOMAIN_OUTPUT,
WV_BUFFER_DOMAIN_CURSOR,
};
struct wv_buffer { struct wv_buffer {
enum wv_buffer_type type; enum wv_buffer_type type;
TAILQ_ENTRY(wv_buffer) link; TAILQ_ENTRY(wv_buffer) link;
LIST_ENTRY(wv_buffer) registry_link;
struct nvnc_fb* nvnc_fb; struct nvnc_fb* nvnc_fb;
struct wl_buffer* wl_buffer; struct wl_buffer* wl_buffer;
@ -49,10 +56,19 @@ struct wv_buffer {
uint32_t format; uint32_t format;
bool y_inverted; bool y_inverted;
struct pixman_region16 damage; enum wv_buffer_domain domain;
struct pixman_region16 frame_damage;
struct pixman_region16 buffer_damage;
/* The following is only applicable to DMABUF */ /* The following is only applicable to DMABUF */
struct gbm_bo* bo; struct gbm_bo* bo;
/* The following is only applicable to cursors */
uint16_t cursor_width;
uint16_t cursor_height;
uint16_t x_hotspot;
uint16_t y_hotspot;
}; };
TAILQ_HEAD(wv_buffer_queue, wv_buffer); TAILQ_HEAD(wv_buffer_queue, wv_buffer);
@ -83,3 +99,6 @@ void wv_buffer_pool_resize(struct wv_buffer_pool* pool, enum wv_buffer_type,
struct wv_buffer* wv_buffer_pool_acquire(struct wv_buffer_pool* pool); struct wv_buffer* wv_buffer_pool_acquire(struct wv_buffer_pool* pool);
void wv_buffer_pool_release(struct wv_buffer_pool* pool, void wv_buffer_pool_release(struct wv_buffer_pool* pool,
struct wv_buffer* buffer); struct wv_buffer* buffer);
void wv_buffer_registry_damage_all(struct pixman_region16* region,
enum wv_buffer_domain domain);

View File

@ -19,13 +19,11 @@
#include <stdlib.h> #include <stdlib.h>
#include <xkbcommon/xkbcommon.h> #include <xkbcommon/xkbcommon.h>
#include <stdbool.h> #include <stdbool.h>
#include <neatvnc.h>
#include "intset.h" #include "intset.h"
struct zwp_virtual_keyboard_v1; struct zwp_virtual_keyboard_v1;
struct table_entry; struct table_entry;
struct nvnc;
struct keyboard { struct keyboard {
struct zwp_virtual_keyboard_v1* virtual_keyboard; struct zwp_virtual_keyboard_v1* virtual_keyboard;
@ -46,4 +44,3 @@ void keyboard_destroy(struct keyboard* self);
void keyboard_feed(struct keyboard* self, xkb_keysym_t symbol, bool is_pressed); void keyboard_feed(struct keyboard* self, xkb_keysym_t symbol, bool is_pressed);
void keyboard_feed_code(struct keyboard* self, xkb_keycode_t code, void keyboard_feed_code(struct keyboard* self, xkb_keycode_t code,
bool is_pressed); bool is_pressed);
enum nvnc_keyboard_led_state keyboard_get_led_state(const struct keyboard*);

View File

@ -0,0 +1,65 @@
/*
* Copyright (c) 2022 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.
*/
#pragma once
#include <stdbool.h>
// TODO: Add a way to set the rate limit
struct zext_screencopy_manager_v1;
struct wl_output;
struct wv_buffer;
enum screencopy_result {
SCREENCOPY_DONE,
SCREENCOPY_FATAL,
SCREENCOPY_FAILED,
};
typedef void (*screencopy_done_fn)(enum screencopy_result,
struct wv_buffer* buffer, void* userdata);
struct screencopy_impl {
struct screencopy* (*create)(struct wl_output*, bool render_cursor);
struct screencopy* (*create_cursor)(struct wl_output*);
void (*destroy)(struct screencopy*);
int (*start)(struct screencopy*, bool immediate);
void (*stop)(struct screencopy*);
};
struct screencopy {
struct screencopy_impl* impl;
double rate_limit;
bool enable_linux_dmabuf;
screencopy_done_fn on_done;
void (*cursor_enter)(void* userdata);
void (*cursor_leave)(void* userdata);
void (*cursor_hotspot)(int x, int y, void* userdata);
void* userdata;
};
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);
int screencopy_start(struct screencopy* self, bool immediate);
void screencopy_stop(struct screencopy* self);

View File

@ -1,81 +0,0 @@
/*
* Copyright (c) 2019 - 2020 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.
*/
#pragma once
#include <stdbool.h>
#include "wlr-screencopy-unstable-v1.h"
#include "smooth.h"
#include "buffer.h"
struct zwlr_screencopy_manager_v1;
struct zwlr_screencopy_frame_v1;
struct wl_output;
struct wl_buffer;
struct wl_shm;
struct aml_timer;
struct renderer;
enum screencopy_status {
SCREENCOPY_STOPPED = 0,
SCREENCOPY_IN_PROGRESS,
SCREENCOPY_FAILED,
SCREENCOPY_FATAL,
SCREENCOPY_DONE,
};
struct screencopy {
enum screencopy_status status;
struct wv_buffer_pool* pool;
struct wv_buffer* front;
struct wv_buffer* back;
struct zwlr_screencopy_manager_v1* manager;
struct zwlr_screencopy_frame_v1* frame;
void* userdata;
void (*on_done)(struct screencopy*);
uint64_t last_time;
uint64_t start_time;
struct aml_timer* timer;
struct smooth delay_smoother;
double delay;
bool is_immediate_copy;
bool overlay_cursor;
struct wl_output* wl_output;
uint32_t wl_shm_width, wl_shm_height, wl_shm_stride;
enum wl_shm_format wl_shm_format;
bool have_linux_dmabuf;
bool enable_linux_dmabuf;
uint32_t dmabuf_width, dmabuf_height;
uint32_t fourcc;
double rate_limit;
};
void screencopy_init(struct screencopy* self);
void screencopy_destroy(struct screencopy* self);
int screencopy_start(struct screencopy* self);
int screencopy_start_immediate(struct screencopy* self);
void screencopy_stop(struct screencopy* self);

View File

@ -16,7 +16,6 @@ prefix = get_option('prefix')
c_args = [ c_args = [
'-D_GNU_SOURCE', '-D_GNU_SOURCE',
'-DAML_UNSTABLE_API=1', '-DAML_UNSTABLE_API=1',
'-DWLR_USE_UNSTABLE=true',
'-Wno-unused-parameter', '-Wno-unused-parameter',
'-Wno-missing-field-initializers', '-Wno-missing-field-initializers',
@ -25,8 +24,8 @@ c_args = [
version = '"@0@"'.format(meson.project_version()) version = '"@0@"'.format(meson.project_version())
git = find_program('git', native: true, required: false) git = find_program('git', native: true, required: false)
if git.found() if git.found()
git_commit = run_command([git, 'rev-parse', '--short', 'HEAD'], check: false) git_commit = run_command([git, 'rev-parse', '--short', 'HEAD'])
git_branch = run_command([git, 'rev-parse', '--abbrev-ref', 'HEAD'], check: false) git_branch = run_command([git, 'rev-parse', '--abbrev-ref', 'HEAD'])
if git_commit.returncode() == 0 and git_branch.returncode() == 0 if git_commit.returncode() == 0 and git_branch.returncode() == 0
version = '"v@0@-@1@ (@2@)"'.format( version = '"v@0@-@1@ (@2@)"'.format(
meson.project_version(), meson.project_version(),
@ -54,16 +53,9 @@ pixman = dependency('pixman-1')
gbm = dependency('gbm', required: get_option('screencopy-dmabuf')) gbm = dependency('gbm', required: get_option('screencopy-dmabuf'))
drm = dependency('libdrm') drm = dependency('libdrm')
xkbcommon = dependency('xkbcommon', version: '>=1.0.0') xkbcommon = dependency('xkbcommon', version: '>=1.0.0')
wayland_server = dependency('wayland-server')
wayland_client = dependency('wayland-client') wayland_client = dependency('wayland-client')
wayland_client_protocol = dependency('wayland-protocols')
wayland_cursor = dependency('wayland-cursor')
jansson = dependency('jansson') jansson = dependency('jansson')
# Cursor image
x11_dep = dependency('x11')
x11_fixes_dep = dependency('xfixes')
aml_version = ['>=0.3.0', '<0.4.0'] aml_version = ['>=0.3.0', '<0.4.0']
neatvnc_version = ['>=0.9', '<0.10.0'] neatvnc_version = ['>=0.9', '<0.10.0']
@ -86,7 +78,7 @@ else
neatvnc = dependency('neatvnc', version: neatvnc_version) neatvnc = dependency('neatvnc', version: neatvnc_version)
endif endif
inc = include_directories('include', '/usr/include/wlroots0.16') inc = include_directories('include')
subdir('protocols') subdir('protocols')
@ -95,6 +87,8 @@ sources = [
'src/strlcpy.c', 'src/strlcpy.c',
'src/shm.c', 'src/shm.c',
'src/screencopy.c', 'src/screencopy.c',
'src/ext-screencopy.c',
'src/screencopy-interface.c',
'src/data-control.c', 'src/data-control.c',
'src/output.c', 'src/output.c',
'src/output-management.c', 'src/output-management.c',
@ -127,11 +121,6 @@ dependencies = [
xkbcommon, xkbcommon,
client_protos, client_protos,
jansson, jansson,
x11_dep,
x11_fixes_dep,
wayland_client_protocol,
wayland_cursor,
wayland_server
] ]
ctlsources = [ ctlsources = [
@ -157,10 +146,6 @@ if host_system == 'linux' and get_option('systemtap') and cc.has_header('sys/sdt
config.set('HAVE_USDT', true) config.set('HAVE_USDT', true)
endif endif
if cc.has_header('linux/dma-heap.h') and cc.has_header('linux/dma-buf.h')
config.set('HAVE_LINUX_DMA_HEAP', true)
endif
if cc.has_function('memfd_create') if cc.has_function('memfd_create')
config.set('HAVE_MEMFD', true) config.set('HAVE_MEMFD', true)
config.set('HAVE_MEMFD_CREATE', true) config.set('HAVE_MEMFD_CREATE', true)

View File

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="ext_image_source_v1">
<copyright>
Copyright © 2022 Andri Yngvason
Copyright © 2024 Simon Ser
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.
Warning! The protocol described in this file is currently in the testing
phase. Backward compatible changes may be added together with the
corresponding interface version bump. Backward incompatible changes can
only be done by creating a new major version of the extension.
</description>
<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.
Note, because ext_image_source_v1 objects are created from multiple
independent factory interfaces, the ext_image_source_v1 interface is
frozen at version 1.
</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>
<interface name="ext_output_image_source_manager_v1" version="1">
<description summary="image source manager for outputs">
A manager for creating image source objects for wl_output objects.
</description>
<request name="create_source">
<description summary="create source object for output">
Creates a source object for an output. Images captured from this source
will show the same content 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>
</protocol>

View File

@ -0,0 +1,446 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="ext_screencopy_v1">
<copyright>
Copyright © 2021-2023 Andri Yngvason
Copyright © 2024 Simon Ser
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 currently in the testing
phase. Backward compatible changes may be added together with the
corresponding interface version bump. Backward incompatible changes can
only be done by creating a new major version of the extension.
</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="error">
<entry name="invalid_option" value="1" summary="invalid option flag"/>
</enum>
<enum name="options" bitfield="true">
<entry name="paint_cursors" value="1" summary="paint cursors onto captured frames"/>
</enum>
<request name="create_session">
<description summary="capture an image source">
Create a capturing session for an image source.
If the paint_cursors option is set, cursors shall be composited onto
the captured frame. The cursor shall 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="create_pointer_cursor_session">
<description summary="capture the pointer cursor of an image source">
Create a cursor capturing session for the pointer of an image source.
The options argument has no effect and must 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="pointer" type="object" interface="wl_pointer"/>
<arg name="options" type="uint"/>
</request>
<request name="destroy" type="destructor">
<description summary="destroy the manager">
Destroy the manager object.
Other objects created via this interface are unaffected.
</description>
</request>
</interface>
<interface name="ext_screencopy_session_v1" version="1">
<description summary="screen capture session">
This object represents an active screencopy session.
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. The compositor may
re-send buffer constraint events whenever they change.
The advertise buffer constraints, the compositor must send in no
particular order: zero or more shm_format and dmabuf_format events, zero
or one dmabuf_device event, and exactly one buffer_size event. Then the
compositor must send a done event.
When the client has received all the buffer constraints, it can create a
buffer accordingly, attach it to the screencopy session using the
attach_buffer request, set the buffer damage using the damage_buffer
request and then send the capture request.
</description>
<event name="buffer_size">
<description summary="image source dimensions">
Provides the dimensions of the source image in buffer pixel coordinates.
The client must attach buffers that match this size.
</description>
<arg name="width" type="uint" summary="buffer width"/>
<arg name="height" type="uint" summary="buffer height"/>
</event>
<event name="shm_format">
<description summary="shm buffer format">
Provides the format that must be used for shared-memory buffers.
This event 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="dmabuf_device">
<description summary="dma-buf device">
This event advertises the device buffers must be allocated on for
dma-buf buffers.
In general the device is a DRM node. The DRM node type (primary vs.
render) is unspecified. Clients must not rely on the compositor sending
a particular node type. Clients cannot check two devices for equality
by comparing the dev_t value.
</description>
<arg name="device" type="array" summary="device dev_t value"/>
</event>
<event name="dmabuf_format">
<description summary="dma-buf format">
Provides the format that must be used for dma-buf buffers.
The client may choose any of the modifiers advertised in the array of
64-bit unsigned integers.
This event may be emitted multiple times, in which case the client may
choose any given format.
</description>
<arg name="format" type="uint" summary="drm format code"/>
<arg name="modifiers" type="array" summary="drm format modifiers"/>
</event>
<event name="done">
<description summary="all constraints have been sent">
This event is sent once when all buffer constraint events have been
sent.
The compositor must always end a batch of buffer constraint events with
this event, regardless of whether it sends the initial constraints or
an update.
</description>
</event>
<event name="stopped">
<description summary="session is no longer available">
This event indicates that the capture session has stopped and is no
longer available. This can happen in a number of cases, e.g. when the
underlying source is destroyed, if the user decides to end the screen
capture, or if an unrecoverable runtime error has occurred.
The client should destroy the session after receiving this event.
</description>
</event>
<request name="create_frame">
<description summary="create a frame">
Create a capture frame for this session.
</description>
<arg name="frame" type="new_id" interface="ext_screencopy_frame_v1"/>
</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.
This request doesn't affect ext_screencopy_frame_v1 objects created by
this object.
</description>
</request>
</interface>
<interface name="ext_screencopy_frame_v1" version="1">
<description summary="screen capture frame">
This object represents a screen capture frame.
The client should attach a buffer, damage the buffer, and then send a
capture request.
If the screen capture is successful, the compositor will send the frame
metadata (transform, damage, presentation_time in any order) followed by
the ready event.
If the screen capture fails, the compositor will send the failed event.
</description>
<enum name="error">
<entry name="no_buffer" value="1" summary="capture sent without attach_buffer"/>
<entry name="invalid_buffer_damage" value="2" summary="invalid buffer damage"/>
<entry name="already_captured" value="3" summary="capture request has been sent"/>
</enum>
<request name="destroy" type="destructor">
<description summary="destroy this object">
Destroys the session. This request can be sent at any time by the
client.
</description>
</request>
<request name="attach_buffer">
<description summary="attach buffer to session">
Attach a buffer to the session.
The wl_buffer.release request is unused.
This request must not be sent after capture, or else the
already_captured protocol error is raised.
</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 captured next. This request
may be sent multiple times to describe a region.
The client indicates the accumulated damage since this wl_buffer was
last captured. During capture, the compositor will update the buffer
with at least the union of the region passed by the client and the
region advertised by ext_screencopy_frame_v1.damage.
When a wl_buffer is captured for the first time, or when the client
doesn't track damage, the client must damage the whole buffer.
This is for optimisation purposes. The compositor may use this
information to reduce copying.
These coordinates originate from the upper left corner of the buffer.
If x or y are strictly negative, or if width or height are negative or
zero, the invalid_buffer_damage protocol error is raised.
This request must not be sent after capture, or else the
already_captured protocol error is raised.
</description>
<arg name="x" type="int" summary="region x coordinate"/>
<arg name="y" type="int" summary="region y coordinate"/>
<arg name="width" type="int" summary="region width"/>
<arg name="height" type="int" summary="region height"/>
</request>
<request name="capture">
<description summary="capture a frame">
Capture a frame.
Unless this is the first successful captured frame performed in this
session, the compositor may wait an indefinite amount of time for the
source content to change before performing the copy.
This request may only be sent once, or else the already_captured
protocol error is raised. A buffer must be attached before this request
is sent, or else the no_buffer protocol error is raised.
</description>
</request>
<event name="transform">
<description summary="buffer transform">
This event is sent before the ready event and holds the transform of
the source buffer.
</description>
<arg name="transform" type="uint" enum="wl_output.transform"/>
</event>
<event name="damage">
<description summary="buffer damaged region">
This event is sent before the ready event. It may be generated multiple
times to describe a region.
The first captured frame in a session will always carry full damage.
Subsequent frames' damaged regions describe which parts of the buffer
have changed since the last ready event.
These coordinates originate in the upper left corner of the buffer.
</description>
<arg name="x" type="int" summary="damage x coordinate"/>
<arg name="y" type="int" summary="damage y coordinate"/>
<arg name="width" type="int" summary="damage width"/>
<arg name="height" type="int" summary="damage height"/>
</event>
<event name="presentation_time">
<description summary="presentation time of the frame">
This event indicates the time at which the frame is presented to the
output in system monotonic time. This event is sent before the ready
event.
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="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 this event.
After receiving this event, the client must destroy the object.
</description>
</event>
<enum name="failure_reason">
<entry name="unknown" value="0">
<description summary="unknown runtime error">
An unspecified runtime error has occurred. The client may retry.
</description>
</entry>
<entry name="buffer_constraints" value="1">
<description summary="buffer constraints mismatch">
The buffer submitted by the client doesn't match the latest session
constraints. The client should re-allocate its buffers and retry.
</description>
</entry>
<entry name="stopped" value="2">
<description summary="session is no longer available">
The session has stopped. See ext_screencopy_session_v1.stopped.
</description>
</entry>
</enum>
<event name="failed">
<description summary="capture 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>
</interface>
<interface name="ext_screencopy_cursor_session_v1" version="1">
<description summary="cursor capture session">
This object represents a cursor capture session. It extends the base
capture session with cursor-specific metadata.
</description>
<enum name="error">
<entry name="duplicate_session" value="1" summary="get_screencopy_session sent twice"/>
</enum>
<request name="destroy" type="destructor">
<description summary="delete this object">
Destroys the session. This request can be sent at any time by the
client.
This request doesn't affect ext_screencopy_frame_v1 objects created by
this object.
</description>
</request>
<request name="get_screencopy_session">
<description summary="get screencopy session">
Gets the screencopy session for this cursor session.
The session will produce frames of the cursor image. The compositor may
pause the session when the cursor leaves the captured area.
This request must not be sent more than once, or else the
duplicate_session protocol error is raised.
</description>
<arg name="session" type="new_id" interface="ext_screencopy_session_v1"/>
</request>
<event name="enter">
<description summary="cursor entered captured area">
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.
The cursor enters the captured area when the cursor image intersects
with the captured area. Note, this is different from e.g.
wl_pointer.enter.
</description>
</event>
<event name="leave">
<description summary="cursor left captured area">
Sent when a cursor leaves the captured area. No "position" or "hotspot"
event is generated for the cursor until the cursor enters the captured
area again.
</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 position coordinates are relative to the main buffer's upper left
corner. The coordinates may be negative or greater than the main buffer
size.
</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 hotspot describes the offset between the cursor image and the
position of the input device.
The given coordinates are the hotspot's offset from the origin in
buffer coordinates.
Clients should not apply the hotspot immediately: the hotspot becomes
effective when the next ext_screencopy_frame_v1.ready event is received.
</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 = [ client_protocols = [
'wlr-export-dmabuf-unstable-v1.xml', 'wlr-export-dmabuf-unstable-v1.xml',
'wlr-screencopy-unstable-v1.xml', 'wlr-screencopy-unstable-v1.xml',
'ext-screencopy-v1.xml',
'ext-image-source-v1.xml',
'wlr-virtual-pointer-unstable-v1.xml', 'wlr-virtual-pointer-unstable-v1.xml',
'virtual-keyboard-unstable-v1.xml', 'virtual-keyboard-unstable-v1.xml',
'xdg-output-unstable-v1.xml', 'xdg-output-unstable-v1.xml',

View File

@ -37,19 +37,20 @@
#include <gbm.h> #include <gbm.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <fcntl.h> #include <fcntl.h>
// #ifdef HAVE_LINUX_DMA_HEAP
#include <linux/dma-buf.h> #include <linux/dma-buf.h>
#include <linux/dma-heap.h> #include <linux/dma-heap.h>
#define LINUX_CMA_PATH "/dev/dma_heap/linux,cma" #define LINUX_CMA_PATH "/dev/dma_heap/linux,cma"
//#endif // HAVE_LINUX_DMA_HEAP #endif
#endif // ENABLE_SCREENCOPY_DMABUF
extern struct wl_shm* wl_shm; extern struct wl_shm* wl_shm;
extern struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf; extern struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf;
extern struct gbm_device* gbm_device; extern struct gbm_device* gbm_device;
LIST_HEAD(wv_buffer_list, wv_buffer);
static struct wv_buffer_list buffer_registry;
enum wv_buffer_type wv_buffer_get_available_types(void) enum wv_buffer_type wv_buffer_get_available_types(void)
{ {
enum wv_buffer_type type = 0; enum wv_buffer_type type = 0;
@ -111,7 +112,10 @@ struct wv_buffer* wv_buffer_create_shm(int width,
nvnc_set_userdata(self->nvnc_fb, self, NULL); nvnc_set_userdata(self->nvnc_fb, self, NULL);
pixman_region_init(&self->damage); pixman_region_init(&self->frame_damage);
pixman_region_init_rect(&self->buffer_damage, 0, 0, width, height);
LIST_INSERT_HEAD(&buffer_registry, self, registry_link);
close(fd); close(fd);
return self; return self;
@ -128,7 +132,6 @@ failure:
} }
#ifdef ENABLE_SCREENCOPY_DMABUF #ifdef ENABLE_SCREENCOPY_DMABUF
#ifdef HAVE_LINUX_DMA_HEAP
static bool have_linux_cma(void) static bool have_linux_cma(void)
{ {
return access(LINUX_CMA_PATH, R_OK | W_OK) == 0; return access(LINUX_CMA_PATH, R_OK | W_OK) == 0;
@ -201,7 +204,6 @@ static struct gbm_bo* create_cma_gbm_bo(int width, int height, uint32_t fourcc)
return bo; return bo;
} }
#endif // HAVE_LINUX_DMA_HEAP
static struct wv_buffer* wv_buffer_create_dmabuf(int width, int height, static struct wv_buffer* wv_buffer_create_dmabuf(int width, int height,
uint32_t fourcc) uint32_t fourcc)
@ -218,17 +220,10 @@ static struct wv_buffer* wv_buffer_create_dmabuf(int width, int height,
self->height = height; self->height = height;
self->format = fourcc; self->format = fourcc;
// Checks not needed anymore. Fixed with SCANOUT and within neatvnc for most GPUs.
// But this could still fail!
//#ifdef HAVE_LINUX_DMA_HEAP
self->bo = have_linux_cma() ? self->bo = have_linux_cma() ?
create_cma_gbm_bo(width, height, fourcc) : create_cma_gbm_bo(width, height, fourcc) :
gbm_bo_create(gbm_device, width, height, fourcc, gbm_bo_create(gbm_device, width, height, fourcc,
GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT); GBM_BO_USE_RENDERING);
//#endif
// self->bo = gbm_bo_create(gbm_device, width, height, fourcc,
// GBM_BO_USE_RENDERING);
if (!self->bo) if (!self->bo)
goto bo_failure; goto bo_failure;
@ -261,6 +256,11 @@ static struct wv_buffer* wv_buffer_create_dmabuf(int width, int height,
nvnc_set_userdata(self->nvnc_fb, self, NULL); nvnc_set_userdata(self->nvnc_fb, self, NULL);
pixman_region_init(&self->frame_damage);
pixman_region_init_rect(&self->buffer_damage, 0, 0, width, height);
LIST_INSERT_HEAD(&buffer_registry, self, registry_link);
return self; return self;
nvnc_fb_failure: nvnc_fb_failure:
@ -313,7 +313,9 @@ static void wv_buffer_destroy_dmabuf(struct wv_buffer* self)
void wv_buffer_destroy(struct wv_buffer* self) void wv_buffer_destroy(struct wv_buffer* self)
{ {
pixman_region_fini(&self->damage); pixman_region_fini(&self->buffer_damage);
pixman_region_fini(&self->frame_damage);
LIST_REMOVE(self, registry_link);
switch (self->type) { switch (self->type) {
case WV_BUFFER_SHM: case WV_BUFFER_SHM:
@ -333,8 +335,8 @@ void wv_buffer_destroy(struct wv_buffer* self)
void wv_buffer_damage_rect(struct wv_buffer* self, int x, int y, int width, void wv_buffer_damage_rect(struct wv_buffer* self, int x, int y, int width,
int height) int height)
{ {
pixman_region_union_rect(&self->damage, &self->damage, x, y, width, pixman_region_union_rect(&self->frame_damage, &self->frame_damage, x, y,
height); width, height);
} }
void wv_buffer_damage_whole(struct wv_buffer* self) void wv_buffer_damage_whole(struct wv_buffer* self)
@ -344,7 +346,7 @@ void wv_buffer_damage_whole(struct wv_buffer* self)
void wv_buffer_damage_clear(struct wv_buffer* self) void wv_buffer_damage_clear(struct wv_buffer* self)
{ {
pixman_region_clear(&self->damage); pixman_region_clear(&self->frame_damage);
} }
struct wv_buffer_pool* wv_buffer_pool_create(enum wv_buffer_type type, struct wv_buffer_pool* wv_buffer_pool_create(enum wv_buffer_type type,
@ -461,3 +463,16 @@ void wv_buffer_pool_release(struct wv_buffer_pool* pool,
wv_buffer_destroy(buffer); wv_buffer_destroy(buffer);
} }
} }
void wv_buffer_registry_damage_all(struct pixman_region16* region,
enum wv_buffer_domain domain)
{
if (domain == WV_BUFFER_DOMAIN_UNSPEC)
return;
struct wv_buffer *buffer;
LIST_FOREACH(buffer, &buffer_registry, registry_link)
if (buffer->domain == domain)
pixman_region_union(&buffer->buffer_damage,
&buffer->buffer_damage, region);
}

View File

@ -0,0 +1,456 @@
/*
* Copyright (c) 2022 - 2024 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_output_image_source_manager_v1* ext_output_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_frame_v1* frame;
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_frame_v1_listener frame_listener;
//static struct ext_screencopy_cursor_session_v1_listener cursor_listener;
static int ext_screencopy_init_session(struct ext_screencopy* self)
{
if (self->frame)
ext_screencopy_frame_v1_destroy(self->frame);
self->frame = NULL;
if (self->session)
ext_screencopy_session_v1_destroy(self->session);
self->session = NULL;
struct ext_image_source_v1* source;
source = ext_output_image_source_manager_v1_create_source(
ext_output_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_PAINT_CURSORS;
self->session = ext_screencopy_manager_v1_create_session(
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) {
// TODO: create_pointer_cursor_session
}
return 0;
}
// TODO: Throttle capturing to max_fps
static void ext_screencopy_schedule_capture(struct ext_screencopy* self,
bool immediate)
{
assert(!self->frame);
// TODO: Restart session on immediate capture
self->buffer = wv_buffer_pool_acquire(self->pool);
self->buffer->domain = self->capture_cursor ? WV_BUFFER_DOMAIN_CURSOR :
WV_BUFFER_DOMAIN_OUTPUT;
self->frame = ext_screencopy_session_v1_create_frame(self->session);
assert(self->frame);
ext_screencopy_frame_v1_attach_buffer(self->frame,
self->buffer->wl_buffer);
ext_screencopy_frame_v1_add_listener(self->frame, &frame_listener,
self);
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_frame_v1_damage_buffer(self->frame, x, y,
width, height);
}
ext_screencopy_frame_v1_capture(self->frame);
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 wl_array* modifiers)
{
struct ext_screencopy* self = data;
#ifdef ENABLE_SCREENCOPY_DMABUF
self->have_linux_dmabuf = true;
self->dmabuf_format = format;
// TODO: Pass modifiers
#endif
}
static void session_handle_dmabuf_device(void* data,
struct ext_screencopy_session_v1* session,
struct wl_array *device)
{
// TODO
}
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_stopped(void* data,
struct ext_screencopy_session_v1* session)
{
// TODO
}
static void frame_handle_transform(void *data,
struct ext_screencopy_frame_v1 *frame, uint32_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 frame_handle_ready(void *data,
struct ext_screencopy_frame_v1 *frame)
{
struct ext_screencopy* self = data;
assert(frame == self->frame);
ext_screencopy_frame_v1_destroy(self->frame);
self->frame = NULL;
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 frame_handle_failed(void *data,
struct ext_screencopy_frame_v1 *frame,
enum ext_screencopy_frame_v1_failure_reason reason)
{
struct ext_screencopy* self = data;
assert(frame == self->frame);
ext_screencopy_frame_v1_destroy(self->frame);
self->frame = NULL;
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_FRAME_V1_FAILURE_REASON_BUFFER_CONSTRAINTS) {
ext_screencopy_init_session(self);
}
self->parent.on_done(SCREENCOPY_FAILED, NULL, self->parent.userdata);
}
static void frame_handle_damage(void *data,
struct ext_screencopy_frame_v1 *frame,
int32_t x, int32_t y, int32_t width, int32_t height)
{
struct ext_screencopy* self = data;
wv_buffer_damage_rect(self->buffer, x, y, width, height);
}
static void frame_handle_presentation_time(void *data,
struct ext_screencopy_frame_v1 *frame,
uint32_t sec_hi, uint32_t sec_lo, uint32_t nsec)
{
// TODO
}
static struct ext_screencopy_session_v1_listener session_listener = {
.shm_format = session_handle_format_shm,
.dmabuf_format = session_handle_format_drm,
.dmabuf_device = session_handle_dmabuf_device,
.buffer_size = session_handle_dimensions,
.done = session_handle_constraints_done,
.stopped = session_handle_stopped,
};
static struct ext_screencopy_frame_v1_listener frame_listener = {
.damage = frame_handle_damage,
.presentation_time = frame_handle_presentation_time,
.transform = frame_handle_transform,
.ready = frame_handle_ready,
.failed = frame_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->frame)
ext_screencopy_frame_v1_destroy(self->frame);
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

@ -436,18 +436,3 @@ void keyboard_feed_code(struct keyboard* self, xkb_keycode_t code,
send_key(self, code, is_pressed); send_key(self, code, is_pressed);
} }
} }
enum nvnc_keyboard_led_state keyboard_get_led_state(
const struct keyboard* self)
{
enum nvnc_keyboard_led_state led_state = 0;
if (xkb_state_led_name_is_active(self->state, XKB_LED_NAME_SCROLL))
led_state |= NVNC_KEYBOARD_LED_SCROLL_LOCK;
if (xkb_state_led_name_is_active(self->state, XKB_LED_NAME_NUM))
led_state |= NVNC_KEYBOARD_LED_NUM_LOCK;
if (xkb_state_led_name_is_active(self->state, XKB_LED_NAME_CAPS))
led_state |= NVNC_KEYBOARD_LED_CAPS_LOCK;
return led_state;
}

View File

@ -35,6 +35,8 @@
#include <fcntl.h> #include <fcntl.h>
#include "wlr-screencopy-unstable-v1.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 "wlr-virtual-pointer-unstable-v1.h"
#include "virtual-keyboard-unstable-v1.h" #include "virtual-keyboard-unstable-v1.h"
#include "xdg-output-unstable-v1.h" #include "xdg-output-unstable-v1.h"
@ -42,7 +44,7 @@
#include "wlr-output-management-unstable-v1.h" #include "wlr-output-management-unstable-v1.h"
#include "linux-dmabuf-unstable-v1.h" #include "linux-dmabuf-unstable-v1.h"
#include "ext-transient-seat-v1.h" #include "ext-transient-seat-v1.h"
#include "screencopy.h" #include "screencopy-interface.h"
#include "data-control.h" #include "data-control.h"
#include "strlcpy.h" #include "strlcpy.h"
#include "output.h" #include "output.h"
@ -56,10 +58,7 @@
#include "ctl-server.h" #include "ctl-server.h"
#include "util.h" #include "util.h"
#include "option-parser.h" #include "option-parser.h"
#include "buffer.h"
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/extensions/Xfixes.h>
#ifdef ENABLE_PAM #ifdef ENABLE_PAM
#include "pam_auth.h" #include "pam_auth.h"
@ -104,7 +103,7 @@ struct wayvnc {
struct output* selected_output; struct output* selected_output;
struct seat* selected_seat; struct seat* selected_seat;
struct screencopy screencopy; struct screencopy* screencopy;
struct aml_handler* wayland_handler; struct aml_handler* wayland_handler;
struct aml_signal* signal_handler; struct aml_signal* signal_handler;
@ -130,6 +129,9 @@ struct wayvnc {
bool is_initializing; bool is_initializing;
bool start_detached; bool start_detached;
bool overlay_cursor;
int max_rate;
bool enable_gpu_features;
struct wayvnc_client* master_layout_client; struct wayvnc_client* master_layout_client;
}; };
@ -148,7 +150,8 @@ struct wayvnc_client {
}; };
void wayvnc_exit(struct wayvnc* self); void wayvnc_exit(struct wayvnc* self);
void on_capture_done(struct screencopy* sc); void on_capture_done(enum screencopy_result result, struct wv_buffer* buffer,
void* userdata);
static void on_nvnc_client_new(struct nvnc_client* client); static void on_nvnc_client_new(struct nvnc_client* client);
void switch_to_output(struct wayvnc*, struct output*); void switch_to_output(struct wayvnc*, struct output*);
void switch_to_next_output(struct wayvnc*); void switch_to_next_output(struct wayvnc*);
@ -168,6 +171,11 @@ struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf = NULL;
struct gbm_device* gbm_device = NULL; struct gbm_device* gbm_device = NULL;
struct zxdg_output_manager_v1* xdg_output_manager = NULL; struct zxdg_output_manager_v1* xdg_output_manager = NULL;
struct zwlr_output_power_manager_v1* wlr_output_power_manager = NULL; struct zwlr_output_power_manager_v1* wlr_output_power_manager = NULL;
struct zwlr_screencopy_manager_v1* screencopy_manager = NULL;
struct ext_output_image_source_manager_v1* ext_output_image_source_manager = NULL;
struct ext_screencopy_manager_v1* ext_screencopy_manager = NULL;
extern struct screencopy_impl wlr_screencopy_impl, ext_screencopy_impl;
static bool registry_add_input(void* data, struct wl_registry* registry, static bool registry_add_input(void* data, struct wl_registry* registry,
uint32_t id, const char* interface, uint32_t id, const char* interface,
@ -279,13 +287,31 @@ static void registry_add(void* data, struct wl_registry* registry,
} }
if (strcmp(interface, zwlr_screencopy_manager_v1_interface.name) == 0) { if (strcmp(interface, zwlr_screencopy_manager_v1_interface.name) == 0) {
self->screencopy.manager = screencopy_manager =
wl_registry_bind(registry, id, wl_registry_bind(registry, id,
&zwlr_screencopy_manager_v1_interface, &zwlr_screencopy_manager_v1_interface,
MIN(3, version)); MIN(3, version));
return; 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_output_image_source_manager_v1_interface.name) == 0) {
ext_output_image_source_manager =
wl_registry_bind(registry, id,
&ext_output_image_source_manager_v1_interface,
MIN(1, version));
return;
}
#endif
if (strcmp(interface, wl_shm_interface.name) == 0) { if (strcmp(interface, wl_shm_interface.name) == 0) {
wl_shm = wl_registry_bind(registry, id, &wl_shm_interface, 1); wl_shm = wl_registry_bind(registry, id, &wl_shm_interface, 1);
return; return;
@ -421,7 +447,6 @@ static void wayland_detach(struct wayvnc* self)
} }
self->selected_output = NULL; self->selected_output = NULL;
self->screencopy.wl_output = NULL;
output_list_destroy(&self->outputs); output_list_destroy(&self->outputs);
seat_list_destroy(&self->seats); seat_list_destroy(&self->seats);
@ -430,12 +455,12 @@ static void wayland_detach(struct wayvnc* self)
zwp_linux_dmabuf_v1_destroy(zwp_linux_dmabuf); zwp_linux_dmabuf_v1_destroy(zwp_linux_dmabuf);
zwp_linux_dmabuf = NULL; zwp_linux_dmabuf = NULL;
if (self->screencopy.manager) { if (screencopy_manager) {
screencopy_stop(&self->screencopy); screencopy_stop(self->screencopy);
screencopy_destroy(&self->screencopy); screencopy_destroy(self->screencopy);
zwlr_screencopy_manager_v1_destroy(self->screencopy.manager); zwlr_screencopy_manager_v1_destroy(screencopy_manager);
} }
self->screencopy.manager = NULL; screencopy_manager = NULL;
if (xdg_output_manager) if (xdg_output_manager)
zxdg_output_manager_v1_destroy(xdg_output_manager); zxdg_output_manager_v1_destroy(xdg_output_manager);
@ -468,6 +493,18 @@ static void wayland_detach(struct wayvnc* self)
} }
self->performance_ticker = NULL; self->performance_ticker = NULL;
if (screencopy_manager)
zwlr_screencopy_manager_v1_destroy(screencopy_manager);
screencopy_manager = NULL;
if (ext_output_image_source_manager)
ext_output_image_source_manager_v1_destroy(ext_output_image_source_manager);
ext_output_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) if (self->capture_retry_timer)
aml_unref(self->capture_retry_timer); aml_unref(self->capture_retry_timer);
self->capture_retry_timer = NULL; self->capture_retry_timer = NULL;
@ -563,7 +600,7 @@ static int init_wayland(struct wayvnc* self, const char* display)
goto failure; goto failure;
} }
if (!self->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."); nvnc_log(NVNC_LOG_ERROR, "Screencopy protocol not supported by compositor. Exiting. Refer to FAQ section in man page.");
goto failure; goto failure;
} }
@ -573,9 +610,6 @@ static int init_wayland(struct wayvnc* self, const char* display)
goto failure; goto failure;
} }
self->screencopy.on_done = on_capture_done;
self->screencopy.userdata = self;
self->wl_handler = aml_handler_new(wl_display_get_fd(self->display), self->wl_handler = aml_handler_new(wl_display_get_fd(self->display),
on_wayland_event, self, NULL); on_wayland_event, self, NULL);
if (!self->wl_handler) if (!self->wl_handler)
@ -649,9 +683,9 @@ static void compose_client_info(const struct wayvnc_client* client,
struct ctl_server_client_info* info) struct ctl_server_client_info* info)
{ {
info->id = client->id; info->id = client->id;
socklen_t addrlen = sizeof(info->address_storage); socklen_t addrlen = sizeof(info->address);
nvnc_client_get_address(client->nvnc_client, nvnc_client_get_address(client->nvnc_client,
(struct sockaddr*)&info->address_storage, &addrlen); (struct sockaddr*)&info->address, &addrlen);
info->username = nvnc_client_get_auth_username(client->nvnc_client); info->username = nvnc_client_get_auth_username(client->nvnc_client);
info->seat = client->seat ? client->seat->name : NULL; info->seat = client->seat ? client->seat->name : NULL;
} }
@ -753,125 +787,6 @@ static void on_pointer_event(struct nvnc_client* client, uint16_t x, uint16_t y,
output_transform_coord(wayvnc->selected_output, x, y, &xfx, &xfy); output_transform_coord(wayvnc->selected_output, x, y, &xfx, &xfy);
pointer_set(&wv_client->pointer, xfx, xfy, button_mask); pointer_set(&wv_client->pointer, xfx, xfy, button_mask);
// This workaround would only work for x11 apps rendered in xwayland.
// It does NOT work for wayland!
// Waylan doesn't have any API to get the cursor image like in x11.
// We would need to grab and parse the surface of the pointer which is a pain.
// Would that even work with hardware cursors?
/*
/
static char ascii_art[] =
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX "
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX "
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXX "
"XXXXXXXXXXXXXXXXXXXXXXXXXXXX "
"XXXXXXXXXXXXXXXXXXXXXXXXXXX "
"XXXXXXXXXXXXXXXXXXXXXXXXXX "
"XXXXXXXXXXXXXXXXXXXXXXXXX "
"XXXXXXXX "
"XXXXXXXX "
"XXXXXXXX "
"XXXXXXXX "
"XXXXXXXX "
"XXXXXXXX "
"XXXXXXXX "
"XXXXXXXX "
"XXXXXXXX "
"XXXXXXXX "
"XXXXXXXX "
"XXXXXXXX "
"XXXXXXXX "
"XXXXXXXX "
"XXXXXXXX "
"XXXXXXXX "
"XXXXXXXX "
"XXXXXXX "
"XXXXXX "
"XXXXX "
"XXXX "
"XXX "
"XX "
"X ";
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
uint32_t colour = 0x00ff00ffULL;
#else
uint32_t colour = 0xff00ff00ULL;
#endif
// Print current x11 image
Display *display = XOpenDisplay(NULL);
if (!display) {
fprintf(stderr, "Failed to open X display\n");
}
XFixesCursorImage *img = XFixesGetCursorImage(display);
printf("Cursor serial: %ld (%d x %d)\n", img->cursor_serial, img->width, img->height);
//struct nvnc_fb* fb = nvnc_fb_new(32, 32, DRM_FORMAT_RGBA8888, 32);
//assert(fb);
//uint32_t* pixels = nvnc_fb_get_addr(fb);
//for (int i = 0; i < 32 * 32; ++i) {
// pixels[i] = ascii_art[i] != ' ' ? colour : 0;
//}
//nvnc_set_cursor(wv_client->server->nvnc, fb, 32, 32, 0, 0, true);
//nvnc_fb_unref(fb);
*/
/**
* The cursor image itself is returned as a single image at 32 bits per
pixel with 8 bits of alpha in the most significant 8 bits of the
pixel followed by 8 bits each of red, green and finally 8 bits of
blue in the least significant 8 bits. The color components are
pre-multiplied with the alpha component.
Colors are 0xrrggbb
*/
/*
struct nvnc_fb* fb = nvnc_fb_new(img->width, img->height, DRM_FORMAT_RGBA8888, 24);
assert(fb);
// Xlib stores 32-bit data in longs, even if longs are 64-bits long.
unsigned long* argb_data = img->pixels;
//uint32_t* dst = reinterpret_cast<uint32_t*>(image->data());
//uint32_t* dst_end = dst + (img->width * img->height);
//while (dst < dst_end) {
// *dst++ = static_cast<uint32_t>(*src++);
//}
uint32_t* pixels = nvnc_fb_get_addr(fb);
for (int i = 0; i < img->width * img->height; ++i) {
// We need to put alpha to the end of hex fo nvnc buffer
uint8_t a = ((argb_data[i] >> 24) & 0xff);
uint8_t r = ((argb_data[i] >> 16) & 0xff);
uint8_t g = ((argb_data[i] >> 8) & 0xff);
uint8_t b = ((argb_data[i] >> 0) & 0xff);
pixels[i] = ((uint32_t)r << 24) | ((uint32_t)g << 16) | ((uint32_t)b << 8) | a;
}
// TODO: use listener!
// CursorNotify
// https://chromium.googlesource.com/external/webrtc/stable/webrtc/+/master/modules/desktop_capture/mouse_cursor_monitor_x11.cc
// https://github.com/zwcloud/XcbSharp/blob/7d012ec64a2f5e6207da708d70856466ab35e173/xfixes.xml#L125
// sudo pacman -S libx11 libxfixes
// g++ file.c -lX11 -lXfixes -o tt
nvnc_set_cursor(wv_client->server->nvnc, fb, img->width, img->height, img->xhot, img->yhot / 2, true);
nvnc_fb_unref(fb);
XFree(img);
XCloseDisplay(display);
*/
} }
static void on_key_event(struct nvnc_client* client, uint32_t symbol, static void on_key_event(struct nvnc_client* client, uint32_t symbol,
@ -883,9 +798,6 @@ static void on_key_event(struct nvnc_client* client, uint32_t symbol,
} }
keyboard_feed(&wv_client->keyboard, symbol, is_pressed); keyboard_feed(&wv_client->keyboard, symbol, is_pressed);
nvnc_client_set_led_state(wv_client->nvnc_client,
keyboard_get_led_state(&wv_client->keyboard));
} }
static void on_key_code_event(struct nvnc_client* client, uint32_t code, static void on_key_code_event(struct nvnc_client* client, uint32_t code,
@ -897,9 +809,6 @@ static void on_key_code_event(struct nvnc_client* client, uint32_t code,
} }
keyboard_feed_code(&wv_client->keyboard, code + 8, is_pressed); keyboard_feed_code(&wv_client->keyboard, code + 8, is_pressed);
nvnc_client_set_led_state(wv_client->nvnc_client,
keyboard_get_led_state(&wv_client->keyboard));
} }
static void on_client_cut_text(struct nvnc_client* nvnc_client, static void on_client_cut_text(struct nvnc_client* nvnc_client,
@ -1042,7 +951,7 @@ static int init_nvnc(struct wayvnc* self, const char* addr, uint16_t port,
nvnc_set_name(self->nvnc, "WayVNC"); nvnc_set_name(self->nvnc, "WayVNC");
nvnc_set_desktop_layout_fn(self->nvnc, on_client_resize); //nvnc_set_desktop_layout_fn(self->nvnc, on_client_resize);
enum nvnc_auth_flags auth_flags = 0; enum nvnc_auth_flags auth_flags = 0;
if (self->cfg.enable_auth) { if (self->cfg.enable_auth) {
@ -1105,7 +1014,7 @@ failure:
int wayvnc_start_capture(struct wayvnc* self) int wayvnc_start_capture(struct wayvnc* self)
{ {
int rc = screencopy_start(&self->screencopy); int rc = screencopy_start(self->screencopy, false);
if (rc < 0) { if (rc < 0) {
nvnc_log(NVNC_LOG_ERROR, "Failed to start capture. Exiting..."); nvnc_log(NVNC_LOG_ERROR, "Failed to start capture. Exiting...");
wayvnc_exit(self); wayvnc_exit(self);
@ -1126,7 +1035,7 @@ int wayvnc_start_capture_immediate(struct wayvnc* self)
return 0; return 0;
} }
int rc = screencopy_start_immediate(&self->screencopy); int rc = screencopy_start(self->screencopy, true);
if (rc < 0) { if (rc < 0) {
nvnc_log(NVNC_LOG_ERROR, "Failed to start capture. Exiting..."); nvnc_log(NVNC_LOG_ERROR, "Failed to start capture. Exiting...");
wayvnc_exit(self); wayvnc_exit(self);
@ -1164,7 +1073,7 @@ void on_output_dimension_change(struct output* output)
nvnc_log(NVNC_LOG_DEBUG, "Output dimensions changed. Restarting frame capturer..."); nvnc_log(NVNC_LOG_DEBUG, "Output dimensions changed. Restarting frame capturer...");
screencopy_stop(&self->screencopy); screencopy_stop(self->screencopy);
wayvnc_start_capture_immediate(self); wayvnc_start_capture_immediate(self);
} }
@ -1183,7 +1092,7 @@ static void on_output_power_change(struct output* output)
break; break;
case OUTPUT_POWER_OFF: case OUTPUT_POWER_OFF:
nvnc_log(NVNC_LOG_WARNING, "Output is now off. Pausing frame capture"); nvnc_log(NVNC_LOG_WARNING, "Output is now off. Pausing frame capture");
screencopy_stop(&self->screencopy); screencopy_stop(self->screencopy);
blank_screen(self); blank_screen(self);
break; break;
default: default:
@ -1208,14 +1117,15 @@ static uint32_t calculate_region_area(struct pixman_region16* region)
return area; return area;
} }
void wayvnc_process_frame(struct wayvnc* self) void wayvnc_process_frame(struct wayvnc* self, struct wv_buffer* buffer)
{ {
struct wv_buffer* buffer = self->screencopy.back; // TODO: Back buffer used to be set to NULL here, what's that about?
self->screencopy.back = NULL;
nvnc_log(NVNC_LOG_DEBUG, "Passing on buffer: %p", buffer);
self->n_frames_captured++; self->n_frames_captured++;
self->damage_area_sum += self->damage_area_sum +=
calculate_region_area(&buffer->damage); calculate_region_area(&buffer->frame_damage);
struct pixman_region16 damage; struct pixman_region16 damage;
pixman_region_init(&damage); pixman_region_init(&damage);
@ -1227,12 +1137,12 @@ void wayvnc_process_frame(struct wayvnc* self)
buffer_transform = wv_output_transform_compose(output_transform, buffer_transform = wv_output_transform_compose(output_transform,
WL_OUTPUT_TRANSFORM_FLIPPED_180); WL_OUTPUT_TRANSFORM_FLIPPED_180);
wv_region_transform(&damage, &buffer->damage, wv_region_transform(&damage, &buffer->frame_damage,
WL_OUTPUT_TRANSFORM_FLIPPED_180, WL_OUTPUT_TRANSFORM_FLIPPED_180,
buffer->width, buffer->height); buffer->width, buffer->height);
} else { } else {
buffer_transform = output_transform; buffer_transform = output_transform;
pixman_region_copy(&damage, &buffer->damage); pixman_region_copy(&damage, &buffer->frame_damage);
} }
nvnc_fb_set_transform(buffer->nvnc_fb, nvnc_fb_set_transform(buffer->nvnc_fb,
@ -1249,15 +1159,12 @@ void wayvnc_process_frame(struct wayvnc* self)
wayvnc_start_capture(self); wayvnc_start_capture(self);
} }
void on_capture_done(struct screencopy* sc) void on_capture_done(enum screencopy_result result, struct wv_buffer* buffer,
void* userdata)
{ {
struct wayvnc* self = sc->userdata; struct wayvnc* self = userdata;
switch (sc->status) { switch (result) {
case SCREENCOPY_STOPPED:
break;
case SCREENCOPY_IN_PROGRESS:
break;
case SCREENCOPY_FATAL: case SCREENCOPY_FATAL:
nvnc_log(NVNC_LOG_ERROR, "Fatal error while capturing. Exiting..."); nvnc_log(NVNC_LOG_ERROR, "Fatal error while capturing. Exiting...");
wayvnc_exit(self); wayvnc_exit(self);
@ -1266,7 +1173,7 @@ void on_capture_done(struct screencopy* sc)
wayvnc_restart_capture(self); wayvnc_restart_capture(self);
break; break;
case SCREENCOPY_DONE: case SCREENCOPY_DONE:
wayvnc_process_frame(self); wayvnc_process_frame(self, buffer);
break; break;
} }
} }
@ -1429,7 +1336,7 @@ static void client_destroy(void* obj)
if (wayvnc->nr_clients == 0 && wayvnc->display) { if (wayvnc->nr_clients == 0 && wayvnc->display) {
nvnc_log(NVNC_LOG_INFO, "Stopping screen capture"); nvnc_log(NVNC_LOG_INFO, "Stopping screen capture");
screencopy_stop(&wayvnc->screencopy); screencopy_stop(wayvnc->screencopy);
stop_performance_ticker(wayvnc); stop_performance_ticker(wayvnc);
} }
@ -1648,15 +1555,35 @@ 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->on_done = on_capture_done;
self->screencopy->userdata = self;
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) { if (self->selected_output) {
self->selected_output->on_dimension_change = NULL; self->selected_output->on_dimension_change = NULL;
} }
self->selected_output = output; self->selected_output = output;
self->screencopy.wl_output = output->wl_output;
output->on_dimension_change = on_output_dimension_change; output->on_dimension_change = on_output_dimension_change;
output->on_power_change = on_output_power_change; output->on_power_change = on_output_power_change;
output->userdata = self; output->userdata = self;
if (self->ctl) if (self->ctl)
ctl_server_event_capture_changed(self->ctl, output->name); ctl_server_event_capture_changed(self->ctl, output->name);
log_selected_output(self); log_selected_output(self);
@ -1669,8 +1596,9 @@ void switch_to_output(struct wayvnc* self, struct output* output)
output->name); output->name);
return; return;
} }
screencopy_stop(&self->screencopy); screencopy_stop(self->screencopy);
set_selected_output(self, output); set_selected_output(self, output);
configure_screencopy(self);
reinitialise_pointers(self); reinitialise_pointers(self);
if (self->nr_clients > 0) if (self->nr_clients > 0)
wayvnc_start_capture_immediate(self); wayvnc_start_capture_immediate(self);
@ -1761,14 +1689,13 @@ static bool wayland_attach(struct wayvnc* self, const char* display,
} }
} }
screencopy_init(&self->screencopy); if (!screencopy_manager) {
if (!self->screencopy.manager) {
nvnc_log(NVNC_LOG_ERROR, "Attached display does not implement wlr-screencopy-v1"); nvnc_log(NVNC_LOG_ERROR, "Attached display does not implement wlr-screencopy-v1");
wayland_detach(self); wayland_detach(self);
return false; return false;
} }
set_selected_output(self, out); set_selected_output(self, out);
configure_screencopy(self);
struct nvnc_client* nvnc_client; struct nvnc_client* nvnc_client;
for (nvnc_client = nvnc_client_first(self->nvnc); nvnc_client; for (nvnc_client = nvnc_client_first(self->nvnc); nvnc_client;
@ -1930,6 +1857,9 @@ int main(int argc, char* argv[])
start_detached = !!option_parser_get_value(&option_parser, "detached"); start_detached = !!option_parser_get_value(&option_parser, "detached");
self.start_detached = start_detached; 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"); keyboard_options = option_parser_get_value(&option_parser, "keyboard");
if (keyboard_options) if (keyboard_options)
@ -2034,9 +1964,6 @@ int main(int argc, char* argv[])
self.selected_seat = seat; self.selected_seat = seat;
} }
self.screencopy.rate_limit = max_rate;
self.screencopy.enable_linux_dmabuf = enable_gpu_features;
#ifdef ENABLE_SCREENCOPY_DMABUF #ifdef ENABLE_SCREENCOPY_DMABUF
if (enable_gpu_features && init_render_node(&drm_fd) < 0) { if (enable_gpu_features && init_render_node(&drm_fd) < 0) {
nvnc_log(NVNC_LOG_ERROR, "Failed to initialise DRM render node. No GPU acceleration will be available."); nvnc_log(NVNC_LOG_ERROR, "Failed to initialise DRM render node. No GPU acceleration will be available.");
@ -2052,17 +1979,14 @@ int main(int argc, char* argv[])
else if (use_websocket) else if (use_websocket)
socket_type = SOCKET_TYPE_WEBSOCKET; socket_type = SOCKET_TYPE_WEBSOCKET;
if (!start_detached) { if (init_nvnc(&self, address, port, socket_type) < 0)
if (self.screencopy.manager) goto nvnc_failure;
screencopy_init(&self.screencopy);
if (!self.screencopy.manager) { if (!start_detached && !configure_screencopy(&self))
nvnc_log(NVNC_LOG_ERROR, "screencopy is not supported by compositor"); goto screencopy_failure;
goto capture_failure;
}
}
self.screencopy.overlay_cursor = overlay_cursor; self.screencopy->on_done = on_capture_done;
self.screencopy->userdata = &self;
if (show_performance) if (show_performance)
self.performance_ticker = aml_ticker_new(1000000, on_perf_tick, self.performance_ticker = aml_ticker_new(1000000, on_perf_tick,
@ -2084,9 +2008,6 @@ int main(int argc, char* argv[])
if (!self.ctl) if (!self.ctl)
goto ctl_server_failure; goto ctl_server_failure;
if (init_nvnc(&self, address, port, socket_type) < 0)
goto nvnc_failure;
if (self.display) if (self.display)
wl_display_dispatch_pending(self.display); wl_display_dispatch_pending(self.display);
@ -2101,7 +2022,7 @@ int main(int argc, char* argv[])
nvnc_log(NVNC_LOG_INFO, "Exiting..."); nvnc_log(NVNC_LOG_INFO, "Exiting...");
if (self.display) if (self.display)
screencopy_stop(&self.screencopy); screencopy_stop(self.screencopy);
ctl_server_destroy(self.ctl); ctl_server_destroy(self.ctl);
self.ctl = NULL; self.ctl = NULL;
@ -2110,6 +2031,10 @@ int main(int argc, char* argv[])
nvnc_close(self.nvnc); nvnc_close(self.nvnc);
self.nvnc = NULL; self.nvnc = NULL;
wayvnc_destroy(&self); wayvnc_destroy(&self);
if (zwp_linux_dmabuf)
zwp_linux_dmabuf_v1_destroy(zwp_linux_dmabuf);
if (self.screencopy)
screencopy_destroy(self.screencopy);
#ifdef ENABLE_SCREENCOPY_DMABUF #ifdef ENABLE_SCREENCOPY_DMABUF
if (gbm_device) { if (gbm_device) {
gbm_device_destroy(gbm_device); gbm_device_destroy(gbm_device);
@ -2120,10 +2045,11 @@ int main(int argc, char* argv[])
return 0; return 0;
nvnc_failure:
ctl_server_destroy(self.ctl);
ctl_server_failure: ctl_server_failure:
capture_failure: screencopy_failure:
nvnc_display_unref(self.nvnc_display);
nvnc_close(self.nvnc);
nvnc_failure:
wayland_failure: wayland_failure:
aml_unref(aml); aml_unref(aml);
failure: failure:

View File

@ -0,0 +1,58 @@
/*
* Copyright (c) 2022 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 "screencopy-interface.h"
#include <unistd.h>
extern struct zwlr_screencopy_manager_v1* screencopy_manager;
extern struct ext_output_image_source_manager_v1* ext_output_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)
{
if (ext_screencopy_manager && ext_output_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,
struct wl_output* output)
{
return impl->create_cursor ? impl->create_cursor(output) : NULL;
}
void screencopy_destroy(struct screencopy* self)
{
if (self)
self->impl->destroy(self);
}
int screencopy_start(struct screencopy* self, bool immediate)
{
return self->impl->start(self, immediate);
}
void screencopy_stop(struct screencopy* self)
{
self->impl->stop(self);
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019 - 2020 Andri Yngvason * Copyright (c) 2019 - 2022 Andri Yngvason
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -19,7 +19,6 @@
#include <assert.h> #include <assert.h>
#include <stdbool.h> #include <stdbool.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <wayland-client-protocol.h>
#include <wayland-client.h> #include <wayland-client.h>
#include <libdrm/drm_fourcc.h> #include <libdrm/drm_fourcc.h>
#include <aml.h> #include <aml.h>
@ -28,7 +27,7 @@
#include "wlr-screencopy-unstable-v1.h" #include "wlr-screencopy-unstable-v1.h"
#include "buffer.h" #include "buffer.h"
#include "shm.h" #include "shm.h"
#include "screencopy.h" #include "screencopy-interface.h"
#include "smooth.h" #include "smooth.h"
#include "time-util.h" #include "time-util.h"
#include "usdt.h" #include "usdt.h"
@ -37,11 +36,52 @@
#define DELAY_SMOOTHER_TIME_CONSTANT 0.5 // s #define DELAY_SMOOTHER_TIME_CONSTANT 0.5 // s
static void screencopy__stop(struct screencopy* self) extern struct zwlr_screencopy_manager_v1* screencopy_manager;
enum wlr_screencopy_status {
WLR_SCREENCOPY_STOPPED = 0,
WLR_SCREENCOPY_IN_PROGRESS,
WLR_SCREENCOPY_FAILED,
WLR_SCREENCOPY_FATAL,
WLR_SCREENCOPY_DONE,
};
struct wlr_screencopy {
struct screencopy parent;
enum wlr_screencopy_status status;
struct wv_buffer_pool* pool;
struct wv_buffer* front;
struct wv_buffer* back;
struct zwlr_screencopy_frame_v1* frame;
uint64_t last_time;
uint64_t start_time;
struct aml_timer* timer;
struct smooth delay_smoother;
double delay;
bool is_immediate_copy;
bool overlay_cursor;
struct wl_output* wl_output;
uint32_t wl_shm_width, wl_shm_height, wl_shm_stride;
enum wl_shm_format wl_shm_format;
bool have_linux_dmabuf;
uint32_t dmabuf_width, dmabuf_height;
uint32_t fourcc;
};
struct screencopy_impl wlr_screencopy_impl;
static void screencopy__stop(struct wlr_screencopy* self)
{ {
aml_stop(aml_get_default(), self->timer); aml_stop(aml_get_default(), self->timer);
self->status = SCREENCOPY_STOPPED; self->status = WLR_SCREENCOPY_STOPPED;
if (self->frame) { if (self->frame) {
zwlr_screencopy_frame_v1_destroy(self->frame); zwlr_screencopy_frame_v1_destroy(self->frame);
@ -49,8 +89,10 @@ static void screencopy__stop(struct screencopy* self)
} }
} }
void screencopy_stop(struct screencopy* self) void wlr_screencopy_stop(struct screencopy* ptr)
{ {
struct wlr_screencopy* self = (struct wlr_screencopy*)ptr;
if (self->front) if (self->front)
wv_buffer_pool_release(self->pool, self->front); wv_buffer_pool_release(self->pool, self->front);
self->front = NULL; self->front = NULL;
@ -63,7 +105,7 @@ static void screencopy_linux_dmabuf(void* data,
uint32_t format, uint32_t width, uint32_t height) uint32_t format, uint32_t width, uint32_t height)
{ {
#ifdef ENABLE_SCREENCOPY_DMABUF #ifdef ENABLE_SCREENCOPY_DMABUF
struct screencopy* self = data; struct wlr_screencopy* self = data;
if (!(wv_buffer_get_available_types() & WV_BUFFER_DMABUF)) if (!(wv_buffer_get_available_types() & WV_BUFFER_DMABUF))
return; return;
@ -78,12 +120,12 @@ static void screencopy_linux_dmabuf(void* data,
static void screencopy_buffer_done(void* data, static void screencopy_buffer_done(void* data,
struct zwlr_screencopy_frame_v1* frame) struct zwlr_screencopy_frame_v1* frame)
{ {
struct screencopy* self = data; struct wlr_screencopy* self = data;
uint32_t width, height, stride, fourcc; uint32_t width, height, stride, fourcc;
enum wv_buffer_type type = WV_BUFFER_UNSPEC; enum wv_buffer_type type = WV_BUFFER_UNSPEC;
#ifdef ENABLE_SCREENCOPY_DMABUF #ifdef ENABLE_SCREENCOPY_DMABUF
if (self->have_linux_dmabuf && self->enable_linux_dmabuf) { if (self->have_linux_dmabuf && self->parent.enable_linux_dmabuf) {
width = self->dmabuf_width; width = self->dmabuf_width;
height = self->dmabuf_height; height = self->dmabuf_height;
stride = 0; stride = 0;
@ -104,8 +146,8 @@ static void screencopy_buffer_done(void* data,
struct wv_buffer* buffer = wv_buffer_pool_acquire(self->pool); struct wv_buffer* buffer = wv_buffer_pool_acquire(self->pool);
if (!buffer) { if (!buffer) {
screencopy__stop(self); screencopy__stop(self);
self->status = SCREENCOPY_FATAL; self->status = WLR_SCREENCOPY_FATAL;
self->on_done(self); self->parent.on_done(SCREENCOPY_FATAL, NULL, self->parent.userdata);
return; return;
} }
@ -124,14 +166,14 @@ static void screencopy_buffer(void* data,
enum wl_shm_format format, uint32_t width, enum wl_shm_format format, uint32_t width,
uint32_t height, uint32_t stride) uint32_t height, uint32_t stride)
{ {
struct screencopy* self = data; struct wlr_screencopy* self = data;
self->wl_shm_format = format; self->wl_shm_format = format;
self->wl_shm_width = width; self->wl_shm_width = width;
self->wl_shm_height = height; self->wl_shm_height = height;
self->wl_shm_stride = stride; self->wl_shm_stride = stride;
int version = zwlr_screencopy_manager_v1_get_version(self->manager); int version = zwlr_screencopy_manager_v1_get_version(screencopy_manager);
if (version < 3) { if (version < 3) {
self->have_linux_dmabuf = false; self->have_linux_dmabuf = false;
screencopy_buffer_done(data, frame); screencopy_buffer_done(data, frame);
@ -145,7 +187,7 @@ static void screencopy_flags(void* data,
{ {
(void)frame; (void)frame;
struct screencopy* self = data; struct wlr_screencopy* self = data;
self->front->y_inverted = self->front->y_inverted =
!!(flags & ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT); !!(flags & ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT);
@ -155,7 +197,11 @@ static void screencopy_ready(void* data,
struct zwlr_screencopy_frame_v1* frame, struct zwlr_screencopy_frame_v1* frame,
uint32_t sec_hi, uint32_t sec_lo, uint32_t nsec) uint32_t sec_hi, uint32_t sec_lo, uint32_t nsec)
{ {
struct screencopy* self = data; (void)sec_hi;
(void)sec_lo;
(void)nsec;
struct wlr_screencopy* self = data;
uint64_t sec = (uint64_t)sec_hi << 32 | (uint64_t)sec_lo; uint64_t sec = (uint64_t)sec_hi << 32 | (uint64_t)sec_lo;
uint64_t pts = sec * UINT64_C(1000000) + (uint64_t)nsec / UINT64_C(1000); uint64_t pts = sec * UINT64_C(1000000) + (uint64_t)nsec / UINT64_C(1000);
@ -179,14 +225,16 @@ static void screencopy_ready(void* data,
nvnc_fb_set_pts(self->back->nvnc_fb, pts); nvnc_fb_set_pts(self->back->nvnc_fb, pts);
self->status = SCREENCOPY_DONE; self->status = WLR_SCREENCOPY_DONE;
self->on_done(self); self->parent.on_done(SCREENCOPY_DONE, self->back, self->parent.userdata);
self->back = NULL;
} }
static void screencopy_failed(void* data, static void screencopy_failed(void* data,
struct zwlr_screencopy_frame_v1* frame) struct zwlr_screencopy_frame_v1* frame)
{ {
struct screencopy* self = data; struct wlr_screencopy* self = data;
DTRACE_PROBE1(wayvnc, screencopy_failed, self); DTRACE_PROBE1(wayvnc, screencopy_failed, self);
@ -196,8 +244,8 @@ static void screencopy_failed(void* data,
wv_buffer_pool_release(self->pool, self->front); wv_buffer_pool_release(self->pool, self->front);
self->front = NULL; self->front = NULL;
self->status = SCREENCOPY_FAILED; self->status = WLR_SCREENCOPY_FAILED;
self->on_done(self); self->parent.on_done(SCREENCOPY_FAILED, NULL, self->parent.userdata);
} }
static void screencopy_damage(void* data, static void screencopy_damage(void* data,
@ -205,14 +253,14 @@ static void screencopy_damage(void* data,
uint32_t x, uint32_t y, uint32_t x, uint32_t y,
uint32_t width, uint32_t height) uint32_t width, uint32_t height)
{ {
struct screencopy* self = data; struct wlr_screencopy* self = data;
DTRACE_PROBE1(wayvnc, screencopy_damage, self); DTRACE_PROBE1(wayvnc, screencopy_damage, self);
wv_buffer_damage_rect(self->front, x, y, width, height); wv_buffer_damage_rect(self->front, x, y, width, height);
} }
static int screencopy__start_capture(struct screencopy* self) static int screencopy__start_capture(struct wlr_screencopy* self)
{ {
DTRACE_PROBE1(wayvnc, screencopy_start, self); DTRACE_PROBE1(wayvnc, screencopy_start, self);
@ -228,8 +276,9 @@ static int screencopy__start_capture(struct screencopy* self)
self->start_time = gettime_us(); self->start_time = gettime_us();
self->frame = zwlr_screencopy_manager_v1_capture_output(self->manager, self->frame = zwlr_screencopy_manager_v1_capture_output(
self->overlay_cursor, self->wl_output); screencopy_manager, self->overlay_cursor,
self->wl_output);
if (!self->frame) if (!self->frame)
return -1; return -1;
@ -241,23 +290,25 @@ static int screencopy__start_capture(struct screencopy* self)
static void screencopy__poll(void* obj) static void screencopy__poll(void* obj)
{ {
struct screencopy* self = aml_get_userdata(obj); struct wlr_screencopy* self = aml_get_userdata(obj);
screencopy__start_capture(self); screencopy__start_capture(self);
} }
static int screencopy__start(struct screencopy* self, bool is_immediate_copy) static int wlr_screencopy_start(struct screencopy* ptr, bool is_immediate_copy)
{ {
if (self->status == SCREENCOPY_IN_PROGRESS) struct wlr_screencopy* self = (struct wlr_screencopy*)ptr;
if (self->status == WLR_SCREENCOPY_IN_PROGRESS)
return -1; return -1;
self->is_immediate_copy = is_immediate_copy; self->is_immediate_copy = is_immediate_copy;
uint64_t now = gettime_us(); uint64_t now = gettime_us();
double dt = (now - self->last_time) * 1.0e-6; double dt = (now - self->last_time) * 1.0e-6;
int32_t time_left = (1.0 / self->rate_limit - dt - self->delay) * 1.0e6; int32_t time_left = (1.0 / ptr->rate_limit - dt - self->delay) * 1.0e3;
self->status = SCREENCOPY_IN_PROGRESS; self->status = WLR_SCREENCOPY_IN_PROGRESS;
if (time_left > 0) { if (time_left > 0) {
aml_set_duration(self->timer, time_left); aml_set_duration(self->timer, time_left);
@ -267,18 +318,19 @@ static int screencopy__start(struct screencopy* self, bool is_immediate_copy)
return screencopy__start_capture(self); return screencopy__start_capture(self);
} }
int screencopy_start(struct screencopy* self) static struct screencopy* wlr_screencopy_create(struct wl_output* output,
bool render_cursor)
{ {
return screencopy__start(self, false); struct wlr_screencopy* self = calloc(1, sizeof(*self));
} if (!self)
return NULL;
int screencopy_start_immediate(struct screencopy* self) self->parent.impl = &wlr_screencopy_impl;
{ self->parent.rate_limit = 30;
return screencopy__start(self, true);
} self->wl_output = output;
self->overlay_cursor = render_cursor;
void screencopy_init(struct screencopy* self)
{
self->pool = wv_buffer_pool_create(0, 0, 0, 0, 0); self->pool = wv_buffer_pool_create(0, 0, 0, 0, 0);
assert(self->pool); assert(self->pool);
@ -286,10 +338,13 @@ void screencopy_init(struct screencopy* self)
assert(self->timer); assert(self->timer);
self->delay_smoother.time_constant = DELAY_SMOOTHER_TIME_CONSTANT; self->delay_smoother.time_constant = DELAY_SMOOTHER_TIME_CONSTANT;
return (struct screencopy*)self;
} }
void screencopy_destroy(struct screencopy* self) static void wlr_screencopy_destroy(struct screencopy* ptr)
{ {
struct wlr_screencopy* self = (struct wlr_screencopy*)ptr;
aml_stop(aml_get_default(), self->timer); aml_stop(aml_get_default(), self->timer);
aml_unref(self->timer); aml_unref(self->timer);
@ -303,3 +358,10 @@ void screencopy_destroy(struct screencopy* self)
wv_buffer_pool_destroy(self->pool); wv_buffer_pool_destroy(self->pool);
} }
struct screencopy_impl wlr_screencopy_impl = {
.create = wlr_screencopy_create,
.destroy = wlr_screencopy_destroy,
.start = wlr_screencopy_start,
.stop = wlr_screencopy_stop,
};

View File

@ -377,4 +377,4 @@ multioutput_test() {
} }
smoke_test smoke_test
#multioutput_test multioutput_test