summaryrefslogtreecommitdiff
path: root/src/platform/wayland/drag.c
diff options
context:
space:
mode:
authorBerke Güzel <wenekar1@gmail.com>2026-10-13 13:44:13 +0300
committerBerke Güzel <wenekar1@gmail.com>2026-10-13 13:44:13 +0300
commitc059e3aa7f93da470f052a5cd8ee0d8dc969e679 (patch)
tree63ffa378287ed2c606eda2e940501b9edc3d2b8b /src/platform/wayland/drag.c
parent87d0bc50d53dc216efb2b04495f090616318bc5c (diff)
rename and simplification
Diffstat (limited to 'src/platform/wayland/drag.c')
-rw-r--r--src/platform/wayland/drag.c241
1 files changed, 241 insertions, 0 deletions
diff --git a/src/platform/wayland/drag.c b/src/platform/wayland/drag.c
new file mode 100644
index 0000000..989dbb6
--- /dev/null
+++ b/src/platform/wayland/drag.c
@@ -0,0 +1,241 @@
+#include "internal.h"
+
+static void icon_clear(struct drag_icon *icon)
+{
+ if (icon->viewport) wp_viewport_destroy(icon->viewport);
+ if (icon->surface) wl_surface_destroy(icon->surface);
+ if (icon->buffer) wl_buffer_destroy(icon->buffer);
+ if (icon->pool) wl_shm_pool_destroy(icon->pool);
+ if (icon->mapping) munmap(icon->mapping, icon->size);
+ if (icon->pool) close(icon->fd);
+ *icon = (struct drag_icon){0};
+}
+
+static bool icon_create(struct wayland_display *c, const struct yasl_drag_image *image)
+{
+ struct drag_icon *icon = &c->drag.icon;
+ size_t bytes;
+ if (!image->pixels || !buffer_size(image->size.x, image->size.y, &bytes))
+ return false;
+ int fd = memfd_create("yasl_drag_icon", MFD_CLOEXEC | MFD_ALLOW_SEALING);
+ if (fd < 0)
+ return false;
+ void *mapping = MAP_FAILED;
+ if (ftruncate(fd, (off_t)bytes) == 0 &&
+ fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK) == 0)
+ mapping = mmap(NULL, bytes, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (mapping == MAP_FAILED) {
+ close(fd);
+ return false;
+ }
+ memcpy(mapping, image->pixels, bytes);
+ icon->mapping = mapping;
+ icon->size = bytes;
+ icon->pool = wl_shm_create_pool(c->shm, fd, (int32_t)bytes);
+ if (!icon->pool) {
+ close(fd);
+ icon_clear(icon);
+ return false;
+ }
+ icon->fd = fd;
+ icon->buffer = wl_shm_pool_create_buffer(icon->pool, 0, image->size.x,
+ image->size.y, image->size.x * 4, WL_SHM_FORMAT_ARGB8888);
+ icon->surface = wl_compositor_create_surface(c->compositor);
+ if (!icon->buffer || !icon->surface) {
+ icon_clear(icon);
+ return false;
+ }
+ return true;
+}
+
+static void icon_show(struct wayland_display *c, const struct yasl_drag_image *image)
+{
+ struct drag_icon *icon = &c->drag.icon;
+ uint32_t scale = c->drag.scale_120;
+ if (c->viewporter) {
+ icon->viewport = wp_viewporter_get_viewport(c->viewporter, icon->surface);
+ if (icon->viewport)
+ wp_viewport_set_destination(icon->viewport,
+ yasl_scale_to_logical(image->size.x, scale),
+ yasl_scale_to_logical(image->size.y, scale));
+ } else if (scale % YASL_SCALE_UNIT == 0 &&
+ wl_proxy_get_version((struct wl_proxy *)icon->surface) >=
+ WL_SURFACE_SET_BUFFER_SCALE_SINCE_VERSION) {
+ wl_surface_set_buffer_scale(icon->surface, (int32_t)(scale / YASL_SCALE_UNIT));
+ }
+ if (wl_proxy_get_version((struct wl_proxy *)icon->surface) >=
+ WL_SURFACE_OFFSET_SINCE_VERSION)
+ wl_surface_offset(icon->surface,
+ -yasl_scale_to_logical(image->hotspot.x, scale),
+ -yasl_scale_to_logical(image->hotspot.y, scale));
+ wl_surface_attach(icon->surface, icon->buffer, 0, 0);
+ wl_surface_damage_buffer(icon->surface, 0, 0, image->size.x, image->size.y);
+ wl_surface_commit(icon->surface);
+}
+
+static void drag_clear(struct wayland_display *c, bool ended)
+{
+ struct conn_drag *d = &c->drag;
+ if (d->toplevel) {
+ if (ended)
+ xdg_toplevel_drag_v1_destroy(d->toplevel);
+ else
+ wl_proxy_destroy((struct wl_proxy *)d->toplevel);
+ }
+ if (d->source)
+ wl_data_source_destroy(d->source);
+ icon_clear(&d->icon);
+ *d = (struct conn_drag){0};
+}
+
+static void drag_finish(struct wayland_display *c, enum yasl_drag_end how, bool ended)
+{
+ struct yasl_drag_listener l = c->drag.listener;
+ void *ud = c->drag.ud;
+ drag_clear(c, ended);
+ if (l.end)
+ l.end(ud, how);
+}
+
+void conn_drag_end(struct wayland_display *c)
+{
+ if (c && c->drag.source)
+ drag_finish(c, YASL_DRAG_CANCELLED, false);
+}
+
+static void source_target(void *ud, struct wl_data_source *s, const char *mime) { (void)ud; (void)s; (void)mime; }
+static void source_send(void *ud, struct wl_data_source *s, const char *mime, int32_t fd) { (void)ud; (void)s; (void)mime; close(fd); }
+static void source_action(void *ud, struct wl_data_source *s, uint32_t action) { (void)ud; (void)s; (void)action; }
+
+static void source_cancelled(void *ud, struct wl_data_source *s)
+{
+ (void)s;
+ struct wayland_display *c = ud;
+ bool released = c->drag.dropped;
+ drag_finish(c, released ? YASL_DRAG_DROPPED : YASL_DRAG_CANCELLED, true);
+}
+
+static void source_dropped(void *ud, struct wl_data_source *s)
+{
+ (void)s;
+ ((struct wayland_display *)ud)->drag.dropped = true;
+}
+
+static void source_finished(void *ud, struct wl_data_source *s)
+{
+ (void)s;
+ drag_finish(ud, YASL_DRAG_DROPPED, true);
+}
+
+static const struct wl_data_source_listener source_listener = {
+ .target = source_target,
+ .send = source_send,
+ .cancelled = source_cancelled,
+ .dnd_drop_performed = source_dropped,
+ .dnd_finished = source_finished,
+ .action = source_action,
+};
+
+static struct seat *drag_seat(struct wayland_display *c, struct wayland_surface *origin)
+{
+ struct wayland_surface *root = surface_root(origin);
+ struct seat *active = conn_active_seat(c);
+ if (active && active->press_serial &&
+ surface_root(active->press_surface) == root)
+ return active;
+ for (struct seat *s = c->seats; s; s = s->next)
+ if (s->press_serial && surface_root(s->press_surface) == root)
+ return s;
+ return NULL;
+}
+
+bool wayland_display_drag_start(struct wayland_display *c, struct wayland_surface *origin,
+ const struct yasl_drag_image *image,
+ const struct yasl_drag_listener *l, void *ud)
+{
+ if (!c || !origin || origin->conn != c || !l || c->drag.source)
+ return false;
+ struct seat *seat = drag_seat(c, origin);
+ if (!seat)
+ return false;
+ struct wl_data_device *device = clipboard_data_device(seat->clipboard);
+ if (!device || wl_data_device_get_version(device) < 3)
+ return false;
+ c->drag.scale_120 = origin->scale_120;
+ if (image && !icon_create(c, image))
+ return false;
+ struct wl_data_source *s = wl_data_device_manager_create_data_source(c->data_manager);
+ if (!s) {
+ icon_clear(&c->drag.icon);
+ return false;
+ }
+ c->drag.source = s;
+ c->drag.seat = seat;
+ c->drag.listener = *l;
+ c->drag.ud = ud;
+ wl_data_source_add_listener(s, &source_listener, c);
+ wl_data_source_offer(s, clipboard_drag_mime());
+ wl_data_source_set_actions(s, WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE);
+ if (c->toplevel_drag_mgr)
+ c->drag.toplevel = xdg_toplevel_drag_manager_v1_get_xdg_toplevel_drag(
+ c->toplevel_drag_mgr, s);
+ wl_data_device_start_drag(device, s, seat->press_surface->surface,
+ c->drag.icon.surface, seat->press_serial);
+ if (image)
+ icon_show(c, image);
+ wl_display_flush(c->wl);
+ return true;
+}
+
+void wayland_display_drag_cancel(struct wayland_display *c)
+{
+ if (c && c->drag.source)
+ drag_finish(c, YASL_DRAG_CANCELLED, false);
+}
+
+bool wayland_surface_drag_carry(struct wayland_surface *w, struct yasl_vector2i offset)
+{
+ struct wayland_display *c = w ? w->conn : NULL;
+ if (!c || !c->drag.source || !c->drag.toplevel || !w->toplevel)
+ return false;
+ uint32_t scale = c->drag.scale_120;
+ xdg_toplevel_drag_v1_attach(c->drag.toplevel, w->toplevel,
+ yasl_scale_to_logical(offset.x, scale),
+ yasl_scale_to_logical(offset.y, scale));
+ c->drag.carrying = true;
+ return true;
+}
+
+bool wayland_surface_place_under_pointer(struct wayland_surface *w, struct yasl_vector2i at)
+{
+ (void)w;
+ (void)at;
+ return false;
+}
+
+static struct yasl_vector2i drag_device_pos(const struct wayland_surface *w, double x, double y)
+{
+ double dx = x * w->scale_factor, dy = y * w->scale_factor;
+ return (struct yasl_vector2i){
+ .x = (int)(dx < 0 ? dx - 0.5 : dx + 0.5),
+ .y = (int)(dy < 0 ? dy - 0.5 : dy + 0.5),
+ };
+}
+
+void conn_drop_target(struct wayland_display *c, enum yasl_drop_event ev,
+ struct wl_surface *on, double x, double y)
+{
+ struct wayland_surface *w = conn_surface_for(c, on);
+ if (!w || w->parent || !w->cbs->drop)
+ return;
+ w->cbs->drop(&w->base, ev, drag_device_pos(w, x, y), w->ud);
+}
+
+void conn_drag_target(struct wayland_display *c, enum yasl_drag_event ev,
+ struct wl_surface *on, double x, double y)
+{
+ struct wayland_surface *w = conn_surface_for(c, on);
+ if (!w || w->parent || !w->cbs->drag)
+ return;
+ w->cbs->drag(&w->base, ev, drag_device_pos(w, x, y), w->ud);
+}