wayvnc/src/main.c

340 lines
7.3 KiB
C
Raw Normal View History

2019-10-06 18:51:39 +00:00
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#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-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-06 18:51:39 +00:00
#include "wlr-export-dmabuf-unstable-v1.h"
#include "render.h"
#include "dmabuf.h"
#include "strlcpy.h"
#include "logging.h"
struct output {
struct wl_output* wl_output;
struct wl_list link;
uint32_t id;
uint32_t width;
uint32_t height;
char make[256];
char model[256];
};
2019-10-06 22:12:29 +00:00
struct wayvnc {
struct wl_display* display;
struct wl_registry* registry;
struct wl_list outputs;
struct renderer renderer;
struct zwlr_export_dmabuf_manager_v1* export_manager;
const struct output* selected_output;
uv_poll_t wayland_poller;
uv_prepare_t flusher;
uv_signal_t signal_handler;
struct nvnc* nvnc;
};
2019-10-06 18:51:39 +00:00
static void output_handle_geometry(void* data, struct wl_output* wl_output,
int32_t x, int32_t y, int32_t phys_width,
int32_t phys_height, int32_t subpixel,
const char* make, const char* model,
int32_t transform)
{
struct output* output = data;
strlcpy(output->make, make, sizeof(output->make));
strlcpy(output->model, model, sizeof(output->make));
}
static void output_handle_mode(void* data, struct wl_output* wl_output,
uint32_t flags, int32_t width, int32_t height,
int32_t refresh)
{
struct output* output = data;
if (!(flags & WL_OUTPUT_MODE_CURRENT))
return;
output->width = width;
output->height = height;
}
static void output_handle_done(void* data, struct wl_output* wl_output)
{
}
static void output_handle_scale(void* data, struct wl_output* wl_output,
int32_t factor)
{
}
static const struct wl_output_listener output_listener = {
.geometry = output_handle_geometry,
.mode = output_handle_mode,
.done = output_handle_done,
.scale = output_handle_scale,
};
void output_destroy(struct output* output)
{
wl_output_destroy(output->wl_output);
free(output);
}
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
void output_list_destroy(struct wl_list* list)
{
struct output* output;
struct output* tmp;
wl_list_for_each_safe(output, tmp, list, link) {
wl_list_remove(&output->link);
output_destroy(output);
}
}
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 output* output = calloc(1, sizeof(*output));
if (!output) {
log_error("OOM\n");
return;
}
output->id = id;
output->wl_output = wl_registry_bind(registry, id,
&wl_output_interface,
version);
wl_output_add_listener(output->wl_output, &output_listener,
output);
wl_list_insert(&self->outputs, &output->link);
return;
}
if (strcmp(interface, zwlr_export_dmabuf_manager_v1_interface.name) == 0) {
self->export_manager =
wl_registry_bind(registry, id,
&zwlr_export_dmabuf_manager_v1_interface,
version);
return;
}
}
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);
zwlr_export_dmabuf_manager_v1_destroy(self->export_manager);
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->export_manager) {
log_error("Compositor does not support %s.\n",
zwlr_export_dmabuf_manager_v1_interface.name);
goto export_manager_failure;
}
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
int wayvnc_select_first_output(struct wayvnc* self)
{
struct output* out;
wl_list_for_each(out, &self->outputs, link) {
self->selected_output = out;
return 0;
}
return -1;
}
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);
uv_close(handle, NULL);
}
void on_signal(uv_signal_t* handle, int signo)
{
struct wayvnc* self = wl_container_of(handle, self, signal_handler);
uv_walk(handle->loop, on_uv_walk, NULL);
}
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);
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;
}
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);
return 0;
}
2019-10-06 18:51:39 +00:00
int main(int argc, char* argv[])
{
struct wayvnc self;
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-06 22:12:29 +00:00
/* TODO: Allow selecting output */
if (wayvnc_select_first_output(&self) < 0) {
log_error("No output found\n");
goto failure;
}
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;
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
nvnc_close(self.nvnc);
renderer_destroy(&self.renderer);
wayvnc_destroy(&self);
2019-10-06 18:51:39 +00:00
return 0;
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
}