Add transient seats
parent
c1281dad60
commit
301b4c0b53
|
@ -0,0 +1,110 @@
|
|||
<?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>
|
|
@ -22,6 +22,7 @@ client_protocols = [
|
|||
'linux-dmabuf-unstable-v1.xml',
|
||||
'wlr-data-control-unstable-v1.xml',
|
||||
'wlr-output-power-management-unstable-v1.xml',
|
||||
'ext-transient-seat-v1.xml',
|
||||
]
|
||||
|
||||
client_protos_src = []
|
||||
|
|
81
src/main.c
81
src/main.c
|
@ -40,6 +40,7 @@
|
|||
#include "xdg-output-unstable-v1.h"
|
||||
#include "wlr-output-power-management-unstable-v1.h"
|
||||
#include "linux-dmabuf-unstable-v1.h"
|
||||
#include "ext-transient-seat-v1.h"
|
||||
#include "screencopy.h"
|
||||
#include "data-control.h"
|
||||
#include "strlcpy.h"
|
||||
|
@ -89,6 +90,7 @@ struct wayvnc {
|
|||
struct zwp_virtual_keyboard_manager_v1* keyboard_manager;
|
||||
struct zwlr_virtual_pointer_manager_v1* pointer_manager;
|
||||
struct zwlr_data_control_manager_v1* data_control_manager;
|
||||
struct ext_transient_seat_manager_v1* transient_seat_manager;
|
||||
|
||||
struct output* selected_output;
|
||||
struct seat* selected_seat;
|
||||
|
@ -108,6 +110,7 @@ struct wayvnc {
|
|||
uint32_t n_frames_captured;
|
||||
|
||||
bool disable_input;
|
||||
bool use_transient_seat;
|
||||
|
||||
int nr_clients;
|
||||
struct aml_ticker* performance_ticker;
|
||||
|
@ -123,6 +126,7 @@ struct wayvnc_client {
|
|||
struct nvnc_client* nvnc_client;
|
||||
|
||||
struct seat* seat;
|
||||
struct ext_transient_seat_v1* transient_seat;
|
||||
|
||||
unsigned id;
|
||||
struct pointer pointer;
|
||||
|
@ -192,6 +196,12 @@ static bool registry_add_input(void* data, struct wl_registry* registry,
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -439,6 +449,11 @@ static int init_wayland(struct wayvnc* self)
|
|||
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;
|
||||
}
|
||||
|
||||
self->screencopy.on_done = on_capture_done;
|
||||
self->screencopy.userdata = self;
|
||||
|
||||
|
@ -1084,6 +1099,9 @@ static void client_destroy(void* obj)
|
|||
struct nvnc* nvnc = nvnc_client_get_server(self->nvnc_client);
|
||||
struct wayvnc* wayvnc = nvnc_get_userdata(nvnc);
|
||||
|
||||
if (self->transient_seat)
|
||||
ext_transient_seat_v1_destroy(self->transient_seat);
|
||||
|
||||
if (self->seat)
|
||||
self->seat->occupancy--;
|
||||
|
||||
|
@ -1189,6 +1207,51 @@ static void client_init_pointer(struct wayvnc_client* self)
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static void handle_transient_seat_denied(void* data,
|
||||
struct ext_transient_seat_v1* transient_seat)
|
||||
{
|
||||
(void)data;
|
||||
(void)transient_seat;
|
||||
|
||||
// TODO: Something more graceful perhaps?
|
||||
nvnc_log(NVNC_LOG_PANIC, "Transient seat denied");
|
||||
}
|
||||
|
||||
static void client_init_transient_seat(struct wayvnc_client* self)
|
||||
{
|
||||
struct wayvnc* wayvnc = self->server;
|
||||
|
||||
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);
|
||||
|
||||
assert(self->seat);
|
||||
}
|
||||
|
||||
static void client_init_seat(struct wayvnc_client* self)
|
||||
{
|
||||
struct wayvnc* wayvnc = self->server;
|
||||
|
@ -1198,6 +1261,8 @@ static void client_init_seat(struct wayvnc_client* self)
|
|||
|
||||
if (wayvnc->selected_seat) {
|
||||
self->seat = wayvnc->selected_seat;
|
||||
} else if (wayvnc->use_transient_seat) {
|
||||
client_init_transient_seat(self);
|
||||
} else {
|
||||
self->seat = seat_find_unoccupied(&wayvnc->seats);
|
||||
if (!self->seat) {
|
||||
|
@ -1358,6 +1423,7 @@ int main(int argc, char* argv[])
|
|||
bool show_performance = false;
|
||||
int max_rate = 30;
|
||||
bool disable_input = false;
|
||||
bool use_transient_seat = false;
|
||||
|
||||
int drm_fd MAYBE_UNUSED = -1;
|
||||
|
||||
|
@ -1382,6 +1448,8 @@ int main(int argc, char* argv[])
|
|||
"Select seat by name." },
|
||||
{ 'S', "socket", "<path>",
|
||||
"Control socket path." },
|
||||
{ 't', "transient-seat", NULL,
|
||||
"Use transient seat." },
|
||||
{ 'r', "render-cursor", NULL,
|
||||
"Enable overlay cursor rendering." },
|
||||
{ 'f', "max-fps", "<fps>",
|
||||
|
@ -1437,6 +1505,8 @@ int main(int argc, char* argv[])
|
|||
log_level = log_level_from_string(
|
||||
option_parser_get_value(&option_parser, "log-level"));
|
||||
max_rate = atoi(option_parser_get_value(&option_parser, "max-fps"));
|
||||
use_transient_seat = atoi(option_parser_get_value(&option_parser,
|
||||
"transient-seat"));
|
||||
|
||||
keyboard_options = option_parser_get_value(&option_parser, "keyboard");
|
||||
if (keyboard_options)
|
||||
|
@ -1461,6 +1531,16 @@ int main(int argc, char* argv[])
|
|||
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;
|
||||
int cfg_rc = cfg_load(&self.cfg, cfg_file);
|
||||
if (cfg_rc != 0 && (cfg_file || errno != ENOENT)) {
|
||||
|
@ -1486,6 +1566,7 @@ int main(int argc, char* argv[])
|
|||
if (!port) port = DEFAULT_PORT;
|
||||
|
||||
self.disable_input = disable_input;
|
||||
self.use_transient_seat = use_transient_seat;
|
||||
|
||||
if (init_wayland(&self) < 0) {
|
||||
nvnc_log(NVNC_LOG_ERROR, "Failed to initialise wayland");
|
||||
|
|
Loading…
Reference in New Issue