Compare commits
8 Commits
master
...
multi-seat
Author | SHA1 | Date |
---|---|---|
Andri Yngvason | 9e034fabbc | |
Andri Yngvason | 5476e431bd | |
Andri Yngvason | e037ca32ec | |
Andri Yngvason | b08a44e446 | |
Andri Yngvason | e4412e4d52 | |
Andri Yngvason | f818da7abe | |
Andri Yngvason | 3ff02f0681 | |
Andri Yngvason | 013dabf41e |
|
@ -17,6 +17,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <wayland-client.h>
|
||||
|
||||
struct seat {
|
||||
|
@ -26,6 +27,12 @@ struct seat {
|
|||
uint32_t id;
|
||||
uint32_t capabilities;
|
||||
char name[256];
|
||||
|
||||
bool has_capabilities;
|
||||
bool has_name;
|
||||
|
||||
void (*on_ready)(struct seat*);
|
||||
void* userdata;
|
||||
};
|
||||
|
||||
struct seat* seat_new(struct wl_seat* wl_seat, uint32_t id);
|
||||
|
|
|
@ -21,6 +21,7 @@ client_protocols = [
|
|||
'xdg-output-unstable-v1.xml',
|
||||
'linux-dmabuf-unstable-v1.xml',
|
||||
'wlr-data-control-unstable-v1.xml',
|
||||
'transient-seat-unstable-v1.xml',
|
||||
]
|
||||
|
||||
client_protos_src = []
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<protocol name="transient_seat_unstable_v1">
|
||||
<copyright>
|
||||
Copyright © 2020 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 clients to create independent
|
||||
seats that will be removed when the client disconnects. The client also gets
|
||||
a handle to the seat which can be used to remove the seat.
|
||||
</description>
|
||||
|
||||
<interface name="zext_transient_seat_manager_v1" version="1">
|
||||
<request name="create">
|
||||
<description summary="Create a transient seat">
|
||||
Create a new seat that is removed when the client closes the connection.
|
||||
|
||||
The client may suggest a name for the seat, but the server may choose
|
||||
whichever name it sees fit, except the name must not collide with the
|
||||
name of another seat.
|
||||
|
||||
It is suggested that the client choose a sufficiently unique name, e.g.
|
||||
something containing the pid and an incremental numeric value.
|
||||
</description>
|
||||
<arg name="suggested_name" type="string"/>
|
||||
<arg name="seat" type="new_id" interface="zext_transient_seat_v1"/>
|
||||
</request>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="Destroy the manager">
|
||||
Destroy the manager
|
||||
</description>
|
||||
</request>
|
||||
</interface>
|
||||
|
||||
<interface name="zext_transient_seat_v1" version="1">
|
||||
<enum name="error">
|
||||
<entry name="unspec" value="0"
|
||||
summary="Operation failed for unspecified reason"/>
|
||||
<entry name="name_taken" value="1"
|
||||
summary="The suggested name was already taken"/>
|
||||
<entry name="invalid_name" value="2"
|
||||
summary="The suggested name contains invalid characters"/>
|
||||
</enum>
|
||||
|
||||
<event name="failed">
|
||||
<description summary="seat creation failed">
|
||||
This event indicates that the seat creation failed. No event is
|
||||
generated after this and the client should destroy the transient seat
|
||||
object.
|
||||
</description>
|
||||
<arg name="error" type="uint" enum="error"/>
|
||||
</event>
|
||||
|
||||
<event name="ready">
|
||||
<description summary="seat has been created">
|
||||
This event indicates that the seat has been created and tells the client
|
||||
which name was chosen for it. It is generated after the seat is created.
|
||||
No event is generated after this.
|
||||
</description>
|
||||
<arg name="name" type="string"/>
|
||||
</event>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="Destroy the seat">
|
||||
Destroy the seat
|
||||
</description>
|
||||
</request>
|
||||
</interface>
|
||||
</protocol>
|
213
src/main.c
213
src/main.c
|
@ -40,6 +40,7 @@
|
|||
#include "virtual-keyboard-unstable-v1.h"
|
||||
#include "xdg-output-unstable-v1.h"
|
||||
#include "linux-dmabuf-unstable-v1.h"
|
||||
#include "transient-seat-unstable-v1.h"
|
||||
#include "screencopy.h"
|
||||
#include "data-control.h"
|
||||
#include "strlcpy.h"
|
||||
|
@ -68,6 +69,17 @@
|
|||
|
||||
#define MAYBE_UNUSED __attribute__((unused))
|
||||
|
||||
struct wv_client {
|
||||
struct wl_list link;
|
||||
|
||||
struct keyboard kb;
|
||||
struct pointer pointer;
|
||||
|
||||
struct wl_seat* wl_seat;
|
||||
char seat_name[256];
|
||||
struct zext_transient_seat_v1* chair;
|
||||
};
|
||||
|
||||
struct wayvnc {
|
||||
bool do_exit;
|
||||
|
||||
|
@ -75,6 +87,7 @@ struct wayvnc {
|
|||
struct wl_registry* registry;
|
||||
struct wl_list outputs;
|
||||
struct wl_list seats;
|
||||
struct wl_list clients;
|
||||
struct cfg cfg;
|
||||
|
||||
struct zxdg_output_manager_v1* xdg_output_manager;
|
||||
|
@ -87,8 +100,6 @@ struct wayvnc {
|
|||
const struct seat* selected_seat;
|
||||
|
||||
struct screencopy screencopy;
|
||||
struct pointer pointer_backend;
|
||||
struct keyboard keyboard_backend;
|
||||
struct data_control data_control;
|
||||
|
||||
struct aml_handler* wayland_handler;
|
||||
|
@ -112,6 +123,7 @@ struct wayvnc {
|
|||
void wayvnc_exit(struct wayvnc* self);
|
||||
void on_capture_done(struct screencopy* sc);
|
||||
static void on_render(struct nvnc_display* display, struct nvnc_fb* fb);
|
||||
static void on_seat_ready(struct seat*);
|
||||
|
||||
#if defined(GIT_VERSION)
|
||||
static const char wayvnc_version[] = GIT_VERSION;
|
||||
|
@ -124,6 +136,9 @@ static const char wayvnc_version[] = "UNKNOWN";
|
|||
struct wl_shm* wl_shm = NULL;
|
||||
struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf = NULL;
|
||||
struct gbm_device* gbm_device = NULL;
|
||||
struct zext_transient_seat_manager_v1* zext_transient_seat_manager = NULL;
|
||||
|
||||
static uint64_t seat_id = 0;
|
||||
|
||||
static void registry_add(void* data, struct wl_registry* registry,
|
||||
uint32_t id, const char* interface,
|
||||
|
@ -188,6 +203,9 @@ static void registry_add(void* data, struct wl_registry* registry,
|
|||
return;
|
||||
}
|
||||
|
||||
seat->on_ready = on_seat_ready;
|
||||
seat->userdata = self;
|
||||
|
||||
wl_list_insert(&self->seats, &seat->link);
|
||||
return;
|
||||
}
|
||||
|
@ -211,6 +229,12 @@ static void registry_add(void* data, struct wl_registry* registry,
|
|||
&zwlr_data_control_manager_v1_interface, 2);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(interface, zext_transient_seat_manager_v1_interface.name) == 0) {
|
||||
zext_transient_seat_manager = wl_registry_bind(registry, id,
|
||||
&zext_transient_seat_manager_v1_interface, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void registry_remove(void* data, struct wl_registry* registry,
|
||||
|
@ -292,16 +316,15 @@ void wayvnc_destroy(struct wayvnc* self)
|
|||
output_list_destroy(&self->outputs);
|
||||
seat_list_destroy(&self->seats);
|
||||
|
||||
zext_transient_seat_manager_v1_destroy(zext_transient_seat_manager);
|
||||
|
||||
zxdg_output_manager_v1_destroy(self->xdg_output_manager);
|
||||
|
||||
wl_shm_destroy(wl_shm);
|
||||
|
||||
zwp_virtual_keyboard_v1_destroy(self->keyboard_backend.virtual_keyboard);
|
||||
zwp_virtual_keyboard_manager_v1_destroy(self->keyboard_manager);
|
||||
keyboard_destroy(&self->keyboard_backend);
|
||||
|
||||
zwlr_virtual_pointer_manager_v1_destroy(self->pointer_manager);
|
||||
pointer_destroy(&self->pointer_backend);
|
||||
|
||||
if (self->screencopy.manager)
|
||||
zwlr_screencopy_manager_v1_destroy(self->screencopy.manager);
|
||||
|
@ -337,6 +360,7 @@ static int init_wayland(struct wayvnc* self)
|
|||
|
||||
wl_list_init(&self->outputs);
|
||||
wl_list_init(&self->seats);
|
||||
wl_list_init(&self->clients);
|
||||
|
||||
self->registry = wl_display_get_registry(self->display);
|
||||
if (!self->registry)
|
||||
|
@ -385,7 +409,7 @@ void on_wayland_event(void* obj)
|
|||
assert(rc == 0);
|
||||
|
||||
if (wl_display_read_events(self->display) < 0) {
|
||||
if (errno == EPIPE) {
|
||||
if (errno == EPIPE || errno == ECONNRESET) {
|
||||
log_error("Compositor has gone away. Exiting...\n");
|
||||
wayvnc_exit(self);
|
||||
} else {
|
||||
|
@ -436,6 +460,128 @@ int init_main_loop(struct wayvnc* self)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void on_client_destroy(struct nvnc_client* nvnc_client)
|
||||
{
|
||||
struct wv_client* client = nvnc_get_userdata(nvnc_client);
|
||||
assert(client);
|
||||
|
||||
if (client->pointer.pointer)
|
||||
pointer_destroy(&client->pointer);
|
||||
|
||||
if (client->kb.virtual_keyboard) {
|
||||
zwp_virtual_keyboard_v1_destroy(client->kb.virtual_keyboard);
|
||||
keyboard_destroy(&client->kb);
|
||||
}
|
||||
|
||||
zext_transient_seat_v1_destroy(client->chair);
|
||||
|
||||
wl_list_remove(&client->link);
|
||||
free(client);
|
||||
}
|
||||
|
||||
static void init_client_inputs(struct wayvnc* wayvnc, struct wv_client* client)
|
||||
{
|
||||
// Keyboard
|
||||
struct xkb_rule_names rule_names = {
|
||||
.rules = wayvnc->cfg.xkb_rules,
|
||||
.layout = wayvnc->kb_layout ? wayvnc->kb_layout :
|
||||
wayvnc->cfg.xkb_layout,
|
||||
.model = wayvnc->cfg.xkb_model ? wayvnc->cfg.xkb_model :
|
||||
"pc105",
|
||||
.variant = wayvnc->kb_variant ? wayvnc->kb_variant :
|
||||
wayvnc->cfg.xkb_variant,
|
||||
.options = wayvnc->cfg.xkb_options,
|
||||
};
|
||||
|
||||
client->kb.virtual_keyboard =
|
||||
zwp_virtual_keyboard_manager_v1_create_virtual_keyboard(
|
||||
wayvnc->keyboard_manager, client->wl_seat);
|
||||
assert(client->kb.virtual_keyboard);
|
||||
|
||||
int rc MAYBE_UNUSED = keyboard_init(&client->kb, &rule_names);
|
||||
assert(rc == 0);
|
||||
|
||||
// Pointer
|
||||
client->pointer.vnc = wayvnc->nvnc;
|
||||
client->pointer.output = wayvnc->selected_output;
|
||||
|
||||
client->pointer.pointer = wayvnc->pointer_manager_version == 2
|
||||
? zwlr_virtual_pointer_manager_v1_create_virtual_pointer_with_output(
|
||||
wayvnc->pointer_manager, client->wl_seat,
|
||||
wayvnc->selected_output->wl_output)
|
||||
: zwlr_virtual_pointer_manager_v1_create_virtual_pointer(
|
||||
wayvnc->pointer_manager, client->wl_seat);
|
||||
|
||||
pointer_init(&client->pointer);
|
||||
}
|
||||
|
||||
void on_seat_ready(struct seat* seat)
|
||||
{
|
||||
struct wayvnc* wayvnc = seat->userdata;
|
||||
|
||||
struct wv_client* client;
|
||||
wl_list_for_each(client, &wayvnc->clients, link)
|
||||
if (!client->wl_seat &&
|
||||
strcmp(client->seat_name, seat->name) == 0) {
|
||||
client->wl_seat = seat->wl_seat;
|
||||
init_client_inputs(wayvnc, client);
|
||||
}
|
||||
}
|
||||
|
||||
void transient_seat_failed(void* data, struct zext_transient_seat_v1* seat,
|
||||
uint32_t reason)
|
||||
{
|
||||
// TODO: Handle properly
|
||||
abort();
|
||||
}
|
||||
|
||||
void transient_seat_ready(void* data, struct zext_transient_seat_v1* chair,
|
||||
const char* name)
|
||||
{
|
||||
struct nvnc_client* nvnc_client = data;
|
||||
struct wv_client* client = nvnc_get_userdata(nvnc_client);
|
||||
struct nvnc* nvnc = nvnc_client_get_server(nvnc_client);
|
||||
struct wayvnc* wayvnc = nvnc_get_userdata(nvnc);
|
||||
|
||||
strlcpy(client->seat_name, name, sizeof(client->seat_name));
|
||||
|
||||
struct seat* seat = seat_find_by_name(&wayvnc->seats, name);
|
||||
if (seat) {
|
||||
client->wl_seat = seat->wl_seat;
|
||||
init_client_inputs(wayvnc, client);
|
||||
}
|
||||
}
|
||||
|
||||
struct zext_transient_seat_v1_listener transient_seat_listener = {
|
||||
.failed = transient_seat_failed,
|
||||
.ready = transient_seat_ready,
|
||||
};
|
||||
|
||||
static void on_new_client(struct nvnc_client* nvnc_client)
|
||||
{
|
||||
struct nvnc* nvnc = nvnc_client_get_server(nvnc_client);
|
||||
struct wayvnc* wayvnc = nvnc_get_userdata(nvnc);
|
||||
|
||||
struct wv_client* client = calloc(1, sizeof(*client));
|
||||
assert(client);
|
||||
|
||||
int pid = getpid();
|
||||
|
||||
char name[256];
|
||||
snprintf(name, sizeof(name), "wayvnc-%d-%" PRIu64, pid, seat_id++);
|
||||
|
||||
client->chair = zext_transient_seat_manager_v1_create(
|
||||
zext_transient_seat_manager, name);
|
||||
|
||||
zext_transient_seat_v1_add_listener(client->chair,
|
||||
&transient_seat_listener, nvnc_client);
|
||||
|
||||
nvnc_set_userdata(nvnc_client, client);
|
||||
nvnc_set_client_cleanup_fn(nvnc_client, on_client_destroy);
|
||||
|
||||
wl_list_insert(&wayvnc->clients, &client->link);
|
||||
}
|
||||
|
||||
static void on_pointer_event(struct nvnc_client* client, uint16_t x, uint16_t y,
|
||||
enum nvnc_button_mask button_mask)
|
||||
{
|
||||
|
@ -443,29 +589,39 @@ static void on_pointer_event(struct nvnc_client* client, uint16_t x, uint16_t y,
|
|||
|
||||
struct nvnc* nvnc = nvnc_client_get_server(client);
|
||||
struct wayvnc* wayvnc = nvnc_get_userdata(nvnc);
|
||||
struct wv_client* wv_client = nvnc_get_userdata(client);
|
||||
|
||||
if (!wv_client->pointer.pointer)
|
||||
return; /* Client does not have a seat yet */
|
||||
|
||||
uint32_t xfx = 0, xfy = 0;
|
||||
output_transform_coord(wayvnc->selected_output, x, y, &xfx, &xfy);
|
||||
|
||||
pointer_set(&wayvnc->pointer_backend, xfx, xfy, button_mask);
|
||||
pointer_set(&wv_client->pointer, xfx, xfy, button_mask);
|
||||
}
|
||||
|
||||
static void on_key_event(struct nvnc_client* client, uint32_t symbol,
|
||||
bool is_pressed)
|
||||
{
|
||||
struct nvnc* nvnc = nvnc_client_get_server(client);
|
||||
struct wayvnc* wayvnc = nvnc_get_userdata(nvnc);
|
||||
struct wv_client* wv_client = nvnc_get_userdata(client);
|
||||
assert(wv_client);
|
||||
|
||||
keyboard_feed(&wayvnc->keyboard_backend, symbol, is_pressed);
|
||||
if (!wv_client->kb.virtual_keyboard)
|
||||
return; /* Client does not have a seat yet */
|
||||
|
||||
keyboard_feed(&wv_client->kb, symbol, is_pressed);
|
||||
}
|
||||
|
||||
static void on_key_code_event(struct nvnc_client* client, uint32_t code,
|
||||
bool is_pressed)
|
||||
{
|
||||
struct nvnc* nvnc = nvnc_client_get_server(client);
|
||||
struct wayvnc* wayvnc = nvnc_get_userdata(nvnc);
|
||||
struct wv_client* wv_client = nvnc_get_userdata(client);
|
||||
assert(wv_client);
|
||||
|
||||
keyboard_feed_code(&wayvnc->keyboard_backend, code + 8, is_pressed);
|
||||
if (!wv_client->kb.virtual_keyboard)
|
||||
return; /* Client does not have a seat yet */
|
||||
|
||||
keyboard_feed_code(&wv_client->kb, code + 8, is_pressed);
|
||||
}
|
||||
|
||||
static void on_client_cut_text(struct nvnc* server, const char* text, uint32_t len)
|
||||
|
@ -512,6 +668,8 @@ int init_nvnc(struct wayvnc* self, const char* addr, uint16_t port)
|
|||
|
||||
nvnc_display_set_render_fn(self->nvnc_display, on_render);
|
||||
|
||||
nvnc_set_new_client_fn(self->nvnc, on_new_client);
|
||||
|
||||
if (self->cfg.enable_auth &&
|
||||
nvnc_enable_auth(self->nvnc, self->cfg.private_key_file,
|
||||
self->cfg.certificate_file, on_auth, self) < 0) {
|
||||
|
@ -522,7 +680,7 @@ int init_nvnc(struct wayvnc* self, const char* addr, uint16_t port)
|
|||
if (self->pointer_manager)
|
||||
nvnc_set_pointer_fn(self->nvnc, on_pointer_event);
|
||||
|
||||
if (self->keyboard_backend.virtual_keyboard) {
|
||||
if (self->keyboard_manager) {
|
||||
nvnc_set_key_fn(self->nvnc, on_key_event);
|
||||
nvnc_set_key_code_fn(self->nvnc, on_key_code_event);
|
||||
}
|
||||
|
@ -941,33 +1099,6 @@ int main(int argc, char* argv[])
|
|||
self.screencopy.wl_output = out->wl_output;
|
||||
self.screencopy.rate_limit = max_rate;
|
||||
|
||||
self.keyboard_backend.virtual_keyboard =
|
||||
zwp_virtual_keyboard_manager_v1_create_virtual_keyboard(
|
||||
self.keyboard_manager, self.selected_seat->wl_seat);
|
||||
|
||||
struct xkb_rule_names rule_names = {
|
||||
.rules = self.cfg.xkb_rules,
|
||||
.layout = self.kb_layout ? self.kb_layout : self.cfg.xkb_layout,
|
||||
.model = self.cfg.xkb_model ? self.cfg.xkb_model : "pc105",
|
||||
.variant = self.kb_variant ? self.kb_variant :
|
||||
self.cfg.xkb_variant,
|
||||
.options = self.cfg.xkb_options,
|
||||
};
|
||||
|
||||
keyboard_init(&self.keyboard_backend, &rule_names);
|
||||
|
||||
self.pointer_backend.vnc = self.nvnc;
|
||||
self.pointer_backend.output = self.selected_output;
|
||||
|
||||
self.pointer_backend.pointer = self.pointer_manager_version == 2
|
||||
? zwlr_virtual_pointer_manager_v1_create_virtual_pointer_with_output(
|
||||
self.pointer_manager, self.selected_seat->wl_seat,
|
||||
out->wl_output)
|
||||
: zwlr_virtual_pointer_manager_v1_create_virtual_pointer(
|
||||
self.pointer_manager, self.selected_seat->wl_seat);
|
||||
|
||||
pointer_init(&self.pointer_backend);
|
||||
|
||||
out->on_dimension_change = on_output_dimension_change;
|
||||
out->userdata = &self;
|
||||
|
||||
|
|
|
@ -30,6 +30,10 @@ static void seat_capabilities(void* data, struct wl_seat* wl_seat,
|
|||
struct seat* self = data;
|
||||
|
||||
self->capabilities = capabilities;
|
||||
self->has_capabilities = true;
|
||||
|
||||
if (self->has_capabilities && self->has_name && self->on_ready)
|
||||
self->on_ready(self);
|
||||
}
|
||||
|
||||
static void seat_name(void* data, struct wl_seat* wl_seat, const char* name)
|
||||
|
@ -37,6 +41,10 @@ static void seat_name(void* data, struct wl_seat* wl_seat, const char* name)
|
|||
struct seat* self = data;
|
||||
|
||||
strlcpy(self->name, name, sizeof(self->name));
|
||||
self->has_name = true;
|
||||
|
||||
if (self->has_capabilities && self->has_name && self->on_ready)
|
||||
self->on_ready(self);
|
||||
}
|
||||
|
||||
static const struct wl_seat_listener seat_listener = {
|
||||
|
|
Loading…
Reference in New Issue