Compare commits
1 Commits
Author | SHA1 | Date |
---|---|---|
Jim Ramsay | 2a3cc5a633 |
|
@ -14,8 +14,6 @@ packages:
|
||||||
- sway
|
- sway
|
||||||
- jq
|
- jq
|
||||||
- lsof
|
- lsof
|
||||||
- python-pycryptodomex # needed by vncdotool
|
|
||||||
- vncdotool
|
|
||||||
|
|
||||||
sources:
|
sources:
|
||||||
- http://github.com/any1/wayvnc
|
- http://github.com/any1/wayvnc
|
||||||
|
|
|
@ -18,7 +18,6 @@ packages:
|
||||||
- textproc/jq
|
- textproc/jq
|
||||||
- sysutils/lsof
|
- sysutils/lsof
|
||||||
- shells/bash
|
- shells/bash
|
||||||
- devel/py-pip
|
|
||||||
|
|
||||||
sources:
|
sources:
|
||||||
- http://github.com/any1/wayvnc
|
- http://github.com/any1/wayvnc
|
||||||
|
@ -26,8 +25,6 @@ sources:
|
||||||
- http://github.com/any1/aml
|
- http://github.com/any1/aml
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
- pip-vncdotool: |
|
|
||||||
sudo pip install vncdotool
|
|
||||||
- aml: |
|
- aml: |
|
||||||
cd aml
|
cd aml
|
||||||
meson --prefix=/usr build
|
meson --prefix=/usr build
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
---
|
|
||||||
name: Bugs
|
|
||||||
about: Crashes and other bugs
|
|
||||||
labels: 'bug'
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Useful information:
|
|
||||||
Please, try to gather as much of useful information as possible and follow
|
|
||||||
these instructions:
|
|
||||||
|
|
||||||
- **Version:**
|
|
||||||
- Run this command: `wayvnc -V`
|
|
||||||
|
|
||||||
- Try to reproduce while capturing a **trace log:**
|
|
||||||
- `wayvnc -Ltrace | tee wayvnc-crash.log`
|
|
||||||
|
|
||||||
- Get the **stack trace**:
|
|
||||||
- If have `coredumpctl`, you can gather the stack trace after a crash using
|
|
||||||
`coredumpctl gdb wayvnc` and then run `bt full` to obtain the stack trace.
|
|
||||||
- Otherwise, you can either locate the core file and load it into gdb or run
|
|
||||||
wayvnc in gdb like so:
|
|
||||||
- `gdb --args wayvnc -Ltrace`
|
|
||||||
- If the lines mentioning wayvnc, neatvnc or aml have `??`, please compile
|
|
||||||
wayvnc and those other projects from source with debug symbols and try
|
|
||||||
again.
|
|
||||||
|
|
||||||
- Describe how to **reproduce** the problem
|
|
|
@ -1,5 +0,0 @@
|
||||||
blank_issues_enabled: false
|
|
||||||
contact_links:
|
|
||||||
- name: Questions
|
|
||||||
url: "https://github.com/any1/wayvnc/discussions"
|
|
||||||
about: "Please ask questions on IRC in #wayvnc on Libera Chat or in Discussions"
|
|
|
@ -1,6 +0,0 @@
|
||||||
---
|
|
||||||
name: Enhancements
|
|
||||||
about: New functionality
|
|
||||||
labels: 'enhancement'
|
|
||||||
|
|
||||||
---
|
|
|
@ -1 +0,0 @@
|
||||||
Please read CONTRIBUTING.md before making a pull request.
|
|
|
@ -27,7 +27,6 @@ jobs:
|
||||||
sudo apt-get install -y meson libdrm-dev libxkbcommon-dev libwlroots-dev libjansson-dev libpam0g-dev libgnutls28-dev libavfilter-dev libavcodec-dev libavutil-dev libturbojpeg0-dev scdoc
|
sudo apt-get install -y meson libdrm-dev libxkbcommon-dev libwlroots-dev libjansson-dev libpam0g-dev libgnutls28-dev libavfilter-dev libavcodec-dev libavutil-dev libturbojpeg0-dev scdoc
|
||||||
# runtime deps for integration testing:
|
# runtime deps for integration testing:
|
||||||
sudo apt-get install -y sway jq lsof
|
sudo apt-get install -y sway jq lsof
|
||||||
pip install vncdotool
|
|
||||||
- name: configure
|
- name: configure
|
||||||
run: meson build -D tests=true
|
run: meson build -D tests=true
|
||||||
- name: compile
|
- name: compile
|
||||||
|
|
|
@ -7,4 +7,3 @@ perf.*
|
||||||
*.pem
|
*.pem
|
||||||
.vimrc
|
.vimrc
|
||||||
.cache
|
.cache
|
||||||
.vscode
|
|
153
CONTRIBUTING.md
153
CONTRIBUTING.md
|
@ -3,8 +3,7 @@
|
||||||
## Commit Messages
|
## Commit Messages
|
||||||
|
|
||||||
Please, try to write good commit messages. Do your best to follow these 7 rules,
|
Please, try to write good commit messages. Do your best to follow these 7 rules,
|
||||||
borrowed from [Chris Beams](https://chris.beams.io/posts/git-commit/), plus 1
|
borrowed from [Chris Beams](https://chris.beams.io/posts/git-commit/):
|
||||||
extra rule:
|
|
||||||
|
|
||||||
1. Separate subject from body with a blank line
|
1. Separate subject from body with a blank line
|
||||||
2. Limit the subject line to 50 characters
|
2. Limit the subject line to 50 characters
|
||||||
|
@ -13,36 +12,15 @@ extra rule:
|
||||||
5. Use the imperative mood in the subject line
|
5. Use the imperative mood in the subject line
|
||||||
6. Wrap the body at 72 characters
|
6. Wrap the body at 72 characters
|
||||||
7. Use the body to explain what and why vs. how
|
7. Use the body to explain what and why vs. how
|
||||||
8. (Extra) Prefix the subject line with the component that's modified
|
|
||||||
|
|
||||||
If you wish to know why we follow these rules, please read Chris Beams' blog
|
If you wish to know why we follow these rules, please read Chris Beams' blog
|
||||||
entry, linked above.
|
entry, linked above.
|
||||||
|
|
||||||
Rule number 8 allows us to quickly gauge if a given commit is relevant to what
|
|
||||||
we're looking for when skimming the log. It adds consistency and simplifies the
|
|
||||||
message. For example
|
|
||||||
```
|
|
||||||
ctl-client: Print trailing newline for events
|
|
||||||
```
|
|
||||||
is better than
|
|
||||||
```
|
|
||||||
Print trailing newline for events in ctl-client
|
|
||||||
```
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
|
|
||||||
```
|
|
||||||
ctl-client: Print trailing newline for events
|
|
||||||
|
|
||||||
If someone wants to parse this instead of using jq, a trailing
|
|
||||||
newline delimits the end of the event.
|
|
||||||
```
|
|
||||||
|
|
||||||
## Style
|
## Style
|
||||||
|
|
||||||
This project follows the the
|
This project follows the the
|
||||||
[Linux kernel's style guide](https://www.kernel.org/doc/html/latest/process/coding-style.html#codingstyle)
|
[Linux kernel's style guide](https://www.kernel.org/doc/html/latest/process/coding-style.html#codingstyle)
|
||||||
as far as coding style is concerned, with the following exceptions:
|
as far as coding style is concererned, with the following exceptions:
|
||||||
|
|
||||||
* When declaring pointer variables, the asterisk (`*`) is placed on the left
|
* When declaring pointer variables, the asterisk (`*`) is placed on the left
|
||||||
with the type rather than the variable name. Declaring multiple variables in
|
with the type rather than the variable name. Declaring multiple variables in
|
||||||
|
@ -51,135 +29,14 @@ as far as coding style is concerned, with the following exceptions:
|
||||||
a lot of code that uses aligned argument lists in the project, but I have
|
a lot of code that uses aligned argument lists in the project, but I have
|
||||||
come to the conclusion that these alignments are not very nice to maintain.
|
come to the conclusion that these alignments are not very nice to maintain.
|
||||||
|
|
||||||
### Summary & Examples:
|
## Unit Tests
|
||||||
|
|
||||||
In case you aren't familiar with Linux's coding style, here is a short summary
|
wayvnc has a small but growing set of unit tests, which are run on every GitHub PR. To run them locally, do the following:
|
||||||
and some examples of acceptable formatting:
|
|
||||||
|
|
||||||
* Use tabs for indenting.
|
|
||||||
* We do not align code (mostly), but when we do, we use spaces rather than
|
|
||||||
tabs. This rule is not according to the Linux style guide.
|
|
||||||
* The preferred limit on the length of a single line is 80 columns.
|
|
||||||
* User-visible string such as log messages must not be split up.
|
|
||||||
* Use space after the following keywords: `if`, `switch`, `case`, `for`, `do`,
|
|
||||||
`while`.
|
|
||||||
* Do **not** use space after others such as: `sizeof`, `typeof`, `alignof`
|
|
||||||
or `__attribute__`.
|
|
||||||
* Do **not** use typedefs.
|
|
||||||
* It is allowed to use typedefs for function pointers. This rule is not
|
|
||||||
according to the Linux style guide.
|
|
||||||
|
|
||||||
#### Functions
|
|
||||||
|
|
||||||
```
|
|
||||||
static int do_something(int number, const char* text)
|
|
||||||
{
|
|
||||||
body of function
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `if`
|
|
||||||
|
|
||||||
```
|
|
||||||
// Single statement
|
|
||||||
if (condition)
|
|
||||||
do_this();
|
|
||||||
|
|
||||||
// Multiple statements
|
|
||||||
if (condition) {
|
|
||||||
do_this(2, "41");
|
|
||||||
do_that();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Single statement if/else
|
|
||||||
if (condition)
|
|
||||||
do_this();
|
|
||||||
else
|
|
||||||
do_that();
|
|
||||||
|
|
||||||
// Multi-statement if/else
|
|
||||||
if (condition) {
|
|
||||||
do_this();
|
|
||||||
do_that();
|
|
||||||
} else {
|
|
||||||
otherwise();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `switch`
|
|
||||||
|
|
||||||
```
|
|
||||||
switch (value) {
|
|
||||||
case 3:
|
|
||||||
printf("three!\n");
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
printf("five!\n");
|
|
||||||
break;
|
|
||||||
case 42:
|
|
||||||
printf("the answer to life, the universe and everything: ");
|
|
||||||
// fallthrough
|
|
||||||
default:
|
|
||||||
printf("%d\n", value);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Arithmetic
|
|
||||||
|
|
||||||
```
|
|
||||||
int a = b * c + 5;
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Pointers
|
|
||||||
|
|
||||||
```
|
|
||||||
char* some_text = "some text";
|
|
||||||
char* just_text = text + 5;
|
|
||||||
char t = *just_text;
|
|
||||||
char e = just_text[1];
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing
|
|
||||||
|
|
||||||
### Unit Tests
|
|
||||||
|
|
||||||
wayvnc has a small but growing set of unit tests, which are run on every GitHub
|
|
||||||
PR. To run them locally, do the following:
|
|
||||||
```bash
|
```bash
|
||||||
meson test -C build
|
meson test -C build
|
||||||
```
|
```
|
||||||
|
|
||||||
### Integration Tests
|
|
||||||
|
|
||||||
There are also a handful of integration tests which also run on every PR. Read
|
|
||||||
the [integration tests documentation](test/integration/README.md) for more
|
|
||||||
details, but to run them locally:
|
|
||||||
```
|
|
||||||
./test/integration/integration.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
### Valgrind
|
|
||||||
|
|
||||||
There is a helper script in [util/valgrind.sh](util/valgrind.sh) to aid in
|
|
||||||
memory profiling of wayvnc and wayvncctl. This can help find and eliminate
|
|
||||||
memory leaks.
|
|
||||||
|
|
||||||
### Automated Tests
|
|
||||||
|
|
||||||
We run a set of tests on every PR, in three different environments.
|
|
||||||
|
|
||||||
Each run ensures that the proposed code change:
|
|
||||||
1. Builds successfully
|
|
||||||
2. Passes all unit tests
|
|
||||||
3. Passes all integration tests
|
|
||||||
|
|
||||||
And does so in 3 different environments:
|
|
||||||
- Ubuntu as a [github action](.github/workflows/build.yml)
|
|
||||||
- Arch Linux as a [sourcehut build](.builds/archlinux.yml)
|
|
||||||
- FreeBSD as a [sourcehut build](.builds/freebsd.yaml)
|
|
||||||
|
|
||||||
## No Brown M&Ms
|
## No Brown M&Ms
|
||||||
|
|
||||||
All pull requests must contain the following sentence in the description:
|
All pull requests must contain the following sentence in the description:
|
||||||
I have read and understood CONTRIBUTING.md.
|
I have read and understood CONTRIBUTING.md and its associated documents.
|
||||||
|
|
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
|
||||||
|
|
80
README.md
80
README.md
|
@ -1,8 +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)
|
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
This is a VNC server for wlroots-based Wayland compositors (:no_entry: Gnome,
|
This is a VNC server for wlroots-based Wayland compositors (:no_entry: Gnome,
|
||||||
|
@ -16,6 +15,27 @@ support, join the #wayvnc IRC channel on libera.chat, or ask your questions on t
|
||||||
GitHub [discussion forum](https://github.com/any1/wayvnc/discussions) for the
|
GitHub [discussion forum](https://github.com/any1/wayvnc/discussions) for the
|
||||||
project.
|
project.
|
||||||
|
|
||||||
|
## Installing
|
||||||
|
```
|
||||||
|
# Arch Linux
|
||||||
|
pacman -S wayvnc
|
||||||
|
|
||||||
|
# FreeBSD
|
||||||
|
pkg install wayvnc
|
||||||
|
|
||||||
|
# Fedora
|
||||||
|
dnf install wayvnc
|
||||||
|
|
||||||
|
# Debian (unstable / testing)
|
||||||
|
apt install wayvnc
|
||||||
|
|
||||||
|
# openSUSE Tumbleweed
|
||||||
|
zypper install wayvnc
|
||||||
|
|
||||||
|
# Void Linux
|
||||||
|
xbps-install wayvnc
|
||||||
|
```
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
### Runtime Dependencies
|
### Runtime Dependencies
|
||||||
* aml
|
* aml
|
||||||
|
@ -38,15 +58,13 @@ project.
|
||||||
pacman -S base-devel libglvnd libxkbcommon pixman gnutls jansson
|
pacman -S base-devel libglvnd libxkbcommon pixman gnutls jansson
|
||||||
```
|
```
|
||||||
|
|
||||||
#### For Fedora 37
|
#### For Fedora 31
|
||||||
```
|
```
|
||||||
dnf install -y meson gcc ninja-build pkg-config egl-wayland egl-wayland-devel \
|
dnf install -y meson gcc ninja-build pkg-config egl-wayland egl-wayland-devel \
|
||||||
mesa-libEGL-devel mesa-libEGL libwayland-egl libglvnd-devel \
|
mesa-libEGL-devel mesa-libEGL libwayland-egl libglvnd-devel \
|
||||||
libglvnd-core-devel libglvnd mesa-libGLES-devel mesa-libGLES \
|
libglvnd-core-devel libglvnd mesa-libGLES-devel mesa-libGLES \
|
||||||
libxkbcommon-devel libxkbcommon libwayland-client \
|
libxkbcommon-devel libxkbcommon libwayland-client libwayland \
|
||||||
pam-devel pixman-devel libgbm-devel libdrm-devel scdoc \
|
wayland-devel gnutls-devel jansson-devel
|
||||||
libavcodec-free-devel libavfilter-free-devel libavutil-free-devel \
|
|
||||||
turbojpeg-devel wayland-devel gnutls-devel jansson-devel
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### For Debian (unstable / testing)
|
#### For Debian (unstable / testing)
|
||||||
|
@ -93,10 +111,11 @@ To run the unit tests:
|
||||||
meson test -C build
|
meson test -C build
|
||||||
```
|
```
|
||||||
|
|
||||||
To run the [integration tests](test/integration/README.md):
|
To run the integration tests:
|
||||||
```
|
```
|
||||||
./test/integration/integration.sh
|
./test/integration/integration.sh
|
||||||
```
|
```
|
||||||
|
The integration tests currently require that sway, lsof, jq, and bash are installed.
|
||||||
|
|
||||||
## Running
|
## Running
|
||||||
Wayvnc can be run from the build directory like so:
|
Wayvnc can be run from the build directory like so:
|
||||||
|
@ -116,17 +135,12 @@ use SSH tunneling while listening on localhost, but users can also be
|
||||||
authenticated when connecting to wayvnc.
|
authenticated when connecting to wayvnc.
|
||||||
|
|
||||||
### Encryption & Authentication
|
### Encryption & Authentication
|
||||||
|
You'll need a private X509 key and a certificate. A self-signed key with a
|
||||||
#### VeNCrypt (TLS)
|
certificate can be generated like so:
|
||||||
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 rsa:4096 -sha256 -days 3650 -nodes \
|
||||||
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:secp384r1 -sha384 \
|
-keyout key.pem -out cert.pem -subj /CN=localhost \
|
||||||
-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
|
-addext subjectAltName=DNS:localhost,DNS:localhost,IP:127.0.0.1
|
||||||
cd -
|
|
||||||
```
|
```
|
||||||
Replace `localhost` and `127.0.0.1` in the command above with your public facing
|
Replace `localhost` and `127.0.0.1` in the command above with your public facing
|
||||||
host name and IP address, respectively, or just keep them as is if you're
|
host name and IP address, respectively, or just keep them as is if you're
|
||||||
|
@ -136,42 +150,14 @@ Create a config with the authentication info and load it using the `--config`
|
||||||
command line option or place it at the default location
|
command line option or place it at the default location
|
||||||
`$HOME/.config/wayvnc/config`.
|
`$HOME/.config/wayvnc/config`.
|
||||||
```
|
```
|
||||||
use_relative_paths=true
|
|
||||||
address=0.0.0.0
|
address=0.0.0.0
|
||||||
enable_auth=true
|
enable_auth=true
|
||||||
username=luser
|
username=luser
|
||||||
password=p455w0rd
|
password=p455w0rd
|
||||||
private_key_file=tls_key.pem
|
private_key_file=/path/to/key.pem
|
||||||
certificate_file=tls_cert.pem
|
certificate_file=/path/to/cert.pem
|
||||||
```
|
```
|
||||||
|
|
||||||
#### RSA-AES
|
|
||||||
The RSA-AES security type combines RSA with AES in EAX mode to provide secure
|
|
||||||
authentication and encryption that's resilient to eavesdropping and MITM. Its
|
|
||||||
main weakness is that the user has to verify the server's credentials on first
|
|
||||||
use. Thereafter, the client software should warn the user if the server's
|
|
||||||
credentials change. It's a Trust on First Use (TOFU) scheme as employed by SSH.
|
|
||||||
|
|
||||||
For the RSA-AES to be enabled, you need to generate an RSA key. This can be
|
|
||||||
achieved like so:
|
|
||||||
```
|
|
||||||
ssh-keygen -m pem -f ~/.config/wayvnc/rsa_key.pem -t rsa -N ""
|
|
||||||
```
|
|
||||||
|
|
||||||
You also need to tell wayvnc where this file is located, by setting setting the
|
|
||||||
`rsa_private_key_file` configuration parameter:
|
|
||||||
```
|
|
||||||
use_relative_paths=true
|
|
||||||
address=0.0.0.0
|
|
||||||
enable_auth=true
|
|
||||||
username=luser
|
|
||||||
password=p455w0rd
|
|
||||||
rsa_private_key_file=rsa_key.pem
|
|
||||||
```
|
|
||||||
|
|
||||||
You may also add credentials for TLS in combination with RSA. The client will
|
|
||||||
choose.
|
|
||||||
|
|
||||||
### wayvncctl control socket
|
### wayvncctl control socket
|
||||||
|
|
||||||
To facilitate runtime interaction and control, wayvnc opens a unix domain socket
|
To facilitate runtime interaction and control, wayvnc opens a unix domain socket
|
||||||
|
|
|
@ -1,88 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import json
|
|
||||||
import re
|
|
||||||
import os
|
|
||||||
import glob
|
|
||||||
|
|
||||||
class Program:
|
|
||||||
command_seq = 0
|
|
||||||
reader = None
|
|
||||||
writer = None
|
|
||||||
read_buffer = ""
|
|
||||||
message_queue = asyncio.Queue()
|
|
||||||
reply_queue = asyncio.Queue()
|
|
||||||
decoder = json.JSONDecoder()
|
|
||||||
tasks = []
|
|
||||||
|
|
||||||
async def read_message(self):
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
result, index = self.decoder.raw_decode(self.read_buffer)
|
|
||||||
self.read_buffer = self.read_buffer[index:].lstrip()
|
|
||||||
return result
|
|
||||||
except json.JSONDecodeError:
|
|
||||||
data = await self.reader.read(4096)
|
|
||||||
self.read_buffer += data.decode('utf-8')
|
|
||||||
|
|
||||||
async def send_command(self, method, params = None):
|
|
||||||
cmd = {
|
|
||||||
"method": method,
|
|
||||||
"id": self.command_seq,
|
|
||||||
}
|
|
||||||
|
|
||||||
if not params is None:
|
|
||||||
cmd['params'] = params
|
|
||||||
|
|
||||||
self.command_seq += 1
|
|
||||||
self.writer.write(json.dumps(cmd).encode())
|
|
||||||
await self.writer.drain()
|
|
||||||
|
|
||||||
reply = await self.reply_queue.get()
|
|
||||||
self.reply_queue.task_done()
|
|
||||||
return reply['code'] == 0
|
|
||||||
|
|
||||||
async def attach(self, display):
|
|
||||||
return await self.send_command('attach', {'display': display})
|
|
||||||
|
|
||||||
async def attach_any(self):
|
|
||||||
for path in glob.iglob('/run/user/*/wayland-*'):
|
|
||||||
if path.endswith('.lock'):
|
|
||||||
continue
|
|
||||||
|
|
||||||
if await self.attach(path):
|
|
||||||
return True
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
async def handle_detached(self):
|
|
||||||
while not await self.attach_any():
|
|
||||||
await asyncio.sleep(1.0)
|
|
||||||
|
|
||||||
async def process_message(self, message):
|
|
||||||
method = message['method']
|
|
||||||
if (method == 'detached'):
|
|
||||||
await self.handle_detached()
|
|
||||||
|
|
||||||
async def message_processor(self):
|
|
||||||
while True:
|
|
||||||
message = await self.read_message()
|
|
||||||
if 'method' in message:
|
|
||||||
await self.message_queue.put(message)
|
|
||||||
elif 'code' in message:
|
|
||||||
await self.reply_queue.put(message)
|
|
||||||
|
|
||||||
async def main(self):
|
|
||||||
self.reader, self.writer = await asyncio.open_unix_connection("/tmp/wayvncctl-0")
|
|
||||||
self.tasks.append(asyncio.create_task(self.message_processor()))
|
|
||||||
|
|
||||||
await self.attach_any()
|
|
||||||
await self.send_command("event-receive")
|
|
||||||
|
|
||||||
while True:
|
|
||||||
message = await self.message_queue.get()
|
|
||||||
await self.process_message(message)
|
|
||||||
|
|
||||||
prog = Program()
|
|
||||||
asyncio.run(prog.main())
|
|
|
@ -1,29 +1,4 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
#
|
|
||||||
# This is free and unencumbered software released into the public domain.
|
|
||||||
#
|
|
||||||
# Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
||||||
# distribute this software, either in source code form or as a compiled
|
|
||||||
# binary, for any purpose, commercial or non-commercial, and by any
|
|
||||||
# means.
|
|
||||||
#
|
|
||||||
# In jurisdictions that recognize copyright laws, the author or authors
|
|
||||||
# of this software dedicate any and all copyright interest in the
|
|
||||||
# software to the public domain. We make this dedication for the benefit
|
|
||||||
# of the public at large and to the detriment of our heirs and
|
|
||||||
# successors. We intend this dedication to be an overt act of
|
|
||||||
# relinquishment in perpetuity of all present and future rights to this
|
|
||||||
# software under copyright law.
|
|
||||||
#
|
|
||||||
# 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 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.
|
|
||||||
#
|
|
||||||
# For more information, please refer to <http://unlicense.org/>
|
|
||||||
|
|
||||||
WAYVNCCTL=${WAYVNCCTL:-wayvncctl}
|
WAYVNCCTL=${WAYVNCCTL:-wayvncctl}
|
||||||
|
|
||||||
|
|
|
@ -1,29 +1,4 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
#
|
|
||||||
# This is free and unencumbered software released into the public domain.
|
|
||||||
#
|
|
||||||
# Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
||||||
# distribute this software, either in source code form or as a compiled
|
|
||||||
# binary, for any purpose, commercial or non-commercial, and by any
|
|
||||||
# means.
|
|
||||||
#
|
|
||||||
# In jurisdictions that recognize copyright laws, the author or authors
|
|
||||||
# of this software dedicate any and all copyright interest in the
|
|
||||||
# software to the public domain. We make this dedication for the benefit
|
|
||||||
# of the public at large and to the detriment of our heirs and
|
|
||||||
# successors. We intend this dedication to be an overt act of
|
|
||||||
# relinquishment in perpetuity of all present and future rights to this
|
|
||||||
# software under copyright law.
|
|
||||||
#
|
|
||||||
# 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 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.
|
|
||||||
#
|
|
||||||
# For more information, please refer to <http://unlicense.org/>
|
|
||||||
|
|
||||||
WAYVNCCTL=${WAYVNCCTL:-wayvncctl}
|
WAYVNCCTL=${WAYVNCCTL:-wayvncctl}
|
||||||
SWAYMSG=${SWAYMSG:-swaymsg}
|
SWAYMSG=${SWAYMSG:-swaymsg}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 - 2023 Andri Yngvason
|
* Copyright (c) 2020 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
|
||||||
|
@ -21,10 +21,8 @@
|
||||||
|
|
||||||
#define X_CFG_LIST \
|
#define X_CFG_LIST \
|
||||||
X(bool, enable_auth) \
|
X(bool, enable_auth) \
|
||||||
X(bool, relax_encryption) \
|
|
||||||
X(string, private_key_file) \
|
X(string, private_key_file) \
|
||||||
X(string, certificate_file) \
|
X(string, certificate_file) \
|
||||||
X(string, rsa_private_key_file) \
|
|
||||||
X(string, username) \
|
X(string, username) \
|
||||||
X(string, password) \
|
X(string, password) \
|
||||||
X(string, address) \
|
X(string, address) \
|
||||||
|
@ -35,10 +33,8 @@
|
||||||
X(string, xkb_layout) \
|
X(string, xkb_layout) \
|
||||||
X(string, xkb_variant) \
|
X(string, xkb_variant) \
|
||||||
X(string, xkb_options) \
|
X(string, xkb_options) \
|
||||||
X(bool, use_relative_paths) \
|
|
||||||
|
|
||||||
struct cfg {
|
struct cfg {
|
||||||
char* directory;
|
|
||||||
#define string char*
|
#define string char*
|
||||||
#define uint uint32_t
|
#define uint uint32_t
|
||||||
#define X(type, name) type name;
|
#define X(type, name) type name;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022-2023 Jim Ramsay
|
* Copyright (c) 2022-2023 Jim Ramsay
|
||||||
* Copyright (c) 2023 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
|
||||||
|
@ -20,8 +19,6 @@
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
enum cmd_type {
|
enum cmd_type {
|
||||||
CMD_ATTACH,
|
|
||||||
CMD_DETACH,
|
|
||||||
CMD_HELP,
|
CMD_HELP,
|
||||||
CMD_EVENT_RECEIVE,
|
CMD_EVENT_RECEIVE,
|
||||||
CMD_CLIENT_LIST,
|
CMD_CLIENT_LIST,
|
||||||
|
@ -39,9 +36,6 @@ enum event_type {
|
||||||
EVT_CAPTURE_CHANGED,
|
EVT_CAPTURE_CHANGED,
|
||||||
EVT_CLIENT_CONNECTED,
|
EVT_CLIENT_CONNECTED,
|
||||||
EVT_CLIENT_DISCONNECTED,
|
EVT_CLIENT_DISCONNECTED,
|
||||||
EVT_DETACHED,
|
|
||||||
EVT_OUTPUT_ADDED,
|
|
||||||
EVT_OUTPUT_REMOVED,
|
|
||||||
EVT_UNKNOWN,
|
EVT_UNKNOWN,
|
||||||
};
|
};
|
||||||
#define EVT_LIST_LEN EVT_UNKNOWN
|
#define EVT_LIST_LEN EVT_UNKNOWN
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022 Jim Ramsay
|
* Copyright (c) 2022 Jim Ramsay
|
||||||
* Copyright (c) 2023 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,21 +18,13 @@
|
||||||
|
|
||||||
#include "output.h"
|
#include "output.h"
|
||||||
|
|
||||||
#include <sys/socket.h>
|
|
||||||
|
|
||||||
struct ctl;
|
struct ctl;
|
||||||
struct cmd_response;
|
struct cmd_response;
|
||||||
|
|
||||||
struct ctl_server_client;
|
struct ctl_server_vnc_client {
|
||||||
|
char id[64];
|
||||||
struct ctl_server_client_info {
|
char hostname[256];
|
||||||
int id;
|
char username[256];
|
||||||
union {
|
|
||||||
struct sockaddr_storage address_storage;
|
|
||||||
struct sockaddr address;
|
|
||||||
};
|
|
||||||
const char* username;
|
|
||||||
const char* seat;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ctl_server_output {
|
struct ctl_server_output {
|
||||||
|
@ -47,8 +38,6 @@ struct ctl_server_output {
|
||||||
|
|
||||||
struct ctl_server_actions {
|
struct ctl_server_actions {
|
||||||
void* userdata;
|
void* userdata;
|
||||||
struct cmd_response* (*on_attach)(struct ctl*, const char* display);
|
|
||||||
struct cmd_response* (*on_detach)(struct ctl*);
|
|
||||||
struct cmd_response* (*on_output_cycle)(struct ctl*,
|
struct cmd_response* (*on_output_cycle)(struct ctl*,
|
||||||
enum output_cycle_direction direction);
|
enum output_cycle_direction direction);
|
||||||
struct cmd_response* (*on_output_switch)(struct ctl*,
|
struct cmd_response* (*on_output_switch)(struct ctl*,
|
||||||
|
@ -57,10 +46,11 @@ struct ctl_server_actions {
|
||||||
const char* id);
|
const char* id);
|
||||||
struct cmd_response* (*on_wayvnc_exit)(struct ctl*);
|
struct cmd_response* (*on_wayvnc_exit)(struct ctl*);
|
||||||
|
|
||||||
struct ctl_server_client *(*client_next)(struct ctl*,
|
// Return number of elements created
|
||||||
struct ctl_server_client* prev);
|
// Allocate 'clients' array or set ton ULL if none
|
||||||
void (*client_info)(const struct ctl_server_client*,
|
// Receiver will free(clients) when done.
|
||||||
struct ctl_server_client_info* info);
|
int (*get_client_list)(struct ctl*,
|
||||||
|
struct ctl_server_vnc_client** clients);
|
||||||
|
|
||||||
// Return number of elements created
|
// Return number of elements created
|
||||||
// Allocate 'outputs' array or set to NULL if none
|
// Allocate 'outputs' array or set to NULL if none
|
||||||
|
@ -78,17 +68,16 @@ struct cmd_response* cmd_ok(void);
|
||||||
struct cmd_response* cmd_failed(const char* fmt, ...);
|
struct cmd_response* cmd_failed(const char* fmt, ...);
|
||||||
|
|
||||||
void ctl_server_event_connected(struct ctl*,
|
void ctl_server_event_connected(struct ctl*,
|
||||||
const struct ctl_server_client_info *info,
|
const char* client_id,
|
||||||
|
const char* client_hostname,
|
||||||
|
const char* client_username,
|
||||||
int new_connection_count);
|
int new_connection_count);
|
||||||
|
|
||||||
void ctl_server_event_disconnected(struct ctl*,
|
void ctl_server_event_disconnected(struct ctl*,
|
||||||
const struct ctl_server_client_info *info,
|
const char* client_id,
|
||||||
|
const char* client_hostname,
|
||||||
|
const char* client_username,
|
||||||
int new_connection_count);
|
int new_connection_count);
|
||||||
|
|
||||||
void ctl_server_event_capture_changed(struct ctl*,
|
void ctl_server_event_capture_changed(struct ctl*,
|
||||||
const char* captured_output);
|
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 <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*);
|
|
||||||
|
|
|
@ -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_dimension_changed;
|
||||||
bool is_transform_changed;
|
bool is_transform_changed;
|
||||||
bool is_headless;
|
|
||||||
|
|
||||||
void (*on_dimension_change)(struct output*);
|
void (*on_dimension_change)(struct output*);
|
||||||
void (*on_transform_change)(struct output*);
|
void (*on_transform_change)(struct output*);
|
||||||
|
|
|
@ -22,4 +22,3 @@
|
||||||
|
|
||||||
enum wl_shm_format fourcc_to_wl_shm(uint32_t in);
|
enum wl_shm_format fourcc_to_wl_shm(uint32_t in);
|
||||||
uint32_t fourcc_from_wl_shm(enum wl_shm_format in);
|
uint32_t fourcc_from_wl_shm(enum wl_shm_format in);
|
||||||
int pixel_size_from_fourcc(uint32_t fourcc);
|
|
||||||
|
|
|
@ -26,8 +26,6 @@ struct seat {
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
uint32_t capabilities;
|
uint32_t capabilities;
|
||||||
char name[256];
|
char name[256];
|
||||||
|
|
||||||
uint32_t occupancy;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct seat* seat_new(struct wl_seat* wl_seat, uint32_t id);
|
struct seat* seat_new(struct wl_seat* wl_seat, uint32_t id);
|
||||||
|
@ -36,5 +34,4 @@ void seat_list_destroy(struct wl_list* list);
|
||||||
|
|
||||||
struct seat* seat_find_by_name(struct wl_list* list, const char* name);
|
struct seat* seat_find_by_name(struct wl_list* list, const char* name);
|
||||||
struct seat* seat_find_by_id(struct wl_list* list, uint32_t id);
|
struct seat* seat_find_by_id(struct wl_list* list, uint32_t id);
|
||||||
struct seat* seat_find_unoccupied(struct wl_list* list);
|
|
||||||
struct seat* seat_first(struct wl_list* list);
|
struct seat* seat_first(struct wl_list* list);
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
#define UDIV_UP(a, b) (((a) + (b) - 1) / (b))
|
#define UDIV_UP(a, b) (((a) + (b) - 1) / (b))
|
||||||
#define ALIGN_UP(a, b) ((b) * UDIV_UP((a), (b)))
|
|
||||||
|
|
||||||
extern const char* wayvnc_version;
|
extern const char* wayvnc_version;
|
||||||
|
|
||||||
|
|
39
meson.build
39
meson.build
|
@ -1,7 +1,7 @@
|
||||||
project(
|
project(
|
||||||
'wayvnc',
|
'wayvnc',
|
||||||
'c',
|
'c',
|
||||||
version: '0.9-dev',
|
version: '0.6.0',
|
||||||
license: 'ISC',
|
license: 'ISC',
|
||||||
default_options: [
|
default_options: [
|
||||||
'c_std=gnu11',
|
'c_std=gnu11',
|
||||||
|
@ -14,30 +14,26 @@ host_system = host_machine.system()
|
||||||
prefix = get_option('prefix')
|
prefix = get_option('prefix')
|
||||||
|
|
||||||
c_args = [
|
c_args = [
|
||||||
|
'-DPROJECT_VERSION="@0@"'.format(meson.project_version()),
|
||||||
'-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',
|
||||||
]
|
]
|
||||||
|
|
||||||
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_describe = run_command([git, 'describe', '--tags', '--long'])
|
||||||
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_describe.returncode() == 0 and git_branch.returncode() == 0
|
||||||
version = '"v@0@-@1@ (@2@)"'.format(
|
c_args += '-DGIT_VERSION="@0@ (@1@)"'.format(
|
||||||
meson.project_version(),
|
git_describe.stdout().strip(),
|
||||||
git_commit.stdout().strip(),
|
|
||||||
git_branch.stdout().strip(),
|
git_branch.stdout().strip(),
|
||||||
)
|
)
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
add_project_arguments('-DPROJECT_VERSION=@0@'.format(version), language: 'c')
|
|
||||||
|
|
||||||
if buildtype != 'debug' and buildtype != 'debugoptimized'
|
if buildtype != 'debug' and buildtype != 'debugoptimized'
|
||||||
c_args += '-DNDEBUG'
|
c_args += '-DNDEBUG'
|
||||||
endif
|
endif
|
||||||
|
@ -54,18 +50,11 @@ 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.6.0', '<0.7.0']
|
||||||
|
|
||||||
neatvnc_project = subproject(
|
neatvnc_project = subproject(
|
||||||
'neatvnc',
|
'neatvnc',
|
||||||
|
@ -86,7 +75,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')
|
||||||
|
|
||||||
|
@ -97,7 +86,6 @@ sources = [
|
||||||
'src/screencopy.c',
|
'src/screencopy.c',
|
||||||
'src/data-control.c',
|
'src/data-control.c',
|
||||||
'src/output.c',
|
'src/output.c',
|
||||||
'src/output-management.c',
|
|
||||||
'src/pointer.c',
|
'src/pointer.c',
|
||||||
'src/keyboard.c',
|
'src/keyboard.c',
|
||||||
'src/seat.c',
|
'src/seat.c',
|
||||||
|
@ -127,11 +115,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 +140,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)
|
||||||
|
|
|
@ -1,110 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<protocol name="ext_transient_seat_v1">
|
|
||||||
<copyright>
|
|
||||||
Copyright © 2020 - 2023 Andri Yngvason
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a
|
|
||||||
copy of this software and associated documentation files (the "Software"),
|
|
||||||
to deal in the Software without restriction, including without limitation
|
|
||||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
||||||
and/or sell copies of the Software, and to permit persons to whom the
|
|
||||||
Software is furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice (including the next
|
|
||||||
paragraph) shall be included in all copies or substantial portions of the
|
|
||||||
Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
||||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
DEALINGS IN THE SOFTWARE.
|
|
||||||
</copyright>
|
|
||||||
|
|
||||||
<description summary="protocol for creating temporary seats">
|
|
||||||
The transient seat protocol can be used by privileged clients to create
|
|
||||||
independent seats that will be removed from the compositor when the client
|
|
||||||
destroys its transient seat.
|
|
||||||
|
|
||||||
This protocol is intended for use with virtual input protocols such as
|
|
||||||
"virtual_keyboard_unstable_v1" or "wlr_virtual_pointer_unstable_v1", both
|
|
||||||
of which allow the user to select a seat.
|
|
||||||
|
|
||||||
The "wl_seat" global created by this protocol does not generate input events
|
|
||||||
on its own, or have any capabilities except those assigned to it by other
|
|
||||||
protocol extensions, such as the ones mentioned above.
|
|
||||||
|
|
||||||
For example, a remote desktop server can create a seat with virtual inputs
|
|
||||||
for each remote user by following these steps for each new connection:
|
|
||||||
* Create a transient seat
|
|
||||||
* Wait for the transient seat to be created
|
|
||||||
* Locate a "wl_seat" global with a matching name
|
|
||||||
* Create virtual inputs using the resulting "wl_seat" global
|
|
||||||
</description>
|
|
||||||
|
|
||||||
<interface name="ext_transient_seat_manager_v1" version="1">
|
|
||||||
<description summary="transient seat manager">
|
|
||||||
The transient seat manager creates short-lived seats.
|
|
||||||
</description>
|
|
||||||
|
|
||||||
<request name="create">
|
|
||||||
<description summary="create a transient seat">
|
|
||||||
Create a new seat that is removed when the client side transient seat
|
|
||||||
object is destroyed.
|
|
||||||
|
|
||||||
The actual seat may be removed sooner, in which case the transient seat
|
|
||||||
object shall become inert.
|
|
||||||
</description>
|
|
||||||
<arg name="seat" type="new_id" interface="ext_transient_seat_v1"/>
|
|
||||||
</request>
|
|
||||||
|
|
||||||
<request name="destroy" type="destructor">
|
|
||||||
<description summary="destroy the manager">
|
|
||||||
Destroy the manager.
|
|
||||||
|
|
||||||
All objects created by the manager will remain valid until they are
|
|
||||||
destroyed themselves.
|
|
||||||
</description>
|
|
||||||
</request>
|
|
||||||
</interface>
|
|
||||||
|
|
||||||
<interface name="ext_transient_seat_v1" version="1">
|
|
||||||
<description summary="transient seat handle">
|
|
||||||
When the transient seat handle is destroyed, the seat itself will also be
|
|
||||||
destroyed.
|
|
||||||
</description>
|
|
||||||
|
|
||||||
<event name="ready">
|
|
||||||
<description summary="transient seat is ready">
|
|
||||||
This event advertises the global name for the wl_seat to be used with
|
|
||||||
wl_registry_bind.
|
|
||||||
|
|
||||||
It is sent exactly once, immediately after the transient seat is created
|
|
||||||
and the new "wl_seat" global is advertised, if and only if the creation
|
|
||||||
of the transient seat was allowed.
|
|
||||||
</description>
|
|
||||||
<arg name="global_name" type="uint"/>
|
|
||||||
</event>
|
|
||||||
|
|
||||||
<event name="denied">
|
|
||||||
<description summary="transient seat creation denied">
|
|
||||||
The event informs the client that the compositor denied its request to
|
|
||||||
create a transient seat.
|
|
||||||
|
|
||||||
It is sent exactly once, immediately after the transient seat object is
|
|
||||||
created, if and only if the creation of the transient seat was denied.
|
|
||||||
|
|
||||||
After receiving this event, the client should destroy the object.
|
|
||||||
</description>
|
|
||||||
</event>
|
|
||||||
|
|
||||||
<request name="destroy" type="destructor">
|
|
||||||
<description summary="destroy transient seat">
|
|
||||||
When the transient seat object is destroyed by the client, the
|
|
||||||
associated seat created by the compositor is also destroyed.
|
|
||||||
</description>
|
|
||||||
</request>
|
|
||||||
</interface>
|
|
||||||
</protocol>
|
|
|
@ -21,9 +21,7 @@ client_protocols = [
|
||||||
'xdg-output-unstable-v1.xml',
|
'xdg-output-unstable-v1.xml',
|
||||||
'linux-dmabuf-unstable-v1.xml',
|
'linux-dmabuf-unstable-v1.xml',
|
||||||
'wlr-data-control-unstable-v1.xml',
|
'wlr-data-control-unstable-v1.xml',
|
||||||
'wlr-output-management-unstable-v1.xml',
|
|
||||||
'wlr-output-power-management-unstable-v1.xml',
|
'wlr-output-power-management-unstable-v1.xml',
|
||||||
'ext-transient-seat-v1.xml',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
client_protos_src = []
|
client_protos_src = []
|
||||||
|
|
|
@ -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>
|
|
107
src/buffer.c
107
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
|
* 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
|
||||||
|
@ -31,20 +31,10 @@
|
||||||
#include "buffer.h"
|
#include "buffer.h"
|
||||||
#include "pixels.h"
|
#include "pixels.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
#ifdef ENABLE_SCREENCOPY_DMABUF
|
#ifdef ENABLE_SCREENCOPY_DMABUF
|
||||||
#include <gbm.h>
|
#include <gbm.h>
|
||||||
#include <sys/ioctl.h>
|
#endif
|
||||||
#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
|
|
||||||
|
|
||||||
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;
|
||||||
|
@ -101,10 +91,9 @@ struct wv_buffer* wv_buffer_create_shm(int width,
|
||||||
if (!self->wl_buffer)
|
if (!self->wl_buffer)
|
||||||
goto shm_failure;
|
goto shm_failure;
|
||||||
|
|
||||||
int bpp = pixel_size_from_fourcc(fourcc);
|
// TODO: Get the pixel size from the format instead of assuming it's 4.
|
||||||
assert(bpp > 0);
|
|
||||||
self->nvnc_fb = nvnc_fb_from_buffer(self->pixels, width, height, fourcc,
|
self->nvnc_fb = nvnc_fb_from_buffer(self->pixels, width, height, fourcc,
|
||||||
stride / bpp);
|
stride / 4);
|
||||||
if (!self->nvnc_fb) {
|
if (!self->nvnc_fb) {
|
||||||
goto nvnc_fb_failure;
|
goto nvnc_fb_failure;
|
||||||
}
|
}
|
||||||
|
@ -128,81 +117,6 @@ failure:
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_SCREENCOPY_DMABUF
|
#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,
|
static struct wv_buffer* wv_buffer_create_dmabuf(int width, int height,
|
||||||
uint32_t fourcc)
|
uint32_t fourcc)
|
||||||
{
|
{
|
||||||
|
@ -218,17 +132,8 @@ 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.
|
self->bo = gbm_bo_create(gbm_device, width, height, fourcc,
|
||||||
// But this could still fail!
|
GBM_BO_USE_RENDERING);
|
||||||
//#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);
|
|
||||||
|
|
||||||
if (!self->bo)
|
if (!self->bo)
|
||||||
goto bo_failure;
|
goto bo_failure;
|
||||||
|
|
||||||
|
|
13
src/cfg.c
13
src/cfg.c
|
@ -18,8 +18,6 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <libgen.h>
|
|
||||||
#include <limits.h>
|
|
||||||
|
|
||||||
#include "cfg.h"
|
#include "cfg.h"
|
||||||
|
|
||||||
|
@ -108,16 +106,11 @@ static int cfg__load_line(struct cfg* self, char* line)
|
||||||
return cfg__load_key_value(self, key, value);
|
return cfg__load_key_value(self, key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static char* cfg__dirname(const char* path)
|
|
||||||
{
|
|
||||||
char buffer[PATH_MAX];
|
|
||||||
return strdup(dirname(realpath(path, buffer)));
|
|
||||||
}
|
|
||||||
|
|
||||||
int cfg_load(struct cfg* self, const char* requested_path)
|
int cfg_load(struct cfg* self, const char* requested_path)
|
||||||
{
|
{
|
||||||
const char* path = requested_path ? requested_path
|
const char* path = requested_path ? requested_path
|
||||||
: cfg__get_default_path();
|
: cfg__get_default_path();
|
||||||
|
|
||||||
if (!path)
|
if (!path)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
@ -125,8 +118,6 @@ int cfg_load(struct cfg* self, const char* requested_path)
|
||||||
if (!stream)
|
if (!stream)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
self->directory = cfg__dirname(path);
|
|
||||||
|
|
||||||
char* line = NULL;
|
char* line = NULL;
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
int lineno = 0;
|
int lineno = 0;
|
||||||
|
@ -145,7 +136,6 @@ int cfg_load(struct cfg* self, const char* requested_path)
|
||||||
failure:
|
failure:
|
||||||
cfg_destroy(self);
|
cfg_destroy(self);
|
||||||
free(line);
|
free(line);
|
||||||
free(self->directory);
|
|
||||||
fclose(stream);
|
fclose(stream);
|
||||||
return lineno;
|
return lineno;
|
||||||
}
|
}
|
||||||
|
@ -163,5 +153,4 @@ void cfg_destroy(struct cfg* self)
|
||||||
#undef DESTROY_string
|
#undef DESTROY_string
|
||||||
#undef DESTROY_uint
|
#undef DESTROY_uint
|
||||||
#undef DESTROY_bool
|
#undef DESTROY_bool
|
||||||
free(self->directory);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022-2023 Jim Ramsay
|
* Copyright (c) 2022-2023 Jim Ramsay
|
||||||
* Copyright (c) 2023 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
|
||||||
|
@ -228,7 +227,7 @@ static json_t* json_from_buffer(struct ctl_client* self)
|
||||||
}
|
}
|
||||||
|
|
||||||
json_error_t err;
|
json_error_t err;
|
||||||
json_t* root = json_loadb(self->read_buffer, self->read_len, JSON_DISABLE_EOF_CHECK, &err);
|
json_t* root = json_loadb(self->read_buffer, self->read_len, 0, &err);
|
||||||
if (root) {
|
if (root) {
|
||||||
advance_read_buffer(&self->read_buffer, &self->read_len,
|
advance_read_buffer(&self->read_buffer, &self->read_len,
|
||||||
err.position);
|
err.position);
|
||||||
|
@ -354,17 +353,17 @@ static void pretty_client_list(json_t* data)
|
||||||
json_t* value;
|
json_t* value;
|
||||||
json_array_foreach(data, i, value) {
|
json_array_foreach(data, i, value) {
|
||||||
char* id = NULL;
|
char* id = NULL;
|
||||||
char* address = NULL;
|
char* hostname = NULL;
|
||||||
char* username = NULL;
|
char* username = NULL;
|
||||||
|
|
||||||
json_unpack(value, "{s:s, s?s, s?s}", "id", &id, "address",
|
json_unpack(value, "{s:s, s?s, s?s}", "id", &id, "hostname",
|
||||||
&address, "username", &username);
|
&hostname, "username", &username);
|
||||||
printf(" %s: ", id);
|
printf(" %s: ", id);
|
||||||
|
|
||||||
if (username)
|
if (username)
|
||||||
printf("%s@", username);
|
printf("%s@", username);
|
||||||
|
|
||||||
printf("%s\n", address ? address : "<unknown>");
|
printf("%s\n", hostname ? hostname : "<unknown>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -404,8 +403,6 @@ static void pretty_print(json_t* data,
|
||||||
case CMD_OUTPUT_LIST:
|
case CMD_OUTPUT_LIST:
|
||||||
pretty_output_list(data);
|
pretty_output_list(data);
|
||||||
break;
|
break;
|
||||||
case CMD_ATTACH:
|
|
||||||
case CMD_DETACH:
|
|
||||||
case CMD_CLIENT_DISCONNECT:
|
case CMD_CLIENT_DISCONNECT:
|
||||||
case CMD_OUTPUT_SET:
|
case CMD_OUTPUT_SET:
|
||||||
case CMD_OUTPUT_CYCLE:
|
case CMD_OUTPUT_CYCLE:
|
||||||
|
@ -621,26 +618,14 @@ static int ctl_client_register_for_events(struct ctl_client* self,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ctl_client_reconnect_event_loop(struct ctl_client* self,
|
static int ctl_client_reconnect_event_loop(struct ctl_client* self,
|
||||||
struct jsonipc_request* request)
|
struct jsonipc_request* request, int timeout)
|
||||||
{
|
{
|
||||||
if (ctl_client_connect(self, -1) != 0)
|
if (ctl_client_connect(self, timeout) != 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return ctl_client_register_for_events(self, request);
|
return ctl_client_register_for_events(self, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int block_until_reconnect(struct ctl_client* self,
|
|
||||||
struct jsonipc_request* request)
|
|
||||||
{
|
|
||||||
while (ctl_client_reconnect_event_loop(self, request) != 0)
|
|
||||||
if (usleep(50000) == -1) {
|
|
||||||
DEBUG("Interrupted waiting for the IPC socket");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ctl_client_event_loop(struct ctl_client* self,
|
static int ctl_client_event_loop(struct ctl_client* self,
|
||||||
struct jsonipc_request* request)
|
struct jsonipc_request* request)
|
||||||
{
|
{
|
||||||
|
@ -657,8 +642,8 @@ static int ctl_client_event_loop(struct ctl_client* self,
|
||||||
if (errno == ECONNRESET) {
|
if (errno == ECONNRESET) {
|
||||||
send_shutdown_event(self);
|
send_shutdown_event(self);
|
||||||
if (self->flags & CTL_CLIENT_RECONNECT &&
|
if (self->flags & CTL_CLIENT_RECONNECT &&
|
||||||
block_until_reconnect(
|
ctl_client_reconnect_event_loop(
|
||||||
self, request) == 0)
|
self, request, -1) == 0)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -674,16 +659,12 @@ static int ctl_client_event_loop(struct ctl_client* self,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ctl_client_print_single_command(struct ctl_client* self,
|
static int ctl_client_print_single_command(struct ctl_client* self,
|
||||||
enum cmd_type cmd, struct jsonipc_request* request)
|
struct jsonipc_request* request)
|
||||||
{
|
{
|
||||||
struct jsonipc_response* response = ctl_client_run_single_command(self,
|
struct jsonipc_response* response = ctl_client_run_single_command(self,
|
||||||
request);
|
request);
|
||||||
if (!response) {
|
if (!response)
|
||||||
if (errno == ECONNRESET && cmd == CMD_WAYVNC_EXIT)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
|
||||||
|
|
||||||
int result = ctl_client_print_response(self, request, response);
|
int result = ctl_client_print_response(self, request, response);
|
||||||
jsonipc_response_destroy(response);
|
jsonipc_response_destroy(response);
|
||||||
|
@ -915,7 +896,7 @@ int ctl_client_run_command(struct ctl_client* self,
|
||||||
result = ctl_client_event_loop(self, request);
|
result = ctl_client_event_loop(self, request);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
result = ctl_client_print_single_command(self, cmd, request);
|
result = ctl_client_print_single_command(self, request);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022-2023 Jim Ramsay
|
* Copyright (c) 2022-2023 Jim Ramsay
|
||||||
* Copyright (c) 2023 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
|
||||||
|
@ -21,19 +20,6 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
struct cmd_info ctl_command_list[] = {
|
struct cmd_info ctl_command_list[] = {
|
||||||
[CMD_ATTACH] = { "attach",
|
|
||||||
"Attach to a running wayland compositor",
|
|
||||||
{
|
|
||||||
{ "display", "Display name", "<name>",
|
|
||||||
.positional = true },
|
|
||||||
{},
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
[CMD_DETACH] = { "detach",
|
|
||||||
"Detach from the wayland compositor",
|
|
||||||
{{}},
|
|
||||||
},
|
|
||||||
[CMD_HELP] = { "help",
|
[CMD_HELP] = { "help",
|
||||||
"List all commands and events, or show usage of a specific command or event",
|
"List all commands and events, or show usage of a specific command or event",
|
||||||
{
|
{
|
||||||
|
@ -98,8 +84,8 @@ struct cmd_info ctl_command_list[] = {
|
||||||
{ "connection_count", \
|
{ "connection_count", \
|
||||||
"The total number of connected VNC clients " including " this one.", \
|
"The total number of connected VNC clients " including " this one.", \
|
||||||
"<integer>" }, \
|
"<integer>" }, \
|
||||||
{ "address", \
|
{ "hostname", \
|
||||||
"The IP address of this client (may be null)", \
|
"The hostname or IP address of this client (may be null)", \
|
||||||
"<name|ip>" }, \
|
"<name|ip>" }, \
|
||||||
{ "username", \
|
{ "username", \
|
||||||
"The username used to authentice this client (may be null).", \
|
"The username used to authentice this client (may be null).", \
|
||||||
|
@ -124,24 +110,6 @@ struct cmd_info ctl_event_list[] = {
|
||||||
"Sent by waynvc when a VNC client disconnects",
|
"Sent by waynvc when a VNC client disconnects",
|
||||||
{ CLIENT_EVENT_PARAMS("not including") }
|
{ CLIENT_EVENT_PARAMS("not including") }
|
||||||
},
|
},
|
||||||
[EVT_DETACHED] = {"detached",
|
|
||||||
"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)
|
enum cmd_type ctl_command_parse_name(const char* name)
|
||||||
|
|
169
src/ctl-server.c
169
src/ctl-server.c
|
@ -1,6 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022-2023 Jim Ramsay
|
* Copyright (c) 2022-2023 Jim Ramsay
|
||||||
* Copyright (c) 2023 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
|
||||||
|
@ -27,8 +26,6 @@
|
||||||
#include <neatvnc.h>
|
#include <neatvnc.h>
|
||||||
#include <aml.h>
|
#include <aml.h>
|
||||||
#include <jansson.h>
|
#include <jansson.h>
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
|
|
||||||
#include "output.h"
|
#include "output.h"
|
||||||
#include "ctl-commands.h"
|
#include "ctl-commands.h"
|
||||||
|
@ -49,11 +46,6 @@ struct cmd {
|
||||||
enum cmd_type type;
|
enum cmd_type type;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct cmd_attach {
|
|
||||||
struct cmd cmd;
|
|
||||||
char display[128];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct cmd_help {
|
struct cmd_help {
|
||||||
struct cmd cmd;
|
struct cmd cmd;
|
||||||
char id[64];
|
char id[64];
|
||||||
|
@ -113,19 +105,6 @@ static void cmd_response_destroy(struct cmd_response* self)
|
||||||
free(self);
|
free(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct cmd_attach* cmd_attach_new(json_t* args,
|
|
||||||
struct jsonipc_error* err)
|
|
||||||
{
|
|
||||||
const char* display = NULL;
|
|
||||||
if (json_unpack(args, "{s:s}", "display", &display) == -1) {
|
|
||||||
jsonipc_error_printf(err, EINVAL, "Missing display name");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
struct cmd_attach* cmd = calloc(1, sizeof(*cmd));
|
|
||||||
strlcpy(cmd->display, display, sizeof(cmd->display));
|
|
||||||
return cmd;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct cmd_help* cmd_help_new(json_t* args,
|
static struct cmd_help* cmd_help_new(json_t* args,
|
||||||
struct jsonipc_error* err)
|
struct jsonipc_error* err)
|
||||||
{
|
{
|
||||||
|
@ -206,9 +185,6 @@ static struct cmd* parse_command(struct jsonipc_request* ipc,
|
||||||
enum cmd_type cmd_type = ctl_command_parse_name(ipc->method);
|
enum cmd_type cmd_type = ctl_command_parse_name(ipc->method);
|
||||||
struct cmd* cmd = NULL;
|
struct cmd* cmd = NULL;
|
||||||
switch (cmd_type) {
|
switch (cmd_type) {
|
||||||
case CMD_ATTACH:
|
|
||||||
cmd = (struct cmd*)cmd_attach_new(ipc->params, err);
|
|
||||||
break;
|
|
||||||
case CMD_HELP:
|
case CMD_HELP:
|
||||||
cmd = (struct cmd*)cmd_help_new(ipc->params, err);
|
cmd = (struct cmd*)cmd_help_new(ipc->params, err);
|
||||||
break;
|
break;
|
||||||
|
@ -218,7 +194,6 @@ static struct cmd* parse_command(struct jsonipc_request* ipc,
|
||||||
case CMD_CLIENT_DISCONNECT:
|
case CMD_CLIENT_DISCONNECT:
|
||||||
cmd = (struct cmd*)cmd_disconnect_client_new(ipc->params, err);
|
cmd = (struct cmd*)cmd_disconnect_client_new(ipc->params, err);
|
||||||
break;
|
break;
|
||||||
case CMD_DETACH:
|
|
||||||
case CMD_VERSION:
|
case CMD_VERSION:
|
||||||
case CMD_EVENT_RECEIVE:
|
case CMD_EVENT_RECEIVE:
|
||||||
case CMD_CLIENT_LIST:
|
case CMD_CLIENT_LIST:
|
||||||
|
@ -353,76 +328,24 @@ static struct cmd_response* generate_version_object()
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct ctl_server_client* ctl_server_client_first(struct ctl* self)
|
|
||||||
{
|
|
||||||
return self->actions.client_next(self, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct ctl_server_client* ctl_server_client_next(struct ctl* self,
|
|
||||||
struct ctl_server_client* prev)
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
return self->actions.client_info(client, info);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct cmd_response* generate_vnc_client_list(struct ctl* self)
|
static struct cmd_response* generate_vnc_client_list(struct ctl* self)
|
||||||
{
|
{
|
||||||
|
struct ctl_server_vnc_client* clients;
|
||||||
|
size_t num_clients = self->actions.get_client_list(self, &clients);
|
||||||
struct cmd_response* response = cmd_ok();
|
struct cmd_response* response = cmd_ok();
|
||||||
|
|
||||||
response->data = json_array();
|
response->data = json_array();
|
||||||
|
for (size_t i = 0; i < num_clients; ++i) {
|
||||||
struct ctl_server_client* client;
|
json_t* packed = json_pack("{s:s}", "id", clients[i].id);
|
||||||
for (client = ctl_server_client_first(self); client;
|
if (clients[i].hostname[0] != '\0')
|
||||||
client = ctl_server_client_next(self, client)) {
|
json_object_set_new(packed, "hostname",
|
||||||
struct ctl_server_client_info info = {};
|
json_string(clients[i].hostname));
|
||||||
ctl_server_client_get_info(self, client, &info);
|
if (clients[i].username[0] != '\0')
|
||||||
|
|
||||||
char id_str[64];
|
|
||||||
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.username)
|
|
||||||
json_object_set_new(packed, "username",
|
json_object_set_new(packed, "username",
|
||||||
json_string(info.username));
|
json_string(clients[i].username));
|
||||||
|
|
||||||
if (info.seat)
|
|
||||||
json_object_set_new(packed, "seat",
|
|
||||||
json_string(info.seat));
|
|
||||||
|
|
||||||
json_array_append_new(response->data, packed);
|
json_array_append_new(response->data, packed);
|
||||||
}
|
}
|
||||||
|
free(clients);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -454,11 +377,6 @@ static struct cmd_response* ctl_server_dispatch_cmd(struct ctl* self,
|
||||||
nvnc_log(NVNC_LOG_INFO, "Dispatching control client command '%s'", info->name);
|
nvnc_log(NVNC_LOG_INFO, "Dispatching control client command '%s'", info->name);
|
||||||
struct cmd_response* response = NULL;
|
struct cmd_response* response = NULL;
|
||||||
switch (cmd->type) {
|
switch (cmd->type) {
|
||||||
case CMD_ATTACH:{
|
|
||||||
struct cmd_attach* c = (struct cmd_attach*)cmd;
|
|
||||||
response = self->actions.on_attach(self, c->display);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CMD_HELP:{
|
case CMD_HELP:{
|
||||||
struct cmd_help* c = (struct cmd_help*)cmd;
|
struct cmd_help* c = (struct cmd_help*)cmd;
|
||||||
response = generate_help_object(c->id, c->id_is_command);
|
response = generate_help_object(c->id, c->id_is_command);
|
||||||
|
@ -475,9 +393,6 @@ static struct cmd_response* ctl_server_dispatch_cmd(struct ctl* self,
|
||||||
response = self->actions.on_disconnect_client(self, c->id);
|
response = self->actions.on_disconnect_client(self, c->id);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CMD_DETACH:
|
|
||||||
response = self->actions.on_detach(self);
|
|
||||||
break;
|
|
||||||
case CMD_WAYVNC_EXIT:
|
case CMD_WAYVNC_EXIT:
|
||||||
response = self->actions.on_wayvnc_exit(self);
|
response = self->actions.on_wayvnc_exit(self);
|
||||||
break;
|
break;
|
||||||
|
@ -914,22 +829,15 @@ struct cmd_response* cmd_failed(const char* fmt, ...)
|
||||||
}
|
}
|
||||||
|
|
||||||
json_t* pack_connection_event_params(
|
json_t* pack_connection_event_params(
|
||||||
const struct ctl_server_client_info *info,
|
const char* client_id,
|
||||||
|
const char* client_hostname,
|
||||||
|
const char* client_username,
|
||||||
int new_connection_count)
|
int new_connection_count)
|
||||||
{
|
{
|
||||||
// TODO: Why is the id a string?
|
return json_pack("{s:s, s:s?, s:s?, s:i}",
|
||||||
char id_str[64];
|
"id", client_id,
|
||||||
snprintf(id_str, sizeof(id_str), "%d", info->id);
|
"hostname", client_hostname,
|
||||||
|
"username", client_username,
|
||||||
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,
|
|
||||||
"username", info->username,
|
|
||||||
"seat", info->seat,
|
|
||||||
"connection_count", new_connection_count);
|
"connection_count", new_connection_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -971,28 +879,34 @@ int ctl_server_enqueue_event(struct ctl* self, enum event_type evt_type,
|
||||||
|
|
||||||
static void ctl_server_event_connect(struct ctl* self,
|
static void ctl_server_event_connect(struct ctl* self,
|
||||||
enum event_type evt_type,
|
enum event_type evt_type,
|
||||||
const struct ctl_server_client_info *info,
|
const char* client_id,
|
||||||
|
const char* client_hostname,
|
||||||
|
const char* client_username,
|
||||||
int new_connection_count)
|
int new_connection_count)
|
||||||
{
|
{
|
||||||
json_t* params =
|
json_t* params = pack_connection_event_params(client_id, client_hostname,
|
||||||
pack_connection_event_params(info, new_connection_count);
|
client_username, new_connection_count);
|
||||||
ctl_server_enqueue_event(self, evt_type, params);
|
ctl_server_enqueue_event(self, evt_type, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ctl_server_event_connected(struct ctl* self,
|
void ctl_server_event_connected(struct ctl* self,
|
||||||
const struct ctl_server_client_info *info,
|
const char* client_id,
|
||||||
|
const char* client_hostname,
|
||||||
|
const char* client_username,
|
||||||
int new_connection_count)
|
int new_connection_count)
|
||||||
{
|
{
|
||||||
ctl_server_event_connect(self, EVT_CLIENT_CONNECTED, info,
|
ctl_server_event_connect(self, EVT_CLIENT_CONNECTED, client_id,
|
||||||
new_connection_count);
|
client_hostname, client_username, new_connection_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ctl_server_event_disconnected(struct ctl* self,
|
void ctl_server_event_disconnected(struct ctl* self,
|
||||||
const struct ctl_server_client_info *info,
|
const char* client_id,
|
||||||
|
const char* client_hostname,
|
||||||
|
const char* client_username,
|
||||||
int new_connection_count)
|
int new_connection_count)
|
||||||
{
|
{
|
||||||
ctl_server_event_connect(self, EVT_CLIENT_DISCONNECTED, info,
|
ctl_server_event_connect(self, EVT_CLIENT_DISCONNECTED, client_id,
|
||||||
new_connection_count);
|
client_hostname, client_username, new_connection_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ctl_server_event_capture_changed(struct ctl* self,
|
void ctl_server_event_capture_changed(struct ctl* self,
|
||||||
|
@ -1001,20 +915,3 @@ void ctl_server_event_capture_changed(struct ctl* self,
|
||||||
ctl_server_enqueue_event(self, EVT_CAPTURE_CHANGED,
|
ctl_server_enqueue_event(self, EVT_CAPTURE_CHANGED,
|
||||||
json_pack("{s:s}", "output", captured_output));
|
json_pack("{s:s}", "output", captured_output));
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
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;
|
|
||||||
}
|
|
||||||
|
|
1025
src/main.c
1025
src/main.c
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
struct output* self = data;
|
||||||
|
|
||||||
strlcpy(self->name, name, sizeof(self->name));
|
strlcpy(self->name, name, sizeof(self->name));
|
||||||
self->is_headless =
|
nvnc_trace("Output %u name: %s", self->id, self->name);
|
||||||
(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");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void output_description(void* data, struct zxdg_output_v1* xdg_output,
|
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 pam_response** response, void* appdata_ptr)
|
||||||
{
|
{
|
||||||
struct credentials* cred = 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++) {
|
for (int i = 0; i < num_msg; i++) {
|
||||||
resp[i].resp_retcode = PAM_SUCCESS;
|
resp[i].resp_retcode = PAM_SUCCESS;
|
||||||
switch(msgm[i]->msg_style) {
|
switch(msgm[i]->msg_style) {
|
||||||
|
|
37
src/pixels.c
37
src/pixels.c
|
@ -44,40 +44,3 @@ uint32_t fourcc_from_wl_shm(enum wl_shm_format in)
|
||||||
|
|
||||||
return in;
|
return in;
|
||||||
}
|
}
|
||||||
|
|
||||||
int pixel_size_from_fourcc(uint32_t fourcc)
|
|
||||||
{
|
|
||||||
switch (fourcc & ~DRM_FORMAT_BIG_ENDIAN) {
|
|
||||||
case DRM_FORMAT_RGBA1010102:
|
|
||||||
case DRM_FORMAT_RGBX1010102:
|
|
||||||
case DRM_FORMAT_BGRA1010102:
|
|
||||||
case DRM_FORMAT_BGRX1010102:
|
|
||||||
case DRM_FORMAT_ARGB2101010:
|
|
||||||
case DRM_FORMAT_XRGB2101010:
|
|
||||||
case DRM_FORMAT_ABGR2101010:
|
|
||||||
case DRM_FORMAT_XBGR2101010:
|
|
||||||
case DRM_FORMAT_RGBA8888:
|
|
||||||
case DRM_FORMAT_RGBX8888:
|
|
||||||
case DRM_FORMAT_BGRA8888:
|
|
||||||
case DRM_FORMAT_BGRX8888:
|
|
||||||
case DRM_FORMAT_ARGB8888:
|
|
||||||
case DRM_FORMAT_XRGB8888:
|
|
||||||
case DRM_FORMAT_ABGR8888:
|
|
||||||
case DRM_FORMAT_XBGR8888:
|
|
||||||
return 4;
|
|
||||||
case DRM_FORMAT_BGR888:
|
|
||||||
case DRM_FORMAT_RGB888:
|
|
||||||
return 3;
|
|
||||||
case DRM_FORMAT_RGBA4444:
|
|
||||||
case DRM_FORMAT_RGBX4444:
|
|
||||||
case DRM_FORMAT_BGRA4444:
|
|
||||||
case DRM_FORMAT_BGRX4444:
|
|
||||||
case DRM_FORMAT_ARGB4444:
|
|
||||||
case DRM_FORMAT_XRGB4444:
|
|
||||||
case DRM_FORMAT_ABGR4444:
|
|
||||||
case DRM_FORMAT_XBGR4444:
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
|
@ -298,8 +298,5 @@ void screencopy_destroy(struct screencopy* self)
|
||||||
if (self->front)
|
if (self->front)
|
||||||
wv_buffer_pool_release(self->pool, self->front);
|
wv_buffer_pool_release(self->pool, self->front);
|
||||||
|
|
||||||
self->back = NULL;
|
|
||||||
self->front = NULL;
|
|
||||||
|
|
||||||
wv_buffer_pool_destroy(self->pool);
|
wv_buffer_pool_destroy(self->pool);
|
||||||
}
|
}
|
||||||
|
|
11
src/seat.c
11
src/seat.c
|
@ -97,17 +97,6 @@ struct seat* seat_find_by_id(struct wl_list* list, uint32_t id)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct seat* seat_find_unoccupied(struct wl_list* list)
|
|
||||||
{
|
|
||||||
struct seat* seat;
|
|
||||||
|
|
||||||
wl_list_for_each(seat, list, link)
|
|
||||||
if (seat->occupancy == 0)
|
|
||||||
return seat;
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct seat* seat_first(struct wl_list* list)
|
struct seat* seat_first(struct wl_list* list)
|
||||||
{
|
{
|
||||||
struct seat* seat;
|
struct seat* seat;
|
||||||
|
|
|
@ -23,7 +23,9 @@
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
const char* wayvnc_version =
|
const char* wayvnc_version =
|
||||||
#if defined(PROJECT_VERSION)
|
#if defined(GIT_VERSION)
|
||||||
|
GIT_VERSION;
|
||||||
|
#elif defined(PROJECT_VERSION)
|
||||||
PROJECT_VERSION;
|
PROJECT_VERSION;
|
||||||
#else
|
#else
|
||||||
"UNKNOWN";
|
"UNKNOWN";
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
# Integration Testing
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
The integration tests currently require that the following tools are installed:
|
|
||||||
- sway (1.8 or later)
|
|
||||||
- lsof
|
|
||||||
- jq
|
|
||||||
- bash
|
|
||||||
- vncdotool
|
|
||||||
|
|
||||||
Most of these are available in your normal distro package manager, except
|
|
||||||
vncdotool which is a python tool and installable via pip:
|
|
||||||
|
|
||||||
```
|
|
||||||
pip install vncdotool
|
|
||||||
```
|
|
||||||
|
|
||||||
## Running
|
|
||||||
|
|
||||||
```
|
|
||||||
./test/integration/integration.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
Two test suites are defined:
|
|
||||||
|
|
||||||
### Smoke test
|
|
||||||
|
|
||||||
Tests basic functionality such as:
|
|
||||||
- Can wayvnc start and connect to wayland?
|
|
||||||
- Does the wayvncctl IPC mechanism work (both control and events)?
|
|
||||||
- Can a VNC client connect and send a keystroke through to sway?
|
|
||||||
|
|
||||||
### Multi-output test
|
|
||||||
|
|
||||||
Tests wayvnc with a multi-output sway, including:
|
|
||||||
- Do we detect additions and removals of outputs?
|
|
||||||
- Do the wayvncctl commands to cycle and switch outputs work?
|
|
||||||
|
|
|
@ -1,43 +1,17 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
#
|
#
|
||||||
# This is free and unencumbered software released into the public domain.
|
|
||||||
#
|
|
||||||
# Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
||||||
# distribute this software, either in source code form or as a compiled
|
|
||||||
# binary, for any purpose, commercial or non-commercial, and by any
|
|
||||||
# means.
|
|
||||||
#
|
|
||||||
# In jurisdictions that recognize copyright laws, the author or authors
|
|
||||||
# of this software dedicate any and all copyright interest in the
|
|
||||||
# software to the public domain. We make this dedication for the benefit
|
|
||||||
# of the public at large and to the detriment of our heirs and
|
|
||||||
# successors. We intend this dedication to be an overt act of
|
|
||||||
# relinquishment in perpetuity of all present and future rights to this
|
|
||||||
# software under copyright law.
|
|
||||||
#
|
|
||||||
# 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 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.
|
|
||||||
#
|
|
||||||
# For more information, please refer to <http://unlicense.org/>
|
|
||||||
|
|
||||||
# Integration test for wayvnc
|
# Integration test for wayvnc
|
||||||
#
|
#
|
||||||
# For now, this doesn't do much, but does check that some basic functionality isn't DOA
|
# For now, this doesn't do much, but does check that some basic functionality isn't DOA
|
||||||
#
|
#
|
||||||
# Prerequisites:
|
# Prerequisites:
|
||||||
# - wayvnc and wayvncctl are built in ../build/, or in the $PATH
|
# - wayvnc and wayvncctl are built in ../build/, or in the $PATH
|
||||||
# - Override by setting $WAYVNC and $WAYVNCCTL or $WAYVNC_BUILD_DIR
|
# - Override by setting $WAYVNC and $WAYVNCCTL
|
||||||
# - sway and swaymsg are in the $PATH
|
# - sway and swaymsg are in the $PATH
|
||||||
# - Override by setting $SWAY and $SWAYMSG
|
# - Override by setting $SWAY and $SWAYMSG
|
||||||
# - jq for parsing json output is in the $PATH
|
# - jq for parsing json output is in the $PATH
|
||||||
# - lsof for TCP port checking is in the $PATH
|
# -lsof for TCP port checking
|
||||||
# - vncdo for client testing is in the $PATH
|
#
|
||||||
# (pip install vncdotool)
|
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
@ -45,9 +19,9 @@ INTEGRATION_ROOT=$(realpath "$(dirname "$0")")
|
||||||
REPO_ROOT=$(realpath "$INTEGRATION_ROOT/../..")
|
REPO_ROOT=$(realpath "$INTEGRATION_ROOT/../..")
|
||||||
WAYVNC_BUILD_DIR=${WAYVNC_BUILD_DIR:-$(realpath "$REPO_ROOT/build")}
|
WAYVNC_BUILD_DIR=${WAYVNC_BUILD_DIR:-$(realpath "$REPO_ROOT/build")}
|
||||||
if [[ -d $WAYVNC_BUILD_DIR ]]; then
|
if [[ -d $WAYVNC_BUILD_DIR ]]; then
|
||||||
export PATH=$WAYVNC_BUILD_DIR:$PATH
|
export PATH=$WAYVNC_BUILD_DIR:$PATH
|
||||||
fi
|
fi
|
||||||
echo "Looking for required binaries..."
|
echo "Looking for required binaries..."
|
||||||
WAYVNC=${WAYVNC:-$(which wayvnc)}
|
WAYVNC=${WAYVNC:-$(which wayvnc)}
|
||||||
WAYVNCCTL=${WAYVNCCTL:-$(which wayvncctl)}
|
WAYVNCCTL=${WAYVNCCTL:-$(which wayvncctl)}
|
||||||
SWAY=${SWAY:-$(which sway)}
|
SWAY=${SWAY:-$(which sway)}
|
||||||
|
@ -55,326 +29,146 @@ SWAYMSG=${SWAYMSG:-$(which swaymsg)}
|
||||||
echo "Found: $WAYVNC $WAYVNCCTL $SWAY $SWAYMSG"
|
echo "Found: $WAYVNC $WAYVNCCTL $SWAY $SWAYMSG"
|
||||||
$WAYVNC --version
|
$WAYVNC --version
|
||||||
$SWAY --version
|
$SWAY --version
|
||||||
IFS=" .-" read -r _ _ SWAYMAJOR SWAYMINOR _ < <($SWAY --version)
|
|
||||||
VNCDO=${VNCDO:-$(which vncdo)}
|
|
||||||
$VNCDO --version 2>/dev/null
|
|
||||||
|
|
||||||
export XDG_CONFIG_HOME=$INTEGRATION_ROOT/xdg_config
|
export XDG_CONFIG_HOME=$INTEGRATION_ROOT/xdg_config
|
||||||
export XDG_RUNTIME_DIR=/tmp/wayvnc-integration-$$
|
export XDG_RUNTIME_DIR=/tmp/wayvnc-integration-$$
|
||||||
|
mkdir -p "$XDG_RUNTIME_DIR"
|
||||||
test_setup() {
|
|
||||||
[[ -d "$XDG_RUNTIME_DIR" ]] && rm -rf "$XDG_RUNTIME_DIR"
|
|
||||||
mkdir -p "$XDG_RUNTIME_DIR"
|
|
||||||
echo "=============================================="
|
|
||||||
echo "$*"
|
|
||||||
echo "=============================================="
|
|
||||||
}
|
|
||||||
|
|
||||||
TIMEOUT_COUNTER=0
|
TIMEOUT_COUNTER=0
|
||||||
TIMEOUT_MAXCOUNT=1
|
TIMEOUT_MAXCOUNT=1
|
||||||
TIMEOUT_DELAY=0.1
|
TIMEOUT_DELAY=0.1
|
||||||
timeout_init() {
|
timeout_init() {
|
||||||
TIMEOUT_COUNTER=0
|
TIMEOUT_COUNTER=0
|
||||||
TIMEOUT_MAXCOUNT=${1:-5}
|
TIMEOUT_MAXCOUNT=${1:-5}
|
||||||
TIMEOUT_DELAY=${2:-0.1}
|
TIMEOUT_DELAY=${2:-0.1}
|
||||||
}
|
}
|
||||||
|
|
||||||
timeout_check() {
|
timeout_check() {
|
||||||
if [[ $(( TIMEOUT_COUNTER++ )) -gt $TIMEOUT_MAXCOUNT ]]; then
|
if [[ $(( TIMEOUT_COUNTER++ )) -gt $TIMEOUT_MAXCOUNT ]]; then
|
||||||
return 1
|
echo "Timeout"
|
||||||
fi
|
return 1
|
||||||
sleep "$TIMEOUT_DELAY"
|
fi
|
||||||
}
|
sleep "$TIMEOUT_DELAY"
|
||||||
|
|
||||||
wait_until() {
|
|
||||||
timeout_init 10
|
|
||||||
local last
|
|
||||||
until last=$(eval "$*" 2>&1); do
|
|
||||||
if ! timeout_check; then
|
|
||||||
echo "Timeout waiting for $*" >&2
|
|
||||||
printf "%s\n" "$last" >&2
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
[[ -z $last ]] || printf "%s\n" "$last"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SWAY_ENV=$XDG_RUNTIME_DIR/sway.env
|
SWAY_ENV=$XDG_RUNTIME_DIR/sway.env
|
||||||
SWAY_PID=
|
SWAY_PID=
|
||||||
start_sway() {
|
start_sway() {
|
||||||
echo "Starting sway..."
|
echo "Starting sway..."
|
||||||
SWAY_LOG=$XDG_RUNTIME_DIR/sway.log
|
SWAY_LOG=$XDG_RUNTIME_DIR/sway.log
|
||||||
WLR_BACKENDS=headless \
|
WLR_BACKENDS=headless \
|
||||||
WLR_LIBINPUT_NO_DEVICES=1 \
|
WLR_LIBINPUT_NO_DEVICES=1 \
|
||||||
$SWAY &>"$SWAY_LOG" &
|
$SWAY &>"$SWAY_LOG" &
|
||||||
SWAY_PID=$!
|
SWAY_PID=$!
|
||||||
wait_until [[ -f "$SWAY_ENV" ]] >/dev/null
|
timeout_init 10
|
||||||
WAYLAND_DISPLAY=$(grep ^WAYLAND_DISPLAY= "$SWAY_ENV" | cut -d= -f2-)
|
while [[ ! -f $SWAY_ENV ]]; do
|
||||||
SWAYSOCK=$(grep ^SWAYSOCK= "$SWAY_ENV" | cut -d= -f2-)
|
timeout_check
|
||||||
export WAYLAND_DISPLAY SWAYSOCK
|
done
|
||||||
echo " sway is managing $WAYLAND_DISPLAY at $SWAYSOCK"
|
grep -e ^WAYLAND_DISPLAY -e ^SWAYSOCK $SWAY_ENV >$SWAY_ENV.trim
|
||||||
|
. $SWAY_ENV.trim
|
||||||
|
export WAYLAND_DISPLAY SWAYSOCK
|
||||||
|
echo " sway is running: $SWAYSOCK"
|
||||||
}
|
}
|
||||||
|
|
||||||
stop_sway() {
|
stop_sway() {
|
||||||
[[ -z $SWAY_PID ]] && return 0
|
[[ -z $SWAY_PID ]] && return 0
|
||||||
echo "Stopping sway ($SWAY_PID)"
|
echo "Stopping sway ($SWAY_PID)"
|
||||||
kill "$SWAY_PID"
|
kill "$SWAY_PID"
|
||||||
unset SWAY_PID WAYLAND_DISPLAY SWAYSOCK
|
unset SWAY_PID WAYLAND_DISPLAY SWAYSOCK
|
||||||
rm -f "$SWAY_ENV" || true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WAYVNC_PID=
|
WAYVNC_PID=
|
||||||
WAYVNC_ADDRESS=127.0.0.1
|
WAYVNC_ADDRESS=localhost
|
||||||
WAYVNC_PORT=5999
|
WAYVNC_PORT=5999
|
||||||
start_wayvnc() {
|
start_wayvnc() {
|
||||||
echo "Starting wayvnc..."
|
echo "Starting wayvnc..."
|
||||||
WAYVNC_LOG=$XDG_RUNTIME_DIR/wayvnc.log
|
WAYVNC_LOG=$XDG_RUNTIME_DIR/wayvnc.log
|
||||||
$WAYVNC "$@" -L debug "$WAYVNC_ADDRESS" "$WAYVNC_PORT" &>$WAYVNC_LOG &
|
$WAYVNC "$WAYVNC_ADDRESS" "$WAYVNC_PORT" &>$WAYVNC_LOG &
|
||||||
WAYVNC_PID=$!
|
WAYVNC_PID=$!
|
||||||
# Wait for the VNC listening port
|
# Wait for the VNC listening port
|
||||||
echo " Started $WAYVNC_PID"
|
echo " Started $WAYVNC_PID"
|
||||||
wait_until lsof -a -p$WAYVNC_PID -iTCP@$WAYVNC_ADDRESS:$WAYVNC_PORT \
|
timeout_init
|
||||||
-sTCP:LISTEN >/dev/null
|
while ! lsof -a -p$WAYVNC_PID -iTCP@$WAYVNC_ADDRESS:$WAYVNC_PORT -sTCP:LISTEN &>/dev/null; do
|
||||||
echo " Listening on $WAYVNC_ADDRESS:$WAYVNC_PORT"
|
timeout_check
|
||||||
# Wait for the control socket
|
done
|
||||||
wait_until [[ -S "$XDG_RUNTIME_DIR/wayvncctl" ]] >/dev/null
|
echo " Listening on $WAYVNC_ADDRESS:$WAYVNC_PORT"
|
||||||
echo " Control socket ready"
|
# Wait for the control socket
|
||||||
|
timeout_init
|
||||||
|
while [[ ! -S $XDG_RUNTIME_DIR/wayvncctl ]]; do
|
||||||
|
timeout_check
|
||||||
|
done
|
||||||
|
echo " Control socket ready"
|
||||||
}
|
}
|
||||||
|
|
||||||
stop_wayvnc() {
|
stop_wayvnc() {
|
||||||
[[ -z $WAYVNC_PID ]] && return 0
|
[[ -z $WAYVNC_PID ]] && return 0
|
||||||
echo "Stopping wayvnc ($WAYVNC_PID)"
|
echo "Stopping wayvnc ($WAYVNC_PID)"
|
||||||
kill "$WAYVNC_PID"
|
kill "$WAYVNC_PID"
|
||||||
unset WAYVNC_PID
|
unset WAYVNC_PID
|
||||||
}
|
|
||||||
|
|
||||||
WAYVNCCTL_PID=
|
|
||||||
WAYVNCCTL_LOG=$XDG_RUNTIME_DIR/wayvncctl.log
|
|
||||||
WAYVNCCTL_EVENTS=$XDG_RUNTIME_DIR/wayvncctl.events
|
|
||||||
start_wayvncctl_events() {
|
|
||||||
$WAYVNCCTL --verbose --wait --reconnect --json event-receive >"$WAYVNCCTL_EVENTS" 2>"$WAYVNCCTL_LOG" &
|
|
||||||
WAYVNCCTL_PID=$!
|
|
||||||
}
|
|
||||||
|
|
||||||
stop_wayvncctl_events() {
|
|
||||||
[[ -z $WAYVNCCTL_PID ]] && return 0
|
|
||||||
echo "Stopping wayvncctl event recorder ($WAYVNCCTL_PID)"
|
|
||||||
kill "$WAYVNCCTL_PID"
|
|
||||||
rm -f "$WAYVNCCTL_EVENTS" || true
|
|
||||||
unset WAYVNCCTL_PID
|
|
||||||
}
|
|
||||||
|
|
||||||
verify_events() {
|
|
||||||
local expected=("$@")
|
|
||||||
echo "Verifying recorded events"
|
|
||||||
local name i=0
|
|
||||||
while IFS= read -r EVT; do
|
|
||||||
name=$(jq -r '.method' <<<"$EVT")
|
|
||||||
ex=${expected[$((i++))]}
|
|
||||||
echo " Event: $name=~$ex"
|
|
||||||
[[ $name == "$ex" ]] || return 1
|
|
||||||
done <"$WAYVNCCTL_EVENTS"
|
|
||||||
if [[ $i -lt ${#expected[@]} ]]; then
|
|
||||||
while [[ $i -lt ${#expected[@]} ]]; do
|
|
||||||
echo " Missing: ${expected[$((i++))]}"
|
|
||||||
done
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
echo "Ok"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup() {
|
cleanup() {
|
||||||
result=$?
|
result=$?
|
||||||
set +e
|
set +e
|
||||||
stop_wayvnc
|
stop_wayvnc
|
||||||
stop_sway
|
stop_sway
|
||||||
stop_wayvncctl_events
|
if [[ $result != 0 ]]; then
|
||||||
if [[ $result != 0 ]]; then
|
echo SWAY LOG
|
||||||
echo
|
echo --------
|
||||||
echo SWAY LOG
|
cat $SWAY_LOG
|
||||||
echo --------
|
echo
|
||||||
cat "$SWAY_LOG"
|
echo WAYVNC_LOG
|
||||||
echo
|
echo ----------
|
||||||
echo WAYVNC_LOG
|
cat $WAYVNC_LOG
|
||||||
echo ----------
|
exit
|
||||||
cat "$WAYVNC_LOG"
|
fi
|
||||||
echo
|
rm -rf $XDG_RUNTIME_DIR
|
||||||
echo WAYVNCCTL_LOG
|
|
||||||
echo ----------
|
|
||||||
cat "$WAYVNCCTL_LOG"
|
|
||||||
echo
|
|
||||||
echo VNCDO_LOG
|
|
||||||
echo ----------
|
|
||||||
cat "$VNCDO_LOG"
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
[[ -d "$XDG_RUNTIME_DIR" ]] && rm -rf "$XDG_RUNTIME_DIR"
|
|
||||||
}
|
}
|
||||||
trap cleanup EXIT
|
trap cleanup EXIT
|
||||||
|
|
||||||
test_version_ipc() {
|
test_version_ipc() {
|
||||||
echo "Checking version command"
|
echo "Checking version command"
|
||||||
local version
|
local version
|
||||||
version=$($WAYVNCCTL --json version)
|
version=$($WAYVNCCTL --json version)
|
||||||
[[ -n $version ]]
|
[[ -n $version ]]
|
||||||
echo " version IPC returned data"
|
echo " version IPC returned data"
|
||||||
echo "ok"
|
echo "ok"
|
||||||
}
|
|
||||||
|
|
||||||
sway_active_outputs() {
|
|
||||||
$SWAYMSG -t get_outputs | jq 'map(select(.active == true))'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test_output_list_ipc() {
|
test_output_list_ipc() {
|
||||||
local expected_capture=${1:-HEADLESS-1}
|
echo "Checking output-list command"
|
||||||
echo "Checking output-list command"
|
local sway_json wayvnc_json
|
||||||
local sway_json wayvnc_json
|
sway_json=$($SWAYMSG -t get_outputs)
|
||||||
sway_json=$(sway_active_outputs)
|
wayvnc_json=$($WAYVNCCTL --json output-list)
|
||||||
wayvnc_json=$($WAYVNCCTL --json output-list)
|
local sway_list wayvnc_list
|
||||||
local sway_list wayvnc_list
|
sway_list=$(jq -r '.[].name' <<<"$sway_json" | sort -u)
|
||||||
sway_list=$(jq -r '.[].name' <<<"$sway_json" | sort -u)
|
wayvnc_list=$(jq -r '.[].name' <<<"$wayvnc_json" | sort -u)
|
||||||
wayvnc_list=$(jq -r '.[].name' <<<"$wayvnc_json" | sort -u)
|
[[ "$sway_list" == "$wayvnc_list" ]]
|
||||||
[[ "$sway_list" == "$wayvnc_list" ]]
|
echo " output-list IPC matches \`swaymsg -t get_outputs\`"
|
||||||
echo " output-list IPC matches \`swaymsg -t get_outputs\`"
|
echo "ok"
|
||||||
wayvnc_capturing=$(jq -r '.[] | select(.captured == true).name' <<<"$wayvnc_json")
|
|
||||||
echo " Capturing: $wayvnc_capturing=~$expected_capture"
|
|
||||||
[[ $wayvnc_capturing == "$expected_capture" ]]
|
|
||||||
echo "ok"
|
|
||||||
}
|
|
||||||
|
|
||||||
verify_wayvnc_exited() {
|
|
||||||
wait_until ! kill -0 $WAYVNC_PID >/dev/null
|
|
||||||
unset WAYVNC_PID
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test_exit_ipc() {
|
test_exit_ipc() {
|
||||||
echo "Checking wayvnc-exit command"
|
echo "Cbecking wayvnc-exit command"
|
||||||
$WAYVNCCTL wayvnc-exit &>/dev/null
|
# Ignore errors because killing the socket races vs receiving
|
||||||
verify_wayvnc_exited
|
# a return message: https://github.com/any1/wayvnc/issues/233
|
||||||
echo " wayvnc is shutdown"
|
$WAYVNCCTL wayvnc-exit &>/dev/null || true
|
||||||
echo "ok"
|
timeout_init
|
||||||
}
|
while kill -0 $WAYVNC_PID &>/dev/null; do
|
||||||
|
timeout_check
|
||||||
client() {
|
done
|
||||||
VNCDO_LOG=$XDG_RUNTIME_DIR/vncdo.log
|
echo " wayvnc is shutdown"
|
||||||
$VNCDO -v --server=$WAYVNC_ADDRESS::$WAYVNC_PORT "$@" &>>$VNCDO_LOG
|
unset WAYVNC_PID
|
||||||
}
|
echo "ok"
|
||||||
|
|
||||||
test_client_connect() {
|
|
||||||
echo "Connecting to send ctrl+t"
|
|
||||||
client key ctrl-t
|
|
||||||
echo " Looking for the result..."
|
|
||||||
[[ -f $XDG_RUNTIME_DIR/test.txt ]]
|
|
||||||
echo "Ok"
|
|
||||||
}
|
|
||||||
|
|
||||||
output_count() {
|
|
||||||
sway_active_outputs | jq 'length'
|
|
||||||
}
|
|
||||||
|
|
||||||
sway_output_create() {
|
|
||||||
local initial_count
|
|
||||||
initial_count=$(output_count)
|
|
||||||
echo "Creating new output"
|
|
||||||
$SWAYMSG create_output &>/dev/null
|
|
||||||
# shellcheck disable=SC2016
|
|
||||||
wait_until [[ '$(output_count)' -gt "$initial_count" ]]
|
|
||||||
echo " $(sway_active_outputs | jq -r '.[-1].name')"
|
|
||||||
echo "Ok"
|
|
||||||
}
|
|
||||||
|
|
||||||
sway_output_is_gone() {
|
|
||||||
local output=$1
|
|
||||||
$SWAYMSG -t get_outputs | jq -e "all(.name != \"$output\")"
|
|
||||||
}
|
|
||||||
|
|
||||||
sway_output_destroy() {
|
|
||||||
local output=$1
|
|
||||||
echo "Removing output $output"
|
|
||||||
$SWAYMSG output "$output" unplug >/dev/null
|
|
||||||
wait_until sway_output_is_gone "$output" >/dev/null
|
|
||||||
echo "Ok"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
smoke_test() {
|
smoke_test() {
|
||||||
test_setup "smoke test"
|
start_sway
|
||||||
start_sway
|
start_wayvnc
|
||||||
start_wayvncctl_events
|
test_version_ipc
|
||||||
start_wayvnc
|
test_output_list_ipc
|
||||||
test_version_ipc
|
test_exit_ipc
|
||||||
wait_until verify_events \
|
stop_sway
|
||||||
wayvnc-startup
|
|
||||||
test_output_list_ipc
|
|
||||||
test_client_connect
|
|
||||||
wait_until verify_events \
|
|
||||||
wayvnc-startup \
|
|
||||||
client-connected \
|
|
||||||
client-disconnected
|
|
||||||
test_exit_ipc
|
|
||||||
wait_until verify_events \
|
|
||||||
wayvnc-startup \
|
|
||||||
client-connected \
|
|
||||||
client-disconnected \
|
|
||||||
wayvnc-shutdown
|
|
||||||
stop_wayvncctl_events
|
|
||||||
stop_sway
|
|
||||||
}
|
|
||||||
|
|
||||||
multioutput_test() {
|
|
||||||
test_setup "multioutput test"
|
|
||||||
start_sway
|
|
||||||
sway_output_create
|
|
||||||
start_wayvncctl_events
|
|
||||||
|
|
||||||
# Ensure outout selection commandline works
|
|
||||||
start_wayvnc -o HEADLESS-1
|
|
||||||
wait_until verify_events \
|
|
||||||
wayvnc-startup
|
|
||||||
test_output_list_ipc HEADLESS-1
|
|
||||||
|
|
||||||
# Test outout-cycle
|
|
||||||
$WAYVNCCTL output-cycle
|
|
||||||
wait_until verify_events \
|
|
||||||
wayvnc-startup \
|
|
||||||
capture-changed
|
|
||||||
test_output_list_ipc HEADLESS-2
|
|
||||||
|
|
||||||
# Test outout-cycle wraps
|
|
||||||
$WAYVNCCTL output-cycle
|
|
||||||
wait_until verify_events \
|
|
||||||
wayvnc-startup \
|
|
||||||
capture-changed \
|
|
||||||
capture-changed
|
|
||||||
test_output_list_ipc HEADLESS-1
|
|
||||||
|
|
||||||
# Add a new output, then switch to it
|
|
||||||
sway_output_create
|
|
||||||
wait_until test_output_list_ipc HEADLESS-1
|
|
||||||
$WAYVNCCTL output-set HEADLESS-3
|
|
||||||
wait_until verify_events \
|
|
||||||
wayvnc-startup \
|
|
||||||
capture-changed \
|
|
||||||
capture-changed \
|
|
||||||
capture-changed
|
|
||||||
test_output_list_ipc HEADLESS-3
|
|
||||||
|
|
||||||
if [[ $SWAYMAJOR -le 1 && $SWAYMINOR -lt 8 ]]; then
|
|
||||||
echo "Warning: sway-1.8 or later is needed for complete testing"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
# Remove the output, and make sure we fallback properly
|
|
||||||
sway_output_destroy HEADLESS-3
|
|
||||||
wait_until verify_events \
|
|
||||||
wayvnc-startup \
|
|
||||||
capture-changed \
|
|
||||||
capture-changed \
|
|
||||||
capture-changed \
|
|
||||||
capture-changed
|
|
||||||
wait_until test_output_list_ipc HEADLESS-1
|
|
||||||
stop_sway
|
|
||||||
verify_wayvnc_exited
|
|
||||||
stop_wayvncctl_events
|
|
||||||
}
|
}
|
||||||
|
|
||||||
smoke_test
|
smoke_test
|
||||||
#multioutput_test
|
|
||||||
|
|
|
@ -1,3 +1,2 @@
|
||||||
xwayland disable
|
xwayland disable
|
||||||
bindsym Ctrl+t exec bash -c "echo OK > $XDG_RUNTIME_DIR/test.txt"
|
|
||||||
exec bash -c "env > $XDG_RUNTIME_DIR/sway.env"
|
exec bash -c "env > $XDG_RUNTIME_DIR/sway.env"
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
#!/bin/bash
|
#!/bin/sh
|
||||||
|
|
||||||
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
|
|
||||||
|
|
||||||
valgrind --leak-check=full \
|
valgrind --leak-check=full \
|
||||||
--show-leak-kinds=all \
|
--show-leak-kinds=all \
|
||||||
--suppressions=$SCRIPT_DIR/valgrind.supp \
|
--suppressions=util/valgrind.supp \
|
||||||
$@
|
$@
|
||||||
|
|
34
wayvnc.scd
34
wayvnc.scd
|
@ -52,9 +52,6 @@ wayvnc - A VNC server for wlroots based Wayland compositors.
|
||||||
*-v,--verbose*
|
*-v,--verbose*
|
||||||
Be more verbose. Same as setting `--log-level=info`.
|
Be more verbose. Same as setting `--log-level=info`.
|
||||||
|
|
||||||
*-w,--websocket*
|
|
||||||
Create a websocket.
|
|
||||||
|
|
||||||
*-L,--log-level*
|
*-L,--log-level*
|
||||||
Set log level. The levels are: error, warning, info, debug, trace and
|
Set log level. The levels are: error, warning, info, debug, trace and
|
||||||
quiet.
|
quiet.
|
||||||
|
@ -114,24 +111,13 @@ considered to be part of the key or the value.
|
||||||
*port*
|
*port*
|
||||||
The port to which the server shall bind. Default is 5900.
|
The port to which the server shall bind. Default is 5900.
|
||||||
|
|
||||||
*private_key_file_file*
|
*private_key_file*
|
||||||
The path to the private key file for TLS encryption. Only applicable
|
The path to the private key file for encryption. Only applicable when
|
||||||
when *enable_auth*=true.
|
*enable_auth*=true.
|
||||||
|
|
||||||
*relax_encryption*
|
|
||||||
Don't require encryption after the user has been authenticated. This
|
|
||||||
enables some security types such as Apple Diffie-Hellman.
|
|
||||||
|
|
||||||
*rsa_private_key_file*
|
|
||||||
The path to the private key file for RSA-AES encryption. Only applicable
|
|
||||||
when *enable_auth*=true.
|
|
||||||
|
|
||||||
*username*
|
*username*
|
||||||
Choose a username for authentication.
|
Choose a username for authentication.
|
||||||
|
|
||||||
*use_relative_paths*
|
|
||||||
Make file paths relative to the location of the config file.
|
|
||||||
|
|
||||||
*xkb_layout*
|
*xkb_layout*
|
||||||
The keyboard layout to use for key code lookup.
|
The keyboard layout to use for key code lookup.
|
||||||
|
|
||||||
|
@ -162,14 +148,12 @@ considered to be part of the key or the value.
|
||||||
## EXAMPLE
|
## EXAMPLE
|
||||||
|
|
||||||
```
|
```
|
||||||
use_relative_paths=true
|
|
||||||
address=0.0.0.0
|
address=0.0.0.0
|
||||||
enable_auth=true
|
enable_auth=true
|
||||||
username=luser
|
username=luser
|
||||||
password=p455w0rd
|
password=p455w0rd
|
||||||
rsa_private_key_file=rsa_key.pem
|
private_key_file=/path/to/key.pem
|
||||||
private_key_file=tls_key.pem
|
certificate_file=/path/to/cert.pem
|
||||||
certificate_file=tls_cert.pem
|
|
||||||
```
|
```
|
||||||
|
|
||||||
# WAYVNCCTL CONTROL SOCKET
|
# WAYVNCCTL CONTROL SOCKET
|
||||||
|
@ -269,8 +253,8 @@ Parameters:
|
||||||
*connection_count=...*
|
*connection_count=...*
|
||||||
The total number of connected VNC clients including this one.
|
The total number of connected VNC clients including this one.
|
||||||
|
|
||||||
*address=...*
|
*hostname=...*
|
||||||
The IP address of this client. May be null.
|
The hostname or IP of this client. May be null.
|
||||||
|
|
||||||
*username=...*
|
*username=...*
|
||||||
The username used to authenticate this client. May be null.
|
The username used to authenticate this client. May be null.
|
||||||
|
@ -288,8 +272,8 @@ Parameters:
|
||||||
*connection_count=...*
|
*connection_count=...*
|
||||||
The total number of connected VNC clients not including this one.
|
The total number of connected VNC clients not including this one.
|
||||||
|
|
||||||
*address=...*
|
*hostname=...*
|
||||||
The IP address of this client. May be null.
|
The hostname or IP of this client. May be null.
|
||||||
|
|
||||||
*username=...*
|
*username=...*
|
||||||
The username used to authenticate this client. May be null.
|
The username used to authenticate this client. May be null.
|
||||||
|
|
|
@ -63,8 +63,8 @@ the end, for ease in scripting:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ wayvncctl --json event-receive
|
$ wayvncctl --json event-receive
|
||||||
{"method":"client-connected","params":{"id":"0x10ef670","address":null,"username":null,"connection_count":1}}
|
{"method":"client-connected","params":{"id":"0x10ef670","hostname":null,"username":null,"connection_count":1}}
|
||||||
{"method":"client-disconnected","params":{"id":"0x10ef670","address":null,"username":null,"connection_count":0}}
|
{"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
|
The default human-readible output is a multi-line yaml-like format, with two
|
||||||
|
@ -75,12 +75,12 @@ $ wayvncctl event-receive
|
||||||
|
|
||||||
client-connected:
|
client-connected:
|
||||||
id: 0x10ef670
|
id: 0x10ef670
|
||||||
address: 192.168.1.18
|
hostname: 192.168.1.18
|
||||||
connection_count: 1
|
connection_count: 1
|
||||||
|
|
||||||
client-disconnected:
|
client-disconnected:
|
||||||
id: 0x10ef670
|
id: 0x10ef670
|
||||||
address: 192.168.1.18
|
hostname: 192.168.1.18
|
||||||
connection_count: 0
|
connection_count: 0
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
Loading…
Reference in New Issue