mirror of
https://github.com/gnif/LookingGlass.git
synced 2024-12-23 06:11:46 +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 "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 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_ClipboardRequestFn requestFn;
|
||||
LG_ClipboardNotifyFn notifyFn;
|
||||
|
@ -31,11 +51,110 @@ struct state
|
|||
|
||||
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()
|
||||
{
|
||||
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(
|
||||
SDL_SysWMinfo * wminfo,
|
||||
LG_ClipboardReleaseFn releaseFn,
|
||||
|
@ -54,6 +173,28 @@ static bool wayland_cb_init(
|
|||
this->releaseFn = releaseFn;
|
||||
this->notifyFn = notifyFn;
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
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()
|
||||
|
|
|
@ -2022,6 +2022,10 @@ static int lg_run()
|
|||
|
||||
g_state.lgc = LG_Clipboards[0];
|
||||
}
|
||||
else if (g_state.wminfo.subsystem == SDL_SYSWM_WAYLAND)
|
||||
{
|
||||
g_state.lgc = LG_Clipboards[1];
|
||||
}
|
||||
} else {
|
||||
DEBUG_ERROR("Could not get SDL window information %s", SDL_GetError());
|
||||
return -1;
|
||||
|
|
Loading…
Reference in a new issue