From 927ae621c9a3d46dbc401b8e9ea7bbed1bf2e97b Mon Sep 17 00:00:00 2001 From: Berke Güzel Date: Sun, 11 Oct 2026 21:53:42 +0300 Subject: initial: Yet Another Surface Library (YASL) 0.1.0 A minimal, modern desktop window surface library in C. Supports Wayland on Linux and Cocoa/Metal on macOS with presentation timing, fractional scaling, IME, and CSD. --- src/wayland/drag.c | 241 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 241 insertions(+) create mode 100644 src/wayland/drag.c (limited to 'src/wayland/drag.c') diff --git a/src/wayland/drag.c b/src/wayland/drag.c new file mode 100644 index 0000000..107ec52 --- /dev/null +++ b/src/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 yasl_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 yasl_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 yasl_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 yasl_display *c, enum yasl_drag_end how, bool ended) +{ + const struct yasl_drag_listener *l = c->drag.listener; + void *ud = c->drag.ud; + drag_clear(c, ended); + if (l && l->end) + l->end(ud, how); +} + +void conn_drag_end(struct yasl_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 yasl_display *c = ud; + bool released = c->drag.dropped || !c->drag.carrying; + 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 yasl_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 yasl_display *c, struct yasl_surface *origin) +{ + struct yasl_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 yasl_display_drag_start(struct yasl_display *c, struct yasl_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 yasl_display_drag_cancel(struct yasl_display *c) +{ + if (c && c->drag.source) + drag_clear(c, false); +} + +bool yasl_surface_drag_carry(struct yasl_surface *w, struct yasl_vector2i offset) +{ + struct yasl_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 yasl_surface_place_under_pointer(struct yasl_surface *w, struct yasl_vector2i at) +{ + (void)w; + (void)at; + return false; +} + +static struct yasl_vector2i drag_device_pos(const struct yasl_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 yasl_display *c, enum yasl_drop_event ev, + struct wl_surface *on, double x, double y) +{ + struct yasl_surface *w = conn_surface_for(c, on); + if (!w || w->parent || !w->cbs->drop) + return; + w->cbs->drop(w, ev, drag_device_pos(w, x, y), w->ud); +} + +void conn_drag_target(struct yasl_display *c, enum yasl_drag_event ev, + struct wl_surface *on, double x, double y) +{ + struct yasl_surface *w = conn_surface_for(c, on); + if (!w || w->parent || !w->cbs->drag) + return; + w->cbs->drag(w, ev, drag_device_pos(w, x, y), w->ud); +} -- cgit v1.2.3