wayvnc/src/main.c

646 lines
16 KiB
C
Raw Normal View History

2019-10-08 18:41:23 +00:00
/*
* Copyright (c) 2019 Andri Yngvason
*
* 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>
2019-10-06 22:12:29 +00:00
#include <neatvnc.h>
#include <uv.h>
#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>
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"
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-10-06 18:51:39 +00:00
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 {
struct wl_display* display;
struct wl_registry* registry;
struct wl_list outputs;
2019-12-24 15:54:58 +00:00
struct wl_seat* seat;
struct zwp_virtual_keyboard_manager_v1* keyboard_manager;
2019-10-06 22:12:29 +00:00
struct renderer renderer;
const struct output* selected_output;
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;
2019-10-06 22:12:29 +00:00
uv_poll_t wayland_poller;
uv_prepare_t flusher;
uv_signal_t signal_handler;
uv_timer_t performance_timer;
2019-10-06 22:12:29 +00:00
struct nvnc* nvnc;
2019-10-07 23:44:55 +00:00
struct nvnc_fb* current_fb;
struct nvnc_fb* last_fb;
uint32_t n_frames;
2019-10-06 22:12:29 +00:00
};
void on_capture_done(struct frame_capture* capture);
2019-10-06 22:50:11 +00:00
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 22:12:29 +00:00
struct output* wayvnc_output_find(struct wayvnc* self, uint32_t id)
{
struct output* output;
wl_list_for_each(output, &self->outputs, link)
if (output->id == id)
return output;
return NULL;
}
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;
}
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) {
self->pointer_backend.manager =
wl_registry_bind(registry, id,
&zwlr_virtual_pointer_manager_v1_interface,
1);
return;
}
2019-12-24 15:54:58 +00:00
if (strcmp(interface, wl_seat_interface.name) == 0) {
self->seat =
wl_registry_bind(registry, id, &wl_seat_interface, 7);
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;
struct output* out = wayvnc_output_find(self, id);
if (out) {
wl_list_remove(&out->link);
output_destroy(out);
/* TODO: If this is the selected output, exit */
}
2019-10-06 18:51:39 +00:00
}
void wayvnc_destroy(struct wayvnc* self)
{
output_list_destroy(&self->outputs);
2019-12-27 14:41:54 +00:00
wl_seat_destroy(self->seat);
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);
2019-12-22 15:41:51 +00:00
zwlr_virtual_pointer_manager_v1_destroy(self->pointer_backend.manager);
2019-12-27 14:41:54 +00:00
pointer_destroy(&self->pointer_backend);
zwlr_screencopy_manager_v1_destroy(self->screencopy_backend.manager);
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);
}
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);
self->registry = wl_display_get_registry(self->display);
if (!self->registry)
goto failure;
wl_registry_add_listener(self->registry, &registry_listener, self);
wl_display_roundtrip(self->display);
wl_display_dispatch(self->display);
if (!self->dmabuf_backend.manager) {
2019-10-06 18:51:39 +00:00
log_error("Compositor does not support %s.\n",
zwlr_export_dmabuf_manager_v1_interface.name);
goto export_manager_failure;
}
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-12-22 15:41:51 +00:00
if (self->pointer_backend.manager) {
self->pointer_backend.vnc = self->nvnc;
pointer_init(&self->pointer_backend);
} else {
log_error("Compositor does not support %s.\n",
zwlr_virtual_pointer_manager_v1_interface.name);
}
2019-12-24 15:54:58 +00:00
assert(self->seat);
assert(self->keyboard_manager);
self->keyboard_backend.virtual_keyboard =
zwp_virtual_keyboard_manager_v1_create_virtual_keyboard(
self->keyboard_manager, self->seat);
keyboard_init(&self->keyboard_backend);
2019-10-06 18:51:39 +00:00
return 0;
export_manager_failure:
output_list_destroy(&self->outputs);
failure:
wl_display_disconnect(self->display);
return -1;
}
2019-10-06 22:12:29 +00:00
void on_wayland_event(uv_poll_t* handle, int status, int event)
{
struct wayvnc* self = wl_container_of(handle, self, wayland_poller);
wl_display_dispatch(self->display);
}
void prepare_for_poll(uv_prepare_t* handle)
{
struct wayvnc* self = wl_container_of(handle, self, flusher);
wl_display_flush(self->display);
}
void on_uv_walk(uv_handle_t* handle, void* arg)
{
uv_unref(handle);
}
2019-10-06 22:50:11 +00:00
void wayvnc_exit(void)
{
uv_walk(uv_default_loop(), on_uv_walk, NULL);
}
2019-10-06 22:12:29 +00:00
void on_signal(uv_signal_t* handle, int signo)
{
2019-10-06 22:50:11 +00:00
wayvnc_exit();
2019-10-06 22:12:29 +00:00
}
void on_performance_tick(uv_timer_t* handle)
{
struct wayvnc* self = wl_container_of(handle, self, performance_timer);
printf("%"PRIu32" FPS\n", self->n_frames);
self->n_frames = 0;
}
2019-10-06 22:12:29 +00:00
int init_main_loop(struct wayvnc* self)
{
uv_loop_t* loop = uv_default_loop();
uv_poll_init(loop, &self->wayland_poller,
wl_display_get_fd(self->display));
uv_poll_start(&self->wayland_poller, UV_READABLE, on_wayland_event);
uv_prepare_init(loop, &self->flusher);
uv_prepare_start(&self->flusher, prepare_for_poll);
uv_signal_init(loop, &self->signal_handler);
uv_signal_start(&self->signal_handler, on_signal, SIGINT);
uv_timer_init(loop, &self->performance_timer);
uv_timer_start(&self->performance_timer, on_performance_tick, 1000,
1000);
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
struct nvnc* nvnc = nvnc_get_server(client);
struct wayvnc* wayvnc = nvnc_get_userdata(nvnc);
pointer_set(&wayvnc->pointer_backend, x, y, button_mask);
}
2019-12-24 15:54:58 +00:00
static void on_key_event(struct nvnc_client* client, uint32_t symbol,
bool is_pressed)
{
struct nvnc* nvnc = nvnc_get_server(client);
struct wayvnc* wayvnc = nvnc_get_userdata(nvnc);
keyboard_feed(&wayvnc->keyboard_backend, symbol, is_pressed);
}
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)
return -1;
nvnc_set_userdata(self->nvnc, self);
uint32_t format = fourcc_from_gl_format(self->renderer.read_format);
if (format == DRM_FORMAT_INVALID)
return -1;
nvnc_set_name(self->nvnc, "WayVNC");
nvnc_set_dimensions(self->nvnc,
self->selected_output->width,
self->selected_output->height,
format);
2019-12-22 15:41:51 +00:00
if (self->pointer_backend.manager)
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;
}
2019-10-06 22:50:11 +00:00
int wayvnc_start_capture(struct wayvnc* self)
{
2019-10-10 22:32:54 +00:00
return frame_capture_start(self->capture_backend);
2019-10-06 22:50:11 +00:00
}
2019-10-07 23:44:55 +00:00
void on_damage_check_done(struct pixman_region16* damage, void* userdata)
{
struct wayvnc* self = userdata;
if (pixman_region_not_empty(damage))
nvnc_feed_frame(self->nvnc, self->current_fb, damage);
if (wayvnc_start_capture(self) < 0) {
log_error("Failed to start capture. Exiting...\n");
wayvnc_exit();
}
}
void wayvnc_update_vnc(struct wayvnc* self, struct nvnc_fb* fb)
2019-10-06 22:50:11 +00:00
{
uint32_t width = nvnc_fb_get_width(fb);
uint32_t height = nvnc_fb_get_height(fb);
2019-10-07 23:07:12 +00:00
2019-10-07 23:44:55 +00:00
if (self->last_fb)
nvnc_fb_unref(self->last_fb);
2019-10-07 23:07:12 +00:00
2019-10-07 23:44:55 +00:00
self->last_fb = self->current_fb;
self->current_fb = fb;
2019-10-07 23:07:12 +00:00
2019-10-07 23:44:55 +00:00
if (self->last_fb) {
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
nvnc_check_damage(self->current_fb, self->last_fb, hint_x,
hint_y, hint_width, hint_height,
on_damage_check_done, self);
2019-10-07 23:44:55 +00:00
return;
}
struct pixman_region16 damage;
pixman_region_init_rect(&damage, 0, 0, width, height);
2019-10-07 23:44:55 +00:00
nvnc_feed_frame(self->nvnc, self->current_fb, &damage);
2019-10-07 23:07:12 +00:00
pixman_region_fini(&damage);
2019-10-06 22:50:11 +00:00
if (wayvnc_start_capture(self) < 0) {
log_error("Failed to start capture. Exiting...\n");
wayvnc_exit();
}
}
void wayvnc_process_frame(struct wayvnc* self)
{
uint32_t format = fourcc_from_gl_format(self->renderer.read_format);
struct dmabuf_frame* frame = &self->dmabuf_backend.frame;
struct nvnc_fb* fb = nvnc_fb_new(frame->width, frame->height, format);
void* addr = nvnc_fb_get_addr(fb);
render_dmabuf_frame(&self->renderer, frame);
render_copy_pixels(&self->renderer, addr, 0, frame->height);
wayvnc_update_vnc(self, fb);
}
void wayvnc_process_screen(struct wayvnc* self)
{
uint32_t format = fourcc_from_gl_format(self->renderer.read_format);
void* pixels = self->screencopy_backend.pixels;
2019-10-10 22:32:54 +00:00
uint32_t width = self->capture_backend->frame_info.width;
uint32_t height = self->capture_backend->frame_info.height;
uint32_t stride = self->capture_backend->frame_info.stride;
struct nvnc_fb* fb = nvnc_fb_new(width, height, format);
void* addr = nvnc_fb_get_addr(fb);
render_framebuffer(&self->renderer, pixels, format, width, height,
stride);
render_copy_pixels(&self->renderer, addr, 0, height);
wayvnc_update_vnc(self, fb);
}
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");
wayvnc_exit();
break;
2019-10-10 22:32:54 +00:00
case CAPTURE_FAILED:
if (wayvnc_start_capture(self) < 0) {
log_error("Failed to start capture. Exiting...\n");
wayvnc_exit();
}
break;
2019-10-10 22:32:54 +00:00
case CAPTURE_DONE:
self->n_frames++;
if (self->capture_backend == (struct frame_capture*)&self->screencopy_backend)
wayvnc_process_screen(self);
else
wayvnc_process_frame(self);
break;
}
}
int wayvnc_usage(FILE* stream, int rc)
{
static const char* usage =
"Usage: wayvnc [options]\n"
"\n"
" -c,--frame-capturing=screencopy|dmabuf Select frame capturing backend.\n"
" -h,--help Get help (this text).\n"
"\n";
fprintf(stream, "%s", usage);
return rc;
}
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
2019-10-13 12:58:56 +00:00
int output_id = -1;
enum frame_capture_backend_type fcbackend =
FRAME_CAPTURE_BACKEND_SCREENCOPY;
2019-10-13 12:58:56 +00:00
static const char* shortopts = "c:o:h";
static const struct option longopts[] = {
{ "frame-capturing", required_argument, NULL, 'c' },
2019-10-13 12:58:56 +00:00
{ "output", required_argument, NULL, 'o' },
{ "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) {
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':
output_id = atoi(optarg);
break;
case 'h':
return wayvnc_usage(stdout, 0);
default:
return wayvnc_usage(stderr, 1);
}
}
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
printf("Outputs:\n");
struct output* out;
wl_list_for_each(out, &self.outputs, link)
printf("%"PRIu32": Make: %s. Model: %s\n", out->id, out->make,
out->model);
2019-10-13 12:58:56 +00:00
if (output_id >= 0) {
out = output_find_by_id(&self.outputs, output_id);
if (!out) {
log_error("No such output\n");
goto failure;
}
}
out = output_first(&self.outputs);
if (!out) {
2019-10-06 22:12:29 +00:00
log_error("No output found\n");
goto failure;
}
2019-10-13 12:58:56 +00:00
self.selected_output = out;
self.dmabuf_backend.fc.wl_output = out->wl_output;
self.screencopy_backend.frame_capture.wl_output = out->wl_output;
2019-12-22 15:41:51 +00:00
if (self.pointer_backend.manager) {
self.pointer_backend.width = out->width;
self.pointer_backend.height = out->height;
}
2019-10-06 22:12:29 +00:00
if (renderer_init(&self.renderer, self.selected_output->width,
self.selected_output->height) < 0) {
log_error("Failed to initialise renderer\n");
goto failure;
}
if (init_main_loop(&self) < 0)
goto main_loop_failure;
if (init_nvnc(&self, "0.0.0.0", 5900) < 0)
goto nvnc_failure;
2019-10-10 22:32:54 +00:00
screencopy_init(&self.screencopy_backend);
dmabuf_capture_init(&self.dmabuf_backend);
switch (fcbackend) {
case FRAME_CAPTURE_BACKEND_SCREENCOPY:
self.capture_backend = &self.screencopy_backend.frame_capture;
break;
case FRAME_CAPTURE_BACKEND_DMABUF:
self.capture_backend = &self.dmabuf_backend.fc;
break;
case FRAME_CAPTURE_BACKEND_NONE:
abort();
break;
}
2019-10-10 22:32:54 +00:00
2019-10-06 22:50:11 +00:00
if (wayvnc_start_capture(&self) < 0)
goto capture_failure;
2019-10-06 22:12:29 +00:00
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
uv_loop_close(uv_default_loop());
frame_capture_stop(self.capture_backend);
2019-10-06 22:50:11 +00:00
2019-10-07 23:44:55 +00:00
if (self.current_fb) nvnc_fb_unref(self.current_fb);
if (self.last_fb) nvnc_fb_unref(self.last_fb);
2019-10-06 22:12:29 +00:00
nvnc_close(self.nvnc);
renderer_destroy(&self.renderer);
2019-12-27 14:41:54 +00:00
screencopy_destroy(&self.screencopy_backend);
2019-10-06 22:12:29 +00:00
wayvnc_destroy(&self);
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:
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
}