Compare commits
6 Commits
master
...
detached-m
Author | SHA1 | Date |
---|---|---|
Andri Yngvason | 7c3b606b8d | |
Andri Yngvason | 13043987e5 | |
Andri Yngvason | f9bacc8eb8 | |
Andri Yngvason | 2c524e28fc | |
Andri Yngvason | 861e140205 | |
Andri Yngvason | 0938162e6f |
|
@ -14,6 +14,7 @@ packages:
|
|||
- sway
|
||||
- jq
|
||||
- lsof
|
||||
- python-pycryptodomex # needed by vncdotool
|
||||
- vncdotool
|
||||
|
||||
sources:
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import re
|
||||
import os
|
||||
import glob
|
||||
|
||||
class Program:
|
||||
command_seq = 0
|
||||
reader = None
|
||||
writer = None
|
||||
read_buffer = ""
|
||||
message_queue = asyncio.Queue()
|
||||
reply_queue = asyncio.Queue()
|
||||
decoder = json.JSONDecoder()
|
||||
tasks = []
|
||||
|
||||
async def read_message(self):
|
||||
while True:
|
||||
try:
|
||||
result, index = self.decoder.raw_decode(self.read_buffer)
|
||||
self.read_buffer = self.read_buffer[index:].lstrip()
|
||||
return result
|
||||
except json.JSONDecodeError:
|
||||
data = await self.reader.read(4096)
|
||||
self.read_buffer += data.decode('utf-8')
|
||||
|
||||
async def send_command(self, method, params = None):
|
||||
cmd = {
|
||||
"method": method,
|
||||
"id": self.command_seq,
|
||||
}
|
||||
|
||||
if not params is None:
|
||||
cmd['params'] = params
|
||||
|
||||
self.command_seq += 1
|
||||
self.writer.write(json.dumps(cmd).encode())
|
||||
await self.writer.drain()
|
||||
|
||||
reply = await self.reply_queue.get()
|
||||
self.reply_queue.task_done()
|
||||
return reply['code'] == 0
|
||||
|
||||
async def attach(self, display):
|
||||
return await self.send_command('attach', {'display': display})
|
||||
|
||||
async def attach_any(self):
|
||||
for path in glob.iglob('/run/user/*/wayland-*'):
|
||||
if path.endswith('.lock'):
|
||||
continue
|
||||
|
||||
if await self.attach(path):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
async def handle_detached(self):
|
||||
while not await self.attach_any():
|
||||
await asyncio.sleep(1.0)
|
||||
|
||||
async def process_message(self, message):
|
||||
method = message['method']
|
||||
if (method == 'detached'):
|
||||
await self.handle_detached()
|
||||
|
||||
async def message_processor(self):
|
||||
while True:
|
||||
message = await self.read_message()
|
||||
if 'method' in message:
|
||||
await self.message_queue.put(message)
|
||||
elif 'code' in message:
|
||||
await self.reply_queue.put(message)
|
||||
|
||||
async def main(self):
|
||||
self.reader, self.writer = await asyncio.open_unix_connection("/tmp/wayvncctl-0")
|
||||
self.tasks.append(asyncio.create_task(self.message_processor()))
|
||||
|
||||
await self.attach_any()
|
||||
await self.send_command("event-receive")
|
||||
|
||||
while True:
|
||||
message = await self.message_queue.get()
|
||||
await self.process_message(message)
|
||||
|
||||
prog = Program()
|
||||
asyncio.run(prog.main())
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2022-2023 Jim Ramsay
|
||||
* Copyright (c) 2023 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
|
||||
|
@ -19,6 +20,8 @@
|
|||
#include <stdbool.h>
|
||||
|
||||
enum cmd_type {
|
||||
CMD_ATTACH,
|
||||
CMD_DETACH,
|
||||
CMD_HELP,
|
||||
CMD_EVENT_RECEIVE,
|
||||
CMD_CLIENT_LIST,
|
||||
|
@ -36,6 +39,7 @@ enum event_type {
|
|||
EVT_CAPTURE_CHANGED,
|
||||
EVT_CLIENT_CONNECTED,
|
||||
EVT_CLIENT_DISCONNECTED,
|
||||
EVT_DETACHED,
|
||||
EVT_UNKNOWN,
|
||||
};
|
||||
#define EVT_LIST_LEN EVT_UNKNOWN
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Jim Ramsay
|
||||
* Copyright (c) 2023 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
|
||||
|
@ -41,6 +42,8 @@ struct ctl_server_output {
|
|||
|
||||
struct ctl_server_actions {
|
||||
void* userdata;
|
||||
struct cmd_response* (*on_attach)(struct ctl*, const char* display);
|
||||
struct cmd_response* (*on_detach)(struct ctl*);
|
||||
struct cmd_response* (*on_output_cycle)(struct ctl*,
|
||||
enum output_cycle_direction direction);
|
||||
struct cmd_response* (*on_output_switch)(struct ctl*,
|
||||
|
@ -79,3 +82,5 @@ void ctl_server_event_disconnected(struct ctl*,
|
|||
|
||||
void ctl_server_event_capture_changed(struct ctl*,
|
||||
const char* captured_output);
|
||||
|
||||
void ctl_server_event_detached(struct ctl*);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2022-2023 Jim Ramsay
|
||||
* Copyright (c) 2023 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
|
||||
|
@ -403,6 +404,8 @@ static void pretty_print(json_t* data,
|
|||
case CMD_OUTPUT_LIST:
|
||||
pretty_output_list(data);
|
||||
break;
|
||||
case CMD_ATTACH:
|
||||
case CMD_DETACH:
|
||||
case CMD_CLIENT_DISCONNECT:
|
||||
case CMD_OUTPUT_SET:
|
||||
case CMD_OUTPUT_CYCLE:
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2022-2023 Jim Ramsay
|
||||
* Copyright (c) 2023 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
|
||||
|
@ -20,6 +21,19 @@
|
|||
#include <string.h>
|
||||
|
||||
struct cmd_info ctl_command_list[] = {
|
||||
[CMD_ATTACH] = { "attach",
|
||||
"Attach to a running wayland compositor",
|
||||
{
|
||||
{ "display", "Display name", "<name>",
|
||||
.positional = true },
|
||||
{},
|
||||
}
|
||||
|
||||
},
|
||||
[CMD_DETACH] = { "detach",
|
||||
"Detach from the wayland compositor",
|
||||
{{}},
|
||||
},
|
||||
[CMD_HELP] = { "help",
|
||||
"List all commands and events, or show usage of a specific command or event",
|
||||
{
|
||||
|
@ -110,6 +124,10 @@ struct cmd_info ctl_event_list[] = {
|
|||
"Sent by waynvc when a VNC client disconnects",
|
||||
{ CLIENT_EVENT_PARAMS("not including") }
|
||||
},
|
||||
[EVT_DETACHED] = {"detached",
|
||||
"Sent after detaching from compositor",
|
||||
{}
|
||||
},
|
||||
};
|
||||
|
||||
enum cmd_type ctl_command_parse_name(const char* name)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2022-2023 Jim Ramsay
|
||||
* Copyright (c) 2023 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
|
||||
|
@ -46,6 +47,11 @@ struct cmd {
|
|||
enum cmd_type type;
|
||||
};
|
||||
|
||||
struct cmd_attach {
|
||||
struct cmd cmd;
|
||||
char display[128];
|
||||
};
|
||||
|
||||
struct cmd_help {
|
||||
struct cmd cmd;
|
||||
char id[64];
|
||||
|
@ -105,6 +111,19 @@ static void cmd_response_destroy(struct cmd_response* self)
|
|||
free(self);
|
||||
}
|
||||
|
||||
static struct cmd_attach* cmd_attach_new(json_t* args,
|
||||
struct jsonipc_error* err)
|
||||
{
|
||||
const char* display = NULL;
|
||||
if (json_unpack(args, "{s:s}", "display", &display) == -1) {
|
||||
jsonipc_error_printf(err, EINVAL, "Missing display name");
|
||||
return NULL;
|
||||
}
|
||||
struct cmd_attach* cmd = calloc(1, sizeof(*cmd));
|
||||
strlcpy(cmd->display, display, sizeof(cmd->display));
|
||||
return cmd;
|
||||
}
|
||||
|
||||
static struct cmd_help* cmd_help_new(json_t* args,
|
||||
struct jsonipc_error* err)
|
||||
{
|
||||
|
@ -185,6 +204,9 @@ static struct cmd* parse_command(struct jsonipc_request* ipc,
|
|||
enum cmd_type cmd_type = ctl_command_parse_name(ipc->method);
|
||||
struct cmd* cmd = NULL;
|
||||
switch (cmd_type) {
|
||||
case CMD_ATTACH:
|
||||
cmd = (struct cmd*)cmd_attach_new(ipc->params, err);
|
||||
break;
|
||||
case CMD_HELP:
|
||||
cmd = (struct cmd*)cmd_help_new(ipc->params, err);
|
||||
break;
|
||||
|
@ -194,6 +216,7 @@ static struct cmd* parse_command(struct jsonipc_request* ipc,
|
|||
case CMD_CLIENT_DISCONNECT:
|
||||
cmd = (struct cmd*)cmd_disconnect_client_new(ipc->params, err);
|
||||
break;
|
||||
case CMD_DETACH:
|
||||
case CMD_VERSION:
|
||||
case CMD_EVENT_RECEIVE:
|
||||
case CMD_CLIENT_LIST:
|
||||
|
@ -407,6 +430,11 @@ static struct cmd_response* ctl_server_dispatch_cmd(struct ctl* self,
|
|||
nvnc_log(NVNC_LOG_INFO, "Dispatching control client command '%s'", info->name);
|
||||
struct cmd_response* response = NULL;
|
||||
switch (cmd->type) {
|
||||
case CMD_ATTACH:{
|
||||
struct cmd_attach* c = (struct cmd_attach*)cmd;
|
||||
response = self->actions.on_attach(self, c->display);
|
||||
break;
|
||||
}
|
||||
case CMD_HELP:{
|
||||
struct cmd_help* c = (struct cmd_help*)cmd;
|
||||
response = generate_help_object(c->id, c->id_is_command);
|
||||
|
@ -423,6 +451,9 @@ static struct cmd_response* ctl_server_dispatch_cmd(struct ctl* self,
|
|||
response = self->actions.on_disconnect_client(self, c->id);
|
||||
break;
|
||||
}
|
||||
case CMD_DETACH:
|
||||
response = self->actions.on_detach(self);
|
||||
break;
|
||||
case CMD_WAYVNC_EXIT:
|
||||
response = self->actions.on_wayvnc_exit(self);
|
||||
break;
|
||||
|
@ -942,3 +973,8 @@ void ctl_server_event_capture_changed(struct ctl* self,
|
|||
ctl_server_enqueue_event(self, EVT_CAPTURE_CHANGED,
|
||||
json_pack("{s:s}", "output", captured_output));
|
||||
}
|
||||
|
||||
void ctl_server_event_detached(struct ctl* self)
|
||||
{
|
||||
ctl_server_enqueue_event(self, EVT_DETACHED, json_object());
|
||||
}
|
||||
|
|
438
src/main.c
438
src/main.c
|
@ -119,6 +119,8 @@ struct wayvnc {
|
|||
|
||||
struct ctl* ctl;
|
||||
bool is_initializing;
|
||||
|
||||
bool start_detached;
|
||||
};
|
||||
|
||||
struct wayvnc_client {
|
||||
|
@ -144,6 +146,11 @@ static void client_init_seat(struct wayvnc_client* self);
|
|||
static void client_init_pointer(struct wayvnc_client* self);
|
||||
static void client_init_keyboard(struct wayvnc_client* self);
|
||||
static void client_init_data_control(struct wayvnc_client* self);
|
||||
static void client_detach_wayland(struct wayvnc_client* self);
|
||||
static int blank_screen(struct wayvnc* self);
|
||||
static bool wayland_attach(struct wayvnc* self, const char* display,
|
||||
const char* output);
|
||||
static void wayland_detach(struct wayvnc* self);
|
||||
|
||||
struct wl_shm* wl_shm = NULL;
|
||||
struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf = NULL;
|
||||
|
@ -303,8 +310,13 @@ static void registry_remove(void* data, struct wl_registry* registry,
|
|||
output_destroy(out);
|
||||
|
||||
if (out == self->selected_output) {
|
||||
nvnc_log(NVNC_LOG_ERROR, "No fallback outputs left. Exiting...");
|
||||
wayvnc_exit(self);
|
||||
if (self->start_detached) {
|
||||
nvnc_log(NVNC_LOG_WARNING, "No fallback outputs left. Detaching...");
|
||||
wayland_detach(self);
|
||||
} else {
|
||||
nvnc_log(NVNC_LOG_ERROR, "No fallback outputs left. Exiting...");
|
||||
wayvnc_exit(self);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -360,44 +372,118 @@ static int init_render_node(int* fd)
|
|||
}
|
||||
#endif
|
||||
|
||||
void wayvnc_destroy(struct wayvnc* self)
|
||||
static void wayland_detach(struct wayvnc* self)
|
||||
{
|
||||
cfg_destroy(&self->cfg);
|
||||
if (!self->display)
|
||||
return;
|
||||
|
||||
// Screen blanking is required to release wl_shm of linux_dmabuf.
|
||||
if (self->nvnc)
|
||||
blank_screen(self);
|
||||
|
||||
if (self->nvnc) {
|
||||
struct nvnc_client* nvnc_client;
|
||||
for (nvnc_client = nvnc_client_first(self->nvnc); nvnc_client;
|
||||
nvnc_client = nvnc_client_next(nvnc_client)) {
|
||||
struct wayvnc_client* client =
|
||||
nvnc_get_userdata(nvnc_client);
|
||||
client_detach_wayland(client);
|
||||
}
|
||||
}
|
||||
|
||||
self->selected_output = NULL;
|
||||
self->screencopy.wl_output = NULL;
|
||||
|
||||
output_list_destroy(&self->outputs);
|
||||
seat_list_destroy(&self->seats);
|
||||
|
||||
if (zwp_linux_dmabuf)
|
||||
zwp_linux_dmabuf_v1_destroy(zwp_linux_dmabuf);
|
||||
zwp_linux_dmabuf = NULL;
|
||||
|
||||
if (self->screencopy.manager) {
|
||||
screencopy_stop(&self->screencopy);
|
||||
screencopy_destroy(&self->screencopy);
|
||||
zwlr_screencopy_manager_v1_destroy(self->screencopy.manager);
|
||||
}
|
||||
self->screencopy.manager = NULL;
|
||||
|
||||
if (xdg_output_manager)
|
||||
zxdg_output_manager_v1_destroy(xdg_output_manager);
|
||||
xdg_output_manager = NULL;
|
||||
|
||||
if (wlr_output_power_manager)
|
||||
zwlr_output_power_manager_v1_destroy(wlr_output_power_manager);
|
||||
wlr_output_power_manager = NULL;
|
||||
|
||||
wl_shm_destroy(wl_shm);
|
||||
wl_shm = NULL;
|
||||
|
||||
if (self->keyboard_manager)
|
||||
zwp_virtual_keyboard_manager_v1_destroy(self->keyboard_manager);
|
||||
self->keyboard_manager = NULL;
|
||||
|
||||
if (self->pointer_manager)
|
||||
zwlr_virtual_pointer_manager_v1_destroy(self->pointer_manager);
|
||||
self->pointer_manager = NULL;
|
||||
|
||||
if (self->data_control_manager)
|
||||
zwlr_data_control_manager_v1_destroy(self->data_control_manager);
|
||||
self->data_control_manager = NULL;
|
||||
|
||||
if (self->screencopy.manager)
|
||||
zwlr_screencopy_manager_v1_destroy(self->screencopy.manager);
|
||||
|
||||
if (self->performance_ticker)
|
||||
if (self->performance_ticker) {
|
||||
aml_stop(aml_get_default(), self->performance_ticker);
|
||||
aml_unref(self->performance_ticker);
|
||||
}
|
||||
self->performance_ticker = NULL;
|
||||
|
||||
if (self->capture_retry_timer)
|
||||
aml_unref(self->capture_retry_timer);
|
||||
self->capture_retry_timer = NULL;
|
||||
|
||||
wl_registry_destroy(self->registry);
|
||||
self->registry = NULL;
|
||||
|
||||
wl_display_disconnect(self->display);
|
||||
self->display = NULL;
|
||||
|
||||
ctl_server_event_detached(self->ctl);
|
||||
}
|
||||
|
||||
static int init_wayland(struct wayvnc* self)
|
||||
void wayvnc_destroy(struct wayvnc* self)
|
||||
{
|
||||
cfg_destroy(&self->cfg);
|
||||
wayland_detach(self);
|
||||
}
|
||||
|
||||
void on_wayland_event(void* obj)
|
||||
{
|
||||
struct wayvnc* self = aml_get_userdata(obj);
|
||||
|
||||
int rc MAYBE_UNUSED = wl_display_prepare_read(self->display);
|
||||
assert(rc == 0);
|
||||
|
||||
if (wl_display_read_events(self->display) < 0) {
|
||||
if (errno == EPIPE || errno == ECONNRESET) {
|
||||
nvnc_log(NVNC_LOG_ERROR, "Compositor has gone away. Exiting...");
|
||||
if (self->start_detached)
|
||||
wayland_detach(self);
|
||||
else
|
||||
wayvnc_exit(self);
|
||||
return;
|
||||
} else {
|
||||
nvnc_log(NVNC_LOG_ERROR, "Failed to read wayland events: %m");
|
||||
}
|
||||
}
|
||||
|
||||
if (wl_display_dispatch_pending(self->display) < 0) {
|
||||
nvnc_log(NVNC_LOG_ERROR, "Failed to dispatch pending");
|
||||
wayland_detach(self);
|
||||
// TODO: Re-attach
|
||||
}
|
||||
}
|
||||
|
||||
static int init_wayland(struct wayvnc* self, const char* display)
|
||||
{
|
||||
self->is_initializing = true;
|
||||
static const struct wl_registry_listener registry_listener = {
|
||||
|
@ -405,9 +491,10 @@ static int init_wayland(struct wayvnc* self)
|
|||
.global_remove = registry_remove,
|
||||
};
|
||||
|
||||
self->display = wl_display_connect(NULL);
|
||||
self->display = wl_display_connect(display);
|
||||
if (!self->display) {
|
||||
const char* display_name = getenv("WAYLAND_DISPLAY");
|
||||
const char* display_name = display ? display:
|
||||
getenv("WAYLAND_DISPLAY");
|
||||
if (!display_name) {
|
||||
nvnc_log(NVNC_LOG_ERROR, "WAYLAND_DISPLAY is not set in the environment");
|
||||
} else {
|
||||
|
@ -457,6 +544,17 @@ static int init_wayland(struct wayvnc* self)
|
|||
self->screencopy.on_done = on_capture_done;
|
||||
self->screencopy.userdata = self;
|
||||
|
||||
struct aml_handler* wl_handler;
|
||||
wl_handler = aml_handler_new(wl_display_get_fd(self->display),
|
||||
on_wayland_event, self, NULL);
|
||||
if (!wl_handler)
|
||||
goto failure;
|
||||
|
||||
int rc = aml_start(aml_get_default(), wl_handler);
|
||||
aml_unref(wl_handler);
|
||||
if (rc < 0)
|
||||
goto failure;
|
||||
|
||||
return 0;
|
||||
|
||||
failure:
|
||||
|
@ -464,26 +562,6 @@ failure:
|
|||
return -1;
|
||||
}
|
||||
|
||||
void on_wayland_event(void* obj)
|
||||
{
|
||||
struct wayvnc* self = aml_get_userdata(obj);
|
||||
|
||||
int rc MAYBE_UNUSED = wl_display_prepare_read(self->display);
|
||||
assert(rc == 0);
|
||||
|
||||
if (wl_display_read_events(self->display) < 0) {
|
||||
if (errno == EPIPE || errno == ECONNRESET) {
|
||||
nvnc_log(NVNC_LOG_ERROR, "Compositor has gone away. Exiting...");
|
||||
wayvnc_exit(self);
|
||||
} else {
|
||||
nvnc_log(NVNC_LOG_ERROR, "Failed to read wayland events: %m");
|
||||
}
|
||||
}
|
||||
|
||||
if (wl_display_dispatch_pending(self->display) < 0)
|
||||
nvnc_log(NVNC_LOG_ERROR, "Failed to dispatch pending");
|
||||
}
|
||||
|
||||
void wayvnc_exit(struct wayvnc* self)
|
||||
{
|
||||
self->do_exit = true;
|
||||
|
@ -606,23 +684,12 @@ int init_main_loop(struct wayvnc* self)
|
|||
{
|
||||
struct aml* loop = aml_get_default();
|
||||
|
||||
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;
|
||||
|
||||
int rc = aml_start(loop, wl_handler);
|
||||
aml_unref(wl_handler);
|
||||
if (rc < 0)
|
||||
return -1;
|
||||
|
||||
struct aml_signal* sig;
|
||||
sig = aml_signal_new(SIGINT, on_signal, self, NULL);
|
||||
if (!sig)
|
||||
return -1;
|
||||
|
||||
rc = aml_start(loop, sig);
|
||||
int rc = aml_start(loop, sig);
|
||||
aml_unref(sig);
|
||||
if (rc < 0)
|
||||
return -1;
|
||||
|
@ -636,6 +703,10 @@ static void on_pointer_event(struct nvnc_client* client, uint16_t x, uint16_t y,
|
|||
struct wayvnc_client* wv_client = nvnc_get_userdata(client);
|
||||
struct wayvnc* wayvnc = wv_client->server;
|
||||
|
||||
if (!wv_client->pointer.pointer) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t xfx = 0, xfy = 0;
|
||||
output_transform_coord(wayvnc->selected_output, x, y, &xfx, &xfy);
|
||||
|
||||
|
@ -646,6 +717,9 @@ static void on_key_event(struct nvnc_client* client, uint32_t symbol,
|
|||
bool is_pressed)
|
||||
{
|
||||
struct wayvnc_client* wv_client = nvnc_get_userdata(client);
|
||||
if (!wv_client->keyboard.virtual_keyboard) {
|
||||
return;
|
||||
}
|
||||
|
||||
keyboard_feed(&wv_client->keyboard, symbol, is_pressed);
|
||||
}
|
||||
|
@ -654,6 +728,9 @@ static void on_key_code_event(struct nvnc_client* client, uint32_t code,
|
|||
bool is_pressed)
|
||||
{
|
||||
struct wayvnc_client* wv_client = nvnc_get_userdata(client);
|
||||
if (!wv_client->keyboard.virtual_keyboard) {
|
||||
return;
|
||||
}
|
||||
|
||||
keyboard_feed_code(&wv_client->keyboard, code + 8, is_pressed);
|
||||
}
|
||||
|
@ -701,9 +778,15 @@ static struct nvnc_fb* create_placeholder_buffer(uint16_t width, uint16_t height
|
|||
|
||||
static int blank_screen(struct wayvnc* self)
|
||||
{
|
||||
struct nvnc_fb* placeholder_fb =
|
||||
create_placeholder_buffer(self->selected_output->width,
|
||||
self->selected_output->height);
|
||||
int width = 1280;
|
||||
int height = 720;
|
||||
|
||||
if (self->selected_output) {
|
||||
width = output_get_transformed_width(self->selected_output);
|
||||
height = output_get_transformed_height(self->selected_output);
|
||||
}
|
||||
|
||||
struct nvnc_fb* placeholder_fb = create_placeholder_buffer(width, height);
|
||||
if (!placeholder_fb) {
|
||||
nvnc_log(NVNC_LOG_ERROR, "Failed to allocate a placeholder buffer");
|
||||
return -1;
|
||||
|
@ -807,13 +890,10 @@ static 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);
|
||||
nvnc_set_pointer_fn(self->nvnc, on_pointer_event);
|
||||
|
||||
if (self->keyboard_manager) {
|
||||
nvnc_set_key_fn(self->nvnc, on_key_event);
|
||||
nvnc_set_key_code_fn(self->nvnc, on_key_code_event);
|
||||
}
|
||||
nvnc_set_key_fn(self->nvnc, on_key_event);
|
||||
nvnc_set_key_code_fn(self->nvnc, on_key_code_event);
|
||||
|
||||
nvnc_set_new_client_fn(self->nvnc, on_nvnc_client_new);
|
||||
nvnc_set_cut_text_fn(self->nvnc, on_client_cut_text);
|
||||
|
@ -1076,6 +1156,34 @@ static void stop_performance_ticker(struct wayvnc* self)
|
|||
aml_stop(aml_get_default(), self->performance_ticker);
|
||||
}
|
||||
|
||||
static void client_init_wayland(struct wayvnc_client* self)
|
||||
{
|
||||
client_init_seat(self);
|
||||
client_init_keyboard(self);
|
||||
client_init_pointer(self);
|
||||
client_init_data_control(self);
|
||||
}
|
||||
|
||||
static void client_detach_wayland(struct wayvnc_client* self)
|
||||
{
|
||||
self->seat = NULL;
|
||||
|
||||
if (self->keyboard.virtual_keyboard) {
|
||||
zwp_virtual_keyboard_v1_destroy(
|
||||
self->keyboard.virtual_keyboard);
|
||||
keyboard_destroy(&self->keyboard);
|
||||
}
|
||||
self->keyboard.virtual_keyboard = NULL;
|
||||
|
||||
if (self->pointer.pointer)
|
||||
pointer_destroy(&self->pointer);
|
||||
self->pointer.pointer = NULL;
|
||||
|
||||
if (self->data_control.manager)
|
||||
data_control_destroy(&self->data_control);
|
||||
self->data_control.manager = NULL;
|
||||
}
|
||||
|
||||
static unsigned next_client_id = 1;
|
||||
|
||||
static struct wayvnc_client* client_create(struct wayvnc* wayvnc,
|
||||
|
@ -1089,10 +1197,10 @@ static struct wayvnc_client* client_create(struct wayvnc* wayvnc,
|
|||
self->nvnc_client = nvnc_client;
|
||||
|
||||
self->id = next_client_id++;
|
||||
client_init_seat(self);
|
||||
client_init_keyboard(self);
|
||||
client_init_pointer(self);
|
||||
client_init_data_control(self);
|
||||
|
||||
if (wayvnc->display) {
|
||||
client_init_wayland(self);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
@ -1122,7 +1230,7 @@ static void client_destroy(void* obj)
|
|||
|
||||
ctl_server_event_disconnected(wayvnc->ctl, &info, wayvnc->nr_clients);
|
||||
|
||||
if (wayvnc->nr_clients == 0) {
|
||||
if (wayvnc->nr_clients == 0 && wayvnc->display) {
|
||||
nvnc_log(NVNC_LOG_INFO, "Stopping screen capture");
|
||||
screencopy_stop(&wayvnc->screencopy);
|
||||
stop_performance_ticker(wayvnc);
|
||||
|
@ -1143,6 +1251,13 @@ static void client_destroy(void* obj)
|
|||
free(self);
|
||||
}
|
||||
|
||||
static void handle_first_client(struct wayvnc* self)
|
||||
{
|
||||
nvnc_log(NVNC_LOG_INFO, "Starting screen capture");
|
||||
start_performance_ticker(self);
|
||||
wayvnc_start_capture_immediate(self);
|
||||
}
|
||||
|
||||
static void on_nvnc_client_new(struct nvnc_client* client)
|
||||
{
|
||||
struct nvnc* nvnc = nvnc_client_get_server(client);
|
||||
|
@ -1152,10 +1267,8 @@ static void on_nvnc_client_new(struct nvnc_client* client)
|
|||
assert(wayvnc_client);
|
||||
nvnc_set_userdata(client, wayvnc_client, client_destroy);
|
||||
|
||||
if (self->nr_clients == 0) {
|
||||
nvnc_log(NVNC_LOG_INFO, "Starting screen capture");
|
||||
start_performance_ticker(self);
|
||||
wayvnc_start_capture_immediate(self);
|
||||
if (self->nr_clients == 0 && self->display) {
|
||||
handle_first_client(self);
|
||||
}
|
||||
self->nr_clients++;
|
||||
nvnc_log(NVNC_LOG_DEBUG, "Client connected, new client count: %d",
|
||||
|
@ -1387,6 +1500,87 @@ void switch_to_prev_output(struct wayvnc* self)
|
|||
switch_to_output(self, prev);
|
||||
}
|
||||
|
||||
static struct cmd_response* on_attach(struct ctl* ctl, const char* display)
|
||||
{
|
||||
struct wayvnc* self = ctl_server_userdata(ctl);
|
||||
assert(self);
|
||||
|
||||
// TODO: Add optional output argument
|
||||
if (!wayland_attach(self, display, NULL))
|
||||
return cmd_failed("Failed to attach to %s", display);
|
||||
|
||||
return cmd_ok();
|
||||
}
|
||||
|
||||
static bool wayland_attach(struct wayvnc* self, const char* display,
|
||||
const char* output)
|
||||
{
|
||||
if (self->display) {
|
||||
wayland_detach(self);
|
||||
}
|
||||
|
||||
nvnc_log(NVNC_LOG_DEBUG, "Attaching to %s", display);
|
||||
|
||||
if (init_wayland(self, display) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct output* out;
|
||||
if (output) {
|
||||
out = output_find_by_name(&self->outputs, output);
|
||||
if (!out) {
|
||||
nvnc_log(NVNC_LOG_ERROR, "No such output: %s", output);
|
||||
wayland_detach(self);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
out = output_first(&self->outputs);
|
||||
if (!out) {
|
||||
nvnc_log(NVNC_LOG_ERROR, "No output available");
|
||||
wayland_detach(self);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
screencopy_init(&self->screencopy);
|
||||
if (!self->screencopy.manager) {
|
||||
nvnc_log(NVNC_LOG_ERROR, "Attached display does not implement wlr-screencopy-v1");
|
||||
wayland_detach(self);
|
||||
return false;
|
||||
}
|
||||
|
||||
set_selected_output(self, out);
|
||||
|
||||
struct nvnc_client* nvnc_client;
|
||||
for (nvnc_client = nvnc_client_first(self->nvnc); nvnc_client;
|
||||
nvnc_client = nvnc_client_next(nvnc_client)) {
|
||||
struct wayvnc_client* client = nvnc_get_userdata(nvnc_client);
|
||||
client_init_wayland(client);
|
||||
}
|
||||
|
||||
nvnc_log(NVNC_LOG_INFO, "Attached to %s", display);
|
||||
|
||||
if (self->nr_clients > 0) {
|
||||
handle_first_client(self);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct cmd_response* on_detach(struct ctl* ctl)
|
||||
{
|
||||
struct wayvnc* self = ctl_server_userdata(ctl);
|
||||
assert(self);
|
||||
|
||||
if (!self->display) {
|
||||
return cmd_failed("Not attached!");
|
||||
}
|
||||
|
||||
wayland_detach(self);
|
||||
nvnc_log(NVNC_LOG_INFO, "Detached from wayland server");
|
||||
return cmd_ok();
|
||||
}
|
||||
|
||||
static int log_level_from_string(const char* str)
|
||||
{
|
||||
if (0 == strcmp(str, "quiet")) return NVNC_LOG_PANIC;
|
||||
|
@ -1417,6 +1611,7 @@ int main(int argc, char* argv[])
|
|||
int port = 0;
|
||||
bool use_unix_socket = false;
|
||||
bool use_websocket = false;
|
||||
bool start_detached = false;
|
||||
|
||||
const char* output_name = NULL;
|
||||
const char* seat_name = NULL;
|
||||
|
@ -1465,6 +1660,8 @@ int main(int argc, char* argv[])
|
|||
"Create unix domain socket." },
|
||||
{ 'd', "disable-input", NULL,
|
||||
"Disable all remote input." },
|
||||
{ 'D', "detached", NULL,
|
||||
"Start detached from a compositor." },
|
||||
{ 'V', "version", NULL,
|
||||
"Show version info." },
|
||||
{ 'v', "verbose", NULL,
|
||||
|
@ -1511,6 +1708,9 @@ int main(int argc, char* argv[])
|
|||
max_rate = atoi(option_parser_get_value(&option_parser, "max-fps"));
|
||||
use_transient_seat = !!option_parser_get_value(&option_parser,
|
||||
"transient-seat");
|
||||
start_detached = !!option_parser_get_value(&option_parser, "detached");
|
||||
|
||||
self.start_detached = start_detached;
|
||||
|
||||
keyboard_options = option_parser_get_value(&option_parser, "keyboard");
|
||||
if (keyboard_options)
|
||||
|
@ -1572,37 +1772,49 @@ int main(int argc, char* argv[])
|
|||
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");
|
||||
return 1;
|
||||
struct aml* aml = aml_new();
|
||||
if (!aml)
|
||||
goto failure;
|
||||
|
||||
aml_set_default(aml);
|
||||
|
||||
if (init_main_loop(&self) < 0)
|
||||
goto failure;
|
||||
|
||||
if (!start_detached) {
|
||||
if (init_wayland(&self, NULL) < 0) {
|
||||
nvnc_log(NVNC_LOG_ERROR, "Failed to initialise wayland");
|
||||
goto wayland_failure;
|
||||
}
|
||||
|
||||
struct output* out;
|
||||
if (output_name) {
|
||||
out = output_find_by_name(&self.outputs, output_name);
|
||||
if (!out) {
|
||||
nvnc_log(NVNC_LOG_ERROR, "No such output");
|
||||
goto wayland_failure;
|
||||
}
|
||||
} else {
|
||||
out = output_first(&self.outputs);
|
||||
if (!out) {
|
||||
nvnc_log(NVNC_LOG_ERROR, "No output found");
|
||||
goto wayland_failure;
|
||||
}
|
||||
}
|
||||
set_selected_output(&self, out);
|
||||
|
||||
struct seat* seat = NULL;
|
||||
if (seat_name) {
|
||||
seat = seat_find_by_name(&self.seats, seat_name);
|
||||
if (!seat) {
|
||||
nvnc_log(NVNC_LOG_ERROR, "No such seat");
|
||||
goto wayland_failure;
|
||||
}
|
||||
}
|
||||
|
||||
self.selected_seat = seat;
|
||||
}
|
||||
|
||||
struct output* out;
|
||||
if (output_name) {
|
||||
out = output_find_by_name(&self.outputs, output_name);
|
||||
if (!out) {
|
||||
nvnc_log(NVNC_LOG_ERROR, "No such output");
|
||||
goto failure;
|
||||
}
|
||||
} else {
|
||||
out = output_first(&self.outputs);
|
||||
if (!out) {
|
||||
nvnc_log(NVNC_LOG_ERROR, "No output found");
|
||||
goto failure;
|
||||
}
|
||||
}
|
||||
set_selected_output(&self, out);
|
||||
|
||||
struct seat* seat = NULL;
|
||||
if (seat_name) {
|
||||
seat = seat_find_by_name(&self.seats, seat_name);
|
||||
if (!seat) {
|
||||
nvnc_log(NVNC_LOG_ERROR, "No such seat");
|
||||
goto failure;
|
||||
}
|
||||
}
|
||||
|
||||
self.selected_seat = seat;
|
||||
self.screencopy.rate_limit = max_rate;
|
||||
self.screencopy.enable_linux_dmabuf = enable_gpu_features;
|
||||
|
||||
|
@ -1615,15 +1827,6 @@ int main(int argc, char* argv[])
|
|||
if (aml_unstable_abi_version != AML_UNSTABLE_API)
|
||||
nvnc_log(NVNC_LOG_PANIC, "libaml is incompatible with this build of wayvnc!");
|
||||
|
||||
struct aml* aml = aml_new();
|
||||
if (!aml)
|
||||
goto main_loop_failure;
|
||||
|
||||
aml_set_default(aml);
|
||||
|
||||
if (init_main_loop(&self) < 0)
|
||||
goto main_loop_failure;
|
||||
|
||||
enum socket_type socket_type = SOCKET_TYPE_TCP;
|
||||
if (use_unix_socket)
|
||||
socket_type = SOCKET_TYPE_UNIX;
|
||||
|
@ -1633,12 +1836,14 @@ int main(int argc, char* argv[])
|
|||
if (init_nvnc(&self, address, port, socket_type) < 0)
|
||||
goto nvnc_failure;
|
||||
|
||||
if (self.screencopy.manager)
|
||||
screencopy_init(&self.screencopy);
|
||||
if (!start_detached) {
|
||||
if (self.screencopy.manager)
|
||||
screencopy_init(&self.screencopy);
|
||||
|
||||
if (!self.screencopy.manager) {
|
||||
nvnc_log(NVNC_LOG_ERROR, "screencopy is not supported by compositor");
|
||||
goto capture_failure;
|
||||
if (!self.screencopy.manager) {
|
||||
nvnc_log(NVNC_LOG_ERROR, "screencopy is not supported by compositor");
|
||||
goto capture_failure;
|
||||
}
|
||||
}
|
||||
|
||||
self.screencopy.overlay_cursor = overlay_cursor;
|
||||
|
@ -1649,6 +1854,8 @@ int main(int argc, char* argv[])
|
|||
|
||||
const struct ctl_server_actions ctl_actions = {
|
||||
.userdata = &self,
|
||||
.on_attach = on_attach,
|
||||
.on_detach = on_detach,
|
||||
.on_output_cycle = on_output_cycle,
|
||||
.on_output_switch = on_output_switch,
|
||||
.client_next = client_next,
|
||||
|
@ -1661,32 +1868,34 @@ int main(int argc, char* argv[])
|
|||
if (!self.ctl)
|
||||
goto ctl_server_failure;
|
||||
|
||||
wl_display_dispatch_pending(self.display);
|
||||
if (self.display)
|
||||
wl_display_dispatch_pending(self.display);
|
||||
|
||||
while (!self.do_exit) {
|
||||
wl_display_flush(self.display);
|
||||
if (self.display)
|
||||
wl_display_flush(self.display);
|
||||
|
||||
aml_poll(aml, -1);
|
||||
aml_dispatch(aml);
|
||||
}
|
||||
|
||||
nvnc_log(NVNC_LOG_INFO, "Exiting...");
|
||||
|
||||
screencopy_stop(&self.screencopy);
|
||||
if (self.display)
|
||||
screencopy_stop(&self.screencopy);
|
||||
|
||||
ctl_server_destroy(self.ctl);
|
||||
|
||||
nvnc_display_unref(self.nvnc_display);
|
||||
nvnc_close(self.nvnc);
|
||||
if (zwp_linux_dmabuf)
|
||||
zwp_linux_dmabuf_v1_destroy(zwp_linux_dmabuf);
|
||||
if (self.screencopy.manager)
|
||||
screencopy_destroy(&self.screencopy);
|
||||
self.nvnc = NULL;
|
||||
wayvnc_destroy(&self);
|
||||
#ifdef ENABLE_SCREENCOPY_DMABUF
|
||||
if (gbm_device) {
|
||||
gbm_device_destroy(gbm_device);
|
||||
close(drm_fd);
|
||||
}
|
||||
#endif
|
||||
wayvnc_destroy(&self);
|
||||
aml_unref(aml);
|
||||
|
||||
return 0;
|
||||
|
@ -1696,15 +1905,16 @@ capture_failure:
|
|||
nvnc_display_unref(self.nvnc_display);
|
||||
nvnc_close(self.nvnc);
|
||||
nvnc_failure:
|
||||
wayland_failure:
|
||||
aml_unref(aml);
|
||||
main_loop_failure:
|
||||
failure:
|
||||
self.nvnc = NULL;
|
||||
wayvnc_destroy(&self);
|
||||
#ifdef ENABLE_SCREENCOPY_DMABUF
|
||||
if (gbm_device)
|
||||
gbm_device_destroy(gbm_device);
|
||||
if (drm_fd >= 0)
|
||||
close(drm_fd);
|
||||
#endif
|
||||
wayvnc_destroy(&self);
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -298,5 +298,8 @@ void screencopy_destroy(struct screencopy* self)
|
|||
if (self->front)
|
||||
wv_buffer_pool_release(self->pool, self->front);
|
||||
|
||||
self->back = NULL;
|
||||
self->front = NULL;
|
||||
|
||||
wv_buffer_pool_destroy(self->pool);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue