Implement ext-transient-seat-v1
parent
30c5581ce4
commit
1ef290d2d4
|
@ -22,6 +22,7 @@ client_protocols = [
|
||||||
'linux-dmabuf-unstable-v1.xml',
|
'linux-dmabuf-unstable-v1.xml',
|
||||||
'wlr-data-control-unstable-v1.xml',
|
'wlr-data-control-unstable-v1.xml',
|
||||||
'wlr-output-power-management-unstable-v1.xml',
|
'wlr-output-power-management-unstable-v1.xml',
|
||||||
|
'transient-seat-v1.xml',
|
||||||
]
|
]
|
||||||
|
|
||||||
client_protos_src = []
|
client_protos_src = []
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<protocol name="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 privileved clients to create
|
||||||
|
independent seats that will be removed from the compositor when the client
|
||||||
|
destroys its 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 object is
|
||||||
|
destroyed.
|
||||||
|
</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">
|
||||||
|
The life time of the transient seat handle is equal to that of the seat
|
||||||
|
itself.
|
||||||
|
</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>
|
110
src/main.c
110
src/main.c
|
@ -40,6 +40,7 @@
|
||||||
#include "xdg-output-unstable-v1.h"
|
#include "xdg-output-unstable-v1.h"
|
||||||
#include "wlr-output-power-management-unstable-v1.h"
|
#include "wlr-output-power-management-unstable-v1.h"
|
||||||
#include "linux-dmabuf-unstable-v1.h"
|
#include "linux-dmabuf-unstable-v1.h"
|
||||||
|
#include "transient-seat-v1.h"
|
||||||
#include "screencopy.h"
|
#include "screencopy.h"
|
||||||
#include "data-control.h"
|
#include "data-control.h"
|
||||||
#include "strlcpy.h"
|
#include "strlcpy.h"
|
||||||
|
@ -83,9 +84,10 @@ struct wayvnc {
|
||||||
struct zwp_virtual_keyboard_manager_v1* keyboard_manager;
|
struct zwp_virtual_keyboard_manager_v1* keyboard_manager;
|
||||||
struct zwlr_virtual_pointer_manager_v1* pointer_manager;
|
struct zwlr_virtual_pointer_manager_v1* pointer_manager;
|
||||||
struct zwlr_data_control_manager_v1* data_control_manager;
|
struct zwlr_data_control_manager_v1* data_control_manager;
|
||||||
|
struct ext_transient_seat_manager_v1* transient_seat_manager;
|
||||||
|
|
||||||
struct output* selected_output;
|
struct output* selected_output;
|
||||||
const struct seat* selected_seat;
|
struct seat* selected_seat;
|
||||||
|
|
||||||
struct screencopy screencopy;
|
struct screencopy screencopy;
|
||||||
|
|
||||||
|
@ -102,6 +104,7 @@ struct wayvnc {
|
||||||
uint32_t n_frames_captured;
|
uint32_t n_frames_captured;
|
||||||
|
|
||||||
bool disable_input;
|
bool disable_input;
|
||||||
|
bool use_transient_seat;
|
||||||
|
|
||||||
int nr_clients;
|
int nr_clients;
|
||||||
struct aml_ticker* performance_ticker;
|
struct aml_ticker* performance_ticker;
|
||||||
|
@ -116,6 +119,9 @@ struct wayvnc_client {
|
||||||
struct wayvnc* server;
|
struct wayvnc* server;
|
||||||
struct nvnc_client* nvnc_client;
|
struct nvnc_client* nvnc_client;
|
||||||
|
|
||||||
|
struct wl_seat* seat;
|
||||||
|
struct ext_transient_seat_v1* transient_seat;
|
||||||
|
|
||||||
unsigned id;
|
unsigned id;
|
||||||
struct pointer pointer;
|
struct pointer pointer;
|
||||||
struct keyboard keyboard;
|
struct keyboard keyboard;
|
||||||
|
@ -128,6 +134,7 @@ static void on_nvnc_client_new(struct nvnc_client* client);
|
||||||
void switch_to_output(struct wayvnc*, struct output*);
|
void switch_to_output(struct wayvnc*, struct output*);
|
||||||
void switch_to_next_output(struct wayvnc*);
|
void switch_to_next_output(struct wayvnc*);
|
||||||
void switch_to_prev_output(struct wayvnc*);
|
void switch_to_prev_output(struct wayvnc*);
|
||||||
|
static void client_init_seat(struct wayvnc_client* self);
|
||||||
static void client_init_pointer(struct wayvnc_client* self);
|
static void client_init_pointer(struct wayvnc_client* self);
|
||||||
static void client_init_keyboard(struct wayvnc_client* self);
|
static void client_init_keyboard(struct wayvnc_client* self);
|
||||||
static void client_init_data_control(struct wayvnc_client* self);
|
static void client_init_data_control(struct wayvnc_client* self);
|
||||||
|
@ -155,7 +162,7 @@ static bool registry_add_input(void* data, struct wl_registry* registry,
|
||||||
|
|
||||||
struct seat* seat = seat_new(wl_seat, id);
|
struct seat* seat = seat_new(wl_seat, id);
|
||||||
if (!seat) {
|
if (!seat) {
|
||||||
wl_seat_destroy(wl_seat);
|
wl_seat_release(wl_seat);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,6 +190,12 @@ static bool registry_add_input(void* data, struct wl_registry* registry,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (strcmp(interface, ext_transient_seat_manager_v1_interface.name) == 0) {
|
||||||
|
self->transient_seat_manager = wl_registry_bind(registry, id,
|
||||||
|
&ext_transient_seat_manager_v1_interface, 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -416,6 +429,11 @@ static int init_wayland(struct wayvnc* self)
|
||||||
goto failure;
|
goto failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!self->transient_seat_manager && self->use_transient_seat) {
|
||||||
|
nvnc_log(NVNC_LOG_ERROR, "Transient seat protocol not supported by compositor.");
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
|
||||||
if (!self->screencopy.manager) {
|
if (!self->screencopy.manager) {
|
||||||
nvnc_log(NVNC_LOG_ERROR, "Screencopy protocol not supported by compositor. Exiting. Refer to FAQ section in man page.");
|
nvnc_log(NVNC_LOG_ERROR, "Screencopy protocol not supported by compositor. Exiting. Refer to FAQ section in man page.");
|
||||||
goto failure;
|
goto failure;
|
||||||
|
@ -1000,6 +1018,7 @@ static struct wayvnc_client* client_create(struct wayvnc* wayvnc,
|
||||||
self->nvnc_client = nvnc_client;
|
self->nvnc_client = nvnc_client;
|
||||||
|
|
||||||
self->id = next_client_id++;
|
self->id = next_client_id++;
|
||||||
|
client_init_seat(self);
|
||||||
client_init_keyboard(self);
|
client_init_keyboard(self);
|
||||||
client_init_pointer(self);
|
client_init_pointer(self);
|
||||||
client_init_data_control(self);
|
client_init_data_control(self);
|
||||||
|
@ -1023,6 +1042,10 @@ static void client_destroy(void* obj)
|
||||||
if (self->data_control.manager)
|
if (self->data_control.manager)
|
||||||
data_control_destroy(&self->data_control);
|
data_control_destroy(&self->data_control);
|
||||||
|
|
||||||
|
if (self->server->use_transient_seat) {
|
||||||
|
ext_transient_seat_v1_destroy(self->transient_seat);
|
||||||
|
}
|
||||||
|
|
||||||
free(self);
|
free(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1107,16 +1130,73 @@ static void client_init_pointer(struct wayvnc_client* self)
|
||||||
|
|
||||||
self->pointer.pointer = pointer_manager_version >= 2
|
self->pointer.pointer = pointer_manager_version >= 2
|
||||||
? zwlr_virtual_pointer_manager_v1_create_virtual_pointer_with_output(
|
? zwlr_virtual_pointer_manager_v1_create_virtual_pointer_with_output(
|
||||||
wayvnc->pointer_manager, wayvnc->selected_seat->wl_seat,
|
wayvnc->pointer_manager, self->seat,
|
||||||
wayvnc->selected_output->wl_output)
|
wayvnc->selected_output->wl_output)
|
||||||
: zwlr_virtual_pointer_manager_v1_create_virtual_pointer(
|
: zwlr_virtual_pointer_manager_v1_create_virtual_pointer(
|
||||||
wayvnc->pointer_manager, wayvnc->selected_seat->wl_seat);
|
wayvnc->pointer_manager, self->seat);
|
||||||
|
|
||||||
if (pointer_init(&self->pointer) < 0) {
|
if (pointer_init(&self->pointer) < 0) {
|
||||||
nvnc_log(NVNC_LOG_ERROR, "Failed to initialise pointer");
|
nvnc_log(NVNC_LOG_ERROR, "Failed to initialise pointer");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void handle_transient_seat_ready(void* data,
|
||||||
|
struct ext_transient_seat_v1* transient_seat,
|
||||||
|
uint32_t global_name)
|
||||||
|
{
|
||||||
|
(void)transient_seat;
|
||||||
|
|
||||||
|
struct wayvnc_client* client = data;
|
||||||
|
struct wayvnc* wayvnc = client->server;
|
||||||
|
|
||||||
|
struct seat* seat = seat_find_by_id(&wayvnc->seats, global_name);
|
||||||
|
assert(seat);
|
||||||
|
|
||||||
|
client->seat = seat->wl_seat;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_transient_seat_denied(void* data,
|
||||||
|
struct ext_transient_seat_v1* transient_seat)
|
||||||
|
{
|
||||||
|
(void)transient_seat;
|
||||||
|
|
||||||
|
struct wayvnc_client* client = data;
|
||||||
|
struct wayvnc* wayvnc = client->server;
|
||||||
|
|
||||||
|
// TODO: Should this perhaps be fatal?
|
||||||
|
nvnc_log(NVNC_LOG_WARNING, "Transient seat denied");
|
||||||
|
|
||||||
|
client->seat = wayvnc->selected_seat->wl_seat;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void client_init_seat(struct wayvnc_client* self)
|
||||||
|
{
|
||||||
|
struct wayvnc* wayvnc = self->server;
|
||||||
|
|
||||||
|
if (wayvnc->disable_input)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (wayvnc->use_transient_seat) {
|
||||||
|
self->transient_seat = ext_transient_seat_manager_v1_create(
|
||||||
|
wayvnc->transient_seat_manager);
|
||||||
|
|
||||||
|
static const struct ext_transient_seat_v1_listener listener = {
|
||||||
|
.ready = handle_transient_seat_ready,
|
||||||
|
.denied = handle_transient_seat_denied,
|
||||||
|
};
|
||||||
|
ext_transient_seat_v1_add_listener(self->transient_seat,
|
||||||
|
&listener, self);
|
||||||
|
|
||||||
|
// TODO: Make this asynchronous
|
||||||
|
wl_display_roundtrip(wayvnc->display);
|
||||||
|
wl_display_dispatch(wayvnc->display);
|
||||||
|
|
||||||
|
assert(self->seat);
|
||||||
|
} else {
|
||||||
|
self->seat = wayvnc->selected_seat->wl_seat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void client_init_keyboard(struct wayvnc_client* self)
|
static void client_init_keyboard(struct wayvnc_client* self)
|
||||||
{
|
{
|
||||||
struct wayvnc* wayvnc = self->server;
|
struct wayvnc* wayvnc = self->server;
|
||||||
|
@ -1126,8 +1206,7 @@ static void client_init_keyboard(struct wayvnc_client* self)
|
||||||
|
|
||||||
self->keyboard.virtual_keyboard =
|
self->keyboard.virtual_keyboard =
|
||||||
zwp_virtual_keyboard_manager_v1_create_virtual_keyboard(
|
zwp_virtual_keyboard_manager_v1_create_virtual_keyboard(
|
||||||
wayvnc->keyboard_manager,
|
wayvnc->keyboard_manager, self->seat);
|
||||||
wayvnc->selected_seat->wl_seat);
|
|
||||||
|
|
||||||
struct xkb_rule_names rule_names = {
|
struct xkb_rule_names rule_names = {
|
||||||
.rules = wayvnc->cfg.xkb_rules,
|
.rules = wayvnc->cfg.xkb_rules,
|
||||||
|
@ -1163,7 +1242,7 @@ static void client_init_data_control(struct wayvnc_client* self)
|
||||||
|
|
||||||
self->data_control.manager = wayvnc->data_control_manager;
|
self->data_control.manager = wayvnc->data_control_manager;
|
||||||
data_control_init(&self->data_control, wayvnc->display, wayvnc->nvnc,
|
data_control_init(&self->data_control, wayvnc->display, wayvnc->nvnc,
|
||||||
wayvnc->selected_seat->wl_seat);
|
self->seat);
|
||||||
}
|
}
|
||||||
|
|
||||||
void log_selected_output(struct wayvnc* self)
|
void log_selected_output(struct wayvnc* self)
|
||||||
|
@ -1266,6 +1345,7 @@ int main(int argc, char* argv[])
|
||||||
bool show_performance = false;
|
bool show_performance = false;
|
||||||
int max_rate = 30;
|
int max_rate = 30;
|
||||||
bool disable_input = false;
|
bool disable_input = false;
|
||||||
|
bool use_transient_seat = false;
|
||||||
|
|
||||||
int drm_fd MAYBE_UNUSED = -1;
|
int drm_fd MAYBE_UNUSED = -1;
|
||||||
|
|
||||||
|
@ -1290,6 +1370,8 @@ int main(int argc, char* argv[])
|
||||||
"Select seat by name." },
|
"Select seat by name." },
|
||||||
{ 'S', "socket", "<path>",
|
{ 'S', "socket", "<path>",
|
||||||
"Control socket path." },
|
"Control socket path." },
|
||||||
|
{ 't', "transient-seat", NULL,
|
||||||
|
"Use transient seat." },
|
||||||
{ 'r', "render-cursor", NULL,
|
{ 'r', "render-cursor", NULL,
|
||||||
"Enable overlay cursor rendering." },
|
"Enable overlay cursor rendering." },
|
||||||
{ 'f', "max-fps", "<fps>",
|
{ 'f', "max-fps", "<fps>",
|
||||||
|
@ -1342,6 +1424,7 @@ int main(int argc, char* argv[])
|
||||||
log_level = log_level_from_string(
|
log_level = log_level_from_string(
|
||||||
option_parser_get_value(&option_parser, "log-level"));
|
option_parser_get_value(&option_parser, "log-level"));
|
||||||
max_rate = atoi(option_parser_get_value(&option_parser, "max-fps"));
|
max_rate = atoi(option_parser_get_value(&option_parser, "max-fps"));
|
||||||
|
use_transient_seat = option_parser_get_value(&option_parser, "transient-seat");
|
||||||
|
|
||||||
keyboard_options = option_parser_get_value(&option_parser, "keyboard");
|
keyboard_options = option_parser_get_value(&option_parser, "keyboard");
|
||||||
if (keyboard_options)
|
if (keyboard_options)
|
||||||
|
@ -1361,6 +1444,16 @@ int main(int argc, char* argv[])
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (use_transient_seat && disable_input) {
|
||||||
|
nvnc_log(NVNC_LOG_ERROR, "transient-seat and disable-input are conflicting options");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (seat_name && use_transient_seat) {
|
||||||
|
nvnc_log(NVNC_LOG_ERROR, "transient seat and seat are conflicting options");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
errno = 0;
|
errno = 0;
|
||||||
int cfg_rc = cfg_load(&self.cfg, cfg_file);
|
int cfg_rc = cfg_load(&self.cfg, cfg_file);
|
||||||
if (cfg_rc != 0 && (cfg_file || errno != ENOENT)) {
|
if (cfg_rc != 0 && (cfg_file || errno != ENOENT)) {
|
||||||
|
@ -1386,6 +1479,7 @@ int main(int argc, char* argv[])
|
||||||
if (!port) port = DEFAULT_PORT;
|
if (!port) port = DEFAULT_PORT;
|
||||||
|
|
||||||
self.disable_input = disable_input;
|
self.disable_input = disable_input;
|
||||||
|
self.use_transient_seat = use_transient_seat;
|
||||||
|
|
||||||
if (init_wayland(&self) < 0) {
|
if (init_wayland(&self) < 0) {
|
||||||
nvnc_log(NVNC_LOG_ERROR, "Failed to initialise wayland");
|
nvnc_log(NVNC_LOG_ERROR, "Failed to initialise wayland");
|
||||||
|
@ -1415,7 +1509,7 @@ int main(int argc, char* argv[])
|
||||||
nvnc_log(NVNC_LOG_ERROR, "No such seat");
|
nvnc_log(NVNC_LOG_ERROR, "No such seat");
|
||||||
goto failure;
|
goto failure;
|
||||||
}
|
}
|
||||||
} else if (!self.disable_input) {
|
} else if (!self.disable_input && !self.use_transient_seat) {
|
||||||
seat = seat_first(&self.seats);
|
seat = seat_first(&self.seats);
|
||||||
if (!seat) {
|
if (!seat) {
|
||||||
nvnc_log(NVNC_LOG_ERROR, "No seat found");
|
nvnc_log(NVNC_LOG_ERROR, "No seat found");
|
||||||
|
|
Loading…
Reference in New Issue