wayvnc/src/main.c

916 lines
23 KiB
C
Raw Normal View History

2019-10-08 18:41:23 +00:00
/*
2020-01-14 19:11:01 +00:00
* Copyright (c) 2019 - 2020 Andri Yngvason
2019-10-08 18:41:23 +00:00
*
* 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.
*/
2019-10-06 18:51:39 +00:00
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <unistd.h>
#include <getopt.h>
2019-10-06 18:51:39 +00:00
#include <assert.h>
#include <inttypes.h>
2020-01-18 18:29:54 +00:00
#include <errno.h>
2019-10-06 22:12:29 +00:00
#include <neatvnc.h>
2020-03-16 22:01:43 +00:00
#include <aml.h>
#include <signal.h>
2019-10-06 22:12:29 +00:00
#include <libdrm/drm_fourcc.h>
2019-12-27 14:41:54 +00:00
#include <wayland-client-protocol.h>
2019-10-06 18:51:39 +00:00
#include <wayland-client.h>
2019-10-06 22:12:29 +00:00
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
2019-10-07 23:07:12 +00:00
#include <pixman.h>
#include <sys/param.h>
2019-10-06 18:51:39 +00:00
#include "frame-capture.h"
2019-10-06 18:51:39 +00:00
#include "wlr-export-dmabuf-unstable-v1.h"
#include "wlr-screencopy-unstable-v1.h"
2019-12-22 15:41:51 +00:00
#include "wlr-virtual-pointer-unstable-v1.h"
2019-12-24 15:54:58 +00:00
#include "virtual-keyboard-unstable-v1.h"
2020-01-24 20:08:57 +00:00
#include "xdg-output-unstable-v1.h"
2019-10-06 18:51:39 +00:00
#include "render.h"
#include "dmabuf.h"
#include "screencopy.h"
2019-10-06 18:51:39 +00:00
#include "strlcpy.h"
#include "logging.h"
#include "output.h"
2019-12-22 15:41:51 +00:00
#include "pointer.h"
2019-12-24 15:54:58 +00:00
#include "keyboard.h"
2019-12-31 13:55:25 +00:00
#include "seat.h"
2020-01-18 18:14:17 +00:00
#include "cfg.h"
2020-03-28 13:07:33 +00:00
#include "damage.h"
2019-10-06 18:51:39 +00:00
#define DEFAULT_ADDRESS "127.0.0.1"
#define DEFAULT_PORT 5900
#define UDIV_UP(a, b) (((a) + (b) - 1) / (b))
#define ALIGN_UP(n, a) (UDIV_UP(n, a) * a)
enum frame_capture_backend_type {
FRAME_CAPTURE_BACKEND_NONE = 0,
FRAME_CAPTURE_BACKEND_SCREENCOPY,
FRAME_CAPTURE_BACKEND_DMABUF,
};
2019-10-06 22:12:29 +00:00
struct wayvnc {
2020-03-16 22:01:43 +00:00
bool do_exit;
2019-10-06 22:12:29 +00:00
struct wl_display* display;
struct wl_registry* registry;
struct wl_list outputs;
2019-12-31 13:55:25 +00:00
struct wl_list seats;
2020-01-18 18:14:17 +00:00
struct cfg cfg;
2019-12-24 15:54:58 +00:00
2020-01-24 20:08:57 +00:00
struct zxdg_output_manager_v1* xdg_output_manager;
2019-12-24 15:54:58 +00:00
struct zwp_virtual_keyboard_manager_v1* keyboard_manager;
2020-01-24 20:56:47 +00:00
struct zwlr_virtual_pointer_manager_v1* pointer_manager;
2019-10-06 22:12:29 +00:00
2020-02-05 22:33:16 +00:00
int pointer_manager_version;
2019-10-06 22:12:29 +00:00
struct renderer renderer;
const struct output* selected_output;
2019-12-31 13:55:25 +00:00
const struct seat* selected_seat;
2019-10-06 22:12:29 +00:00
struct dmabuf_capture dmabuf_backend;
struct screencopy screencopy_backend;
2019-10-10 22:32:54 +00:00
struct frame_capture* capture_backend;
2019-12-22 15:41:51 +00:00
struct pointer pointer_backend;
2019-12-24 15:54:58 +00:00
struct keyboard keyboard_backend;
2020-03-16 22:01:43 +00:00
struct aml_handler* wayland_handler;
struct aml_signal* signal_handler;
2019-10-06 22:12:29 +00:00
struct nvnc* nvnc;
2020-04-12 21:46:48 +00:00
struct nvnc_display* nvnc_display;
struct nvnc_fb* buffer;
2019-10-07 23:44:55 +00:00
struct pixman_region16 current_damage;
2019-12-30 10:15:12 +00:00
const char* kb_layout;
2019-10-06 22:12:29 +00:00
};
2020-03-16 22:01:43 +00:00
void wayvnc_exit(struct wayvnc* self);
void on_capture_done(struct frame_capture* capture);
2020-04-12 21:46:48 +00:00
static void on_render(struct nvnc_display* display, struct nvnc_fb* fb);
static enum frame_capture_backend_type
frame_capture_backend_from_string(const char* str)
{
if (strcmp(str, "screencopy") == 0)
return FRAME_CAPTURE_BACKEND_SCREENCOPY;
if (strcmp(str, "dmabuf") == 0)
return FRAME_CAPTURE_BACKEND_DMABUF;
return FRAME_CAPTURE_BACKEND_NONE;
}
2019-10-06 18:51:39 +00:00
static void registry_add(void* data, struct wl_registry* registry,
uint32_t id, const char* interface,
uint32_t version)
{
struct wayvnc* self = data;
if (strcmp(interface, wl_output_interface.name) == 0) {
struct wl_output* wl_output =
wl_registry_bind(registry, id, &wl_output_interface,
version);
if (!wl_output)
2019-10-06 18:51:39 +00:00
return;
struct output* output = output_new(wl_output, id);
if (!output)
return;
2019-10-06 18:51:39 +00:00
wl_list_insert(&self->outputs, &output->link);
return;
}
2020-01-24 20:08:57 +00:00
if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) {
self->xdg_output_manager =
wl_registry_bind(registry, id,
&zxdg_output_manager_v1_interface, 3);
return;
}
if (strcmp(interface, wl_shm_interface.name) == 0) {
self->screencopy_backend.wl_shm
= wl_registry_bind(registry, id, &wl_shm_interface, 1);
return;
}
2019-10-06 18:51:39 +00:00
if (strcmp(interface, zwlr_export_dmabuf_manager_v1_interface.name) == 0) {
self->dmabuf_backend.manager =
2019-10-06 18:51:39 +00:00
wl_registry_bind(registry, id,
&zwlr_export_dmabuf_manager_v1_interface,
1);
return;
}
if (strcmp(interface, zwlr_screencopy_manager_v1_interface.name) == 0) {
self->screencopy_backend.manager =
wl_registry_bind(registry, id,
&zwlr_screencopy_manager_v1_interface,
2);
2019-10-06 18:51:39 +00:00
return;
}
2019-12-22 15:41:51 +00:00
if (strcmp(interface, zwlr_virtual_pointer_manager_v1_interface.name) == 0) {
2020-01-24 20:56:47 +00:00
self->pointer_manager =
2019-12-22 15:41:51 +00:00
wl_registry_bind(registry, id,
&zwlr_virtual_pointer_manager_v1_interface,
2020-02-05 22:33:16 +00:00
version);
self->pointer_manager_version = version;
2019-12-22 15:41:51 +00:00
return;
};
2019-12-24 15:54:58 +00:00
if (strcmp(interface, wl_seat_interface.name) == 0) {
2019-12-31 13:55:25 +00:00
struct wl_seat* wl_seat =
2019-12-24 15:54:58 +00:00
wl_registry_bind(registry, id, &wl_seat_interface, 7);
2019-12-31 13:55:25 +00:00
if (!wl_seat)
return;
2020-01-14 19:18:33 +00:00
struct seat* seat = seat_new(wl_seat, id);
2019-12-31 13:55:25 +00:00
if (!seat) {
wl_seat_destroy(wl_seat);
return;
}
wl_list_insert(&self->seats, &seat->link);
2019-12-24 15:54:58 +00:00
return;
}
if (strcmp(interface, zwp_virtual_keyboard_manager_v1_interface.name) == 0) {
self->keyboard_manager =
wl_registry_bind(registry, id,
&zwp_virtual_keyboard_manager_v1_interface,
1);
return;
}
2019-10-06 18:51:39 +00:00
}
static void registry_remove(void* data, struct wl_registry* registry,
uint32_t id)
{
2019-10-06 22:12:29 +00:00
struct wayvnc* self = data;
2020-01-14 19:11:01 +00:00
struct output* out = output_find_by_id(&self->outputs, id);
2019-10-06 22:12:29 +00:00
if (out) {
wl_list_remove(&out->link);
output_destroy(out);
if (out == self->selected_output) {
log_error("Selected output went away. Exiting...\n");
wayvnc_exit(self);
}
2020-01-14 19:18:33 +00:00
return;
}
struct seat* seat = seat_find_by_id(&self->seats, id);
if (seat) {
wl_list_remove(&seat->link);
seat_destroy(seat);
if (seat == self->selected_seat) {
log_error("Selected seat went away. Exiting...\n");
wayvnc_exit(self);
}
2020-01-14 19:18:33 +00:00
return;
2019-10-06 22:12:29 +00:00
}
2019-10-06 18:51:39 +00:00
}
void wayvnc_destroy(struct wayvnc* self)
{
output_list_destroy(&self->outputs);
2019-12-31 13:55:25 +00:00
seat_list_destroy(&self->seats);
2019-12-27 14:41:54 +00:00
2020-01-24 20:08:57 +00:00
zxdg_output_manager_v1_destroy(self->xdg_output_manager);
2019-12-27 14:41:54 +00:00
wl_shm_destroy(self->screencopy_backend.wl_shm);
2019-12-24 15:54:58 +00:00
zwp_virtual_keyboard_v1_destroy(self->keyboard_backend.virtual_keyboard);
zwp_virtual_keyboard_manager_v1_destroy(self->keyboard_manager);
2019-12-27 14:41:54 +00:00
keyboard_destroy(&self->keyboard_backend);
2020-01-24 20:56:47 +00:00
zwlr_virtual_pointer_manager_v1_destroy(self->pointer_manager);
2019-12-27 14:41:54 +00:00
pointer_destroy(&self->pointer_backend);
if (self->screencopy_backend.manager)
zwlr_screencopy_manager_v1_destroy(self->screencopy_backend.manager);
2019-12-27 14:41:54 +00:00
if (self->dmabuf_backend.manager)
2019-12-22 15:41:51 +00:00
zwlr_export_dmabuf_manager_v1_destroy(self->dmabuf_backend.manager);
2019-12-27 14:41:54 +00:00
2019-10-06 18:51:39 +00:00
wl_display_disconnect(self->display);
}
2020-01-24 20:56:47 +00:00
static void init_xdg_outputs(struct wayvnc* self)
{
struct output* output;
wl_list_for_each(output, &self->outputs, link) {
struct zxdg_output_v1* xdg_output =
zxdg_output_manager_v1_get_xdg_output(
self->xdg_output_manager, output->wl_output);
output_set_xdg_output(output, xdg_output);
}
}
2019-10-06 18:51:39 +00:00
static int init_wayland(struct wayvnc* self)
{
static const struct wl_registry_listener registry_listener = {
.global = registry_add,
.global_remove = registry_remove,
};
self->display = wl_display_connect(NULL);
if (!self->display)
return -1;
wl_list_init(&self->outputs);
2019-12-31 13:55:25 +00:00
wl_list_init(&self->seats);
2019-10-06 18:51:39 +00:00
self->registry = wl_display_get_registry(self->display);
if (!self->registry)
goto failure;
wl_registry_add_listener(self->registry, &registry_listener, self);
wl_display_dispatch(self->display);
2020-01-24 20:08:57 +00:00
wl_display_roundtrip(self->display);
2019-10-06 18:51:39 +00:00
2020-01-24 20:56:47 +00:00
init_xdg_outputs(self);
if (!self->pointer_manager) {
log_error("Virtual Pointer protocol not supported by compositor.\n");
goto failure;
}
if (!self->keyboard_manager) {
log_error("Virtual Keyboard protocol not supported by compositor.\n");
goto failure;
}
wl_display_dispatch(self->display);
wl_display_roundtrip(self->display);
if (!self->dmabuf_backend.manager && !self->screencopy_backend.manager) {
log_error("Compositor supports neither screencopy nor export-dmabuf! Exiting.\n");
goto failure;
2019-10-06 18:51:39 +00:00
}
self->dmabuf_backend.fc.on_done = on_capture_done;
self->dmabuf_backend.fc.userdata = self;
self->screencopy_backend.frame_capture.on_done = on_capture_done;
2019-10-10 22:32:54 +00:00
self->screencopy_backend.frame_capture.userdata = self;
2019-10-06 18:51:39 +00:00
return 0;
failure:
wl_display_disconnect(self->display);
return -1;
}
2020-03-16 22:01:43 +00:00
void on_wayland_event(void* obj)
2019-10-06 22:12:29 +00:00
{
2020-03-16 22:01:43 +00:00
struct wayvnc* self = aml_get_userdata(obj);
2020-01-21 19:16:36 +00:00
int rc = wl_display_prepare_read(self->display);
assert(rc == 0);
if (wl_display_read_events(self->display) < 0) {
if (errno == EPIPE) {
log_error("Compositor has gone away. Exiting...\n");
wayvnc_exit(self);
} else {
log_error("Failed to read wayland events: %m\n");
}
2020-03-22 20:29:13 +00:00
}
if (wl_display_dispatch_pending(self->display) < 0)
log_error("Failed to dispatch pending\n");
2019-10-06 22:12:29 +00:00
}
2020-03-16 22:01:43 +00:00
void wayvnc_exit(struct wayvnc* self)
2019-10-06 22:12:29 +00:00
{
2020-03-16 22:01:43 +00:00
self->do_exit = true;
2019-10-06 22:12:29 +00:00
}
2020-03-16 22:01:43 +00:00
void on_signal(void* obj)
{
2020-03-16 22:01:43 +00:00
struct wayvnc* self = aml_get_userdata(obj);
wayvnc_exit(self);
}
2019-10-06 22:12:29 +00:00
int init_main_loop(struct wayvnc* self)
{
2020-03-16 22:01:43 +00:00
struct aml* loop = aml_get_default();
2019-10-06 22:12:29 +00:00
2020-03-16 22:01:43 +00:00
struct aml_handler* wl_handler;
wl_handler = aml_handler_new(wl_display_get_fd(self->display),
on_wayland_event, self, NULL);
if (!wl_handler)
return -1;
2019-10-06 22:12:29 +00:00
2020-03-16 22:01:43 +00:00
int rc = aml_start(loop, wl_handler);
aml_unref(wl_handler);
if (rc < 0)
return -1;
2019-10-06 22:12:29 +00:00
2020-03-16 22:01:43 +00:00
struct aml_signal* sig;
sig = aml_signal_new(SIGINT, on_signal, self, NULL);
if (!sig)
return -1;
2019-10-06 22:12:29 +00:00
2020-03-16 22:01:43 +00:00
rc = aml_start(loop, sig);
aml_unref(sig);
if (rc < 0)
return -1;
2019-10-06 22:12:29 +00:00
return 0;
}
uint32_t fourcc_from_gl_format(uint32_t format)
{
switch (format) {
case GL_BGRA_EXT: return DRM_FORMAT_XRGB8888;
case GL_RGBA: return DRM_FORMAT_XBGR8888;
}
return DRM_FORMAT_INVALID;
}
2019-12-22 15:41:51 +00:00
static void on_pointer_event(struct nvnc_client* client, uint16_t x, uint16_t y,
enum nvnc_button_mask button_mask)
{
// TODO: Have a seat per client
2020-04-12 21:46:48 +00:00
struct nvnc* nvnc = nvnc_client_get_server(client);
2019-12-22 15:41:51 +00:00
struct wayvnc* wayvnc = nvnc_get_userdata(nvnc);
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);
2019-12-22 15:41:51 +00:00
}
2019-12-24 15:54:58 +00:00
static void on_key_event(struct nvnc_client* client, uint32_t symbol,
bool is_pressed)
{
2020-04-12 21:46:48 +00:00
struct nvnc* nvnc = nvnc_client_get_server(client);
2019-12-24 15:54:58 +00:00
struct wayvnc* wayvnc = nvnc_get_userdata(nvnc);
keyboard_feed(&wayvnc->keyboard_backend, symbol, is_pressed);
}
2020-01-18 18:29:54 +00:00
bool on_auth(const char* username, const char* password, void* ud)
{
struct wayvnc* self = ud;
if (strcmp(username, self->cfg.username) != 0)
return false;
if (strcmp(password, self->cfg.password) != 0)
return false;
return true;
}
2019-10-06 22:12:29 +00:00
int init_nvnc(struct wayvnc* self, const char* addr, uint16_t port)
{
self->nvnc = nvnc_open(addr, port);
if (!self->nvnc) {
log_error("Failed to bind to address\n");
2019-10-06 22:12:29 +00:00
return -1;
}
2019-10-06 22:12:29 +00:00
2020-04-12 21:46:48 +00:00
self->nvnc_display = nvnc_display_new(0, 0);
if (!self->nvnc_display)
goto failure;
nvnc_add_display(self->nvnc, self->nvnc_display);
2019-10-06 22:12:29 +00:00
nvnc_set_userdata(self->nvnc, self);
nvnc_set_name(self->nvnc, "WayVNC");
2020-04-12 21:46:48 +00:00
nvnc_display_set_render_fn(self->nvnc_display, on_render);
2020-01-18 18:29:54 +00:00
if (self->cfg.enable_auth)
nvnc_enable_auth(self->nvnc, self->cfg.private_key_file,
self->cfg.certificate_file, on_auth, self);
2020-01-24 20:56:47 +00:00
if (self->pointer_manager)
2019-12-22 15:41:51 +00:00
nvnc_set_pointer_fn(self->nvnc, on_pointer_event);
2019-12-24 15:54:58 +00:00
if (self->keyboard_backend.virtual_keyboard)
nvnc_set_key_fn(self->nvnc, on_key_event);
2019-10-06 22:12:29 +00:00
return 0;
2020-04-12 21:46:48 +00:00
failure:
nvnc_close(self->nvnc);
return -1;
2019-10-06 22:12:29 +00:00
}
int wayvnc_start_capture(struct wayvnc* self, enum frame_capture_options opt)
2019-10-06 22:50:11 +00:00
{
return frame_capture_start(self->capture_backend, opt);
2019-10-06 22:50:11 +00:00
}
2020-04-12 21:46:48 +00:00
static void on_render(struct nvnc_display* display, struct nvnc_fb* fb)
2019-10-07 23:44:55 +00:00
{
2020-04-12 21:46:48 +00:00
struct nvnc* nvnc = nvnc_display_get_server(display);
struct wayvnc* self = nvnc_get_userdata(nvnc);
2019-10-07 23:44:55 +00:00
struct pixman_box16* ext = pixman_region_extents(&self->current_damage);
uint32_t y = ext->y1;
uint32_t damage_height = ext->y2 - ext->y1;
2020-03-28 15:05:04 +00:00
uint32_t* addr = nvnc_fb_get_addr(fb);
uint32_t width = nvnc_fb_get_width(fb);
2020-03-28 15:05:04 +00:00
renderer_read_frame(&self->renderer, addr + y * width, y, damage_height);
pixman_region_clear(&self->current_damage);
}
static void wayvnc_damage_region(struct wayvnc* self,
struct pixman_region16* damage)
{
pixman_region_union(&self->current_damage, &self->current_damage, damage);
2020-04-12 21:46:48 +00:00
nvnc_display_damage_region(self->nvnc_display, damage);
}
2020-03-28 15:05:04 +00:00
static void wayvnc_damage_whole(struct wayvnc* self)
{
uint16_t width = nvnc_fb_get_width(self->buffer);
uint16_t height = nvnc_fb_get_height(self->buffer);
struct pixman_region16 damage;
pixman_region_init_rect(&damage, 0, 0, width, height);
wayvnc_damage_region(self, &damage);
pixman_region_fini(&damage);
}
static void on_damage_check_done(struct pixman_region16* damage, void* userdata)
{
struct wayvnc* self = userdata;
wayvnc_damage_region(self, damage);
2019-10-07 23:44:55 +00:00
if (wayvnc_start_capture(self, 0) < 0) {
2019-10-07 23:44:55 +00:00
log_error("Failed to start capture. Exiting...\n");
2020-03-16 22:01:43 +00:00
wayvnc_exit(self);
2019-10-07 23:44:55 +00:00
}
}
2020-03-28 14:09:29 +00:00
void wayvnc_process_frame(struct wayvnc* self)
2019-10-06 22:50:11 +00:00
{
2020-03-28 14:09:29 +00:00
uint32_t format = fourcc_from_gl_format(self->renderer.read_format);
uint32_t width = output_get_transformed_width(self->selected_output);
uint32_t height = output_get_transformed_height(self->selected_output);
bool is_first_frame = false;
if (!self->buffer) {
self->buffer = nvnc_fb_new(width, height, format);
2020-04-12 21:46:48 +00:00
nvnc_display_set_buffer(self->nvnc_display, self->buffer);
is_first_frame = true;
} else {
// TODO: Reallocate
assert(width == nvnc_fb_get_width(self->buffer));
assert(height == nvnc_fb_get_height(self->buffer));
}
struct nvnc_fb* fb = self->buffer;
// TODO: Fix constness on fb in this function
2020-03-28 14:09:29 +00:00
frame_capture_render(self->capture_backend, &self->renderer, fb);
2019-10-07 23:07:12 +00:00
if (!is_first_frame) {
2019-10-10 22:32:54 +00:00
uint32_t hint_x = self->capture_backend->damage_hint.x;
uint32_t hint_y = self->capture_backend->damage_hint.y;
uint32_t hint_width = self->capture_backend->damage_hint.width;
uint32_t hint_height = self->capture_backend->damage_hint.height;
2019-10-10 21:40:22 +00:00
uint32_t tfx0, tfy0, tfx1, tfy1;
output_transform_box_coord(self->selected_output,
hint_x, hint_y,
hint_x + hint_width,
hint_y + hint_height,
&tfx0, &tfy0, &tfx1, &tfy1);
2020-03-28 13:07:33 +00:00
struct pixman_box16 hint = {
.x1 = tfx0, .y1 = tfy0,
.x2 = tfx1, .y2 = tfy1,
};
size_t alignment = MAX(4, sizeof(void*));
size_t damage_buffer_size = ALIGN_UP(width * height, alignment);
uint8_t* damage_buffer =
aligned_alloc(alignment, damage_buffer_size);
2020-03-28 13:07:33 +00:00
render_damage(&self->renderer);
renderer_read_damage(&self->renderer, damage_buffer, 0, height);
damage_check_async(damage_buffer, width, height, &hint,
on_damage_check_done, self);
2019-10-07 23:44:55 +00:00
return;
}
wayvnc_damage_whole(self);
2020-03-28 15:05:04 +00:00
if (wayvnc_start_capture(self, 0) < 0) {
2019-10-06 22:50:11 +00:00
log_error("Failed to start capture. Exiting...\n");
2020-03-16 22:01:43 +00:00
wayvnc_exit(self);
2019-10-06 22:50:11 +00:00
}
}
void on_capture_done(struct frame_capture* capture)
{
struct wayvnc* self = capture->userdata;
switch (capture->status) {
2019-10-10 22:32:54 +00:00
case CAPTURE_STOPPED:
break;
case CAPTURE_IN_PROGRESS:
break;
2019-10-10 22:32:54 +00:00
case CAPTURE_FATAL:
log_error("Fatal error while capturing. Exiting...\n");
2020-03-16 22:01:43 +00:00
wayvnc_exit(self);
break;
2019-10-10 22:32:54 +00:00
case CAPTURE_FAILED:
if (wayvnc_start_capture(self, CAPTURE_NOW) < 0) {
log_error("Failed to start capture. Exiting...\n");
2020-03-16 22:01:43 +00:00
wayvnc_exit(self);
}
break;
2019-10-10 22:32:54 +00:00
case CAPTURE_DONE:
wayvnc_process_frame(self);
break;
}
}
int wayvnc_usage(FILE* stream, int rc)
{
static const char* usage =
"Usage: wayvnc [options] [address [port]]\n"
"\n"
2020-01-18 18:14:17 +00:00
" -C,--config=<path> Select a config file.\n"
" -c,--frame-capturing=screencopy|dmabuf Select frame capturing backend.\n"
2020-01-24 20:19:57 +00:00
" -o,--output=<name> Select output to capture.\n"
2019-12-30 10:15:12 +00:00
" -k,--keyboard=<layout> Select keyboard layout.\n"
2019-12-31 13:55:25 +00:00
" -s,--seat=<name> Select seat by name.\n"
" -r,--render-cursor Enable overlay cursor rendering.\n"
" -h,--help Get help (this text).\n"
"\n";
fprintf(stream, "%s", usage);
return rc;
}
2020-01-18 18:29:54 +00:00
int check_cfg_sanity(struct cfg* cfg)
{
if (cfg->enable_auth) {
int rc = 0;
if (!nvnc_has_auth()) {
log_error("Authentication can't be enabled because it was not selected during build\n");
return -1;
}
if (!cfg->certificate_file) {
log_error("Authentication enabled, but missing certificate_file\n");
rc = -1;
}
if (!cfg->private_key_file) {
log_error("Authentication enabled, but missing private_key_file\n");
rc = -1;
}
if (!cfg->username) {
log_error("Authentication enabled, but missing username\n");
rc = -1;
}
if (!cfg->password) {
log_error("Authentication enabled, but missing password\n");
rc = -1;
}
return rc;
}
return 0;
}
2019-10-06 18:51:39 +00:00
int main(int argc, char* argv[])
{
2019-10-07 23:44:55 +00:00
struct wayvnc self = { 0 };
2019-10-06 18:51:39 +00:00
2020-01-18 18:14:17 +00:00
const char* cfg_file = NULL;
const char* address = NULL;
int port = 0;
2020-01-24 20:19:57 +00:00
const char* output_name = NULL;
enum frame_capture_backend_type fcbackend = FRAME_CAPTURE_BACKEND_NONE;
2019-12-31 13:55:25 +00:00
const char* seat_name = NULL;
bool overlay_cursor = false;
static const char* shortopts = "C:c:o:k:s:rh";
static const struct option longopts[] = {
2020-01-18 18:14:17 +00:00
{ "config", required_argument, NULL, 'C' },
{ "frame-capturing", required_argument, NULL, 'c' },
2019-10-13 12:58:56 +00:00
{ "output", required_argument, NULL, 'o' },
2019-12-30 10:15:12 +00:00
{ "keyboard", required_argument, NULL, 'k' },
2019-12-31 13:55:25 +00:00
{ "seat", required_argument, NULL, 's' },
{ "render-cursor", no_argument, NULL, 'r' },
{ "help", no_argument, NULL, 'h' },
{ NULL, 0, NULL, 0 }
};
while (1) {
int c = getopt_long(argc, argv, shortopts, longopts, NULL);
if (c < 0)
break;
switch (c) {
2020-01-18 18:14:17 +00:00
case 'C':
cfg_file = optarg;
break;
case 'c':
fcbackend = frame_capture_backend_from_string(optarg);
if (fcbackend == FRAME_CAPTURE_BACKEND_NONE) {
fprintf(stderr, "Invalid backend: %s\n\n",
optarg);
return wayvnc_usage(stderr, 1);
}
break;
2019-10-13 12:58:56 +00:00
case 'o':
2020-01-24 20:19:57 +00:00
output_name = optarg;
2019-10-13 12:58:56 +00:00
break;
2019-12-30 10:15:12 +00:00
case 'k':
self.kb_layout = optarg;
break;
2019-12-31 13:55:25 +00:00
case 's':
seat_name = optarg;
break;
case 'r':
overlay_cursor = true;
break;
case 'h':
return wayvnc_usage(stdout, 0);
default:
return wayvnc_usage(stderr, 1);
}
}
int n_args = argc - optind;
if (n_args >= 1)
address = argv[optind];
if (n_args >= 2)
port = atoi(argv[optind + 1]);
2020-01-18 18:29:54 +00:00
errno = 0;
2020-01-18 18:14:17 +00:00
int cfg_rc = cfg_load(&self.cfg, cfg_file);
2020-01-29 19:45:04 +00:00
if (cfg_rc != 0 && (cfg_file || errno != ENOENT)) {
2020-01-18 18:14:17 +00:00
if (cfg_rc > 0) {
log_error("Failed to load config. Error on line %d\n",
cfg_rc);
} else {
log_error("Failed to load config. %m\n");
}
return 1;
}
2020-01-18 18:29:54 +00:00
if (check_cfg_sanity(&self.cfg) < 0)
return 1;
2020-01-18 18:14:17 +00:00
if (cfg_rc == 0) {
if (!address) address = self.cfg.address;
if (!port) port = self.cfg.port;
}
if (!address) address = DEFAULT_ADDRESS;
if (!port) port = DEFAULT_PORT;
2019-10-06 22:12:29 +00:00
if (init_wayland(&self) < 0) {
log_error("Failed to initialise wayland\n");
2019-10-06 18:51:39 +00:00
return 1;
2019-10-06 22:12:29 +00:00
}
2019-10-06 18:51:39 +00:00
struct output* out;
2020-01-24 20:19:57 +00:00
if (output_name) {
out = output_find_by_name(&self.outputs, output_name);
2019-10-13 12:58:56 +00:00
if (!out) {
log_error("No such output\n");
goto failure;
}
2019-12-30 21:13:44 +00:00
} else {
out = output_first(&self.outputs);
if (!out) {
log_error("No output found\n");
goto failure;
}
2019-10-06 22:12:29 +00:00
}
2019-12-31 13:55:25 +00:00
struct seat* seat;
if (seat_name) {
seat = seat_find_by_name(&self.seats, seat_name);
if (!seat) {
log_error("No such seat\n");
goto failure;
}
} else {
seat = seat_first(&self.seats);
if (!seat) {
log_error("No seat found\n");
goto failure;
}
}
2019-10-13 12:58:56 +00:00
self.selected_output = out;
2019-12-31 13:55:25 +00:00
self.selected_seat = seat;
2019-10-13 12:58:56 +00:00
self.dmabuf_backend.fc.wl_output = out->wl_output;
self.screencopy_backend.frame_capture.wl_output = out->wl_output;
2019-12-31 13:55:25 +00:00
self.keyboard_backend.virtual_keyboard =
zwp_virtual_keyboard_manager_v1_create_virtual_keyboard(
self.keyboard_manager, self.selected_seat->wl_seat);
keyboard_init(&self.keyboard_backend, self.kb_layout);
2020-01-24 20:56:47 +00:00
self.pointer_backend.vnc = self.nvnc;
self.pointer_backend.output = self.selected_output;
2020-01-24 20:56:47 +00:00
2020-02-05 22:33:16 +00:00
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(
2020-01-24 20:56:47 +00:00
self.pointer_manager, self.selected_seat->wl_seat);
pointer_init(&self.pointer_backend);
2019-12-22 15:41:51 +00:00
enum renderer_input_type renderer_input_type =
fcbackend == FRAME_CAPTURE_BACKEND_DMABUF ?
RENDERER_INPUT_DMABUF : RENDERER_INPUT_FB;
if (renderer_init(&self.renderer, self.selected_output,
renderer_input_type) < 0) {
2019-10-06 22:12:29 +00:00
log_error("Failed to initialise renderer\n");
goto failure;
}
2020-07-06 16:46:30 +00:00
struct aml* aml = aml_new();
2020-03-16 22:01:43 +00:00
if (!aml)
goto main_loop_failure;
aml_set_default(aml);
2019-10-06 22:12:29 +00:00
if (init_main_loop(&self) < 0)
goto main_loop_failure;
if (init_nvnc(&self, address, port) < 0)
2019-10-06 22:12:29 +00:00
goto nvnc_failure;
if (self.screencopy_backend.manager)
screencopy_init(&self.screencopy_backend);
if (self.dmabuf_backend.manager)
dmabuf_capture_init(&self.dmabuf_backend);
switch (fcbackend) {
case FRAME_CAPTURE_BACKEND_SCREENCOPY:
if (!self.screencopy_backend.manager) {
log_error("screencopy is not supported by compositor\n");
goto capture_failure;
}
self.capture_backend = &self.screencopy_backend.frame_capture;
break;
case FRAME_CAPTURE_BACKEND_DMABUF:
if (!self.screencopy_backend.manager) {
log_error("export-dmabuf is not supported by compositor\n");
goto capture_failure;
}
self.capture_backend = &self.dmabuf_backend.fc;
break;
case FRAME_CAPTURE_BACKEND_NONE:
if (self.screencopy_backend.manager)
self.capture_backend = &self.screencopy_backend.frame_capture;
else if (self.dmabuf_backend.manager)
self.capture_backend = &self.dmabuf_backend.fc;
else
goto capture_failure;
break;
}
2019-10-10 22:32:54 +00:00
pixman_region_init(&self.current_damage);
self.capture_backend->overlay_cursor = overlay_cursor;
if (wayvnc_start_capture(&self, 0) < 0)
2019-10-06 22:50:11 +00:00
goto capture_failure;
2020-03-16 22:01:43 +00:00
wl_display_dispatch(self.display);
2019-10-06 22:12:29 +00:00
2020-03-16 22:01:43 +00:00
while (!self.do_exit) {
wl_display_flush(self.display);
aml_poll(aml, -1);
aml_dispatch(aml);
}
frame_capture_stop(self.capture_backend);
2019-10-06 22:50:11 +00:00
if (self.buffer) nvnc_fb_unref(self.buffer);
pixman_region_fini(&self.current_damage);
2019-10-07 23:44:55 +00:00
2020-04-12 21:46:48 +00:00
nvnc_display_unref(self.nvnc_display);
2019-10-06 22:12:29 +00:00
nvnc_close(self.nvnc);
renderer_destroy(&self.renderer);
2020-05-03 19:58:50 +00:00
if (self.screencopy_backend.manager)
screencopy_destroy(&self.screencopy_backend);
if (self.dmabuf_backend.manager)
dmabuf_capture_destroy(&self.dmabuf_backend);
2019-10-06 22:12:29 +00:00
wayvnc_destroy(&self);
2020-03-16 22:01:43 +00:00
aml_unref(aml);
2019-10-06 18:51:39 +00:00
return 0;
2019-10-06 22:12:29 +00:00
2019-10-06 22:50:11 +00:00
capture_failure:
2020-04-12 21:46:48 +00:00
nvnc_display_unref(self.nvnc_display);
2019-10-06 22:50:11 +00:00
nvnc_close(self.nvnc);
2019-10-06 22:12:29 +00:00
nvnc_failure:
main_loop_failure:
renderer_destroy(&self.renderer);
failure:
wayvnc_destroy(&self);
return 1;
2019-10-06 18:51:39 +00:00
}