diff options
| author | Berke Güzel <wenekar1@gmail.com> | 2026-10-13 21:23:03 +0300 |
|---|---|---|
| committer | Berke Güzel <wenekar1@gmail.com> | 2026-10-13 21:23:03 +0300 |
| commit | dd1775a9927922c14f8679ccbc4d3ca8d5917e20 (patch) | |
| tree | 3acc442bb4a3470364b74aa82ff67cb84d555c45 /src/platform/wayland/clipboard.c | |
| parent | ecb81aeea4c0c880b3a60a0dc8418c4bf2a7cf85 (diff) | |
public api refactor
Diffstat (limited to 'src/platform/wayland/clipboard.c')
| -rw-r--r-- | src/platform/wayland/clipboard.c | 152 |
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; } |
