summaryrefslogtreecommitdiff
path: root/src/platform/wayland/clipboard.c
diff options
context:
space:
mode:
authorBerke Güzel <wenekar1@gmail.com>2026-10-13 21:23:03 +0300
committerBerke Güzel <wenekar1@gmail.com>2026-10-13 21:23:03 +0300
commitdd1775a9927922c14f8679ccbc4d3ca8d5917e20 (patch)
tree3acc442bb4a3470364b74aa82ff67cb84d555c45 /src/platform/wayland/clipboard.c
parentecb81aeea4c0c880b3a60a0dc8418c4bf2a7cf85 (diff)
public api refactor
Diffstat (limited to 'src/platform/wayland/clipboard.c')
-rw-r--r--src/platform/wayland/clipboard.c152
1 files changed, 120 insertions, 32 deletions
diff --git a/src/platform/wayland/clipboard.c b/src/platform/wayland/clipboard.c
index 353e993..8ca9b93 100644
--- a/src/platform/wayland/clipboard.c
+++ b/src/platform/wayland/clipboard.c
@@ -5,6 +5,7 @@
#include <errno.h>
#define CLIPBOARD_TEXT_MAX (256u * 1024)
+#define CLIPBOARD_TRANSFER_TIMEOUT_NS (30ull * 1000000000ull)
struct offer {
struct offer *next;
@@ -24,10 +25,14 @@ struct transfer {
struct offer *drop;
struct wl_surface *drop_surface;
int fd, id;
+ yasl_timer_id timeout;
char *text;
size_t len, offset, capacity;
+ uint64_t request_id;
bool sending;
bool uri_list;
+ bool paste;
+ bool cancelled;
};
struct clipboard {
struct wl_display *display;
@@ -82,20 +87,48 @@ static void drop_event(struct clipboard *c, enum yasl_drop_event ev)
static void free_offer(struct offer *o);
+static void transfer_end(struct transfer *t, bool complete);
+
+static uint64_t transfer_timeout(void *ud, yasl_timer_id id, uint64_t interval)
+{
+ (void)interval;
+ struct transfer *t = ud;
+ if (t->timeout != id)
+ return 0;
+ t->timeout = 0;
+ transfer_end(t, false);
+ return 0;
+}
+
static void transfer_end(struct transfer *t, bool complete)
{
- struct transfer **p = &t->owner->transfers;
+ struct clipboard *owner = t->owner;
+ struct transfer **p = &owner->transfers;
while (*p && *p != t) p = &(*p)->next;
if (*p) *p = t->next;
- yasl_loop_del_fd(t->owner->loop, t->id);
+ yasl_loop_del_fd(owner->loop, t->id);
+ if (t->timeout)
+ yasl_loop_del_timer(owner->loop, t->timeout);
close(t->fd);
if (t->drop) {
if (complete && wl_data_offer_get_version(t->drop->proxy) >=
WL_DATA_OFFER_FINISH_SINCE_VERSION)
wl_data_offer_finish(t->drop->proxy);
free_offer(t->drop);
- wl_display_flush(t->owner->display);
+ wl_display_flush(owner->display);
}
+ bool notify = owner->receive && !t->cancelled &&
+ ((!t->sending && complete) || (t->paste && !complete));
+ clipboard_receive_fn receive = owner->receive;
+ void *receive_ud = owner->ud;
+ struct wl_surface *surface = t->drop_surface;
+ uint64_t request_id = t->request_id;
+ bool uri_list = t->uri_list;
+ enum yasl_transfer_status status = complete ? YASL_TRANSFER_OK : YASL_TRANSFER_FAILED;
+ const char *text = complete ? (t->text ? t->text : "") : NULL;
+ size_t len = complete ? t->len : 0;
+ if (notify)
+ receive(text, len, uri_list, surface, request_id, status, receive_ud);
free(t->text);
free(t);
}
@@ -124,9 +157,6 @@ static int transfer_io(int fd, short events, void *ud)
t->len += (size_t)n;
} else if (n == 0) {
bool valid = yasl_utf8_validate(t->text, t->len);
- if (valid)
- t->owner->receive(t->text ? t->text : "", t->len, t->uri_list,
- t->drop_surface, t->owner->ud);
transfer_end(t, valid);
} else if (errno != EAGAIN && errno != EINTR) transfer_end(t, false);
}
@@ -148,10 +178,31 @@ static struct transfer *start_transfer_text(struct clipboard *c, int fd, bool se
}
t->id = yasl_loop_add_fd(c->loop, fd, sending ? POLLOUT : POLLIN, transfer_io, t);
if (t->id < 0) { close(fd); free(t->text); free(t); return NULL; }
+ t->timeout = yasl_loop_add_timer(c->loop, CLIPBOARD_TRANSFER_TIMEOUT_NS,
+ transfer_timeout, t);
+ if (!t->timeout) {
+ yasl_loop_del_fd(c->loop, t->id);
+ close(fd);
+ free(t->text);
+ free(t);
+ return NULL;
+ }
t->next = c->transfers; c->transfers = t;
return t;
}
+static struct transfer *start_transfer_paste(struct clipboard *c, int fd,
+ struct wl_surface *target, uint64_t request_id)
+{
+ struct transfer *t = start_transfer_text(c, fd, false, false, NULL);
+ if (t) {
+ t->drop_surface = target;
+ t->request_id = request_id;
+ t->paste = true;
+ }
+ return t;
+}
+
static const char *match_text_mime(const char *mime)
{
if (unlikely(!mime)) return NULL;
@@ -528,15 +579,17 @@ bool clipboard_set_seat(struct clipboard *c, struct wl_seat *seat)
return true;
}
-void clipboard_copy(struct clipboard *c, const char *text, uint32_t serial)
+bool clipboard_copy(struct clipboard *c, const char *text, uint32_t serial)
{
- if (!c || !c->device || !text || !serial) return;
+ if (!c || !c->device) { errno = ENOTSUP; return false; }
+ if (!text) { errno = EINVAL; return false; }
+ if (!serial) { errno = EAGAIN; return false; }
char *copy = strdup(text);
- if (!copy) return;
+ if (!copy) { errno = ENOMEM; return false; }
if (c->source) wl_data_source_destroy(c->source);
free(c->text); c->text = copy;
c->source = wl_data_device_manager_create_data_source(c->manager);
- if (!c->source) return;
+ if (!c->source) { errno = ENOMEM; return false; }
wl_data_source_add_listener(c->source, &source_listener, c);
wl_data_source_offer(c->source, "text/plain;charset=utf-8");
wl_data_source_offer(c->source, "text/plain");
@@ -544,30 +597,26 @@ void clipboard_copy(struct clipboard *c, const char *text, uint32_t serial)
wl_data_source_offer(c->source, "STRING");
wl_data_source_offer(c->source, "TEXT");
wl_data_device_set_selection(c->device, c->source, serial);
+ return true;
}
void clipboard_paste(struct clipboard *c, struct wl_surface *target)
{
- if (!c || !c->device || !c->selection || !c->selection->mime) return;
- int fds[2];
- if (pipe2(fds, O_CLOEXEC) < 0) return;
- wl_data_offer_receive(c->selection->proxy, c->selection->mime, fds[1]);
- close(fds[1]);
- struct transfer *t = start_transfer_text(c, fds[0], false, false, c->text);
- if (t) t->drop_surface = target;
- wl_display_flush(c->display);
+ if (c && target)
+ (void)clipboard_request_paste(c, YASL_CLIPBOARD, target, 0);
}
-void clipboard_copy_primary(struct clipboard *c, const char *text, uint32_t serial)
+bool clipboard_copy_primary(struct clipboard *c, const char *text, uint32_t serial)
{
- if (!c || !text || !serial) return;
- if (!c->primary_device || !c->primary_manager) return;
+ if (!c || !c->primary_device || !c->primary_manager) { errno = ENOTSUP; return false; }
+ if (!text) { errno = EINVAL; return false; }
+ if (!serial) { errno = EAGAIN; return false; }
char *copy = strdup(text);
- if (!copy) return;
+ if (!copy) { errno = ENOMEM; return false; }
if (c->primary_source) zwp_primary_selection_source_v1_destroy(c->primary_source);
free(c->primary_text); c->primary_text = copy;
c->primary_source = zwp_primary_selection_device_manager_v1_create_source(c->primary_manager);
- if (!c->primary_source) return;
+ if (!c->primary_source) { errno = ENOMEM; return false; }
zwp_primary_selection_source_v1_add_listener(c->primary_source, &primary_source_listener, c);
zwp_primary_selection_source_v1_offer(c->primary_source, "text/plain;charset=utf-8");
zwp_primary_selection_source_v1_offer(c->primary_source, "text/plain");
@@ -575,22 +624,58 @@ void clipboard_copy_primary(struct clipboard *c, const char *text, uint32_t seri
zwp_primary_selection_source_v1_offer(c->primary_source, "STRING");
zwp_primary_selection_source_v1_offer(c->primary_source, "TEXT");
zwp_primary_selection_device_v1_set_selection(c->primary_device, c->primary_source, serial);
+ return true;
}
void clipboard_paste_primary(struct clipboard *c, struct wl_surface *target)
{
- if (!c) return;
- if (c->primary_device && c->primary_selection && c->primary_selection->mime) {
- int fds[2];
- if (pipe2(fds, O_CLOEXEC) < 0) return;
+ if (c && target)
+ (void)clipboard_request_paste(c, YASL_PRIMARY, target, 0);
+}
+
+bool clipboard_request_paste(struct clipboard *c, enum yasl_selection selection,
+ struct wl_surface *target, uint64_t request_id)
+{
+ if (!c || !target || !c->receive)
+ return false;
+ int fds[2];
+ if (selection == YASL_PRIMARY) {
+ if (!c->primary_device || !c->primary_selection || !c->primary_selection->mime) {
+ c->receive(NULL, 0, false, target, request_id,
+ YASL_TRANSFER_UNAVAILABLE, c->ud);
+ return true;
+ }
+ if (pipe2(fds, O_CLOEXEC) < 0) {
+ c->receive(NULL, 0, false, target, request_id,
+ YASL_TRANSFER_FAILED, c->ud);
+ return true;
+ }
zwp_primary_selection_offer_v1_receive(c->primary_selection->proxy,
c->primary_selection->mime, fds[1]);
close(fds[1]);
- struct transfer *t = start_transfer_text(c, fds[0], false, false, c->text);
- if (t) t->drop_surface = target;
- wl_display_flush(c->display);
- return;
+ } else {
+ if (!c->device || !c->selection || !c->selection->mime) {
+ c->receive(NULL, 0, false, target, request_id,
+ YASL_TRANSFER_UNAVAILABLE, c->ud);
+ return true;
+ }
+ if (pipe2(fds, O_CLOEXEC) < 0) {
+ c->receive(NULL, 0, false, target, request_id,
+ YASL_TRANSFER_FAILED, c->ud);
+ return true;
+ }
+ wl_data_offer_receive(c->selection->proxy, c->selection->mime, fds[1]);
+ close(fds[1]);
+ }
+ struct transfer *transfer = start_transfer_paste(c, fds[0], target, request_id);
+ if (!transfer) {
+ c->receive(NULL, 0, false, target, request_id,
+ YASL_TRANSFER_FAILED, c->ud);
+ return true;
}
+ if (wl_display_flush(c->display) < 0 && errno != EAGAIN)
+ transfer_end(transfer, false);
+ return true;
}
void clipboard_forget_surface(struct clipboard *c, struct wl_surface *surface)
@@ -598,7 +683,10 @@ void clipboard_forget_surface(struct clipboard *c, struct wl_surface *surface)
if (!c || !surface) return;
for (struct transfer *t = c->transfers, *next; t; t = next) {
next = t->next;
- if (t->drop_surface == surface) transfer_end(t, false);
+ if (t->drop_surface == surface) {
+ t->cancelled = true;
+ transfer_end(t, false);
+ }
}
if (c->drag_surface == surface) c->drag_surface = NULL;
}