Compare commits
4 Commits
master
...
ext-screen
Author | SHA1 | Date |
---|---|---|
Andri Yngvason | 9a3bdbfdbe | |
Andri Yngvason | 610d69fae0 | |
Andri Yngvason | d7e256942f | |
Andri Yngvason | 3652f1e6cf |
|
@ -7,4 +7,3 @@ perf.*
|
||||||
*.pem
|
*.pem
|
||||||
.vimrc
|
.vimrc
|
||||||
.cache
|
.cache
|
||||||
.vscode
|
|
10
FAQ.md
10
FAQ.md
|
@ -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
|
||||||
|
|
|
@ -1,2 +1 @@
|
||||||
github: any1
|
|
||||||
patreon: andriyngvason
|
patreon: andriyngvason
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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*);
|
|
||||||
|
|
|
@ -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);
|
|
@ -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);
|
|
25
meson.build
25
meson.build
|
@ -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)
|
||||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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',
|
||||||
|
|
53
src/buffer.c
53
src/buffer.c
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
};
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
306
src/main.c
306
src/main.c
|
@ -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:
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
140
src/screencopy.c
140
src/screencopy.c
|
@ -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,
|
||||||
|
};
|
||||||
|
|
|
@ -377,4 +377,4 @@ multioutput_test() {
|
||||||
}
|
}
|
||||||
|
|
||||||
smoke_test
|
smoke_test
|
||||||
#multioutput_test
|
multioutput_test
|
||||||
|
|
Loading…
Reference in New Issue