mirror of
https://github.com/gnif/LookingGlass.git
synced 2024-12-23 14:21:57 +00:00
[client] clipboard/wayland: implement VM-to-host text copy
Co-authored-by: Quantum <quantum2048@gmail.com>
This commit is contained in:
parent
a205515d91
commit
c58f33f5ab
2 changed files with 228 additions and 0 deletions
|
@ -20,8 +20,28 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
#include "interface/clipboard.h"
|
#include "interface/clipboard.h"
|
||||||
#include "common/debug.h"
|
#include "common/debug.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <wayland-client.h>
|
||||||
|
|
||||||
|
struct transfer {
|
||||||
|
void * data;
|
||||||
|
size_t size;
|
||||||
|
};
|
||||||
|
|
||||||
struct state
|
struct state
|
||||||
{
|
{
|
||||||
|
struct wl_display * display;
|
||||||
|
struct wl_registry * registry;
|
||||||
|
struct wl_data_device_manager * data_device_manager;
|
||||||
|
struct wl_seat * seat;
|
||||||
|
struct wl_data_device * data_device;
|
||||||
|
|
||||||
|
struct wl_keyboard * keyboard;
|
||||||
|
uint32_t keyboard_enter_serial;
|
||||||
|
uint32_t capabilities;
|
||||||
|
|
||||||
LG_ClipboardReleaseFn releaseFn;
|
LG_ClipboardReleaseFn releaseFn;
|
||||||
LG_ClipboardRequestFn requestFn;
|
LG_ClipboardRequestFn requestFn;
|
||||||
LG_ClipboardNotifyFn notifyFn;
|
LG_ClipboardNotifyFn notifyFn;
|
||||||
|
@ -31,11 +51,110 @@ struct state
|
||||||
|
|
||||||
static struct state * this = NULL;
|
static struct state * this = NULL;
|
||||||
|
|
||||||
|
static const char * text_mimetypes[] =
|
||||||
|
{
|
||||||
|
"text/plain",
|
||||||
|
"text/plain;charset=utf-8",
|
||||||
|
"TEXT",
|
||||||
|
"STRING",
|
||||||
|
"UTF8_STRING",
|
||||||
|
};
|
||||||
|
|
||||||
static const char * wayland_cb_getName()
|
static const char * wayland_cb_getName()
|
||||||
{
|
{
|
||||||
return "Wayland";
|
return "Wayland";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keyboard-handling listeners.
|
||||||
|
|
||||||
|
static void keyboard_keymap_handler(
|
||||||
|
void * data,
|
||||||
|
struct wl_keyboard * keyboard,
|
||||||
|
uint32_t format,
|
||||||
|
int fd,
|
||||||
|
uint32_t size
|
||||||
|
) {
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void keyboard_enter_handler(void * data, struct wl_keyboard * keyboard,
|
||||||
|
uint32_t serial, struct wl_surface * surface, struct wl_array * keys)
|
||||||
|
{
|
||||||
|
this->keyboard_enter_serial = serial;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void keyboard_leave_handler(void * data, struct wl_keyboard * keyboard,
|
||||||
|
uint32_t serial, struct wl_surface * surface)
|
||||||
|
{
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
static void keyboard_key_handler(void * data, struct wl_keyboard * keyboard,
|
||||||
|
uint32_t serial, uint32_t time, uint32_t key, uint32_t state)
|
||||||
|
{
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
static void keyboard_modifiers_handler(void * data,
|
||||||
|
struct wl_keyboard * keyboard, uint32_t serial, uint32_t mods_depressed,
|
||||||
|
uint32_t mods_latched, uint32_t mods_locked, uint32_t group)
|
||||||
|
{
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wl_keyboard_listener keyboard_listener = {
|
||||||
|
.keymap = keyboard_keymap_handler,
|
||||||
|
.enter = keyboard_enter_handler,
|
||||||
|
.leave = keyboard_leave_handler,
|
||||||
|
.key = keyboard_key_handler,
|
||||||
|
.modifiers = keyboard_modifiers_handler,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Seat-handling listeners.
|
||||||
|
|
||||||
|
static void seat_capabilities_handler(void * data, struct wl_seat * seat,
|
||||||
|
uint32_t capabilities)
|
||||||
|
{
|
||||||
|
this->capabilities = capabilities;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void seat_name_handler(void * data, struct wl_seat * seat,
|
||||||
|
const char * name)
|
||||||
|
{
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wl_seat_listener seat_listener = {
|
||||||
|
.capabilities = seat_capabilities_handler,
|
||||||
|
.name = seat_name_handler
|
||||||
|
};
|
||||||
|
|
||||||
|
// Registry-handling listeners.
|
||||||
|
|
||||||
|
static void registry_global_handler(void * data,
|
||||||
|
struct wl_registry * wl_registry, uint32_t name, const char * interface,
|
||||||
|
uint32_t version) {
|
||||||
|
if (!strcmp(interface, wl_data_device_manager_interface.name))
|
||||||
|
this->data_device_manager = wl_registry_bind(this->registry, name,
|
||||||
|
&wl_data_device_manager_interface, 3);
|
||||||
|
else if (!strcmp(interface, wl_seat_interface.name) && !this->seat)
|
||||||
|
// TODO: multi-seat support.
|
||||||
|
this->seat = wl_registry_bind(this->registry, name,
|
||||||
|
&wl_seat_interface, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void registry_global_remove_handler(void * data,
|
||||||
|
struct wl_registry * wl_registry, uint32_t name) {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wl_registry_listener registry_listener = {
|
||||||
|
.global = registry_global_handler,
|
||||||
|
.global_remove = registry_global_remove_handler,
|
||||||
|
};
|
||||||
|
|
||||||
|
// End of Wayland handlers.
|
||||||
|
|
||||||
static bool wayland_cb_init(
|
static bool wayland_cb_init(
|
||||||
SDL_SysWMinfo * wminfo,
|
SDL_SysWMinfo * wminfo,
|
||||||
LG_ClipboardReleaseFn releaseFn,
|
LG_ClipboardReleaseFn releaseFn,
|
||||||
|
@ -54,6 +173,28 @@ static bool wayland_cb_init(
|
||||||
this->releaseFn = releaseFn;
|
this->releaseFn = releaseFn;
|
||||||
this->notifyFn = notifyFn;
|
this->notifyFn = notifyFn;
|
||||||
this->dataFn = dataFn;
|
this->dataFn = dataFn;
|
||||||
|
this->display = wminfo->info.wl.display;
|
||||||
|
this->registry = wl_display_get_registry(this->display);
|
||||||
|
|
||||||
|
// Wait for the initial set of globals to appear.
|
||||||
|
wl_registry_add_listener(this->registry, ®istry_listener, NULL);
|
||||||
|
wl_display_roundtrip(this->display);
|
||||||
|
|
||||||
|
this->data_device = wl_data_device_manager_get_data_device(
|
||||||
|
this->data_device_manager, this->seat);
|
||||||
|
|
||||||
|
// Wait for the compositor to let us know of capabilities.
|
||||||
|
wl_seat_add_listener(this->seat, &seat_listener, NULL);
|
||||||
|
wl_display_roundtrip(this->display);
|
||||||
|
if (!(this->capabilities & WL_SEAT_CAPABILITY_KEYBOARD))
|
||||||
|
{
|
||||||
|
// TODO: keyboard hotplug support.
|
||||||
|
DEBUG_ERROR("no keyboard");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->keyboard = wl_seat_get_keyboard(this->seat);
|
||||||
|
wl_keyboard_add_listener(this->keyboard, &keyboard_listener, NULL);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,8 +208,91 @@ static void wayland_cb_wmevent(SDL_SysWMmsg * msg)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool is_text_mimetype(const char * mime_type)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < sizeof(text_mimetypes)/sizeof(char *); i++)
|
||||||
|
if (!strcmp(mime_type, text_mimetypes[i]))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void data_source_handle_send(void * data, struct wl_data_source * source,
|
||||||
|
const char * mime_type, int fd) {
|
||||||
|
struct transfer * transfer = (struct transfer *) data;
|
||||||
|
if (is_text_mimetype(mime_type))
|
||||||
|
{
|
||||||
|
// Consider making this do non-blocking sends to not stall the Wayland
|
||||||
|
// event loop if it becomes a problem. This is "fine" in the sense that
|
||||||
|
// wl-copy also stalls like this, but it's not necessary.
|
||||||
|
fcntl(fd, F_SETFL, 0);
|
||||||
|
|
||||||
|
size_t pos = 0;
|
||||||
|
while (pos < transfer->size)
|
||||||
|
{
|
||||||
|
ssize_t written = write(fd, transfer->data + pos, transfer->size - pos);
|
||||||
|
if (written < 0)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to write clipboard data: %s", strerror(errno));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos += written;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
error:
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void data_source_handle_cancelled(void * data,
|
||||||
|
struct wl_data_source * source) {
|
||||||
|
struct transfer * transfer = (struct transfer *) data;
|
||||||
|
free(transfer->data);
|
||||||
|
free(transfer);
|
||||||
|
wl_data_source_destroy(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wl_data_source_listener data_source_listener = {
|
||||||
|
.send = data_source_handle_send,
|
||||||
|
.cancelled = data_source_handle_cancelled,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void wayland_cb_reply_fn(void * opaque, LG_ClipboardData type, uint8_t * data, uint32_t size)
|
||||||
|
{
|
||||||
|
struct transfer * transfer = malloc(sizeof(struct transfer));
|
||||||
|
void * data_copy = malloc(size);
|
||||||
|
memcpy(data_copy, data, size);
|
||||||
|
transfer->data = data_copy;
|
||||||
|
transfer->size = size;
|
||||||
|
|
||||||
|
struct wl_data_source *source =
|
||||||
|
wl_data_device_manager_create_data_source(this->data_device_manager);
|
||||||
|
wl_data_source_add_listener(source, &data_source_listener, transfer);
|
||||||
|
for (int i = 0; i < sizeof(text_mimetypes)/sizeof(*text_mimetypes); i++)
|
||||||
|
{
|
||||||
|
wl_data_source_offer(source, text_mimetypes[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_data_device_set_selection(this->data_device, source,
|
||||||
|
this->keyboard_enter_serial);
|
||||||
|
}
|
||||||
|
|
||||||
static void wayland_cb_notice(LG_ClipboardRequestFn requestFn, LG_ClipboardData type)
|
static void wayland_cb_notice(LG_ClipboardRequestFn requestFn, LG_ClipboardData type)
|
||||||
{
|
{
|
||||||
|
if (type != LG_CLIPBOARD_DATA_TEXT)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Only text selection currently supported");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->requestFn = requestFn;
|
||||||
|
this->type = type;
|
||||||
|
|
||||||
|
if (!this->requestFn)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this->requestFn(wayland_cb_reply_fn, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wayland_cb_release()
|
static void wayland_cb_release()
|
||||||
|
|
|
@ -2022,6 +2022,10 @@ static int lg_run()
|
||||||
|
|
||||||
g_state.lgc = LG_Clipboards[0];
|
g_state.lgc = LG_Clipboards[0];
|
||||||
}
|
}
|
||||||
|
else if (g_state.wminfo.subsystem == SDL_SYSWM_WAYLAND)
|
||||||
|
{
|
||||||
|
g_state.lgc = LG_Clipboards[1];
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
DEBUG_ERROR("Could not get SDL window information %s", SDL_GetError());
|
DEBUG_ERROR("Could not get SDL window information %s", SDL_GetError());
|
||||||
return -1;
|
return -1;
|
||||||
|
|
Loading…
Reference in a new issue