Add bidirectional clipboard bridge with text prefetch
Bridge the nested wlroots seat's clipboard with the host compositor. Uses wlr-data-control when the host exposes it, otherwise a focus-based wl_data_device fallback (Mutter). Text selections are eagerly prefetched at mirror time so every paste is served instantly from a compositor-side buffer instead of paying the host source's latency per paste; non-text MIMEs keep the zero-copy live relay. Also fix a critical bug: prefetch_fd was zero-initialized instead of -1, so the first bridge_prefetch_cancel() closed stdin, breaking the test event feed and spinning the main loop on EBADF.
This commit is contained in:
parent
5052a9b3ba
commit
d05ded946b
5 changed files with 1755 additions and 2 deletions
|
|
@ -39,6 +39,7 @@ executable('tabbed-wlroots',
|
|||
'tabbed-wlroots.c',
|
||||
'xdg-shell-protocol.c',
|
||||
'xdg-decoration-protocol.c',
|
||||
'wlr-data-control-protocol.c',
|
||||
dependencies: deps,
|
||||
include_directories: include_directories('wlr-protocols'),
|
||||
c_args: ['-DWLR_USE_UNSTABLE'])
|
||||
|
|
|
|||
690
tabbed-wlroots.c
690
tabbed-wlroots.c
|
|
@ -36,6 +36,7 @@
|
|||
|
||||
#include "xdg-decoration-client.h"
|
||||
#include "xdg-shell-client.h"
|
||||
#include "wlr-data-control-client.h"
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
|
|
@ -55,6 +56,7 @@
|
|||
#include <wlr/render/wlr_renderer.h>
|
||||
#include <wlr/types/wlr_buffer.h>
|
||||
#include <wlr/types/wlr_compositor.h>
|
||||
#include <wlr/types/wlr_data_control_v1.h>
|
||||
#include <wlr/types/wlr_data_device.h>
|
||||
#include <wlr/types/wlr_keyboard.h>
|
||||
#include <wlr/types/wlr_linux_dmabuf_v1.h>
|
||||
|
|
@ -172,6 +174,76 @@ static volatile sig_atomic_t needs_redraw;
|
|||
static bool frame_pending;
|
||||
static bool shutting_down;
|
||||
|
||||
/* ===================== Clipboard bridge ===================== */
|
||||
|
||||
enum bridge_backend {
|
||||
BRIDGE_BACKEND_NONE = 0,
|
||||
BRIDGE_BACKEND_DATA_CONTROL,
|
||||
BRIDGE_BACKEND_WL_DATA_DEVICE,
|
||||
};
|
||||
|
||||
/* Bridges the nested seat's clipboard with the host compositor's. Two host
|
||||
backends share one inner (nested-seat) implementation:
|
||||
- data-control: zwlr_data_control_v1 (labwc/sway/etc.), full duplex,
|
||||
independent of keyboard focus.
|
||||
- wl_data_device: plain stable protocol (Mutter/GNOME fallback). Writing
|
||||
the host selection always works (needs a captured keyboard serial);
|
||||
reading the host selection only works while our window is focused.
|
||||
Data flows by passing file descriptors straight through in both directions
|
||||
(host offer -> nested source, nested source -> host source), so no
|
||||
intermediate copy is needed. */
|
||||
struct clipboard_bridge {
|
||||
enum bridge_backend backend;
|
||||
|
||||
/* nested (wlroots) seat */
|
||||
struct wl_listener request_set_selection;
|
||||
struct wl_listener set_selection;
|
||||
|
||||
/* the nested selection source currently mirrored to the host; serves
|
||||
host paste requests until it is destroyed */
|
||||
struct wlr_data_source *inner_src;
|
||||
struct wl_listener inner_src_destroy;
|
||||
bool inner_clear_requested; /* nested client asked to clear the selection */
|
||||
bool ignore_next_inner; /* one-shot echo guard for host->nested clears */
|
||||
|
||||
/* host: standard wl_data_device (always bound; the fallback backend) */
|
||||
struct wl_data_device_manager *ddm;
|
||||
struct wl_data_device *dd;
|
||||
struct wl_data_source *dd_source; /* our host-side source (nested copy) */
|
||||
struct wl_data_offer *dd_offer; /* current host selection offer */
|
||||
struct wl_data_offer *dd_echo_offer; /* offer describing our own set */
|
||||
uint32_t kbd_serial; /* last host keyboard/enter serial */
|
||||
|
||||
/* host: wlr-data-control backend (if the host exposes it) */
|
||||
struct zwlr_data_control_manager_v1 *dc_manager;
|
||||
struct zwlr_data_control_device_v1 *dc_device;
|
||||
struct zwlr_data_control_source_v1 *dc_source;
|
||||
struct zwlr_data_control_offer_v1 *dc_offer;
|
||||
struct zwlr_data_control_offer_v1 *dc_echo_offer;
|
||||
|
||||
/* MIME list currently being collected from a host offer */
|
||||
char **mimes;
|
||||
size_t mimes_len, mimes_cap;
|
||||
bool pending_echo; /* set before host set_selection to catch its echo */
|
||||
bool host_set_pending; /* nested selection not yet on the host; retry on focus */
|
||||
|
||||
/* Eager text prefetch: when a real host selection is mirrored, its primary
|
||||
text MIME is pulled into this buffer at copy time so pastes are served
|
||||
instantly instead of asking the host for each one. */
|
||||
int prefetch_fd; /* pipe read end, -1 when idle */
|
||||
struct wl_event_source *prefetch_src;
|
||||
char *prefetch_buf;
|
||||
size_t prefetch_len, prefetch_cap;
|
||||
char prefetch_mime[64]; /* canonical text MIME, "" if none */
|
||||
bool prefetch_ready; /* buffer complete (EOF received) */
|
||||
};
|
||||
|
||||
static struct clipboard_bridge bridge = { .prefetch_fd = -1 };
|
||||
|
||||
static void bridge_cleanup(void);
|
||||
static void bridge_init(void);
|
||||
static void bridge_attach_seat(void);
|
||||
|
||||
/* Temporary frame-dump keys (F8/F9) to attribute the lxterm grey band: F8
|
||||
saves the scene buffer (client content as wlroots drew it), F9 saves our
|
||||
composited host back buffer. To be removed after diagnosis. */
|
||||
|
|
@ -289,6 +361,7 @@ static void shutdown_cleanup(void) {
|
|||
server.apps = NULL;
|
||||
if (server.xkb_keymap) xkb_keymap_unref(server.xkb_keymap);
|
||||
if (server.xkb_ctx) xkb_context_unref(server.xkb_ctx);
|
||||
bridge_cleanup();
|
||||
remove_socket_file();
|
||||
}
|
||||
|
||||
|
|
@ -1011,6 +1084,599 @@ static void kbd_repeat_start(int key, int dir) {
|
|||
wl_event_source_timer_update(host.repeat_timer, TABBED_REPEAT_DELAY_MS);
|
||||
}
|
||||
|
||||
/* ===================== Clipboard bridge implementation ===================== */
|
||||
|
||||
static void bridge_mimes_reset(void) {
|
||||
for (size_t i = 0; i < bridge.mimes_len; i++)
|
||||
free(bridge.mimes[i]);
|
||||
bridge.mimes_len = 0;
|
||||
}
|
||||
|
||||
static void bridge_mimes_append(const char *mime) {
|
||||
if (bridge.mimes_len == bridge.mimes_cap) {
|
||||
size_t ncap = bridge.mimes_cap ? bridge.mimes_cap * 2 : 8;
|
||||
char **nm = realloc(bridge.mimes, ncap * sizeof(*nm));
|
||||
if (!nm)
|
||||
return;
|
||||
bridge.mimes = nm;
|
||||
bridge.mimes_cap = ncap;
|
||||
}
|
||||
bridge.mimes[bridge.mimes_len++] = strdup(mime);
|
||||
}
|
||||
|
||||
static void bridge_inner_src_destroy(struct wl_listener *l, void *data);
|
||||
static bool bridge_prefetch_mime_compatible(const char *mime);
|
||||
static void bridge_prefetch_serve(const char *mime, int32_t fd);
|
||||
|
||||
/* Compositor-side nested source: when a nested client pastes, wlroots calls
|
||||
our impl->send() and we pull the data from the host offer straight into the
|
||||
fd wlroots gave us (zero copy, no relay). */
|
||||
static void bridge_inner_source_send(struct wlr_data_source *src,
|
||||
const char *mime, int32_t fd) {
|
||||
(void)src;
|
||||
if (bridge_prefetch_mime_compatible(mime)) {
|
||||
bridge_prefetch_serve(mime, fd);
|
||||
return;
|
||||
}
|
||||
if (bridge.backend == BRIDGE_BACKEND_DATA_CONTROL && bridge.dc_offer) {
|
||||
zwlr_data_control_offer_v1_receive(bridge.dc_offer, mime, fd);
|
||||
close(fd);
|
||||
} else if (bridge.backend == BRIDGE_BACKEND_WL_DATA_DEVICE &&
|
||||
bridge.dd_offer) {
|
||||
wl_data_offer_receive(bridge.dd_offer, mime, fd);
|
||||
close(fd);
|
||||
} else {
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
static void bridge_inner_source_destroy(struct wlr_data_source *src) {
|
||||
free(src);
|
||||
}
|
||||
|
||||
static const struct wlr_data_source_impl bridge_inner_source_impl = {
|
||||
.send = bridge_inner_source_send,
|
||||
.destroy = bridge_inner_source_destroy,
|
||||
};
|
||||
|
||||
static void bridge_set_inner_src(struct wlr_data_source *src) {
|
||||
if (bridge.inner_src == src)
|
||||
return;
|
||||
if (bridge.inner_src) {
|
||||
wl_list_remove(&bridge.inner_src_destroy.link);
|
||||
bridge.inner_src = NULL;
|
||||
}
|
||||
bridge.inner_src = src;
|
||||
if (src) {
|
||||
bridge.inner_src_destroy.notify = bridge_inner_src_destroy;
|
||||
wl_signal_add(&src->events.destroy, &bridge.inner_src_destroy);
|
||||
}
|
||||
}
|
||||
|
||||
static void bridge_inner_src_destroy(struct wl_listener *l, void *data) {
|
||||
(void)data;
|
||||
wl_list_remove(&l->link);
|
||||
bridge.inner_src = NULL;
|
||||
}
|
||||
|
||||
/* Put a host selection into the nested seat. The source stays valid for the
|
||||
whole lifetime of the host offer, so pastes can be served asynchronously. */
|
||||
static void bridge_mirror_to_nested(void) {
|
||||
struct wlr_data_source *src = calloc(1, sizeof(*src));
|
||||
if (!src) {
|
||||
bridge_mimes_reset();
|
||||
return;
|
||||
}
|
||||
wlr_data_source_init(src, &bridge_inner_source_impl);
|
||||
for (size_t i = 0; i < bridge.mimes_len; i++) {
|
||||
char **p = wl_array_add(&src->mime_types, sizeof(char *));
|
||||
if (!p) {
|
||||
for (size_t j = i; j < bridge.mimes_len; j++)
|
||||
free(bridge.mimes[j]);
|
||||
bridge.mimes_len = 0;
|
||||
break;
|
||||
}
|
||||
*p = bridge.mimes[i];
|
||||
bridge.mimes[i] = NULL; /* ownership moves into the wl_array */
|
||||
}
|
||||
bridge.mimes_len = 0;
|
||||
bridge_set_inner_src(src);
|
||||
wlr_seat_set_selection(server.seat, src,
|
||||
wl_display_next_serial(server.wl_display));
|
||||
}
|
||||
|
||||
static void bridge_clear_nested(void) {
|
||||
if (!server.seat->selection_source)
|
||||
return;
|
||||
bridge.ignore_next_inner = true;
|
||||
wlr_seat_set_selection(server.seat, NULL,
|
||||
wl_display_next_serial(server.wl_display));
|
||||
}
|
||||
|
||||
/* ---------------- text prefetch: serve pastes from a buffered copy -------- */
|
||||
|
||||
#define TABBED_PREFETCH_MAX (16 * 1024 * 1024)
|
||||
|
||||
static void bridge_prefetch_cancel(void) {
|
||||
if (bridge.prefetch_src) {
|
||||
wl_event_source_remove(bridge.prefetch_src);
|
||||
bridge.prefetch_src = NULL;
|
||||
}
|
||||
if (bridge.prefetch_fd >= 0) {
|
||||
close(bridge.prefetch_fd);
|
||||
bridge.prefetch_fd = -1;
|
||||
}
|
||||
free(bridge.prefetch_buf);
|
||||
bridge.prefetch_buf = NULL;
|
||||
bridge.prefetch_len = bridge.prefetch_cap = 0;
|
||||
bridge.prefetch_mime[0] = '\0';
|
||||
bridge.prefetch_ready = false;
|
||||
}
|
||||
|
||||
/* Drain the prefetch pipe into the buffer. Returns true when the stream is
|
||||
finished (EOF/error) and the buffer should be kept as the paste payload. */
|
||||
static bool bridge_prefetch_drain(int fd) {
|
||||
for (;;) {
|
||||
size_t room = bridge.prefetch_cap - bridge.prefetch_len;
|
||||
if (bridge.prefetch_len + 4096 > bridge.prefetch_cap &&
|
||||
bridge.prefetch_cap < TABBED_PREFETCH_MAX) {
|
||||
size_t ncap = bridge.prefetch_cap ? bridge.prefetch_cap * 2 : 8192;
|
||||
if (ncap > TABBED_PREFETCH_MAX)
|
||||
ncap = TABBED_PREFETCH_MAX;
|
||||
char *nb = realloc(bridge.prefetch_buf, ncap);
|
||||
if (nb) {
|
||||
bridge.prefetch_buf = nb;
|
||||
bridge.prefetch_cap = ncap;
|
||||
room = bridge.prefetch_cap - bridge.prefetch_len;
|
||||
}
|
||||
}
|
||||
if (room == 0) {
|
||||
/* cap reached: drain without storing to keep the writer unblocked */
|
||||
char tmp;
|
||||
ssize_t n = read(fd, &tmp, 1);
|
||||
if (n > 0)
|
||||
continue;
|
||||
if (n == 0)
|
||||
return true;
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
if (errno == EAGAIN)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
ssize_t n = read(fd, bridge.prefetch_buf + bridge.prefetch_len, room);
|
||||
if (n > 0) {
|
||||
bridge.prefetch_len += (size_t)n;
|
||||
continue;
|
||||
}
|
||||
if (n == 0)
|
||||
return true; /* EOF */
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
if (errno == EAGAIN)
|
||||
return false; /* more data pending */
|
||||
return true; /* read error: keep whatever we got */
|
||||
}
|
||||
}
|
||||
|
||||
static int bridge_prefetch_cb(int fd, uint32_t mask, void *data) {
|
||||
(void)data; (void)mask;
|
||||
if (!bridge_prefetch_drain(fd))
|
||||
return 0;
|
||||
if (bridge.prefetch_src) {
|
||||
wl_event_source_remove(bridge.prefetch_src);
|
||||
bridge.prefetch_src = NULL;
|
||||
}
|
||||
close(fd);
|
||||
bridge.prefetch_fd = -1;
|
||||
bridge.prefetch_ready = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Pick the best text MIME offered for prefetching. */
|
||||
static const char *bridge_pick_text_mime(void) {
|
||||
static const char *pref[] = {
|
||||
"text/plain;charset=utf-8", "text/plain", "UTF8_STRING", NULL,
|
||||
};
|
||||
for (int i = 0; pref[i]; i++)
|
||||
for (size_t j = 0; j < bridge.mimes_len; j++)
|
||||
if (strcmp(bridge.mimes[j], pref[i]) == 0)
|
||||
return bridge.mimes[j];
|
||||
for (size_t j = 0; j < bridge.mimes_len; j++)
|
||||
if (strncmp(bridge.mimes[j], "text/", 5) == 0)
|
||||
return bridge.mimes[j];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Start pulling the current host offer's primary text MIME into our buffer.
|
||||
Must be called while bridge.mimes holds the freshly collected list. */
|
||||
static void bridge_prefetch_start(void) {
|
||||
bridge_prefetch_cancel();
|
||||
if (!bridge.mimes_len)
|
||||
return;
|
||||
const char *mime = bridge_pick_text_mime();
|
||||
if (!mime)
|
||||
return;
|
||||
int p[2];
|
||||
if (pipe2(p, O_CLOEXEC | O_NONBLOCK) != 0)
|
||||
return;
|
||||
bridge.prefetch_fd = p[0];
|
||||
bridge.prefetch_src = wl_event_loop_add_fd(server.loop, p[0],
|
||||
WL_EVENT_READABLE, bridge_prefetch_cb, NULL);
|
||||
if (!bridge.prefetch_src) {
|
||||
close(p[0]);
|
||||
close(p[1]);
|
||||
bridge.prefetch_fd = -1;
|
||||
return;
|
||||
}
|
||||
snprintf(bridge.prefetch_mime, sizeof(bridge.prefetch_mime), "%s", mime);
|
||||
if (bridge.backend == BRIDGE_BACKEND_DATA_CONTROL && bridge.dc_offer) {
|
||||
zwlr_data_control_offer_v1_receive(bridge.dc_offer, mime, p[1]);
|
||||
close(p[1]);
|
||||
} else if (bridge.backend == BRIDGE_BACKEND_WL_DATA_DEVICE &&
|
||||
bridge.dd_offer) {
|
||||
wl_data_offer_receive(bridge.dd_offer, mime, p[1]);
|
||||
close(p[1]);
|
||||
} else {
|
||||
bridge_prefetch_cancel();
|
||||
}
|
||||
}
|
||||
|
||||
/* The requested MIME can be served from the prefetched plain-text buffer. */
|
||||
static bool bridge_prefetch_mime_compatible(const char *mime) {
|
||||
if (!bridge.prefetch_ready || bridge.prefetch_mime[0] == '\0')
|
||||
return false;
|
||||
if (strcmp(mime, bridge.prefetch_mime) == 0)
|
||||
return true;
|
||||
bool a = strncmp(mime, "text/plain", 10) == 0 ||
|
||||
strcmp(mime, "UTF8_STRING") == 0;
|
||||
bool b = strncmp(bridge.prefetch_mime, "text/plain", 10) == 0 ||
|
||||
strcmp(bridge.prefetch_mime, "UTF8_STRING") == 0;
|
||||
return a && b;
|
||||
}
|
||||
|
||||
static void bridge_prefetch_serve(const char *mime, int32_t fd) {
|
||||
if (!bridge_prefetch_mime_compatible(mime)) {
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
size_t off = 0;
|
||||
while (off < bridge.prefetch_len) {
|
||||
ssize_t n = write(fd, bridge.prefetch_buf + off,
|
||||
bridge.prefetch_len - off);
|
||||
if (n > 0) {
|
||||
off += (size_t)n;
|
||||
} else if (n < 0 && (errno == EAGAIN || errno == EINTR)) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/* ---------------- host backend: wlr-data-control ---------------- */
|
||||
|
||||
static void bridge_dc_source_send(void *data,
|
||||
struct zwlr_data_control_source_v1 *src, const char *mime, int32_t fd) {
|
||||
(void)data; (void)src;
|
||||
if (bridge.inner_src)
|
||||
wlr_data_source_send(bridge.inner_src, mime, fd);
|
||||
else
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void bridge_dc_source_cancelled(void *data,
|
||||
struct zwlr_data_control_source_v1 *src) {
|
||||
(void)data;
|
||||
zwlr_data_control_source_v1_destroy(src);
|
||||
bridge.dc_source = NULL;
|
||||
bridge.pending_echo = false;
|
||||
}
|
||||
|
||||
static const struct zwlr_data_control_source_v1_listener bridge_dc_source_listener = {
|
||||
.send = bridge_dc_source_send,
|
||||
.cancelled = bridge_dc_source_cancelled,
|
||||
};
|
||||
|
||||
static void bridge_dc_offer_mime(void *data,
|
||||
struct zwlr_data_control_offer_v1 *offer, const char *mime) {
|
||||
(void)data; (void)offer;
|
||||
bridge_mimes_append(mime);
|
||||
}
|
||||
|
||||
static const struct zwlr_data_control_offer_v1_listener bridge_dc_offer_listener = {
|
||||
.offer = bridge_dc_offer_mime,
|
||||
};
|
||||
|
||||
static void bridge_dc_device_finished(void *data,
|
||||
struct zwlr_data_control_device_v1 *dev) {
|
||||
(void)data; (void)dev;
|
||||
bridge.backend = bridge.dd ? BRIDGE_BACKEND_WL_DATA_DEVICE
|
||||
: BRIDGE_BACKEND_NONE;
|
||||
if (bridge.dc_offer) {
|
||||
zwlr_data_control_offer_v1_destroy(bridge.dc_offer);
|
||||
bridge.dc_offer = NULL;
|
||||
}
|
||||
if (bridge.dc_device) {
|
||||
zwlr_data_control_device_v1_destroy(bridge.dc_device);
|
||||
bridge.dc_device = NULL;
|
||||
}
|
||||
tabbed_log(TABBED_LOG_INFO, "clipboard bridge: host data-control "
|
||||
"finished; falling back to %s\n",
|
||||
bridge.backend == BRIDGE_BACKEND_WL_DATA_DEVICE ?
|
||||
"focus-based wl_data_device" : "none");
|
||||
}
|
||||
|
||||
static void bridge_dc_device_data_offer(void *data,
|
||||
struct zwlr_data_control_device_v1 *dev,
|
||||
struct zwlr_data_control_offer_v1 *offer) {
|
||||
(void)data; (void)dev;
|
||||
if (bridge.pending_echo) {
|
||||
bridge.pending_echo = false;
|
||||
bridge.dc_echo_offer = offer;
|
||||
}
|
||||
bridge_mimes_reset();
|
||||
zwlr_data_control_offer_v1_add_listener(offer, &bridge_dc_offer_listener, NULL);
|
||||
}
|
||||
|
||||
static void bridge_dc_device_selection(void *data,
|
||||
struct zwlr_data_control_device_v1 *dev,
|
||||
struct zwlr_data_control_offer_v1 *offer) {
|
||||
(void)data; (void)dev;
|
||||
bridge_prefetch_cancel();
|
||||
if (offer && offer == bridge.dc_echo_offer) {
|
||||
bridge.dc_echo_offer = NULL;
|
||||
bridge_mimes_reset();
|
||||
if (offer)
|
||||
zwlr_data_control_offer_v1_destroy(offer);
|
||||
return;
|
||||
}
|
||||
if (bridge.dc_offer)
|
||||
zwlr_data_control_offer_v1_destroy(bridge.dc_offer);
|
||||
bridge.dc_offer = offer;
|
||||
if (!offer) {
|
||||
bridge_mimes_reset();
|
||||
bridge_clear_nested();
|
||||
return;
|
||||
}
|
||||
bridge_prefetch_start();
|
||||
bridge_mirror_to_nested();
|
||||
}
|
||||
|
||||
static const struct zwlr_data_control_device_v1_listener bridge_dc_listener = {
|
||||
.data_offer = bridge_dc_device_data_offer,
|
||||
.selection = bridge_dc_device_selection,
|
||||
.finished = bridge_dc_device_finished,
|
||||
/* primary_selection left unset: not bridged */
|
||||
};
|
||||
|
||||
/* ---------------- host backend: wl_data_device (focus-based) ---------------- */
|
||||
|
||||
static void bridge_dd_source_send(void *data, struct wl_data_source *src,
|
||||
const char *mime, int32_t fd) {
|
||||
(void)data; (void)src;
|
||||
if (bridge.inner_src)
|
||||
wlr_data_source_send(bridge.inner_src, mime, fd);
|
||||
else
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void bridge_dd_source_cancelled(void *data, struct wl_data_source *src) {
|
||||
(void)data;
|
||||
wl_data_source_destroy(src);
|
||||
bridge.dd_source = NULL;
|
||||
bridge.pending_echo = false;
|
||||
bridge.host_set_pending = true;
|
||||
}
|
||||
|
||||
static const struct wl_data_source_listener bridge_dd_source_listener = {
|
||||
.send = bridge_dd_source_send,
|
||||
.cancelled = bridge_dd_source_cancelled,
|
||||
};
|
||||
|
||||
static void bridge_dd_offer_mime(void *data, struct wl_data_offer *offer,
|
||||
const char *mime) {
|
||||
(void)data; (void)offer;
|
||||
bridge_mimes_append(mime);
|
||||
}
|
||||
|
||||
static const struct wl_data_offer_listener bridge_dd_offer_listener = {
|
||||
.offer = bridge_dd_offer_mime,
|
||||
/* source_actions / action left unset: drag & drop is not bridged */
|
||||
};
|
||||
|
||||
static void bridge_dd_data_offer(void *data, struct wl_data_device *dev,
|
||||
struct wl_data_offer *offer) {
|
||||
(void)data; (void)dev;
|
||||
if (bridge.pending_echo) {
|
||||
bridge.pending_echo = false;
|
||||
bridge.dd_echo_offer = offer;
|
||||
}
|
||||
bridge_mimes_reset();
|
||||
wl_data_offer_add_listener(offer, &bridge_dd_offer_listener, NULL);
|
||||
}
|
||||
|
||||
static void bridge_dd_selection(void *data, struct wl_data_device *dev,
|
||||
struct wl_data_offer *offer) {
|
||||
(void)data; (void)dev;
|
||||
bridge_prefetch_cancel();
|
||||
if (offer && offer == bridge.dd_echo_offer) {
|
||||
bridge.dd_echo_offer = NULL;
|
||||
bridge_mimes_reset();
|
||||
if (offer)
|
||||
wl_data_offer_destroy(offer);
|
||||
return;
|
||||
}
|
||||
if (bridge.dd_offer)
|
||||
wl_data_offer_destroy(bridge.dd_offer);
|
||||
bridge.dd_offer = offer;
|
||||
if (!offer) {
|
||||
/* selection cleared, or we lost keyboard focus: keep the last-known
|
||||
nested selection rather than emptying it on every focus change */
|
||||
bridge_mimes_reset();
|
||||
return;
|
||||
}
|
||||
bridge_prefetch_start();
|
||||
bridge_mirror_to_nested();
|
||||
}
|
||||
|
||||
static const struct wl_data_device_listener bridge_dd_listener = {
|
||||
.data_offer = bridge_dd_data_offer,
|
||||
.selection = bridge_dd_selection,
|
||||
/* enter/leave/motion/drop left unset: drag & drop is not bridged */
|
||||
};
|
||||
|
||||
/* ---------------- nested seat side ---------------- */
|
||||
|
||||
static void bridge_seat_request_set_selection(struct wl_listener *l, void *data) {
|
||||
(void)l;
|
||||
struct wlr_seat_request_set_selection_event *event = data;
|
||||
bridge.inner_clear_requested = (event->source == NULL);
|
||||
wlr_seat_set_selection(server.seat, event->source, event->serial);
|
||||
}
|
||||
|
||||
static void bridge_host_set_selection(struct wlr_data_source *src) {
|
||||
bridge_set_inner_src(src);
|
||||
|
||||
if (bridge.backend == BRIDGE_BACKEND_DATA_CONTROL && bridge.dc_device) {
|
||||
if (bridge.dc_source) {
|
||||
zwlr_data_control_source_v1_destroy(bridge.dc_source);
|
||||
bridge.dc_source = NULL;
|
||||
}
|
||||
struct zwlr_data_control_source_v1 *s =
|
||||
zwlr_data_control_manager_v1_create_data_source(bridge.dc_manager);
|
||||
char **p;
|
||||
wl_array_for_each(p, &src->mime_types)
|
||||
zwlr_data_control_source_v1_offer(s, *p);
|
||||
zwlr_data_control_source_v1_add_listener(s, &bridge_dc_source_listener, NULL);
|
||||
bridge.dc_source = s;
|
||||
bridge.pending_echo = true;
|
||||
zwlr_data_control_device_v1_set_selection(bridge.dc_device, s);
|
||||
bridge.host_set_pending = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (bridge.backend == BRIDGE_BACKEND_WL_DATA_DEVICE && bridge.dd) {
|
||||
if (bridge.dd_source) {
|
||||
wl_data_source_destroy(bridge.dd_source);
|
||||
bridge.dd_source = NULL;
|
||||
}
|
||||
if (!bridge.kbd_serial) {
|
||||
bridge.host_set_pending = true;
|
||||
tabbed_log(TABBED_LOG_INFO, "clipboard bridge: no host keyboard "
|
||||
"serial yet; deferring host set_selection until focus\n");
|
||||
return;
|
||||
}
|
||||
struct wl_data_source *s =
|
||||
wl_data_device_manager_create_data_source(bridge.ddm);
|
||||
char **p;
|
||||
wl_array_for_each(p, &src->mime_types)
|
||||
wl_data_source_offer(s, *p);
|
||||
wl_data_source_add_listener(s, &bridge_dd_source_listener, NULL);
|
||||
bridge.dd_source = s;
|
||||
bridge.pending_echo = true;
|
||||
wl_data_device_set_selection(bridge.dd, s, bridge.kbd_serial);
|
||||
bridge.host_set_pending = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void bridge_host_clear(void) {
|
||||
if (bridge.backend == BRIDGE_BACKEND_DATA_CONTROL && bridge.dc_device)
|
||||
zwlr_data_control_device_v1_set_selection(bridge.dc_device, NULL);
|
||||
/* focus-based backend: nothing reliable to clear without focus/serial */
|
||||
}
|
||||
|
||||
static void bridge_seat_set_selection(struct wl_listener *l, void *data) {
|
||||
(void)l; (void)data;
|
||||
struct wlr_data_source *src = server.seat->selection_source;
|
||||
if (src && src->impl == &bridge_inner_source_impl)
|
||||
return; /* our own host->nested mirror */
|
||||
if (bridge.ignore_next_inner) {
|
||||
bridge.ignore_next_inner = false;
|
||||
return;
|
||||
}
|
||||
if (!src) {
|
||||
if (bridge.inner_clear_requested) {
|
||||
bridge.inner_clear_requested = false;
|
||||
bridge_host_clear();
|
||||
}
|
||||
return;
|
||||
}
|
||||
bridge.inner_clear_requested = false;
|
||||
bridge_host_set_selection(src);
|
||||
}
|
||||
|
||||
/* ---------------- lifecycle ---------------- */
|
||||
|
||||
static void bridge_attach_seat(void) {
|
||||
bridge.request_set_selection.notify = bridge_seat_request_set_selection;
|
||||
wl_signal_add(&server.seat->events.request_set_selection,
|
||||
&bridge.request_set_selection);
|
||||
bridge.set_selection.notify = bridge_seat_set_selection;
|
||||
wl_signal_add(&server.seat->events.set_selection, &bridge.set_selection);
|
||||
}
|
||||
|
||||
static void bridge_init(void) {
|
||||
if (!host.seat) {
|
||||
tabbed_log(TABBED_LOG_INFO, "clipboard bridge: no host seat; disabled\n");
|
||||
return;
|
||||
}
|
||||
if (bridge.ddm) {
|
||||
bridge.dd = wl_data_device_manager_get_data_device(bridge.ddm, host.seat);
|
||||
wl_data_device_add_listener(bridge.dd, &bridge_dd_listener, NULL);
|
||||
}
|
||||
if (bridge.dc_manager) {
|
||||
bridge.dc_device = zwlr_data_control_manager_v1_get_data_device(
|
||||
bridge.dc_manager, host.seat);
|
||||
zwlr_data_control_device_v1_add_listener(bridge.dc_device,
|
||||
&bridge_dc_listener, NULL);
|
||||
bridge.backend = BRIDGE_BACKEND_DATA_CONTROL;
|
||||
tabbed_log(TABBED_LOG_INFO, "clipboard bridge: host data-control backend\n");
|
||||
} else if (bridge.dd) {
|
||||
bridge.backend = BRIDGE_BACKEND_WL_DATA_DEVICE;
|
||||
tabbed_log(TABBED_LOG_INFO, "clipboard bridge: focus-based wl_data_device "
|
||||
"backend (host lacks wlr-data-control; host->nested sync needs "
|
||||
"focus)\n");
|
||||
} else {
|
||||
tabbed_log(TABBED_LOG_INFO, "clipboard bridge: no host clipboard path\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void bridge_cleanup(void) {
|
||||
if (bridge.inner_src) {
|
||||
wl_list_remove(&bridge.inner_src_destroy.link);
|
||||
bridge.inner_src = NULL;
|
||||
}
|
||||
if (bridge.dc_source) {
|
||||
zwlr_data_control_source_v1_destroy(bridge.dc_source);
|
||||
bridge.dc_source = NULL;
|
||||
}
|
||||
if (bridge.dd_source) {
|
||||
wl_data_source_destroy(bridge.dd_source);
|
||||
bridge.dd_source = NULL;
|
||||
}
|
||||
if (bridge.dc_offer) {
|
||||
zwlr_data_control_offer_v1_destroy(bridge.dc_offer);
|
||||
bridge.dc_offer = NULL;
|
||||
}
|
||||
if (bridge.dd_offer) {
|
||||
wl_data_offer_destroy(bridge.dd_offer);
|
||||
bridge.dd_offer = NULL;
|
||||
}
|
||||
if (bridge.dc_device) {
|
||||
zwlr_data_control_device_v1_destroy(bridge.dc_device);
|
||||
bridge.dc_device = NULL;
|
||||
}
|
||||
bridge_mimes_reset();
|
||||
free(bridge.mimes);
|
||||
bridge.mimes = NULL;
|
||||
bridge.mimes_cap = 0;
|
||||
bridge_prefetch_cancel();
|
||||
bridge.backend = BRIDGE_BACKEND_NONE;
|
||||
}
|
||||
|
||||
/* ===================== Host-side listeners ===================== */
|
||||
|
||||
static void host_registry_global(void *data, struct wl_registry *registry,
|
||||
|
|
@ -1030,6 +1696,13 @@ static void host_registry_global(void *data, struct wl_registry *registry,
|
|||
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
|
||||
host.seat = wl_registry_bind(registry, name,
|
||||
&wl_seat_interface, version < 5 ? version : 5);
|
||||
} else if (strcmp(interface, wl_data_device_manager_interface.name) == 0) {
|
||||
bridge.ddm = wl_registry_bind(registry, name,
|
||||
&wl_data_device_manager_interface, version < 3 ? version : 3);
|
||||
} else if (strcmp(interface,
|
||||
zwlr_data_control_manager_v1_interface.name) == 0) {
|
||||
bridge.dc_manager = wl_registry_bind(registry, name,
|
||||
&zwlr_data_control_manager_v1_interface, version < 2 ? version : 2);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1195,7 +1868,16 @@ static void host_kbd_keymap(void *data, struct wl_keyboard *kbd, uint32_t format
|
|||
static void host_kbd_enter(void *data, struct wl_keyboard *kbd, uint32_t serial,
|
||||
struct wl_surface *surface, struct wl_array *keys)
|
||||
{
|
||||
(void)data; (void)kbd; (void)serial; (void)surface; (void)keys;
|
||||
(void)data; (void)kbd; (void)surface; (void)keys;
|
||||
/* Remember the enter serial for host clipboard set_selection, and drop any
|
||||
stale echo guard from a set_selection made while unfocused. */
|
||||
bridge.kbd_serial = serial;
|
||||
bridge.pending_echo = false;
|
||||
if (bridge.host_set_pending && server.seat->selection_source &&
|
||||
server.seat->selection_source->impl != &bridge_inner_source_impl) {
|
||||
bridge.host_set_pending = false;
|
||||
bridge_host_set_selection(server.seat->selection_source);
|
||||
}
|
||||
if (server.active)
|
||||
wlr_seat_keyboard_notify_enter(server.seat,
|
||||
server.active->base->surface,
|
||||
|
|
@ -1305,7 +1987,8 @@ static void dump_host_back(void) {
|
|||
static void host_kbd_key(void *data, struct wl_keyboard *kbd, uint32_t serial,
|
||||
uint32_t time, uint32_t key, uint32_t state)
|
||||
{
|
||||
(void)data; (void)kbd; (void)serial;
|
||||
(void)data; (void)kbd;
|
||||
bridge.kbd_serial = serial;
|
||||
tabbed_log(TABBED_LOG_DEBUG,
|
||||
"[kbd] key=%u state=%u ctrl=%d repeat_key=%u\n", key, state,
|
||||
(host.host_mods_depressed & (1u << 2)) != 0, host.repeat_key);
|
||||
|
|
@ -1627,6 +2310,7 @@ static bool host_init(void) {
|
|||
if (host.pointer)
|
||||
wl_pointer_add_listener(host.pointer, &host_ptr_listener, NULL);
|
||||
}
|
||||
bridge_init();
|
||||
|
||||
/* Try EGL on the host wayland platform */
|
||||
pfn_eglGetPlatformDisplayEXT = (PFNEGLGETPLATFORMDISPLAYEXTPROC)
|
||||
|
|
@ -2149,6 +2833,7 @@ int main(int argc, char *argv[]) {
|
|||
wlr_shm_create_with_renderer(server.wl_display, 1, server.renderer);
|
||||
wlr_linux_dmabuf_v1_create_with_renderer(server.wl_display, 5, server.renderer);
|
||||
wlr_data_device_manager_create(server.wl_display);
|
||||
wlr_data_control_manager_v1_create(server.wl_display);
|
||||
|
||||
/* Never let wlroots present a client buffer to the headless output
|
||||
directly (direct scanout). We must composite into our own swapchain so
|
||||
|
|
@ -2164,6 +2849,7 @@ int main(int argc, char *argv[]) {
|
|||
server.seat = wlr_seat_create(server.wl_display, "tabbed");
|
||||
wlr_seat_set_capabilities(server.seat,
|
||||
WL_SEAT_CAPABILITY_POINTER | WL_SEAT_CAPABILITY_KEYBOARD);
|
||||
bridge_attach_seat();
|
||||
|
||||
/* virtual keyboard fed from the host keyboard */
|
||||
static const struct wlr_keyboard_impl keyboard_impl = {
|
||||
|
|
|
|||
665
wlr-data-control-client.h
Normal file
665
wlr-data-control-client.h
Normal file
|
|
@ -0,0 +1,665 @@
|
|||
/* Generated by wayland-scanner 1.26.0 */
|
||||
|
||||
#ifndef WLR_DATA_CONTROL_UNSTABLE_V1_CLIENT_PROTOCOL_H
|
||||
#define WLR_DATA_CONTROL_UNSTABLE_V1_CLIENT_PROTOCOL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "wayland-client.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @page page_wlr_data_control_unstable_v1 The wlr_data_control_unstable_v1 protocol
|
||||
* control data devices
|
||||
*
|
||||
* @section page_desc_wlr_data_control_unstable_v1 Description
|
||||
*
|
||||
* This protocol allows a privileged client to control data devices. In
|
||||
* particular, the client will be able to manage the current selection and take
|
||||
* the role of a clipboard manager.
|
||||
*
|
||||
* Warning! The protocol described in this file is experimental and
|
||||
* backward incompatible changes may be made. Backward compatible changes
|
||||
* may be added together with the corresponding interface version bump.
|
||||
* Backward incompatible changes are done by bumping the version number in
|
||||
* the protocol and interface names and resetting the interface version.
|
||||
* Once the protocol is to be declared stable, the 'z' prefix and the
|
||||
* version number in the protocol and interface names are removed and the
|
||||
* interface version number is reset.
|
||||
*
|
||||
* @section page_ifaces_wlr_data_control_unstable_v1 Interfaces
|
||||
* - @subpage page_iface_zwlr_data_control_manager_v1 - manager to control data devices
|
||||
* - @subpage page_iface_zwlr_data_control_device_v1 - manage a data device for a seat
|
||||
* - @subpage page_iface_zwlr_data_control_source_v1 - offer to transfer data
|
||||
* - @subpage page_iface_zwlr_data_control_offer_v1 - offer to transfer data
|
||||
* @section page_copyright_wlr_data_control_unstable_v1 Copyright
|
||||
* <pre>
|
||||
*
|
||||
* Copyright © 2018 Simon Ser
|
||||
* Copyright © 2019 Ivan Molodetskikh
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this
|
||||
* software and its documentation for any purpose is hereby granted
|
||||
* without fee, provided that the above copyright notice appear in
|
||||
* all copies and that both that copyright notice and this permission
|
||||
* notice appear in supporting documentation, and that the name of
|
||||
* the copyright holders not be used in advertising or publicity
|
||||
* pertaining to distribution of the software without specific,
|
||||
* written prior permission. The copyright holders make no
|
||||
* representations about the suitability of this software for any
|
||||
* purpose. It is provided "as is" without express or implied
|
||||
* warranty.
|
||||
*
|
||||
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
||||
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* SPECIAL, 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.
|
||||
* </pre>
|
||||
*/
|
||||
struct wl_seat;
|
||||
struct zwlr_data_control_device_v1;
|
||||
struct zwlr_data_control_manager_v1;
|
||||
struct zwlr_data_control_offer_v1;
|
||||
struct zwlr_data_control_source_v1;
|
||||
|
||||
#ifndef ZWLR_DATA_CONTROL_MANAGER_V1_INTERFACE
|
||||
#define ZWLR_DATA_CONTROL_MANAGER_V1_INTERFACE
|
||||
/**
|
||||
* @page page_iface_zwlr_data_control_manager_v1 zwlr_data_control_manager_v1
|
||||
* @section page_iface_zwlr_data_control_manager_v1_desc Description
|
||||
*
|
||||
* This interface is a manager that allows creating per-seat data device
|
||||
* controls.
|
||||
* @section page_iface_zwlr_data_control_manager_v1_api API
|
||||
* See @ref iface_zwlr_data_control_manager_v1.
|
||||
*/
|
||||
/**
|
||||
* @defgroup iface_zwlr_data_control_manager_v1 The zwlr_data_control_manager_v1 interface
|
||||
*
|
||||
* This interface is a manager that allows creating per-seat data device
|
||||
* controls.
|
||||
*/
|
||||
extern const struct wl_interface zwlr_data_control_manager_v1_interface;
|
||||
#endif
|
||||
#ifndef ZWLR_DATA_CONTROL_DEVICE_V1_INTERFACE
|
||||
#define ZWLR_DATA_CONTROL_DEVICE_V1_INTERFACE
|
||||
/**
|
||||
* @page page_iface_zwlr_data_control_device_v1 zwlr_data_control_device_v1
|
||||
* @section page_iface_zwlr_data_control_device_v1_desc Description
|
||||
*
|
||||
* This interface allows a client to manage a seat's selection.
|
||||
*
|
||||
* When the seat is destroyed, this object becomes inert.
|
||||
* @section page_iface_zwlr_data_control_device_v1_api API
|
||||
* See @ref iface_zwlr_data_control_device_v1.
|
||||
*/
|
||||
/**
|
||||
* @defgroup iface_zwlr_data_control_device_v1 The zwlr_data_control_device_v1 interface
|
||||
*
|
||||
* This interface allows a client to manage a seat's selection.
|
||||
*
|
||||
* When the seat is destroyed, this object becomes inert.
|
||||
*/
|
||||
extern const struct wl_interface zwlr_data_control_device_v1_interface;
|
||||
#endif
|
||||
#ifndef ZWLR_DATA_CONTROL_SOURCE_V1_INTERFACE
|
||||
#define ZWLR_DATA_CONTROL_SOURCE_V1_INTERFACE
|
||||
/**
|
||||
* @page page_iface_zwlr_data_control_source_v1 zwlr_data_control_source_v1
|
||||
* @section page_iface_zwlr_data_control_source_v1_desc Description
|
||||
*
|
||||
* The wlr_data_control_source object is the source side of a
|
||||
* wlr_data_control_offer. It is created by the source client in a data
|
||||
* transfer and provides a way to describe the offered data and a way to
|
||||
* respond to requests to transfer the data.
|
||||
* @section page_iface_zwlr_data_control_source_v1_api API
|
||||
* See @ref iface_zwlr_data_control_source_v1.
|
||||
*/
|
||||
/**
|
||||
* @defgroup iface_zwlr_data_control_source_v1 The zwlr_data_control_source_v1 interface
|
||||
*
|
||||
* The wlr_data_control_source object is the source side of a
|
||||
* wlr_data_control_offer. It is created by the source client in a data
|
||||
* transfer and provides a way to describe the offered data and a way to
|
||||
* respond to requests to transfer the data.
|
||||
*/
|
||||
extern const struct wl_interface zwlr_data_control_source_v1_interface;
|
||||
#endif
|
||||
#ifndef ZWLR_DATA_CONTROL_OFFER_V1_INTERFACE
|
||||
#define ZWLR_DATA_CONTROL_OFFER_V1_INTERFACE
|
||||
/**
|
||||
* @page page_iface_zwlr_data_control_offer_v1 zwlr_data_control_offer_v1
|
||||
* @section page_iface_zwlr_data_control_offer_v1_desc Description
|
||||
*
|
||||
* A wlr_data_control_offer represents a piece of data offered for transfer
|
||||
* by another client (the source client). The offer describes the different
|
||||
* MIME types that the data can be converted to and provides the mechanism
|
||||
* for transferring the data directly from the source client.
|
||||
* @section page_iface_zwlr_data_control_offer_v1_api API
|
||||
* See @ref iface_zwlr_data_control_offer_v1.
|
||||
*/
|
||||
/**
|
||||
* @defgroup iface_zwlr_data_control_offer_v1 The zwlr_data_control_offer_v1 interface
|
||||
*
|
||||
* A wlr_data_control_offer represents a piece of data offered for transfer
|
||||
* by another client (the source client). The offer describes the different
|
||||
* MIME types that the data can be converted to and provides the mechanism
|
||||
* for transferring the data directly from the source client.
|
||||
*/
|
||||
extern const struct wl_interface zwlr_data_control_offer_v1_interface;
|
||||
#endif
|
||||
|
||||
#define ZWLR_DATA_CONTROL_MANAGER_V1_CREATE_DATA_SOURCE 0
|
||||
#define ZWLR_DATA_CONTROL_MANAGER_V1_GET_DATA_DEVICE 1
|
||||
#define ZWLR_DATA_CONTROL_MANAGER_V1_DESTROY 2
|
||||
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_data_control_manager_v1
|
||||
*/
|
||||
#define ZWLR_DATA_CONTROL_MANAGER_V1_CREATE_DATA_SOURCE_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_data_control_manager_v1
|
||||
*/
|
||||
#define ZWLR_DATA_CONTROL_MANAGER_V1_GET_DATA_DEVICE_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_data_control_manager_v1
|
||||
*/
|
||||
#define ZWLR_DATA_CONTROL_MANAGER_V1_DESTROY_SINCE_VERSION 1
|
||||
|
||||
/** @ingroup iface_zwlr_data_control_manager_v1 */
|
||||
static inline void
|
||||
zwlr_data_control_manager_v1_set_user_data(struct zwlr_data_control_manager_v1 *zwlr_data_control_manager_v1, void *user_data)
|
||||
{
|
||||
wl_proxy_set_user_data((struct wl_proxy *) zwlr_data_control_manager_v1, user_data);
|
||||
}
|
||||
|
||||
/** @ingroup iface_zwlr_data_control_manager_v1 */
|
||||
static inline void *
|
||||
zwlr_data_control_manager_v1_get_user_data(struct zwlr_data_control_manager_v1 *zwlr_data_control_manager_v1)
|
||||
{
|
||||
return wl_proxy_get_user_data((struct wl_proxy *) zwlr_data_control_manager_v1);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
zwlr_data_control_manager_v1_get_version(struct zwlr_data_control_manager_v1 *zwlr_data_control_manager_v1)
|
||||
{
|
||||
return wl_proxy_get_version((struct wl_proxy *) zwlr_data_control_manager_v1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_data_control_manager_v1
|
||||
*
|
||||
* Create a new data source.
|
||||
*/
|
||||
static inline struct zwlr_data_control_source_v1 *
|
||||
zwlr_data_control_manager_v1_create_data_source(struct zwlr_data_control_manager_v1 *zwlr_data_control_manager_v1)
|
||||
{
|
||||
struct wl_proxy *id;
|
||||
|
||||
id = wl_proxy_marshal_flags((struct wl_proxy *) zwlr_data_control_manager_v1,
|
||||
ZWLR_DATA_CONTROL_MANAGER_V1_CREATE_DATA_SOURCE, &zwlr_data_control_source_v1_interface, wl_proxy_get_version((struct wl_proxy *) zwlr_data_control_manager_v1), 0, NULL);
|
||||
|
||||
return (struct zwlr_data_control_source_v1 *) id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_data_control_manager_v1
|
||||
*
|
||||
* Create a data device that can be used to manage a seat's selection.
|
||||
*/
|
||||
static inline struct zwlr_data_control_device_v1 *
|
||||
zwlr_data_control_manager_v1_get_data_device(struct zwlr_data_control_manager_v1 *zwlr_data_control_manager_v1, struct wl_seat *seat)
|
||||
{
|
||||
struct wl_proxy *id;
|
||||
|
||||
id = wl_proxy_marshal_flags((struct wl_proxy *) zwlr_data_control_manager_v1,
|
||||
ZWLR_DATA_CONTROL_MANAGER_V1_GET_DATA_DEVICE, &zwlr_data_control_device_v1_interface, wl_proxy_get_version((struct wl_proxy *) zwlr_data_control_manager_v1), 0, NULL, seat);
|
||||
|
||||
return (struct zwlr_data_control_device_v1 *) id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_data_control_manager_v1
|
||||
*
|
||||
* All objects created by the manager will still remain valid, until their
|
||||
* appropriate destroy request has been called.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_data_control_manager_v1_destroy(struct zwlr_data_control_manager_v1 *zwlr_data_control_manager_v1)
|
||||
{
|
||||
wl_proxy_marshal_flags((struct wl_proxy *) zwlr_data_control_manager_v1,
|
||||
ZWLR_DATA_CONTROL_MANAGER_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_data_control_manager_v1), WL_MARSHAL_FLAG_DESTROY);
|
||||
}
|
||||
|
||||
#ifndef ZWLR_DATA_CONTROL_DEVICE_V1_ERROR_ENUM
|
||||
#define ZWLR_DATA_CONTROL_DEVICE_V1_ERROR_ENUM
|
||||
enum zwlr_data_control_device_v1_error {
|
||||
/**
|
||||
* source given to set_selection or set_primary_selection was already used before
|
||||
*/
|
||||
ZWLR_DATA_CONTROL_DEVICE_V1_ERROR_USED_SOURCE = 1,
|
||||
};
|
||||
#endif /* ZWLR_DATA_CONTROL_DEVICE_V1_ERROR_ENUM */
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_data_control_device_v1
|
||||
* @struct zwlr_data_control_device_v1_listener
|
||||
*/
|
||||
struct zwlr_data_control_device_v1_listener {
|
||||
/**
|
||||
* introduce a new wlr_data_control_offer
|
||||
*
|
||||
* The data_offer event introduces a new wlr_data_control_offer
|
||||
* object, which will subsequently be used in either the
|
||||
* wlr_data_control_device.selection event (for the regular
|
||||
* clipboard selections) or the
|
||||
* wlr_data_control_device.primary_selection event (for the primary
|
||||
* clipboard selections). Immediately following the
|
||||
* wlr_data_control_device.data_offer event, the new data_offer
|
||||
* object will send out wlr_data_control_offer.offer events to
|
||||
* describe the MIME types it offers.
|
||||
*/
|
||||
void (*data_offer)(void *data,
|
||||
struct zwlr_data_control_device_v1 *zwlr_data_control_device_v1,
|
||||
struct zwlr_data_control_offer_v1 *id);
|
||||
/**
|
||||
* advertise new selection
|
||||
*
|
||||
* The selection event is sent out to notify the client of a new
|
||||
* wlr_data_control_offer for the selection for this device. The
|
||||
* wlr_data_control_device.data_offer and the
|
||||
* wlr_data_control_offer.offer events are sent out immediately
|
||||
* before this event to introduce the data offer object. The
|
||||
* selection event is sent to a client when a new selection is set.
|
||||
* The wlr_data_control_offer is valid until a new
|
||||
* wlr_data_control_offer or NULL is received. The client must
|
||||
* destroy the previous selection wlr_data_control_offer, if any,
|
||||
* upon receiving this event.
|
||||
*
|
||||
* The first selection event is sent upon binding the
|
||||
* wlr_data_control_device object.
|
||||
*/
|
||||
void (*selection)(void *data,
|
||||
struct zwlr_data_control_device_v1 *zwlr_data_control_device_v1,
|
||||
struct zwlr_data_control_offer_v1 *id);
|
||||
/**
|
||||
* this data control is no longer valid
|
||||
*
|
||||
* This data control object is no longer valid and should be
|
||||
* destroyed by the client.
|
||||
*/
|
||||
void (*finished)(void *data,
|
||||
struct zwlr_data_control_device_v1 *zwlr_data_control_device_v1);
|
||||
/**
|
||||
* advertise new primary selection
|
||||
*
|
||||
* The primary_selection event is sent out to notify the client
|
||||
* of a new wlr_data_control_offer for the primary selection for
|
||||
* this device. The wlr_data_control_device.data_offer and the
|
||||
* wlr_data_control_offer.offer events are sent out immediately
|
||||
* before this event to introduce the data offer object. The
|
||||
* primary_selection event is sent to a client when a new primary
|
||||
* selection is set. The wlr_data_control_offer is valid until a
|
||||
* new wlr_data_control_offer or NULL is received. The client must
|
||||
* destroy the previous primary selection wlr_data_control_offer,
|
||||
* if any, upon receiving this event.
|
||||
*
|
||||
* If the compositor supports primary selection, the first
|
||||
* primary_selection event is sent upon binding the
|
||||
* wlr_data_control_device object.
|
||||
* @since 2
|
||||
*/
|
||||
void (*primary_selection)(void *data,
|
||||
struct zwlr_data_control_device_v1 *zwlr_data_control_device_v1,
|
||||
struct zwlr_data_control_offer_v1 *id);
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_data_control_device_v1
|
||||
*/
|
||||
static inline int
|
||||
zwlr_data_control_device_v1_add_listener(struct zwlr_data_control_device_v1 *zwlr_data_control_device_v1,
|
||||
const struct zwlr_data_control_device_v1_listener *listener, void *data)
|
||||
{
|
||||
return wl_proxy_add_listener((struct wl_proxy *) zwlr_data_control_device_v1,
|
||||
(void (**)(void)) listener, data);
|
||||
}
|
||||
|
||||
#define ZWLR_DATA_CONTROL_DEVICE_V1_SET_SELECTION 0
|
||||
#define ZWLR_DATA_CONTROL_DEVICE_V1_DESTROY 1
|
||||
#define ZWLR_DATA_CONTROL_DEVICE_V1_SET_PRIMARY_SELECTION 2
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_data_control_device_v1
|
||||
*/
|
||||
#define ZWLR_DATA_CONTROL_DEVICE_V1_DATA_OFFER_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_data_control_device_v1
|
||||
*/
|
||||
#define ZWLR_DATA_CONTROL_DEVICE_V1_SELECTION_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_data_control_device_v1
|
||||
*/
|
||||
#define ZWLR_DATA_CONTROL_DEVICE_V1_FINISHED_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_data_control_device_v1
|
||||
*/
|
||||
#define ZWLR_DATA_CONTROL_DEVICE_V1_PRIMARY_SELECTION_SINCE_VERSION 2
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_data_control_device_v1
|
||||
*/
|
||||
#define ZWLR_DATA_CONTROL_DEVICE_V1_SET_SELECTION_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_data_control_device_v1
|
||||
*/
|
||||
#define ZWLR_DATA_CONTROL_DEVICE_V1_DESTROY_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_data_control_device_v1
|
||||
*/
|
||||
#define ZWLR_DATA_CONTROL_DEVICE_V1_SET_PRIMARY_SELECTION_SINCE_VERSION 2
|
||||
|
||||
/** @ingroup iface_zwlr_data_control_device_v1 */
|
||||
static inline void
|
||||
zwlr_data_control_device_v1_set_user_data(struct zwlr_data_control_device_v1 *zwlr_data_control_device_v1, void *user_data)
|
||||
{
|
||||
wl_proxy_set_user_data((struct wl_proxy *) zwlr_data_control_device_v1, user_data);
|
||||
}
|
||||
|
||||
/** @ingroup iface_zwlr_data_control_device_v1 */
|
||||
static inline void *
|
||||
zwlr_data_control_device_v1_get_user_data(struct zwlr_data_control_device_v1 *zwlr_data_control_device_v1)
|
||||
{
|
||||
return wl_proxy_get_user_data((struct wl_proxy *) zwlr_data_control_device_v1);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
zwlr_data_control_device_v1_get_version(struct zwlr_data_control_device_v1 *zwlr_data_control_device_v1)
|
||||
{
|
||||
return wl_proxy_get_version((struct wl_proxy *) zwlr_data_control_device_v1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_data_control_device_v1
|
||||
*
|
||||
* This request asks the compositor to set the selection to the data from
|
||||
* the source on behalf of the client.
|
||||
*
|
||||
* The given source may not be used in any further set_selection or
|
||||
* set_primary_selection requests. Attempting to use a previously used
|
||||
* source is a protocol error.
|
||||
*
|
||||
* To unset the selection, set the source to NULL.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_data_control_device_v1_set_selection(struct zwlr_data_control_device_v1 *zwlr_data_control_device_v1, struct zwlr_data_control_source_v1 *source)
|
||||
{
|
||||
wl_proxy_marshal_flags((struct wl_proxy *) zwlr_data_control_device_v1,
|
||||
ZWLR_DATA_CONTROL_DEVICE_V1_SET_SELECTION, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_data_control_device_v1), 0, source);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_data_control_device_v1
|
||||
*
|
||||
* Destroys the data device object.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_data_control_device_v1_destroy(struct zwlr_data_control_device_v1 *zwlr_data_control_device_v1)
|
||||
{
|
||||
wl_proxy_marshal_flags((struct wl_proxy *) zwlr_data_control_device_v1,
|
||||
ZWLR_DATA_CONTROL_DEVICE_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_data_control_device_v1), WL_MARSHAL_FLAG_DESTROY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_data_control_device_v1
|
||||
*
|
||||
* This request asks the compositor to set the primary selection to the
|
||||
* data from the source on behalf of the client.
|
||||
*
|
||||
* The given source may not be used in any further set_selection or
|
||||
* set_primary_selection requests. Attempting to use a previously used
|
||||
* source is a protocol error.
|
||||
*
|
||||
* To unset the primary selection, set the source to NULL.
|
||||
*
|
||||
* The compositor will ignore this request if it does not support primary
|
||||
* selection.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_data_control_device_v1_set_primary_selection(struct zwlr_data_control_device_v1 *zwlr_data_control_device_v1, struct zwlr_data_control_source_v1 *source)
|
||||
{
|
||||
wl_proxy_marshal_flags((struct wl_proxy *) zwlr_data_control_device_v1,
|
||||
ZWLR_DATA_CONTROL_DEVICE_V1_SET_PRIMARY_SELECTION, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_data_control_device_v1), 0, source);
|
||||
}
|
||||
|
||||
#ifndef ZWLR_DATA_CONTROL_SOURCE_V1_ERROR_ENUM
|
||||
#define ZWLR_DATA_CONTROL_SOURCE_V1_ERROR_ENUM
|
||||
enum zwlr_data_control_source_v1_error {
|
||||
/**
|
||||
* offer sent after wlr_data_control_device.set_selection
|
||||
*/
|
||||
ZWLR_DATA_CONTROL_SOURCE_V1_ERROR_INVALID_OFFER = 1,
|
||||
};
|
||||
#endif /* ZWLR_DATA_CONTROL_SOURCE_V1_ERROR_ENUM */
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_data_control_source_v1
|
||||
* @struct zwlr_data_control_source_v1_listener
|
||||
*/
|
||||
struct zwlr_data_control_source_v1_listener {
|
||||
/**
|
||||
* send the data
|
||||
*
|
||||
* Request for data from the client. Send the data as the
|
||||
* specified MIME type over the passed file descriptor, then close
|
||||
* it.
|
||||
* @param mime_type MIME type for the data
|
||||
* @param fd file descriptor for the data
|
||||
*/
|
||||
void (*send)(void *data,
|
||||
struct zwlr_data_control_source_v1 *zwlr_data_control_source_v1,
|
||||
const char *mime_type,
|
||||
int32_t fd);
|
||||
/**
|
||||
* selection was cancelled
|
||||
*
|
||||
* This data source is no longer valid. The data source has been
|
||||
* replaced by another data source.
|
||||
*
|
||||
* The client should clean up and destroy this data source.
|
||||
*/
|
||||
void (*cancelled)(void *data,
|
||||
struct zwlr_data_control_source_v1 *zwlr_data_control_source_v1);
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_data_control_source_v1
|
||||
*/
|
||||
static inline int
|
||||
zwlr_data_control_source_v1_add_listener(struct zwlr_data_control_source_v1 *zwlr_data_control_source_v1,
|
||||
const struct zwlr_data_control_source_v1_listener *listener, void *data)
|
||||
{
|
||||
return wl_proxy_add_listener((struct wl_proxy *) zwlr_data_control_source_v1,
|
||||
(void (**)(void)) listener, data);
|
||||
}
|
||||
|
||||
#define ZWLR_DATA_CONTROL_SOURCE_V1_OFFER 0
|
||||
#define ZWLR_DATA_CONTROL_SOURCE_V1_DESTROY 1
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_data_control_source_v1
|
||||
*/
|
||||
#define ZWLR_DATA_CONTROL_SOURCE_V1_SEND_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_data_control_source_v1
|
||||
*/
|
||||
#define ZWLR_DATA_CONTROL_SOURCE_V1_CANCELLED_SINCE_VERSION 1
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_data_control_source_v1
|
||||
*/
|
||||
#define ZWLR_DATA_CONTROL_SOURCE_V1_OFFER_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_data_control_source_v1
|
||||
*/
|
||||
#define ZWLR_DATA_CONTROL_SOURCE_V1_DESTROY_SINCE_VERSION 1
|
||||
|
||||
/** @ingroup iface_zwlr_data_control_source_v1 */
|
||||
static inline void
|
||||
zwlr_data_control_source_v1_set_user_data(struct zwlr_data_control_source_v1 *zwlr_data_control_source_v1, void *user_data)
|
||||
{
|
||||
wl_proxy_set_user_data((struct wl_proxy *) zwlr_data_control_source_v1, user_data);
|
||||
}
|
||||
|
||||
/** @ingroup iface_zwlr_data_control_source_v1 */
|
||||
static inline void *
|
||||
zwlr_data_control_source_v1_get_user_data(struct zwlr_data_control_source_v1 *zwlr_data_control_source_v1)
|
||||
{
|
||||
return wl_proxy_get_user_data((struct wl_proxy *) zwlr_data_control_source_v1);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
zwlr_data_control_source_v1_get_version(struct zwlr_data_control_source_v1 *zwlr_data_control_source_v1)
|
||||
{
|
||||
return wl_proxy_get_version((struct wl_proxy *) zwlr_data_control_source_v1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_data_control_source_v1
|
||||
*
|
||||
* This request adds a MIME type to the set of MIME types advertised to
|
||||
* targets. Can be called several times to offer multiple types.
|
||||
*
|
||||
* Calling this after wlr_data_control_device.set_selection is a protocol
|
||||
* error.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_data_control_source_v1_offer(struct zwlr_data_control_source_v1 *zwlr_data_control_source_v1, const char *mime_type)
|
||||
{
|
||||
wl_proxy_marshal_flags((struct wl_proxy *) zwlr_data_control_source_v1,
|
||||
ZWLR_DATA_CONTROL_SOURCE_V1_OFFER, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_data_control_source_v1), 0, mime_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_data_control_source_v1
|
||||
*
|
||||
* Destroys the data source object.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_data_control_source_v1_destroy(struct zwlr_data_control_source_v1 *zwlr_data_control_source_v1)
|
||||
{
|
||||
wl_proxy_marshal_flags((struct wl_proxy *) zwlr_data_control_source_v1,
|
||||
ZWLR_DATA_CONTROL_SOURCE_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_data_control_source_v1), WL_MARSHAL_FLAG_DESTROY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_data_control_offer_v1
|
||||
* @struct zwlr_data_control_offer_v1_listener
|
||||
*/
|
||||
struct zwlr_data_control_offer_v1_listener {
|
||||
/**
|
||||
* advertise offered MIME type
|
||||
*
|
||||
* Sent immediately after creating the wlr_data_control_offer
|
||||
* object. One event per offered MIME type.
|
||||
* @param mime_type offered MIME type
|
||||
*/
|
||||
void (*offer)(void *data,
|
||||
struct zwlr_data_control_offer_v1 *zwlr_data_control_offer_v1,
|
||||
const char *mime_type);
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_data_control_offer_v1
|
||||
*/
|
||||
static inline int
|
||||
zwlr_data_control_offer_v1_add_listener(struct zwlr_data_control_offer_v1 *zwlr_data_control_offer_v1,
|
||||
const struct zwlr_data_control_offer_v1_listener *listener, void *data)
|
||||
{
|
||||
return wl_proxy_add_listener((struct wl_proxy *) zwlr_data_control_offer_v1,
|
||||
(void (**)(void)) listener, data);
|
||||
}
|
||||
|
||||
#define ZWLR_DATA_CONTROL_OFFER_V1_RECEIVE 0
|
||||
#define ZWLR_DATA_CONTROL_OFFER_V1_DESTROY 1
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_data_control_offer_v1
|
||||
*/
|
||||
#define ZWLR_DATA_CONTROL_OFFER_V1_OFFER_SINCE_VERSION 1
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_data_control_offer_v1
|
||||
*/
|
||||
#define ZWLR_DATA_CONTROL_OFFER_V1_RECEIVE_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_data_control_offer_v1
|
||||
*/
|
||||
#define ZWLR_DATA_CONTROL_OFFER_V1_DESTROY_SINCE_VERSION 1
|
||||
|
||||
/** @ingroup iface_zwlr_data_control_offer_v1 */
|
||||
static inline void
|
||||
zwlr_data_control_offer_v1_set_user_data(struct zwlr_data_control_offer_v1 *zwlr_data_control_offer_v1, void *user_data)
|
||||
{
|
||||
wl_proxy_set_user_data((struct wl_proxy *) zwlr_data_control_offer_v1, user_data);
|
||||
}
|
||||
|
||||
/** @ingroup iface_zwlr_data_control_offer_v1 */
|
||||
static inline void *
|
||||
zwlr_data_control_offer_v1_get_user_data(struct zwlr_data_control_offer_v1 *zwlr_data_control_offer_v1)
|
||||
{
|
||||
return wl_proxy_get_user_data((struct wl_proxy *) zwlr_data_control_offer_v1);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
zwlr_data_control_offer_v1_get_version(struct zwlr_data_control_offer_v1 *zwlr_data_control_offer_v1)
|
||||
{
|
||||
return wl_proxy_get_version((struct wl_proxy *) zwlr_data_control_offer_v1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_data_control_offer_v1
|
||||
*
|
||||
* To transfer the offered data, the client issues this request and
|
||||
* indicates the MIME type it wants to receive. The transfer happens
|
||||
* through the passed file descriptor (typically created with the pipe
|
||||
* system call). The source client writes the data in the MIME type
|
||||
* representation requested and then closes the file descriptor.
|
||||
*
|
||||
* The receiving client reads from the read end of the pipe until EOF and
|
||||
* then closes its end, at which point the transfer is complete.
|
||||
*
|
||||
* This request may happen multiple times for different MIME types.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_data_control_offer_v1_receive(struct zwlr_data_control_offer_v1 *zwlr_data_control_offer_v1, const char *mime_type, int32_t fd)
|
||||
{
|
||||
wl_proxy_marshal_flags((struct wl_proxy *) zwlr_data_control_offer_v1,
|
||||
ZWLR_DATA_CONTROL_OFFER_V1_RECEIVE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_data_control_offer_v1), 0, mime_type, fd);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_data_control_offer_v1
|
||||
*
|
||||
* Destroys the data offer object.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_data_control_offer_v1_destroy(struct zwlr_data_control_offer_v1 *zwlr_data_control_offer_v1)
|
||||
{
|
||||
wl_proxy_marshal_flags((struct wl_proxy *) zwlr_data_control_offer_v1,
|
||||
ZWLR_DATA_CONTROL_OFFER_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_data_control_offer_v1), WL_MARSHAL_FLAG_DESTROY);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
123
wlr-data-control-protocol.c
Normal file
123
wlr-data-control-protocol.c
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
/* Generated by wayland-scanner 1.26.0 */
|
||||
|
||||
/*
|
||||
* Copyright © 2018 Simon Ser
|
||||
* Copyright © 2019 Ivan Molodetskikh
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this
|
||||
* software and its documentation for any purpose is hereby granted
|
||||
* without fee, provided that the above copyright notice appear in
|
||||
* all copies and that both that copyright notice and this permission
|
||||
* notice appear in supporting documentation, and that the name of
|
||||
* the copyright holders not be used in advertising or publicity
|
||||
* pertaining to distribution of the software without specific,
|
||||
* written prior permission. The copyright holders make no
|
||||
* representations about the suitability of this software for any
|
||||
* purpose. It is provided "as is" without express or implied
|
||||
* warranty.
|
||||
*
|
||||
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
||||
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* SPECIAL, 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.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include "wayland-util.h"
|
||||
|
||||
#ifndef __has_attribute
|
||||
# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */
|
||||
#endif
|
||||
|
||||
#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
|
||||
#define WL_PRIVATE __attribute__ ((visibility("hidden")))
|
||||
#else
|
||||
#define WL_PRIVATE
|
||||
#endif
|
||||
|
||||
extern const struct wl_interface wl_seat_interface;
|
||||
extern const struct wl_interface zwlr_data_control_device_v1_interface;
|
||||
extern const struct wl_interface zwlr_data_control_offer_v1_interface;
|
||||
extern const struct wl_interface zwlr_data_control_source_v1_interface;
|
||||
|
||||
static const struct wl_interface *wlr_data_control_unstable_v1_types[] = {
|
||||
NULL,
|
||||
NULL,
|
||||
&zwlr_data_control_source_v1_interface,
|
||||
&zwlr_data_control_device_v1_interface,
|
||||
&wl_seat_interface,
|
||||
&zwlr_data_control_source_v1_interface,
|
||||
&zwlr_data_control_source_v1_interface,
|
||||
&zwlr_data_control_offer_v1_interface,
|
||||
&zwlr_data_control_offer_v1_interface,
|
||||
&zwlr_data_control_offer_v1_interface,
|
||||
};
|
||||
|
||||
static const struct wl_message zwlr_data_control_manager_v1_requests[] = {
|
||||
{ "create_data_source", "n", wlr_data_control_unstable_v1_types + 2 },
|
||||
{ "get_data_device", "no", wlr_data_control_unstable_v1_types + 3 },
|
||||
{ "destroy", "", wlr_data_control_unstable_v1_types + 0 },
|
||||
};
|
||||
|
||||
WL_PRIVATE const struct wl_interface zwlr_data_control_manager_v1_interface = {
|
||||
"zwlr_data_control_manager_v1", 2,
|
||||
3, zwlr_data_control_manager_v1_requests,
|
||||
0, NULL,
|
||||
};
|
||||
|
||||
static const struct wl_message zwlr_data_control_device_v1_requests[] = {
|
||||
{ "set_selection", "?o", wlr_data_control_unstable_v1_types + 5 },
|
||||
{ "destroy", "", wlr_data_control_unstable_v1_types + 0 },
|
||||
{ "set_primary_selection", "2?o", wlr_data_control_unstable_v1_types + 6 },
|
||||
};
|
||||
|
||||
static const struct wl_message zwlr_data_control_device_v1_events[] = {
|
||||
{ "data_offer", "n", wlr_data_control_unstable_v1_types + 7 },
|
||||
{ "selection", "?o", wlr_data_control_unstable_v1_types + 8 },
|
||||
{ "finished", "", wlr_data_control_unstable_v1_types + 0 },
|
||||
{ "primary_selection", "2?o", wlr_data_control_unstable_v1_types + 9 },
|
||||
};
|
||||
|
||||
WL_PRIVATE const struct wl_interface zwlr_data_control_device_v1_interface = {
|
||||
"zwlr_data_control_device_v1", 2,
|
||||
3, zwlr_data_control_device_v1_requests,
|
||||
4, zwlr_data_control_device_v1_events,
|
||||
};
|
||||
|
||||
static const struct wl_message zwlr_data_control_source_v1_requests[] = {
|
||||
{ "offer", "s", wlr_data_control_unstable_v1_types + 0 },
|
||||
{ "destroy", "", wlr_data_control_unstable_v1_types + 0 },
|
||||
};
|
||||
|
||||
static const struct wl_message zwlr_data_control_source_v1_events[] = {
|
||||
{ "send", "sh", wlr_data_control_unstable_v1_types + 0 },
|
||||
{ "cancelled", "", wlr_data_control_unstable_v1_types + 0 },
|
||||
};
|
||||
|
||||
WL_PRIVATE const struct wl_interface zwlr_data_control_source_v1_interface = {
|
||||
"zwlr_data_control_source_v1", 1,
|
||||
2, zwlr_data_control_source_v1_requests,
|
||||
2, zwlr_data_control_source_v1_events,
|
||||
};
|
||||
|
||||
static const struct wl_message zwlr_data_control_offer_v1_requests[] = {
|
||||
{ "receive", "sh", wlr_data_control_unstable_v1_types + 0 },
|
||||
{ "destroy", "", wlr_data_control_unstable_v1_types + 0 },
|
||||
};
|
||||
|
||||
static const struct wl_message zwlr_data_control_offer_v1_events[] = {
|
||||
{ "offer", "s", wlr_data_control_unstable_v1_types + 0 },
|
||||
};
|
||||
|
||||
WL_PRIVATE const struct wl_interface zwlr_data_control_offer_v1_interface = {
|
||||
"zwlr_data_control_offer_v1", 1,
|
||||
2, zwlr_data_control_offer_v1_requests,
|
||||
1, zwlr_data_control_offer_v1_events,
|
||||
};
|
||||
|
||||
278
wlr-protocols/wlr-data-control-unstable-v1.xml
Normal file
278
wlr-protocols/wlr-data-control-unstable-v1.xml
Normal file
|
|
@ -0,0 +1,278 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<protocol name="wlr_data_control_unstable_v1">
|
||||
<copyright>
|
||||
Copyright © 2018 Simon Ser
|
||||
Copyright © 2019 Ivan Molodetskikh
|
||||
|
||||
Permission to use, copy, modify, distribute, and sell this
|
||||
software and its documentation for any purpose is hereby granted
|
||||
without fee, provided that the above copyright notice appear in
|
||||
all copies and that both that copyright notice and this permission
|
||||
notice appear in supporting documentation, and that the name of
|
||||
the copyright holders not be used in advertising or publicity
|
||||
pertaining to distribution of the software without specific,
|
||||
written prior permission. The copyright holders make no
|
||||
representations about the suitability of this software for any
|
||||
purpose. It is provided "as is" without express or implied
|
||||
warranty.
|
||||
|
||||
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
||||
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
SPECIAL, 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.
|
||||
</copyright>
|
||||
|
||||
<description summary="control data devices">
|
||||
This protocol allows a privileged client to control data devices. In
|
||||
particular, the client will be able to manage the current selection and take
|
||||
the role of a clipboard manager.
|
||||
|
||||
Warning! The protocol described in this file is experimental and
|
||||
backward incompatible changes may be made. Backward compatible changes
|
||||
may be added together with the corresponding interface version bump.
|
||||
Backward incompatible changes are done by bumping the version number in
|
||||
the protocol and interface names and resetting the interface version.
|
||||
Once the protocol is to be declared stable, the 'z' prefix and the
|
||||
version number in the protocol and interface names are removed and the
|
||||
interface version number is reset.
|
||||
</description>
|
||||
|
||||
<interface name="zwlr_data_control_manager_v1" version="2">
|
||||
<description summary="manager to control data devices">
|
||||
This interface is a manager that allows creating per-seat data device
|
||||
controls.
|
||||
</description>
|
||||
|
||||
<request name="create_data_source">
|
||||
<description summary="create a new data source">
|
||||
Create a new data source.
|
||||
</description>
|
||||
<arg name="id" type="new_id" interface="zwlr_data_control_source_v1"
|
||||
summary="data source to create"/>
|
||||
</request>
|
||||
|
||||
<request name="get_data_device">
|
||||
<description summary="get a data device for a seat">
|
||||
Create a data device that can be used to manage a seat's selection.
|
||||
</description>
|
||||
<arg name="id" type="new_id" interface="zwlr_data_control_device_v1"/>
|
||||
<arg name="seat" type="object" interface="wl_seat"/>
|
||||
</request>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="destroy the manager">
|
||||
All objects created by the manager will still remain valid, until their
|
||||
appropriate destroy request has been called.
|
||||
</description>
|
||||
</request>
|
||||
</interface>
|
||||
|
||||
<interface name="zwlr_data_control_device_v1" version="2">
|
||||
<description summary="manage a data device for a seat">
|
||||
This interface allows a client to manage a seat's selection.
|
||||
|
||||
When the seat is destroyed, this object becomes inert.
|
||||
</description>
|
||||
|
||||
<request name="set_selection">
|
||||
<description summary="copy data to the selection">
|
||||
This request asks the compositor to set the selection to the data from
|
||||
the source on behalf of the client.
|
||||
|
||||
The given source may not be used in any further set_selection or
|
||||
set_primary_selection requests. Attempting to use a previously used
|
||||
source is a protocol error.
|
||||
|
||||
To unset the selection, set the source to NULL.
|
||||
</description>
|
||||
<arg name="source" type="object" interface="zwlr_data_control_source_v1"
|
||||
allow-null="true"/>
|
||||
</request>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="destroy this data device">
|
||||
Destroys the data device object.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<event name="data_offer">
|
||||
<description summary="introduce a new wlr_data_control_offer">
|
||||
The data_offer event introduces a new wlr_data_control_offer object,
|
||||
which will subsequently be used in either the
|
||||
wlr_data_control_device.selection event (for the regular clipboard
|
||||
selections) or the wlr_data_control_device.primary_selection event (for
|
||||
the primary clipboard selections). Immediately following the
|
||||
wlr_data_control_device.data_offer event, the new data_offer object
|
||||
will send out wlr_data_control_offer.offer events to describe the MIME
|
||||
types it offers.
|
||||
</description>
|
||||
<arg name="id" type="new_id" interface="zwlr_data_control_offer_v1"/>
|
||||
</event>
|
||||
|
||||
<event name="selection">
|
||||
<description summary="advertise new selection">
|
||||
The selection event is sent out to notify the client of a new
|
||||
wlr_data_control_offer for the selection for this device. The
|
||||
wlr_data_control_device.data_offer and the wlr_data_control_offer.offer
|
||||
events are sent out immediately before this event to introduce the data
|
||||
offer object. The selection event is sent to a client when a new
|
||||
selection is set. The wlr_data_control_offer is valid until a new
|
||||
wlr_data_control_offer or NULL is received. The client must destroy the
|
||||
previous selection wlr_data_control_offer, if any, upon receiving this
|
||||
event.
|
||||
|
||||
The first selection event is sent upon binding the
|
||||
wlr_data_control_device object.
|
||||
</description>
|
||||
<arg name="id" type="object" interface="zwlr_data_control_offer_v1"
|
||||
allow-null="true"/>
|
||||
</event>
|
||||
|
||||
<event name="finished">
|
||||
<description summary="this data control is no longer valid">
|
||||
This data control object is no longer valid and should be destroyed by
|
||||
the client.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<!-- Version 2 additions -->
|
||||
|
||||
<event name="primary_selection" since="2">
|
||||
<description summary="advertise new primary selection">
|
||||
The primary_selection event is sent out to notify the client of a new
|
||||
wlr_data_control_offer for the primary selection for this device. The
|
||||
wlr_data_control_device.data_offer and the wlr_data_control_offer.offer
|
||||
events are sent out immediately before this event to introduce the data
|
||||
offer object. The primary_selection event is sent to a client when a
|
||||
new primary selection is set. The wlr_data_control_offer is valid until
|
||||
a new wlr_data_control_offer or NULL is received. The client must
|
||||
destroy the previous primary selection wlr_data_control_offer, if any,
|
||||
upon receiving this event.
|
||||
|
||||
If the compositor supports primary selection, the first
|
||||
primary_selection event is sent upon binding the
|
||||
wlr_data_control_device object.
|
||||
</description>
|
||||
<arg name="id" type="object" interface="zwlr_data_control_offer_v1"
|
||||
allow-null="true"/>
|
||||
</event>
|
||||
|
||||
<request name="set_primary_selection" since="2">
|
||||
<description summary="copy data to the primary selection">
|
||||
This request asks the compositor to set the primary selection to the
|
||||
data from the source on behalf of the client.
|
||||
|
||||
The given source may not be used in any further set_selection or
|
||||
set_primary_selection requests. Attempting to use a previously used
|
||||
source is a protocol error.
|
||||
|
||||
To unset the primary selection, set the source to NULL.
|
||||
|
||||
The compositor will ignore this request if it does not support primary
|
||||
selection.
|
||||
</description>
|
||||
<arg name="source" type="object" interface="zwlr_data_control_source_v1"
|
||||
allow-null="true"/>
|
||||
</request>
|
||||
|
||||
<enum name="error" since="2">
|
||||
<entry name="used_source" value="1"
|
||||
summary="source given to set_selection or set_primary_selection was already used before"/>
|
||||
</enum>
|
||||
</interface>
|
||||
|
||||
<interface name="zwlr_data_control_source_v1" version="1">
|
||||
<description summary="offer to transfer data">
|
||||
The wlr_data_control_source object is the source side of a
|
||||
wlr_data_control_offer. It is created by the source client in a data
|
||||
transfer and provides a way to describe the offered data and a way to
|
||||
respond to requests to transfer the data.
|
||||
</description>
|
||||
|
||||
<enum name="error">
|
||||
<entry name="invalid_offer" value="1"
|
||||
summary="offer sent after wlr_data_control_device.set_selection"/>
|
||||
</enum>
|
||||
|
||||
<request name="offer">
|
||||
<description summary="add an offered MIME type">
|
||||
This request adds a MIME type to the set of MIME types advertised to
|
||||
targets. Can be called several times to offer multiple types.
|
||||
|
||||
Calling this after wlr_data_control_device.set_selection is a protocol
|
||||
error.
|
||||
</description>
|
||||
<arg name="mime_type" type="string"
|
||||
summary="MIME type offered by the data source"/>
|
||||
</request>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="destroy this source">
|
||||
Destroys the data source object.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<event name="send">
|
||||
<description summary="send the data">
|
||||
Request for data from the client. Send the data as the specified MIME
|
||||
type over the passed file descriptor, then close it.
|
||||
</description>
|
||||
<arg name="mime_type" type="string" summary="MIME type for the data"/>
|
||||
<arg name="fd" type="fd" summary="file descriptor for the data"/>
|
||||
</event>
|
||||
|
||||
<event name="cancelled">
|
||||
<description summary="selection was cancelled">
|
||||
This data source is no longer valid. The data source has been replaced
|
||||
by another data source.
|
||||
|
||||
The client should clean up and destroy this data source.
|
||||
</description>
|
||||
</event>
|
||||
</interface>
|
||||
|
||||
<interface name="zwlr_data_control_offer_v1" version="1">
|
||||
<description summary="offer to transfer data">
|
||||
A wlr_data_control_offer represents a piece of data offered for transfer
|
||||
by another client (the source client). The offer describes the different
|
||||
MIME types that the data can be converted to and provides the mechanism
|
||||
for transferring the data directly from the source client.
|
||||
</description>
|
||||
|
||||
<request name="receive">
|
||||
<description summary="request that the data is transferred">
|
||||
To transfer the offered data, the client issues this request and
|
||||
indicates the MIME type it wants to receive. The transfer happens
|
||||
through the passed file descriptor (typically created with the pipe
|
||||
system call). The source client writes the data in the MIME type
|
||||
representation requested and then closes the file descriptor.
|
||||
|
||||
The receiving client reads from the read end of the pipe until EOF and
|
||||
then closes its end, at which point the transfer is complete.
|
||||
|
||||
This request may happen multiple times for different MIME types.
|
||||
</description>
|
||||
<arg name="mime_type" type="string"
|
||||
summary="MIME type desired by receiver"/>
|
||||
<arg name="fd" type="fd" summary="file descriptor for data transfer"/>
|
||||
</request>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="destroy this offer">
|
||||
Destroys the data offer object.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<event name="offer">
|
||||
<description summary="advertise offered MIME type">
|
||||
Sent immediately after creating the wlr_data_control_offer object.
|
||||
One event per offered MIME type.
|
||||
</description>
|
||||
<arg name="mime_type" type="string" summary="offered MIME type"/>
|
||||
</event>
|
||||
</interface>
|
||||
</protocol>
|
||||
Loading…
Add table
Add a link
Reference in a new issue