Compare commits
6 Commits
master
...
detached-m
Author | SHA1 | Date |
---|---|---|
Andri Yngvason | 7c3b606b8d | |
Andri Yngvason | 13043987e5 | |
Andri Yngvason | f9bacc8eb8 | |
Andri Yngvason | 2c524e28fc | |
Andri Yngvason | 861e140205 | |
Andri Yngvason | 0938162e6f |
|
@ -7,4 +7,3 @@ perf.*
|
|||
*.pem
|
||||
.vimrc
|
||||
.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
|
||||
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?**
|
||||
|
||||
A: Try setting the keyboard layout in wayvnc to the one that most closely
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
github: any1
|
||||
patreon: andriyngvason
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# 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)
|
||||
[![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)
|
||||
|
||||
## Introduction
|
||||
|
@ -122,9 +122,8 @@ For TLS, you'll need a private X509 key and a certificate. A self-signed key
|
|||
with a certificate can be generated like so:
|
||||
```
|
||||
cd ~/.config/wayvnc
|
||||
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:secp384r1 -sha384 \
|
||||
-days 3650 -nodes -keyout tls_key.pem -out tls_cert.pem \
|
||||
-subj /CN=localhost \
|
||||
openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes \
|
||||
-keyout tls_key.pem -out tls_cert.pem -subj /CN=localhost \
|
||||
-addext subjectAltName=DNS:localhost,DNS:localhost,IP:127.0.0.1
|
||||
cd -
|
||||
```
|
||||
|
|
|
@ -40,8 +40,6 @@ enum event_type {
|
|||
EVT_CLIENT_CONNECTED,
|
||||
EVT_CLIENT_DISCONNECTED,
|
||||
EVT_DETACHED,
|
||||
EVT_OUTPUT_ADDED,
|
||||
EVT_OUTPUT_REMOVED,
|
||||
EVT_UNKNOWN,
|
||||
};
|
||||
#define EVT_LIST_LEN EVT_UNKNOWN
|
||||
|
|
|
@ -19,8 +19,6 @@
|
|||
|
||||
#include "output.h"
|
||||
|
||||
#include <sys/socket.h>
|
||||
|
||||
struct ctl;
|
||||
struct cmd_response;
|
||||
|
||||
|
@ -28,12 +26,9 @@ struct ctl_server_client;
|
|||
|
||||
struct ctl_server_client_info {
|
||||
int id;
|
||||
union {
|
||||
struct sockaddr_storage address_storage;
|
||||
struct sockaddr address;
|
||||
};
|
||||
const char* username;
|
||||
const char* seat;
|
||||
const char *hostname;
|
||||
const char *username;
|
||||
const char *seat;
|
||||
};
|
||||
|
||||
struct ctl_server_output {
|
||||
|
@ -89,6 +84,3 @@ void ctl_server_event_capture_changed(struct ctl*,
|
|||
const char* captured_output);
|
||||
|
||||
void ctl_server_event_detached(struct ctl*);
|
||||
|
||||
void ctl_server_event_output_added(struct ctl*, const char* name);
|
||||
void ctl_server_event_output_removed(struct ctl*, const char* name);
|
||||
|
|
|
@ -19,13 +19,11 @@
|
|||
#include <stdlib.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#include <stdbool.h>
|
||||
#include <neatvnc.h>
|
||||
|
||||
#include "intset.h"
|
||||
|
||||
struct zwp_virtual_keyboard_v1;
|
||||
struct table_entry;
|
||||
struct nvnc;
|
||||
|
||||
struct 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_code(struct keyboard* self, xkb_keycode_t code,
|
||||
bool is_pressed);
|
||||
enum nvnc_keyboard_led_state keyboard_get_led_state(const struct keyboard*);
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023 The wayvnc authors
|
||||
*
|
||||
* 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 <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
struct output;
|
||||
struct zwlr_output_manager_v1;
|
||||
|
||||
void wlr_output_manager_setup(struct zwlr_output_manager_v1* output_manager);
|
||||
bool wlr_output_manager_resize_output(struct output* output,
|
||||
uint16_t width, uint16_t height);
|
||||
void wlr_output_manager_destroy(void);
|
|
@ -57,7 +57,6 @@ struct output {
|
|||
|
||||
bool is_dimension_changed;
|
||||
bool is_transform_changed;
|
||||
bool is_headless;
|
||||
|
||||
void (*on_dimension_change)(struct output*);
|
||||
void (*on_transform_change)(struct output*);
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include <sys/types.h>
|
||||
|
||||
#define UDIV_UP(a, b) (((a) + (b) - 1) / (b))
|
||||
#define ALIGN_UP(a, b) ((b) * UDIV_UP((a), (b)))
|
||||
|
||||
extern const char* wayvnc_version;
|
||||
|
||||
|
|
28
meson.build
28
meson.build
|
@ -1,7 +1,7 @@
|
|||
project(
|
||||
'wayvnc',
|
||||
'c',
|
||||
version: '0.9-dev',
|
||||
version: '0.8-dev',
|
||||
license: 'ISC',
|
||||
default_options: [
|
||||
'c_std=gnu11',
|
||||
|
@ -16,7 +16,6 @@ prefix = get_option('prefix')
|
|||
c_args = [
|
||||
'-D_GNU_SOURCE',
|
||||
'-DAML_UNSTABLE_API=1',
|
||||
'-DWLR_USE_UNSTABLE=true',
|
||||
|
||||
'-Wno-unused-parameter',
|
||||
'-Wno-missing-field-initializers',
|
||||
|
@ -25,8 +24,8 @@ c_args = [
|
|||
version = '"@0@"'.format(meson.project_version())
|
||||
git = find_program('git', native: true, required: false)
|
||||
if git.found()
|
||||
git_commit = run_command([git, 'rev-parse', '--short', 'HEAD'], check: false)
|
||||
git_branch = run_command([git, 'rev-parse', '--abbrev-ref', 'HEAD'], check: false)
|
||||
git_commit = run_command([git, 'rev-parse', '--short', 'HEAD'])
|
||||
git_branch = run_command([git, 'rev-parse', '--abbrev-ref', 'HEAD'])
|
||||
if git_commit.returncode() == 0 and git_branch.returncode() == 0
|
||||
version = '"v@0@-@1@ (@2@)"'.format(
|
||||
meson.project_version(),
|
||||
|
@ -54,18 +53,11 @@ pixman = dependency('pixman-1')
|
|||
gbm = dependency('gbm', required: get_option('screencopy-dmabuf'))
|
||||
drm = dependency('libdrm')
|
||||
xkbcommon = dependency('xkbcommon', version: '>=1.0.0')
|
||||
wayland_server = dependency('wayland-server')
|
||||
wayland_client = dependency('wayland-client')
|
||||
wayland_client_protocol = dependency('wayland-protocols')
|
||||
wayland_cursor = dependency('wayland-cursor')
|
||||
jansson = dependency('jansson')
|
||||
|
||||
# Cursor image
|
||||
x11_dep = dependency('x11')
|
||||
x11_fixes_dep = dependency('xfixes')
|
||||
|
||||
aml_version = ['>=0.3.0', '<0.4.0']
|
||||
neatvnc_version = ['>=0.9', '<0.10.0']
|
||||
neatvnc_version = ['>=0.7.0', '<0.9.0']
|
||||
|
||||
neatvnc_project = subproject(
|
||||
'neatvnc',
|
||||
|
@ -86,7 +78,7 @@ else
|
|||
neatvnc = dependency('neatvnc', version: neatvnc_version)
|
||||
endif
|
||||
|
||||
inc = include_directories('include', '/usr/include/wlroots0.16')
|
||||
inc = include_directories('include')
|
||||
|
||||
subdir('protocols')
|
||||
|
||||
|
@ -97,7 +89,6 @@ sources = [
|
|||
'src/screencopy.c',
|
||||
'src/data-control.c',
|
||||
'src/output.c',
|
||||
'src/output-management.c',
|
||||
'src/pointer.c',
|
||||
'src/keyboard.c',
|
||||
'src/seat.c',
|
||||
|
@ -127,11 +118,6 @@ dependencies = [
|
|||
xkbcommon,
|
||||
client_protos,
|
||||
jansson,
|
||||
x11_dep,
|
||||
x11_fixes_dep,
|
||||
wayland_client_protocol,
|
||||
wayland_cursor,
|
||||
wayland_server
|
||||
]
|
||||
|
||||
ctlsources = [
|
||||
|
@ -157,10 +143,6 @@ if host_system == 'linux' and get_option('systemtap') and cc.has_header('sys/sdt
|
|||
config.set('HAVE_USDT', true)
|
||||
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')
|
||||
config.set('HAVE_MEMFD', true)
|
||||
config.set('HAVE_MEMFD_CREATE', true)
|
||||
|
|
|
@ -21,7 +21,6 @@ client_protocols = [
|
|||
'xdg-output-unstable-v1.xml',
|
||||
'linux-dmabuf-unstable-v1.xml',
|
||||
'wlr-data-control-unstable-v1.xml',
|
||||
'wlr-output-management-unstable-v1.xml',
|
||||
'wlr-output-power-management-unstable-v1.xml',
|
||||
'ext-transient-seat-v1.xml',
|
||||
]
|
||||
|
|
|
@ -1,601 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<protocol name="wlr_output_management_unstable_v1">
|
||||
<copyright>
|
||||
Copyright © 2019 Purism SPC
|
||||
|
||||
Permission to use, copy, modify, distribute, and sell this
|
||||
software and its documentation for any purpose is hereby granted
|
||||
without fee, provided that the above copyright notice appear in
|
||||
all copies and that both that copyright notice and this permission
|
||||
notice appear in supporting documentation, and that the name of
|
||||
the copyright holders not be used in advertising or publicity
|
||||
pertaining to distribution of the software without specific,
|
||||
written prior permission. The copyright holders make no
|
||||
representations about the suitability of this software for any
|
||||
purpose. It is provided "as is" without express or implied
|
||||
warranty.
|
||||
|
||||
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
||||
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
SPECIAL, 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.
|
||||
</copyright>
|
||||
|
||||
<description summary="protocol to configure output devices">
|
||||
This protocol exposes interfaces to obtain and modify output device
|
||||
configuration.
|
||||
|
||||
Warning! The protocol described in this file is experimental and
|
||||
backward incompatible changes may be made. Backward compatible changes
|
||||
may be added together with the corresponding interface version bump.
|
||||
Backward incompatible changes are done by bumping the version number in
|
||||
the protocol and interface names and resetting the interface version.
|
||||
Once the protocol is to be declared stable, the 'z' prefix and the
|
||||
version number in the protocol and interface names are removed and the
|
||||
interface version number is reset.
|
||||
</description>
|
||||
|
||||
<interface name="zwlr_output_manager_v1" version="4">
|
||||
<description summary="output device configuration manager">
|
||||
This interface is a manager that allows reading and writing the current
|
||||
output device configuration.
|
||||
|
||||
Output devices that display pixels (e.g. a physical monitor or a virtual
|
||||
output in a window) are represented as heads. Heads cannot be created nor
|
||||
destroyed by the client, but they can be enabled or disabled and their
|
||||
properties can be changed. Each head may have one or more available modes.
|
||||
|
||||
Whenever a head appears (e.g. a monitor is plugged in), it will be
|
||||
advertised via the head event. Immediately after the output manager is
|
||||
bound, all current heads are advertised.
|
||||
|
||||
Whenever a head's properties change, the relevant wlr_output_head events
|
||||
will be sent. Not all head properties will be sent: only properties that
|
||||
have changed need to.
|
||||
|
||||
Whenever a head disappears (e.g. a monitor is unplugged), a
|
||||
wlr_output_head.finished event will be sent.
|
||||
|
||||
After one or more heads appear, change or disappear, the done event will
|
||||
be sent. It carries a serial which can be used in a create_configuration
|
||||
request to update heads properties.
|
||||
|
||||
The information obtained from this protocol should only be used for output
|
||||
configuration purposes. This protocol is not designed to be a generic
|
||||
output property advertisement protocol for regular clients. Instead,
|
||||
protocols such as xdg-output should be used.
|
||||
</description>
|
||||
|
||||
<event name="head">
|
||||
<description summary="introduce a new head">
|
||||
This event introduces a new head. This happens whenever a new head
|
||||
appears (e.g. a monitor is plugged in) or after the output manager is
|
||||
bound.
|
||||
</description>
|
||||
<arg name="head" type="new_id" interface="zwlr_output_head_v1"/>
|
||||
</event>
|
||||
|
||||
<event name="done">
|
||||
<description summary="sent all information about current configuration">
|
||||
This event is sent after all information has been sent after binding to
|
||||
the output manager object and after any subsequent changes. This applies
|
||||
to child head and mode objects as well. In other words, this event is
|
||||
sent whenever a head or mode is created or destroyed and whenever one of
|
||||
their properties has been changed. Not all state is re-sent each time
|
||||
the current configuration changes: only the actual changes are sent.
|
||||
|
||||
This allows changes to the output configuration to be seen as atomic,
|
||||
even if they happen via multiple events.
|
||||
|
||||
A serial is sent to be used in a future create_configuration request.
|
||||
</description>
|
||||
<arg name="serial" type="uint" summary="current configuration serial"/>
|
||||
</event>
|
||||
|
||||
<request name="create_configuration">
|
||||
<description summary="create a new output configuration object">
|
||||
Create a new output configuration object. This allows to update head
|
||||
properties.
|
||||
</description>
|
||||
<arg name="id" type="new_id" interface="zwlr_output_configuration_v1"/>
|
||||
<arg name="serial" type="uint"/>
|
||||
</request>
|
||||
|
||||
<request name="stop">
|
||||
<description summary="stop sending events">
|
||||
Indicates the client no longer wishes to receive events for output
|
||||
configuration changes. However the compositor may emit further events,
|
||||
until the finished event is emitted.
|
||||
|
||||
The client must not send any more requests after this one.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<event name="finished" type="destructor">
|
||||
<description summary="the compositor has finished with the manager">
|
||||
This event indicates that the compositor is done sending manager events.
|
||||
The compositor will destroy the object immediately after sending this
|
||||
event, so it will become invalid and the client should release any
|
||||
resources associated with it.
|
||||
</description>
|
||||
</event>
|
||||
</interface>
|
||||
|
||||
<interface name="zwlr_output_head_v1" version="4">
|
||||
<description summary="output device">
|
||||
A head is an output device. The difference between a wl_output object and
|
||||
a head is that heads are advertised even if they are turned off. A head
|
||||
object only advertises properties and cannot be used directly to change
|
||||
them.
|
||||
|
||||
A head has some read-only properties: modes, name, description and
|
||||
physical_size. These cannot be changed by clients.
|
||||
|
||||
Other properties can be updated via a wlr_output_configuration object.
|
||||
|
||||
Properties sent via this interface are applied atomically via the
|
||||
wlr_output_manager.done event. No guarantees are made regarding the order
|
||||
in which properties are sent.
|
||||
</description>
|
||||
|
||||
<event name="name">
|
||||
<description summary="head name">
|
||||
This event describes the head name.
|
||||
|
||||
The naming convention is compositor defined, but limited to alphanumeric
|
||||
characters and dashes (-). Each name is unique among all wlr_output_head
|
||||
objects, but if a wlr_output_head object is destroyed the same name may
|
||||
be reused later. The names will also remain consistent across sessions
|
||||
with the same hardware and software configuration.
|
||||
|
||||
Examples of names include 'HDMI-A-1', 'WL-1', 'X11-1', etc. However, do
|
||||
not assume that the name is a reflection of an underlying DRM
|
||||
connector, X11 connection, etc.
|
||||
|
||||
If the compositor implements the xdg-output protocol and this head is
|
||||
enabled, the xdg_output.name event must report the same name.
|
||||
|
||||
The name event is sent after a wlr_output_head object is created. This
|
||||
event is only sent once per object, and the name does not change over
|
||||
the lifetime of the wlr_output_head object.
|
||||
</description>
|
||||
<arg name="name" type="string"/>
|
||||
</event>
|
||||
|
||||
<event name="description">
|
||||
<description summary="head description">
|
||||
This event describes a human-readable description of the head.
|
||||
|
||||
The description is a UTF-8 string with no convention defined for its
|
||||
contents. Examples might include 'Foocorp 11" Display' or 'Virtual X11
|
||||
output via :1'. However, do not assume that the name is a reflection of
|
||||
the make, model, serial of the underlying DRM connector or the display
|
||||
name of the underlying X11 connection, etc.
|
||||
|
||||
If the compositor implements xdg-output and this head is enabled,
|
||||
the xdg_output.description must report the same description.
|
||||
|
||||
The description event is sent after a wlr_output_head object is created.
|
||||
This event is only sent once per object, and the description does not
|
||||
change over the lifetime of the wlr_output_head object.
|
||||
</description>
|
||||
<arg name="description" type="string"/>
|
||||
</event>
|
||||
|
||||
<event name="physical_size">
|
||||
<description summary="head physical size">
|
||||
This event describes the physical size of the head. This event is only
|
||||
sent if the head has a physical size (e.g. is not a projector or a
|
||||
virtual device).
|
||||
</description>
|
||||
<arg name="width" type="int" summary="width in millimeters of the output"/>
|
||||
<arg name="height" type="int" summary="height in millimeters of the output"/>
|
||||
</event>
|
||||
|
||||
<event name="mode">
|
||||
<description summary="introduce a mode">
|
||||
This event introduces a mode for this head. It is sent once per
|
||||
supported mode.
|
||||
</description>
|
||||
<arg name="mode" type="new_id" interface="zwlr_output_mode_v1"/>
|
||||
</event>
|
||||
|
||||
<event name="enabled">
|
||||
<description summary="head is enabled or disabled">
|
||||
This event describes whether the head is enabled. A disabled head is not
|
||||
mapped to a region of the global compositor space.
|
||||
|
||||
When a head is disabled, some properties (current_mode, position,
|
||||
transform and scale) are irrelevant.
|
||||
</description>
|
||||
<arg name="enabled" type="int" summary="zero if disabled, non-zero if enabled"/>
|
||||
</event>
|
||||
|
||||
<event name="current_mode">
|
||||
<description summary="current mode">
|
||||
This event describes the mode currently in use for this head. It is only
|
||||
sent if the output is enabled.
|
||||
</description>
|
||||
<arg name="mode" type="object" interface="zwlr_output_mode_v1"/>
|
||||
</event>
|
||||
|
||||
<event name="position">
|
||||
<description summary="current position">
|
||||
This events describes the position of the head in the global compositor
|
||||
space. It is only sent if the output is enabled.
|
||||
</description>
|
||||
<arg name="x" type="int"
|
||||
summary="x position within the global compositor space"/>
|
||||
<arg name="y" type="int"
|
||||
summary="y position within the global compositor space"/>
|
||||
</event>
|
||||
|
||||
<event name="transform">
|
||||
<description summary="current transformation">
|
||||
This event describes the transformation currently applied to the head.
|
||||
It is only sent if the output is enabled.
|
||||
</description>
|
||||
<arg name="transform" type="int" enum="wl_output.transform"/>
|
||||
</event>
|
||||
|
||||
<event name="scale">
|
||||
<description summary="current scale">
|
||||
This events describes the scale of the head in the global compositor
|
||||
space. It is only sent if the output is enabled.
|
||||
</description>
|
||||
<arg name="scale" type="fixed"/>
|
||||
</event>
|
||||
|
||||
<event name="finished">
|
||||
<description summary="the head has disappeared">
|
||||
This event indicates that the head is no longer available. The head
|
||||
object becomes inert. Clients should send a destroy request and release
|
||||
any resources associated with it.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<!-- Version 2 additions -->
|
||||
|
||||
<event name="make" since="2">
|
||||
<description summary="head manufacturer">
|
||||
This event describes the manufacturer of the head.
|
||||
|
||||
This must report the same make as the wl_output interface does in its
|
||||
geometry event.
|
||||
|
||||
Together with the model and serial_number events the purpose is to
|
||||
allow clients to recognize heads from previous sessions and for example
|
||||
load head-specific configurations back.
|
||||
|
||||
It is not guaranteed this event will be ever sent. A reason for that
|
||||
can be that the compositor does not have information about the make of
|
||||
the head or the definition of a make is not sensible in the current
|
||||
setup, for example in a virtual session. Clients can still try to
|
||||
identify the head by available information from other events but should
|
||||
be aware that there is an increased risk of false positives.
|
||||
|
||||
It is not recommended to display the make string in UI to users. For
|
||||
that the string provided by the description event should be preferred.
|
||||
</description>
|
||||
<arg name="make" type="string"/>
|
||||
</event>
|
||||
|
||||
<event name="model" since="2">
|
||||
<description summary="head model">
|
||||
This event describes the model of the head.
|
||||
|
||||
This must report the same model as the wl_output interface does in its
|
||||
geometry event.
|
||||
|
||||
Together with the make and serial_number events the purpose is to
|
||||
allow clients to recognize heads from previous sessions and for example
|
||||
load head-specific configurations back.
|
||||
|
||||
It is not guaranteed this event will be ever sent. A reason for that
|
||||
can be that the compositor does not have information about the model of
|
||||
the head or the definition of a model is not sensible in the current
|
||||
setup, for example in a virtual session. Clients can still try to
|
||||
identify the head by available information from other events but should
|
||||
be aware that there is an increased risk of false positives.
|
||||
|
||||
It is not recommended to display the model string in UI to users. For
|
||||
that the string provided by the description event should be preferred.
|
||||
</description>
|
||||
<arg name="model" type="string"/>
|
||||
</event>
|
||||
|
||||
<event name="serial_number" since="2">
|
||||
<description summary="head serial number">
|
||||
This event describes the serial number of the head.
|
||||
|
||||
Together with the make and model events the purpose is to allow clients
|
||||
to recognize heads from previous sessions and for example load head-
|
||||
specific configurations back.
|
||||
|
||||
It is not guaranteed this event will be ever sent. A reason for that
|
||||
can be that the compositor does not have information about the serial
|
||||
number of the head or the definition of a serial number is not sensible
|
||||
in the current setup. Clients can still try to identify the head by
|
||||
available information from other events but should be aware that there
|
||||
is an increased risk of false positives.
|
||||
|
||||
It is not recommended to display the serial_number string in UI to
|
||||
users. For that the string provided by the description event should be
|
||||
preferred.
|
||||
</description>
|
||||
<arg name="serial_number" type="string"/>
|
||||
</event>
|
||||
|
||||
<!-- Version 3 additions -->
|
||||
|
||||
<request name="release" type="destructor" since="3">
|
||||
<description summary="destroy the head object">
|
||||
This request indicates that the client will no longer use this head
|
||||
object.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<!-- Version 4 additions -->
|
||||
|
||||
<enum name="adaptive_sync_state" since="4">
|
||||
<entry name="disabled" value="0" summary="adaptive sync is disabled"/>
|
||||
<entry name="enabled" value="1" summary="adaptive sync is enabled"/>
|
||||
</enum>
|
||||
|
||||
<event name="adaptive_sync" since="4">
|
||||
<description summary="current adaptive sync state">
|
||||
This event describes whether adaptive sync is currently enabled for
|
||||
the head or not. Adaptive sync is also known as Variable Refresh
|
||||
Rate or VRR.
|
||||
</description>
|
||||
<arg name="state" type="uint" enum="adaptive_sync_state"/>
|
||||
</event>
|
||||
</interface>
|
||||
|
||||
<interface name="zwlr_output_mode_v1" version="3">
|
||||
<description summary="output mode">
|
||||
This object describes an output mode.
|
||||
|
||||
Some heads don't support output modes, in which case modes won't be
|
||||
advertised.
|
||||
|
||||
Properties sent via this interface are applied atomically via the
|
||||
wlr_output_manager.done event. No guarantees are made regarding the order
|
||||
in which properties are sent.
|
||||
</description>
|
||||
|
||||
<event name="size">
|
||||
<description summary="mode size">
|
||||
This event describes the mode size. The size is given in physical
|
||||
hardware units of the output device. This is not necessarily the same as
|
||||
the output size in the global compositor space. For instance, the output
|
||||
may be scaled or transformed.
|
||||
</description>
|
||||
<arg name="width" type="int" summary="width of the mode in hardware units"/>
|
||||
<arg name="height" type="int" summary="height of the mode in hardware units"/>
|
||||
</event>
|
||||
|
||||
<event name="refresh">
|
||||
<description summary="mode refresh rate">
|
||||
This event describes the mode's fixed vertical refresh rate. It is only
|
||||
sent if the mode has a fixed refresh rate.
|
||||
</description>
|
||||
<arg name="refresh" type="int" summary="vertical refresh rate in mHz"/>
|
||||
</event>
|
||||
|
||||
<event name="preferred">
|
||||
<description summary="mode is preferred">
|
||||
This event advertises this mode as preferred.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<event name="finished">
|
||||
<description summary="the mode has disappeared">
|
||||
This event indicates that the mode is no longer available. The mode
|
||||
object becomes inert. Clients should send a destroy request and release
|
||||
any resources associated with it.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<!-- Version 3 additions -->
|
||||
|
||||
<request name="release" type="destructor" since="3">
|
||||
<description summary="destroy the mode object">
|
||||
This request indicates that the client will no longer use this mode
|
||||
object.
|
||||
</description>
|
||||
</request>
|
||||
</interface>
|
||||
|
||||
<interface name="zwlr_output_configuration_v1" version="4">
|
||||
<description summary="output configuration">
|
||||
This object is used by the client to describe a full output configuration.
|
||||
|
||||
First, the client needs to setup the output configuration. Each head can
|
||||
be either enabled (and configured) or disabled. It is a protocol error to
|
||||
send two enable_head or disable_head requests with the same head. It is a
|
||||
protocol error to omit a head in a configuration.
|
||||
|
||||
Then, the client can apply or test the configuration. The compositor will
|
||||
then reply with a succeeded, failed or cancelled event. Finally the client
|
||||
should destroy the configuration object.
|
||||
</description>
|
||||
|
||||
<enum name="error">
|
||||
<entry name="already_configured_head" value="1"
|
||||
summary="head has been configured twice"/>
|
||||
<entry name="unconfigured_head" value="2"
|
||||
summary="head has not been configured"/>
|
||||
<entry name="already_used" value="3"
|
||||
summary="request sent after configuration has been applied or tested"/>
|
||||
</enum>
|
||||
|
||||
<request name="enable_head">
|
||||
<description summary="enable and configure a head">
|
||||
Enable a head. This request creates a head configuration object that can
|
||||
be used to change the head's properties.
|
||||
</description>
|
||||
<arg name="id" type="new_id" interface="zwlr_output_configuration_head_v1"
|
||||
summary="a new object to configure the head"/>
|
||||
<arg name="head" type="object" interface="zwlr_output_head_v1"
|
||||
summary="the head to be enabled"/>
|
||||
</request>
|
||||
|
||||
<request name="disable_head">
|
||||
<description summary="disable a head">
|
||||
Disable a head.
|
||||
</description>
|
||||
<arg name="head" type="object" interface="zwlr_output_head_v1"
|
||||
summary="the head to be disabled"/>
|
||||
</request>
|
||||
|
||||
<request name="apply">
|
||||
<description summary="apply the configuration">
|
||||
Apply the new output configuration.
|
||||
|
||||
In case the configuration is successfully applied, there is no guarantee
|
||||
that the new output state matches completely the requested
|
||||
configuration. For instance, a compositor might round the scale if it
|
||||
doesn't support fractional scaling.
|
||||
|
||||
After this request has been sent, the compositor must respond with an
|
||||
succeeded, failed or cancelled event. Sending a request that isn't the
|
||||
destructor is a protocol error.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<request name="test">
|
||||
<description summary="test the configuration">
|
||||
Test the new output configuration. The configuration won't be applied,
|
||||
but will only be validated.
|
||||
|
||||
Even if the compositor succeeds to test a configuration, applying it may
|
||||
fail.
|
||||
|
||||
After this request has been sent, the compositor must respond with an
|
||||
succeeded, failed or cancelled event. Sending a request that isn't the
|
||||
destructor is a protocol error.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<event name="succeeded">
|
||||
<description summary="configuration changes succeeded">
|
||||
Sent after the compositor has successfully applied the changes or
|
||||
tested them.
|
||||
|
||||
Upon receiving this event, the client should destroy this object.
|
||||
|
||||
If the current configuration has changed, events to describe the changes
|
||||
will be sent followed by a wlr_output_manager.done event.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<event name="failed">
|
||||
<description summary="configuration changes failed">
|
||||
Sent if the compositor rejects the changes or failed to apply them. The
|
||||
compositor should revert any changes made by the apply request that
|
||||
triggered this event.
|
||||
|
||||
Upon receiving this event, the client should destroy this object.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<event name="cancelled">
|
||||
<description summary="configuration has been cancelled">
|
||||
Sent if the compositor cancels the configuration because the state of an
|
||||
output changed and the client has outdated information (e.g. after an
|
||||
output has been hotplugged).
|
||||
|
||||
The client can create a new configuration with a newer serial and try
|
||||
again.
|
||||
|
||||
Upon receiving this event, the client should destroy this object.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="destroy the output configuration">
|
||||
Using this request a client can tell the compositor that it is not going
|
||||
to use the configuration object anymore. Any changes to the outputs
|
||||
that have not been applied will be discarded.
|
||||
|
||||
This request also destroys wlr_output_configuration_head objects created
|
||||
via this object.
|
||||
</description>
|
||||
</request>
|
||||
</interface>
|
||||
|
||||
<interface name="zwlr_output_configuration_head_v1" version="4">
|
||||
<description summary="head configuration">
|
||||
This object is used by the client to update a single head's configuration.
|
||||
|
||||
It is a protocol error to set the same property twice.
|
||||
</description>
|
||||
|
||||
<enum name="error">
|
||||
<entry name="already_set" value="1" summary="property has already been set"/>
|
||||
<entry name="invalid_mode" value="2" summary="mode doesn't belong to head"/>
|
||||
<entry name="invalid_custom_mode" value="3" summary="mode is invalid"/>
|
||||
<entry name="invalid_transform" value="4" summary="transform value outside enum"/>
|
||||
<entry name="invalid_scale" value="5" summary="scale negative or zero"/>
|
||||
<entry name="invalid_adaptive_sync_state" value="6" since="4"
|
||||
summary="invalid enum value used in the set_adaptive_sync request"/>
|
||||
</enum>
|
||||
|
||||
<request name="set_mode">
|
||||
<description summary="set the mode">
|
||||
This request sets the head's mode.
|
||||
</description>
|
||||
<arg name="mode" type="object" interface="zwlr_output_mode_v1"/>
|
||||
</request>
|
||||
|
||||
<request name="set_custom_mode">
|
||||
<description summary="set a custom mode">
|
||||
This request assigns a custom mode to the head. The size is given in
|
||||
physical hardware units of the output device. If set to zero, the
|
||||
refresh rate is unspecified.
|
||||
|
||||
It is a protocol error to set both a mode and a custom mode.
|
||||
</description>
|
||||
<arg name="width" type="int" summary="width of the mode in hardware units"/>
|
||||
<arg name="height" type="int" summary="height of the mode in hardware units"/>
|
||||
<arg name="refresh" type="int" summary="vertical refresh rate in mHz or zero"/>
|
||||
</request>
|
||||
|
||||
<request name="set_position">
|
||||
<description summary="set the position">
|
||||
This request sets the head's position in the global compositor space.
|
||||
</description>
|
||||
<arg name="x" type="int" summary="x position in the global compositor space"/>
|
||||
<arg name="y" type="int" summary="y position in the global compositor space"/>
|
||||
</request>
|
||||
|
||||
<request name="set_transform">
|
||||
<description summary="set the transform">
|
||||
This request sets the head's transform.
|
||||
</description>
|
||||
<arg name="transform" type="int" enum="wl_output.transform"/>
|
||||
</request>
|
||||
|
||||
<request name="set_scale">
|
||||
<description summary="set the scale">
|
||||
This request sets the head's scale.
|
||||
</description>
|
||||
<arg name="scale" type="fixed"/>
|
||||
</request>
|
||||
|
||||
<!-- Version 4 additions -->
|
||||
|
||||
<request name="set_adaptive_sync" since="4">
|
||||
<description summary="enable/disable adaptive sync">
|
||||
This request enables/disables adaptive sync. Adaptive sync is also
|
||||
known as Variable Refresh Rate or VRR.
|
||||
</description>
|
||||
<arg name="state" type="uint" enum="zwlr_output_head_v1.adaptive_sync_state"/>
|
||||
</request>
|
||||
</interface>
|
||||
</protocol>
|
102
src/buffer.c
102
src/buffer.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 2024 Andri Yngvason
|
||||
* Copyright (c) 2020 - 2021 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
|
||||
|
@ -31,20 +31,10 @@
|
|||
#include "buffer.h"
|
||||
#include "pixels.h"
|
||||
#include "config.h"
|
||||
#include "util.h"
|
||||
|
||||
#ifdef ENABLE_SCREENCOPY_DMABUF
|
||||
#include <gbm.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
// #ifdef HAVE_LINUX_DMA_HEAP
|
||||
#include <linux/dma-buf.h>
|
||||
#include <linux/dma-heap.h>
|
||||
|
||||
#define LINUX_CMA_PATH "/dev/dma_heap/linux,cma"
|
||||
//#endif // HAVE_LINUX_DMA_HEAP
|
||||
#endif // ENABLE_SCREENCOPY_DMABUF
|
||||
#endif
|
||||
|
||||
extern struct wl_shm* wl_shm;
|
||||
extern struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf;
|
||||
|
@ -128,81 +118,6 @@ failure:
|
|||
}
|
||||
|
||||
#ifdef ENABLE_SCREENCOPY_DMABUF
|
||||
#ifdef HAVE_LINUX_DMA_HEAP
|
||||
static bool have_linux_cma(void)
|
||||
{
|
||||
return access(LINUX_CMA_PATH, R_OK | W_OK) == 0;
|
||||
}
|
||||
|
||||
static int linux_cma_alloc(size_t size)
|
||||
{
|
||||
int fd = open(LINUX_CMA_PATH, O_RDWR | O_CLOEXEC, 0);
|
||||
if (fd < 0) {
|
||||
nvnc_log(NVNC_LOG_ERROR, "Failed to open CMA device: %m");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct dma_heap_allocation_data data = {
|
||||
.len = size,
|
||||
.fd_flags = O_CLOEXEC | O_RDWR,
|
||||
};
|
||||
|
||||
int r = ioctl(fd, DMA_HEAP_IOCTL_ALLOC, &data);
|
||||
if (r < 0) {
|
||||
nvnc_log(NVNC_LOG_ERROR, "Failed to allocate CMA buffer: %m");
|
||||
return -1;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
return data.fd;
|
||||
}
|
||||
|
||||
// Some devices (mostly ARM SBCs) need CMA for hardware encoders.
|
||||
static struct gbm_bo* create_cma_gbm_bo(int width, int height, uint32_t fourcc)
|
||||
{
|
||||
assert(gbm_device);
|
||||
|
||||
int bpp = pixel_size_from_fourcc(fourcc);
|
||||
if (!bpp) {
|
||||
nvnc_log(NVNC_LOG_PANIC, "Unsupported pixel format: %" PRIu32,
|
||||
fourcc);
|
||||
}
|
||||
|
||||
/* TODO: Get alignment through feedback mechanism.
|
||||
* Buffer sizes are aligned on both axes by 16 and we'll do the same
|
||||
* in the encoder, but this requirement should come from the encoder.
|
||||
*/
|
||||
int stride = bpp * ALIGN_UP(width, 16);
|
||||
|
||||
int fd = linux_cma_alloc(stride * ALIGN_UP(height, 16));
|
||||
if (fd < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct gbm_import_fd_modifier_data d = {
|
||||
.format = fourcc,
|
||||
.width = width,
|
||||
.height = height,
|
||||
// v4l2m2m doesn't support modifiers, so we use linear
|
||||
.modifier = DRM_FORMAT_MOD_LINEAR,
|
||||
.num_fds = 1,
|
||||
.fds[0] = fd,
|
||||
.offsets[0] = 0,
|
||||
.strides[0] = stride,
|
||||
};
|
||||
|
||||
struct gbm_bo* bo = gbm_bo_import(gbm_device, GBM_BO_IMPORT_FD_MODIFIER,
|
||||
&d, 0);
|
||||
if (!bo) {
|
||||
nvnc_log(NVNC_LOG_DEBUG, "Failed to import dmabuf: %m");
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return bo;
|
||||
}
|
||||
#endif // HAVE_LINUX_DMA_HEAP
|
||||
|
||||
static struct wv_buffer* wv_buffer_create_dmabuf(int width, int height,
|
||||
uint32_t fourcc)
|
||||
{
|
||||
|
@ -218,17 +133,8 @@ static struct wv_buffer* wv_buffer_create_dmabuf(int width, int height,
|
|||
self->height = height;
|
||||
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() ?
|
||||
create_cma_gbm_bo(width, height, fourcc) :
|
||||
gbm_bo_create(gbm_device, width, height, fourcc,
|
||||
GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT);
|
||||
//#endif
|
||||
// self->bo = gbm_bo_create(gbm_device, width, height, fourcc,
|
||||
// GBM_BO_USE_RENDERING);
|
||||
|
||||
self->bo = gbm_bo_create(gbm_device, width, height, fourcc,
|
||||
GBM_BO_USE_RENDERING);
|
||||
if (!self->bo)
|
||||
goto bo_failure;
|
||||
|
||||
|
|
|
@ -354,17 +354,17 @@ static void pretty_client_list(json_t* data)
|
|||
json_t* value;
|
||||
json_array_foreach(data, i, value) {
|
||||
char* id = NULL;
|
||||
char* address = NULL;
|
||||
char* hostname = NULL;
|
||||
char* username = NULL;
|
||||
|
||||
json_unpack(value, "{s:s, s?s, s?s}", "id", &id, "address",
|
||||
&address, "username", &username);
|
||||
json_unpack(value, "{s:s, s?s, s?s}", "id", &id, "hostname",
|
||||
&hostname, "username", &username);
|
||||
printf(" %s: ", id);
|
||||
|
||||
if (username)
|
||||
printf("%s@", username);
|
||||
|
||||
printf("%s\n", address ? address : "<unknown>");
|
||||
printf("%s\n", hostname ? hostname : "<unknown>");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -98,8 +98,8 @@ struct cmd_info ctl_command_list[] = {
|
|||
{ "connection_count", \
|
||||
"The total number of connected VNC clients " including " this one.", \
|
||||
"<integer>" }, \
|
||||
{ "address", \
|
||||
"The IP address of this client (may be null)", \
|
||||
{ "hostname", \
|
||||
"The hostname or IP address of this client (may be null)", \
|
||||
"<name|ip>" }, \
|
||||
{ "username", \
|
||||
"The username used to authentice this client (may be null).", \
|
||||
|
@ -128,20 +128,6 @@ struct cmd_info ctl_event_list[] = {
|
|||
"Sent after detaching from compositor",
|
||||
{}
|
||||
},
|
||||
[EVT_OUTPUT_ADDED] = {"output-added",
|
||||
"Sent when an output is added by the compositor",
|
||||
{
|
||||
{ "name", "Output name", "<string>" },
|
||||
{}
|
||||
}
|
||||
},
|
||||
[EVT_OUTPUT_REMOVED] = {"output-removed",
|
||||
"Sent when an output is removed by the compositor",
|
||||
{
|
||||
{ "name", "Output name", "<string>" },
|
||||
{}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
enum cmd_type ctl_command_parse_name(const char* name)
|
||||
|
|
|
@ -27,8 +27,6 @@
|
|||
#include <neatvnc.h>
|
||||
#include <aml.h>
|
||||
#include <jansson.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include "output.h"
|
||||
#include "ctl-commands.h"
|
||||
|
@ -364,25 +362,6 @@ static struct ctl_server_client* ctl_server_client_next(struct ctl* self,
|
|||
return self->actions.client_next(self, prev);
|
||||
}
|
||||
|
||||
static int sockaddr_to_string(char* dst, size_t sz, const struct sockaddr* addr)
|
||||
{
|
||||
struct sockaddr_in* sa_in = (struct sockaddr_in*)addr;
|
||||
struct sockaddr_in6* sa_in6 = (struct sockaddr_in6*)addr;
|
||||
|
||||
switch (addr->sa_family) {
|
||||
case AF_INET:
|
||||
inet_ntop(addr->sa_family, &sa_in->sin_addr, dst, sz);
|
||||
return 0;
|
||||
case AF_INET6:
|
||||
inet_ntop(addr->sa_family, &sa_in6->sin6_addr, dst, sz);
|
||||
return 0;
|
||||
}
|
||||
nvnc_log(NVNC_LOG_DEBUG,
|
||||
"Don't know how to convert sa_family %d to string",
|
||||
addr->sa_family);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void ctl_server_client_get_info(struct ctl* self,
|
||||
const struct ctl_server_client* client,
|
||||
struct ctl_server_client_info* info)
|
||||
|
@ -405,12 +384,9 @@ static struct cmd_response* generate_vnc_client_list(struct ctl* self)
|
|||
snprintf(id_str, sizeof(id_str), "%d", info.id);
|
||||
json_t* packed = json_pack("{s:s}", "id", id_str);
|
||||
|
||||
char address_string[256];
|
||||
if (sockaddr_to_string(address_string, sizeof(address_string),
|
||||
&info.address) == 0) {
|
||||
json_object_set_new(packed, "address",
|
||||
json_string(address_string));
|
||||
}
|
||||
if (info.hostname)
|
||||
json_object_set_new(packed, "hostname",
|
||||
json_string(info.hostname));
|
||||
|
||||
if (info.username)
|
||||
json_object_set_new(packed, "username",
|
||||
|
@ -921,13 +897,9 @@ json_t* pack_connection_event_params(
|
|||
char id_str[64];
|
||||
snprintf(id_str, sizeof(id_str), "%d", info->id);
|
||||
|
||||
char address_string[256];
|
||||
bool have_addr = sockaddr_to_string(address_string,
|
||||
sizeof(address_string), &info->address) == 0;
|
||||
|
||||
return json_pack("{s:s, s:s?, s:s?, s:s?, s:i}",
|
||||
"id", id_str,
|
||||
"address", have_addr ? address_string : NULL,
|
||||
"hostname", info->hostname,
|
||||
"username", info->username,
|
||||
"seat", info->seat,
|
||||
"connection_count", new_connection_count);
|
||||
|
@ -1006,15 +978,3 @@ void ctl_server_event_detached(struct ctl* self)
|
|||
{
|
||||
ctl_server_enqueue_event(self, EVT_DETACHED, json_object());
|
||||
}
|
||||
|
||||
void ctl_server_event_output_added(struct ctl* self, const char* name)
|
||||
{
|
||||
ctl_server_enqueue_event(self, EVT_OUTPUT_ADDED,
|
||||
json_pack("{s:s}", "name", name));
|
||||
}
|
||||
|
||||
void ctl_server_event_output_removed(struct ctl* self, const char* name)
|
||||
{
|
||||
ctl_server_enqueue_event(self, EVT_OUTPUT_REMOVED,
|
||||
json_pack("{s:s}", "name", name));
|
||||
}
|
||||
|
|
|
@ -436,18 +436,3 @@ void keyboard_feed_code(struct keyboard* self, xkb_keycode_t code,
|
|||
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;
|
||||
}
|
||||
|
|
287
src/main.c
287
src/main.c
|
@ -39,14 +39,12 @@
|
|||
#include "virtual-keyboard-unstable-v1.h"
|
||||
#include "xdg-output-unstable-v1.h"
|
||||
#include "wlr-output-power-management-unstable-v1.h"
|
||||
#include "wlr-output-management-unstable-v1.h"
|
||||
#include "linux-dmabuf-unstable-v1.h"
|
||||
#include "ext-transient-seat-v1.h"
|
||||
#include "screencopy.h"
|
||||
#include "data-control.h"
|
||||
#include "strlcpy.h"
|
||||
#include "output.h"
|
||||
#include "output-management.h"
|
||||
#include "pointer.h"
|
||||
#include "keyboard.h"
|
||||
#include "seat.h"
|
||||
|
@ -57,10 +55,6 @@
|
|||
#include "util.h"
|
||||
#include "option-parser.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/extensions/Xfixes.h>
|
||||
|
||||
#ifdef ENABLE_PAM
|
||||
#include "pam_auth.h"
|
||||
#endif
|
||||
|
@ -78,8 +72,6 @@
|
|||
|
||||
#define MAYBE_UNUSED __attribute__((unused))
|
||||
|
||||
struct wayvnc_client;
|
||||
|
||||
enum socket_type {
|
||||
SOCKET_TYPE_TCP = 0,
|
||||
SOCKET_TYPE_UNIX,
|
||||
|
@ -91,7 +83,6 @@ struct wayvnc {
|
|||
|
||||
struct wl_display* display;
|
||||
struct wl_registry* registry;
|
||||
struct aml_handler* wl_handler;
|
||||
struct wl_list outputs;
|
||||
struct wl_list seats;
|
||||
struct cfg cfg;
|
||||
|
@ -130,8 +121,6 @@ struct wayvnc {
|
|||
bool is_initializing;
|
||||
|
||||
bool start_detached;
|
||||
|
||||
struct wayvnc_client* master_layout_client;
|
||||
};
|
||||
|
||||
struct wayvnc_client {
|
||||
|
@ -244,10 +233,7 @@ static void registry_add(void* data, struct wl_registry* registry,
|
|||
if (!self->is_initializing) {
|
||||
wl_display_dispatch(self->display);
|
||||
wl_display_roundtrip(self->display);
|
||||
|
||||
ctl_server_event_output_added(self->ctl, output->name);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -269,15 +255,6 @@ static void registry_add(void* data, struct wl_registry* registry,
|
|||
return;
|
||||
}
|
||||
|
||||
if (strcmp(interface, zwlr_output_manager_v1_interface.name) == 0) {
|
||||
nvnc_trace("Registering new wlr_output_manager");
|
||||
struct zwlr_output_manager_v1* wlr_output_manager =
|
||||
wl_registry_bind(registry, id,
|
||||
&zwlr_output_manager_v1_interface, 1);
|
||||
wlr_output_manager_setup(wlr_output_manager);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(interface, zwlr_screencopy_manager_v1_interface.name) == 0) {
|
||||
self->screencopy.manager =
|
||||
wl_registry_bind(registry, id,
|
||||
|
@ -329,8 +306,6 @@ static void registry_remove(void* data, struct wl_registry* registry,
|
|||
} else
|
||||
nvnc_log(NVNC_LOG_INFO, "Output %s went away", out->name);
|
||||
|
||||
ctl_server_event_output_removed(self->ctl, out->name);
|
||||
|
||||
wl_list_remove(&out->link);
|
||||
output_destroy(out);
|
||||
|
||||
|
@ -402,10 +377,6 @@ static void wayland_detach(struct wayvnc* self)
|
|||
if (!self->display)
|
||||
return;
|
||||
|
||||
aml_stop(aml_get_default(), self->wl_handler);
|
||||
aml_unref(self->wl_handler);
|
||||
self->wl_handler = NULL;
|
||||
|
||||
// Screen blanking is required to release wl_shm of linux_dmabuf.
|
||||
if (self->nvnc)
|
||||
blank_screen(self);
|
||||
|
@ -445,8 +416,6 @@ static void wayland_detach(struct wayvnc* self)
|
|||
zwlr_output_power_manager_v1_destroy(wlr_output_power_manager);
|
||||
wlr_output_power_manager = NULL;
|
||||
|
||||
wlr_output_manager_destroy();
|
||||
|
||||
wl_shm_destroy(wl_shm);
|
||||
wl_shm = NULL;
|
||||
|
||||
|
@ -478,8 +447,7 @@ static void wayland_detach(struct wayvnc* self)
|
|||
wl_display_disconnect(self->display);
|
||||
self->display = NULL;
|
||||
|
||||
if (self->ctl)
|
||||
ctl_server_event_detached(self->ctl);
|
||||
ctl_server_event_detached(self->ctl);
|
||||
}
|
||||
|
||||
void wayvnc_destroy(struct wayvnc* self)
|
||||
|
@ -576,24 +544,21 @@ static int init_wayland(struct wayvnc* self, const char* display)
|
|||
self->screencopy.on_done = on_capture_done;
|
||||
self->screencopy.userdata = self;
|
||||
|
||||
self->wl_handler = aml_handler_new(wl_display_get_fd(self->display),
|
||||
struct aml_handler* wl_handler;
|
||||
wl_handler = aml_handler_new(wl_display_get_fd(self->display),
|
||||
on_wayland_event, self, NULL);
|
||||
if (!self->wl_handler)
|
||||
if (!wl_handler)
|
||||
goto failure;
|
||||
|
||||
int rc = aml_start(aml_get_default(), self->wl_handler);
|
||||
int rc = aml_start(aml_get_default(), wl_handler);
|
||||
aml_unref(wl_handler);
|
||||
if (rc < 0)
|
||||
goto handler_failure;
|
||||
goto failure;
|
||||
|
||||
return 0;
|
||||
|
||||
failure:
|
||||
wl_display_disconnect(self->display);
|
||||
self->display = NULL;
|
||||
handler_failure:
|
||||
if (self->wl_handler)
|
||||
aml_unref(self->wl_handler);
|
||||
self->wl_handler = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -645,24 +610,17 @@ static struct ctl_server_client *client_next(struct ctl* ctl,
|
|||
(struct ctl_server_client*)nvnc_client_first(self->nvnc);
|
||||
}
|
||||
|
||||
static void compose_client_info(const struct wayvnc_client* client,
|
||||
struct ctl_server_client_info* info)
|
||||
{
|
||||
info->id = client->id;
|
||||
socklen_t addrlen = sizeof(info->address_storage);
|
||||
nvnc_client_get_address(client->nvnc_client,
|
||||
(struct sockaddr*)&info->address_storage, &addrlen);
|
||||
info->username = nvnc_client_get_auth_username(client->nvnc_client);
|
||||
info->seat = client->seat ? client->seat->name : NULL;
|
||||
}
|
||||
|
||||
static void client_info(const struct ctl_server_client* client_handle,
|
||||
struct ctl_server_client_info* info)
|
||||
{
|
||||
const struct nvnc_client *vnc_client =
|
||||
(const struct nvnc_client*)client_handle;
|
||||
const struct wayvnc_client *client = nvnc_get_userdata(vnc_client);
|
||||
compose_client_info(client, info);
|
||||
|
||||
info->id = client->id;
|
||||
info->hostname = nvnc_client_get_hostname(vnc_client);
|
||||
info->username = nvnc_client_get_auth_username(vnc_client);
|
||||
info->seat = client->seat ? client->seat->name : NULL;
|
||||
}
|
||||
|
||||
static int get_output_list(struct ctl* ctl,
|
||||
|
@ -753,125 +711,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);
|
||||
|
||||
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,
|
||||
|
@ -883,9 +722,6 @@ static void on_key_event(struct nvnc_client* client, uint32_t symbol,
|
|||
}
|
||||
|
||||
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,
|
||||
|
@ -897,9 +733,6 @@ static void on_key_code_event(struct nvnc_client* client, uint32_t code,
|
|||
}
|
||||
|
||||
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,
|
||||
|
@ -912,32 +745,6 @@ static void on_client_cut_text(struct nvnc_client* nvnc_client,
|
|||
}
|
||||
}
|
||||
|
||||
static bool on_client_resize(struct nvnc_client* nvnc_client,
|
||||
const struct nvnc_desktop_layout* layout)
|
||||
{
|
||||
struct wayvnc_client* client = nvnc_get_userdata(nvnc_client);
|
||||
struct wayvnc* self = client->server;
|
||||
|
||||
uint16_t width = nvnc_desktop_layout_get_width(layout);
|
||||
uint16_t height = nvnc_desktop_layout_get_height(layout);
|
||||
struct output* output = client->server->selected_output;
|
||||
|
||||
if (output == NULL)
|
||||
return false;
|
||||
|
||||
if (self->master_layout_client && self->master_layout_client != client)
|
||||
return false;
|
||||
|
||||
self->master_layout_client = client;
|
||||
|
||||
nvnc_log(NVNC_LOG_DEBUG,
|
||||
"Client resolution changed: %ux%u, capturing output %s which is headless: %s",
|
||||
width, height, output->name,
|
||||
output->is_headless ? "yes" : "no");
|
||||
|
||||
return wlr_output_manager_resize_output(output, width, height);
|
||||
}
|
||||
|
||||
bool on_auth(const char* username, const char* password, void* ud)
|
||||
{
|
||||
struct wayvnc* self = ud;
|
||||
|
@ -1042,8 +849,6 @@ static int init_nvnc(struct wayvnc* self, const char* addr, uint16_t port,
|
|||
|
||||
nvnc_set_name(self->nvnc, "WayVNC");
|
||||
|
||||
nvnc_set_desktop_layout_fn(self->nvnc, on_client_resize);
|
||||
|
||||
enum nvnc_auth_flags auth_flags = 0;
|
||||
if (self->cfg.enable_auth) {
|
||||
auth_flags |= NVNC_AUTH_REQUIRE_AUTH;
|
||||
|
@ -1406,9 +1211,6 @@ static void client_destroy(void* obj)
|
|||
struct nvnc* nvnc = nvnc_client_get_server(self->nvnc_client);
|
||||
struct wayvnc* wayvnc = nvnc_get_userdata(nvnc);
|
||||
|
||||
if (self == wayvnc->master_layout_client)
|
||||
wayvnc->master_layout_client = NULL;
|
||||
|
||||
if (self->transient_seat)
|
||||
ext_transient_seat_v1_destroy(self->transient_seat);
|
||||
|
||||
|
@ -1419,13 +1221,14 @@ static void client_destroy(void* obj)
|
|||
nvnc_log(NVNC_LOG_DEBUG, "Client disconnected, new client count: %d",
|
||||
wayvnc->nr_clients);
|
||||
|
||||
if (wayvnc->ctl) {
|
||||
struct ctl_server_client_info info = {};
|
||||
compose_client_info(self, &info);
|
||||
struct ctl_server_client_info info = {
|
||||
.id = self->id,
|
||||
.hostname = nvnc_client_get_hostname(self->nvnc_client),
|
||||
.username = nvnc_client_get_auth_username(self->nvnc_client),
|
||||
.seat = self->seat ? self->seat->name : NULL,
|
||||
};
|
||||
|
||||
ctl_server_event_disconnected(wayvnc->ctl, &info,
|
||||
wayvnc->nr_clients);
|
||||
}
|
||||
ctl_server_event_disconnected(wayvnc->ctl, &info, wayvnc->nr_clients);
|
||||
|
||||
if (wayvnc->nr_clients == 0 && wayvnc->display) {
|
||||
nvnc_log(NVNC_LOG_INFO, "Stopping screen capture");
|
||||
|
@ -1471,8 +1274,12 @@ static void on_nvnc_client_new(struct nvnc_client* client)
|
|||
nvnc_log(NVNC_LOG_DEBUG, "Client connected, new client count: %d",
|
||||
self->nr_clients);
|
||||
|
||||
struct ctl_server_client_info info = {};
|
||||
compose_client_info(wayvnc_client, &info);
|
||||
struct ctl_server_client_info info = {
|
||||
.id = wayvnc_client->id,
|
||||
.hostname = nvnc_client_get_hostname(client),
|
||||
.username = nvnc_client_get_auth_username(client),
|
||||
.seat = wayvnc_client->seat ? wayvnc_client->seat->name : NULL,
|
||||
};
|
||||
|
||||
ctl_server_event_connected(self->ctl, &info, self->nr_clients);
|
||||
}
|
||||
|
@ -1693,42 +1500,16 @@ void switch_to_prev_output(struct wayvnc* self)
|
|||
switch_to_output(self, prev);
|
||||
}
|
||||
|
||||
static char intercepted_error[256];
|
||||
|
||||
static void intercept_cmd_error(const struct nvnc_log_data* meta,
|
||||
const char* message)
|
||||
{
|
||||
if (meta->level != NVNC_LOG_ERROR) {
|
||||
nvnc_default_logger(meta, message);
|
||||
return;
|
||||
}
|
||||
|
||||
struct nvnc_log_data meta_override = *meta;
|
||||
meta_override.level = NVNC_LOG_DEBUG;
|
||||
nvnc_default_logger(&meta_override, message);
|
||||
|
||||
size_t len = strlen(intercepted_error);
|
||||
if (len != 0 && len < sizeof(intercepted_error) - 2)
|
||||
intercepted_error[len++] = '\n';
|
||||
|
||||
strlcpy(intercepted_error + len, message,
|
||||
sizeof(intercepted_error) - len);
|
||||
}
|
||||
|
||||
static struct cmd_response* on_attach(struct ctl* ctl, const char* display)
|
||||
{
|
||||
struct wayvnc* self = ctl_server_userdata(ctl);
|
||||
assert(self);
|
||||
|
||||
memset(intercepted_error, 0, sizeof(intercepted_error));
|
||||
nvnc_set_log_fn_thread_local(intercept_cmd_error);
|
||||
|
||||
// TODO: Add optional output argument
|
||||
bool ok = wayland_attach(self, display, NULL);
|
||||
if (!wayland_attach(self, display, NULL))
|
||||
return cmd_failed("Failed to attach to %s", display);
|
||||
|
||||
nvnc_set_log_fn_thread_local(NULL);
|
||||
|
||||
return ok ? cmd_ok() : cmd_failed("%s", intercepted_error);
|
||||
return cmd_ok();
|
||||
}
|
||||
|
||||
static bool wayland_attach(struct wayvnc* self, const char* display,
|
||||
|
@ -2052,6 +1833,9 @@ int main(int argc, char* argv[])
|
|||
else if (use_websocket)
|
||||
socket_type = SOCKET_TYPE_WEBSOCKET;
|
||||
|
||||
if (init_nvnc(&self, address, port, socket_type) < 0)
|
||||
goto nvnc_failure;
|
||||
|
||||
if (!start_detached) {
|
||||
if (self.screencopy.manager)
|
||||
screencopy_init(&self.screencopy);
|
||||
|
@ -2084,9 +1868,6 @@ int main(int argc, char* argv[])
|
|||
if (!self.ctl)
|
||||
goto ctl_server_failure;
|
||||
|
||||
if (init_nvnc(&self, address, port, socket_type) < 0)
|
||||
goto nvnc_failure;
|
||||
|
||||
if (self.display)
|
||||
wl_display_dispatch_pending(self.display);
|
||||
|
||||
|
@ -2104,7 +1885,6 @@ int main(int argc, char* argv[])
|
|||
screencopy_stop(&self.screencopy);
|
||||
|
||||
ctl_server_destroy(self.ctl);
|
||||
self.ctl = NULL;
|
||||
|
||||
nvnc_display_unref(self.nvnc_display);
|
||||
nvnc_close(self.nvnc);
|
||||
|
@ -2120,10 +1900,11 @@ int main(int argc, char* argv[])
|
|||
|
||||
return 0;
|
||||
|
||||
nvnc_failure:
|
||||
ctl_server_destroy(self.ctl);
|
||||
ctl_server_failure:
|
||||
capture_failure:
|
||||
nvnc_display_unref(self.nvnc_display);
|
||||
nvnc_close(self.nvnc);
|
||||
nvnc_failure:
|
||||
wayland_failure:
|
||||
aml_unref(aml);
|
||||
failure:
|
||||
|
|
|
@ -1,283 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023 The wayvnc authors
|
||||
*
|
||||
* 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 <neatvnc.h>
|
||||
#include <string.h>
|
||||
#include <wayland-client.h>
|
||||
|
||||
#include "output.h"
|
||||
#include "output-management.h"
|
||||
|
||||
#include "wlr-output-management-unstable-v1.h"
|
||||
|
||||
struct output_manager_head {
|
||||
struct zwlr_output_head_v1* head;
|
||||
struct wl_list link;
|
||||
char* name;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
static struct wl_list heads;
|
||||
static uint32_t last_config_serial;
|
||||
static struct zwlr_output_manager_v1* wlr_output_manager;
|
||||
|
||||
/* single head properties */
|
||||
static void output_head_name(void* data,
|
||||
struct zwlr_output_head_v1* output_head, const char* name)
|
||||
{
|
||||
struct output_manager_head* head = data;
|
||||
nvnc_trace("Got head name: %s", name);
|
||||
free(head->name);
|
||||
head->name = strdup(name);
|
||||
}
|
||||
|
||||
static void output_head_description(void* data,
|
||||
struct zwlr_output_head_v1* output_head,
|
||||
const char* description)
|
||||
{
|
||||
nvnc_trace("Got head description: %s", description);
|
||||
}
|
||||
|
||||
static void output_head_physical_size(void* data,
|
||||
struct zwlr_output_head_v1* output_head,
|
||||
int32_t width, int32_t height)
|
||||
{
|
||||
nvnc_trace("Got head size: %dx%d", width, height);
|
||||
}
|
||||
|
||||
static void output_head_mode(void* data,
|
||||
struct zwlr_output_head_v1* output_head,
|
||||
struct zwlr_output_mode_v1* mode)
|
||||
{
|
||||
nvnc_trace("Got head mode");
|
||||
}
|
||||
|
||||
static void output_head_enabled(void* data,
|
||||
struct zwlr_output_head_v1* output_head, int32_t enabled)
|
||||
{
|
||||
nvnc_trace("Got head enabled: %s", enabled ? "yes" : "no");
|
||||
struct output_manager_head* head = data;
|
||||
head->enabled = !!enabled;
|
||||
}
|
||||
|
||||
static void output_head_current_mode(void* data,
|
||||
struct zwlr_output_head_v1* output_head,
|
||||
struct zwlr_output_mode_v1* mode)
|
||||
{
|
||||
nvnc_trace("Got head current mode");
|
||||
}
|
||||
|
||||
static void output_head_position(void* data,
|
||||
struct zwlr_output_head_v1* output_head, int32_t x, int32_t y)
|
||||
{
|
||||
nvnc_trace("Got head position: %d,%d", x, y);
|
||||
}
|
||||
|
||||
static void output_head_transform(void* data,
|
||||
struct zwlr_output_head_v1* output_head, int32_t transform)
|
||||
{
|
||||
nvnc_trace("Got head transform: %d", transform);
|
||||
}
|
||||
|
||||
static void output_head_scale(void* data,
|
||||
struct zwlr_output_head_v1* output_head, wl_fixed_t scale_f)
|
||||
{
|
||||
double scale = wl_fixed_to_double(scale_f);
|
||||
nvnc_trace("Got head scale: %.2f", scale);
|
||||
}
|
||||
|
||||
static void output_head_finished(void* data,
|
||||
struct zwlr_output_head_v1* output_head)
|
||||
{
|
||||
nvnc_trace("head gone, removing");
|
||||
struct output_manager_head* head = data;
|
||||
zwlr_output_head_v1_destroy(output_head);
|
||||
wl_list_remove(&head->link);
|
||||
free(head->name);
|
||||
head->name = NULL;
|
||||
head->head = NULL;
|
||||
free(head);
|
||||
}
|
||||
|
||||
struct zwlr_output_head_v1_listener wlr_output_head_listener = {
|
||||
.name = output_head_name,
|
||||
.description = output_head_description,
|
||||
.physical_size = output_head_physical_size,
|
||||
.mode = output_head_mode,
|
||||
.enabled = output_head_enabled,
|
||||
.current_mode = output_head_current_mode,
|
||||
.position = output_head_position,
|
||||
.transform = output_head_transform,
|
||||
.scale = output_head_scale,
|
||||
.finished = output_head_finished,
|
||||
};
|
||||
|
||||
/* config object */
|
||||
static void output_manager_config_succeeded(void* data,
|
||||
struct zwlr_output_configuration_v1* config)
|
||||
{
|
||||
nvnc_trace("config request succeeded");
|
||||
zwlr_output_configuration_v1_destroy(config);
|
||||
}
|
||||
|
||||
static void output_manager_config_failed(void* data,
|
||||
struct zwlr_output_configuration_v1* config)
|
||||
{
|
||||
nvnc_trace("config request failed");
|
||||
zwlr_output_configuration_v1_destroy(config);
|
||||
}
|
||||
|
||||
static void output_manager_config_cancelled(void* data,
|
||||
struct zwlr_output_configuration_v1* config)
|
||||
{
|
||||
nvnc_trace("config request cancelled");
|
||||
zwlr_output_configuration_v1_destroy(config);
|
||||
}
|
||||
|
||||
struct zwlr_output_configuration_v1_listener wlr_output_config_listener = {
|
||||
.succeeded = output_manager_config_succeeded,
|
||||
.failed = output_manager_config_failed,
|
||||
.cancelled = output_manager_config_cancelled,
|
||||
};
|
||||
|
||||
/* manager itself */
|
||||
static void output_manager_done(void* data,
|
||||
struct zwlr_output_manager_v1* zwlr_output_manager_v1,
|
||||
uint32_t serial)
|
||||
{
|
||||
last_config_serial = serial;
|
||||
nvnc_trace("Got new serial: %u", serial);
|
||||
}
|
||||
|
||||
static void output_manager_finished(void* data,
|
||||
struct zwlr_output_manager_v1* zwlr_output_manager_v1)
|
||||
{
|
||||
nvnc_trace("output-manager destroyed");
|
||||
wlr_output_manager = NULL;
|
||||
}
|
||||
|
||||
static void output_manager_head(void* data,
|
||||
struct zwlr_output_manager_v1* zwlr_output_manager_v1,
|
||||
struct zwlr_output_head_v1* output_head)
|
||||
{
|
||||
struct output_manager_head* head = calloc(1, sizeof(*head));
|
||||
if (!head) {
|
||||
nvnc_log(NVNC_LOG_ERROR, "OOM");
|
||||
return;
|
||||
}
|
||||
|
||||
head->head = output_head;
|
||||
wl_list_insert(heads.prev, &head->link);
|
||||
nvnc_trace("New head, now at %lu", wl_list_length(&heads));
|
||||
|
||||
zwlr_output_head_v1_add_listener(head->head,
|
||||
&wlr_output_head_listener, head);
|
||||
}
|
||||
|
||||
static const struct zwlr_output_manager_v1_listener
|
||||
wlr_output_manager_listener = {
|
||||
.head = output_manager_head,
|
||||
.done = output_manager_done,
|
||||
.finished = output_manager_finished,
|
||||
};
|
||||
|
||||
/* Public API */
|
||||
void wlr_output_manager_setup(struct zwlr_output_manager_v1* output_manager)
|
||||
{
|
||||
if (wlr_output_manager)
|
||||
return;
|
||||
|
||||
wl_list_init(&heads);
|
||||
wlr_output_manager = output_manager;
|
||||
zwlr_output_manager_v1_add_listener(wlr_output_manager,
|
||||
&wlr_output_manager_listener, NULL);
|
||||
}
|
||||
|
||||
void wlr_output_manager_destroy(void)
|
||||
{
|
||||
if (!wlr_output_manager)
|
||||
return;
|
||||
|
||||
struct output_manager_head* head;
|
||||
struct output_manager_head* tmp;
|
||||
wl_list_for_each_safe(head, tmp, &heads, link) {
|
||||
wl_list_remove(&head->link);
|
||||
free(head->name);
|
||||
free(head);
|
||||
}
|
||||
|
||||
zwlr_output_manager_v1_destroy(wlr_output_manager);
|
||||
wlr_output_manager = NULL;
|
||||
|
||||
last_config_serial = 0;
|
||||
}
|
||||
|
||||
bool wlr_output_manager_resize_output(struct output* output,
|
||||
uint16_t width, uint16_t height)
|
||||
{
|
||||
if (!wlr_output_manager) {
|
||||
nvnc_log(NVNC_LOG_INFO,
|
||||
"output-management protocol not available, not resizing output");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!output->is_headless) {
|
||||
nvnc_log(NVNC_LOG_INFO,
|
||||
"not resizing output %s: not a headless one",
|
||||
output->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: This could be synced to --max-fps
|
||||
int refresh_rate = 0;
|
||||
|
||||
struct zwlr_output_configuration_v1* config;
|
||||
struct zwlr_output_configuration_head_v1* config_head;
|
||||
|
||||
config = zwlr_output_manager_v1_create_configuration(
|
||||
wlr_output_manager, last_config_serial);
|
||||
zwlr_output_configuration_v1_add_listener(config,
|
||||
&wlr_output_config_listener, NULL);
|
||||
|
||||
struct output_manager_head* head;
|
||||
wl_list_for_each(head, &heads, link) {
|
||||
if (!head->enabled) {
|
||||
nvnc_trace("disabling output %s", head->name);
|
||||
zwlr_output_configuration_v1_disable_head(
|
||||
config, head->head);
|
||||
continue;
|
||||
}
|
||||
|
||||
config_head = zwlr_output_configuration_v1_enable_head(
|
||||
config, head->head);
|
||||
if (head->name && strcmp(head->name, output->name) == 0) {
|
||||
nvnc_trace("reconfiguring output %s", head->name);
|
||||
zwlr_output_configuration_head_v1_set_custom_mode(
|
||||
config_head, width, height, refresh_rate);
|
||||
|
||||
/* It doesn't make any sense to have rotation on a
|
||||
* headless display, so we set the transform here to be
|
||||
* sure.
|
||||
*/
|
||||
zwlr_output_configuration_head_v1_set_transform(
|
||||
config_head, WL_OUTPUT_TRANSFORM_NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
nvnc_trace("applying new output config");
|
||||
zwlr_output_configuration_v1_apply(config);
|
||||
return true;
|
||||
}
|
|
@ -219,12 +219,7 @@ void output_name(void* data, struct zxdg_output_v1* xdg_output,
|
|||
struct output* self = data;
|
||||
|
||||
strlcpy(self->name, name, sizeof(self->name));
|
||||
self->is_headless =
|
||||
(strncmp(name, "HEADLESS-", strlen("HEADLESS-")) == 0) ||
|
||||
(strncmp(name, "NOOP-", strlen("NOOP-")) == 0);
|
||||
|
||||
nvnc_trace("Output %u name: %s, headless: %s", self->id, self->name,
|
||||
self->is_headless ? "yes" : "no");
|
||||
nvnc_trace("Output %u name: %s", self->id, self->name);
|
||||
}
|
||||
|
||||
void output_description(void* data, struct zxdg_output_v1* xdg_output,
|
||||
|
|
|
@ -30,7 +30,7 @@ static int pam_return_pwd(int num_msg, const struct pam_message** msgm,
|
|||
struct pam_response** response, void* appdata_ptr)
|
||||
{
|
||||
struct credentials* cred = appdata_ptr;
|
||||
struct pam_response* resp = calloc(num_msg, sizeof(*resp));
|
||||
struct pam_response* resp = calloc(sizeof(*response), num_msg);
|
||||
for (int i = 0; i < num_msg; i++) {
|
||||
resp[i].resp_retcode = PAM_SUCCESS;
|
||||
switch(msgm[i]->msg_style) {
|
||||
|
|
|
@ -377,4 +377,4 @@ multioutput_test() {
|
|||
}
|
||||
|
||||
smoke_test
|
||||
#multioutput_test
|
||||
multioutput_test
|
||||
|
|
|
@ -269,8 +269,8 @@ Parameters:
|
|||
*connection_count=...*
|
||||
The total number of connected VNC clients including this one.
|
||||
|
||||
*address=...*
|
||||
The IP address of this client. May be null.
|
||||
*hostname=...*
|
||||
The hostname or IP of this client. May be null.
|
||||
|
||||
*username=...*
|
||||
The username used to authenticate this client. May be null.
|
||||
|
@ -288,8 +288,8 @@ Parameters:
|
|||
*connection_count=...*
|
||||
The total number of connected VNC clients not including this one.
|
||||
|
||||
*address=...*
|
||||
The IP address of this client. May be null.
|
||||
*hostname=...*
|
||||
The hostname or IP of this client. May be null.
|
||||
|
||||
*username=...*
|
||||
The username used to authenticate this client. May be null.
|
||||
|
|
|
@ -63,8 +63,8 @@ the end, for ease in scripting:
|
|||
|
||||
```
|
||||
$ wayvncctl --json event-receive
|
||||
{"method":"client-connected","params":{"id":"0x10ef670","address":null,"username":null,"connection_count":1}}
|
||||
{"method":"client-disconnected","params":{"id":"0x10ef670","address":null,"username":null,"connection_count":0}}
|
||||
{"method":"client-connected","params":{"id":"0x10ef670","hostname":null,"username":null,"connection_count":1}}
|
||||
{"method":"client-disconnected","params":{"id":"0x10ef670","hostname":null,"username":null,"connection_count":0}}
|
||||
```
|
||||
|
||||
The default human-readible output is a multi-line yaml-like format, with two
|
||||
|
@ -75,12 +75,12 @@ $ wayvncctl event-receive
|
|||
|
||||
client-connected:
|
||||
id: 0x10ef670
|
||||
address: 192.168.1.18
|
||||
hostname: 192.168.1.18
|
||||
connection_count: 1
|
||||
|
||||
client-disconnected:
|
||||
id: 0x10ef670
|
||||
address: 192.168.1.18
|
||||
hostname: 192.168.1.18
|
||||
connection_count: 0
|
||||
|
||||
```
|
||||
|
|
Loading…
Reference in New Issue