From 5206933399775e5ff46baefbbb3a776c2a9bb84c Mon Sep 17 00:00:00 2001 From: Berke Güzel Date: Mon, 12 Oct 2026 10:53:22 +0300 Subject: feat: add type-safe native platform headers and portable drag support - Add include/yasl/native/{wayland,cocoa,x11}.h with type-safe handle accessors. - Unify drag-and-drop and window carry support across Wayland, X11, and Cocoa. - Preserve full ABI compatibility for capability flags. - Install native headers conditionally based on enabled backend. - Include missing tablet example and backend test suites. --- src/cocoa/window.m | 84 +++++++++++++++++++++++++++++++++++++++++++++++------ src/common/native.c | 36 +++++++++++++++++++++++ src/wayland/api.h | 1 + src/x11/window.c | 72 ++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 183 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/cocoa/window.m b/src/cocoa/window.m index 6235b77..7b59865 100644 --- a/src/cocoa/window.m +++ b/src/cocoa/window.m @@ -3,6 +3,7 @@ #import #include "cocoa/api.h" +#include "yasl/native/cocoa.h" #include "common/timing.h" #include "common/loop.h" #include "cocoa/keycodes.h" @@ -112,6 +113,14 @@ struct cocoa_display { YaslAppDelegate *delegate; NSPoint cascade; struct cocoa_surface *dead; + struct { + bool active; + struct cocoa_surface *origin; + struct cocoa_surface *carried; + struct yasl_vector2i carry_offset; + struct yasl_drag_listener listener; + void *ud; + } drag; int depth; bool open; bool launched; @@ -121,6 +130,18 @@ struct cocoa_display { static struct cocoa_display darwin_ctx; +static void drag_end(struct cocoa_display *d, enum yasl_drag_end how) +{ + if (!d || !d->drag.active) return; + struct yasl_drag_listener l = d->drag.listener; + void *ud = d->drag.ud; + d->drag.active = false; + d->drag.origin = NULL; + d->drag.carried = NULL; + if (l.end) + l.end(ud, how); +} + static void render_and_commit(struct cocoa_surface *w); static void reap_dead(struct cocoa_display *d); @@ -341,6 +362,11 @@ static void push_pointer(struct cocoa_surface *w, NSEvent *ev, enum yasl_pointer }, }; input_queue_push(&w->input, &input_ev); + if (kind == YASL_PTR_MOTION && darwin_ctx.drag.active && darwin_ctx.drag.carried) { + cocoa_surface_place_under_pointer(darwin_ctx.drag.carried, darwin_ctx.drag.carry_offset); + } else if (kind == YASL_PTR_RELEASE && darwin_ctx.drag.active) { + drag_end(&darwin_ctx, YASL_DRAG_DROPPED); + } if (proximity) { w->tablet_device = YASL_PTR_DEV_MOUSE; w->tablet_down = false; @@ -1079,6 +1105,7 @@ struct cocoa_display *cocoa_display_open(struct yasl_loop *loop, const struct ya void cocoa_display_close(struct cocoa_display *d) { if (!d) return; + drag_end(d, YASL_DRAG_CANCELLED); assert(!d->count && "destroy every surface before its display"); reap_dead(d); if (d->key_up_monitor) { @@ -1101,7 +1128,8 @@ void cocoa_display_close(struct cocoa_display *d) uint32_t cocoa_display_caps(const struct cocoa_display *d) { - return d ? (YASL_CAP_PLACE_WINDOW | YASL_CAP_CSD | YASL_CAP_NATIVE_GRAPHICS | + return d ? (YASL_CAP_DRAG | YASL_CAP_DRAG_CARRY | YASL_CAP_PLACE_WINDOW | + YASL_CAP_CSD | YASL_CAP_NATIVE_GRAPHICS | YASL_CAP_FRAME_PACING | YASL_CAP_TABLET) : 0; } @@ -1220,6 +1248,8 @@ void cocoa_surface_destroy(struct cocoa_surface *w) { assert([NSThread isMainThread]); if (!w || w->destroyed) return; + if (darwin_ctx.drag.active && (darwin_ctx.drag.origin == w || darwin_ctx.drag.carried == w)) + drag_end(&darwin_ctx, YASL_DRAG_CANCELLED); darwin_ctx.depth++; bool native_closed = w->closed; static const struct yasl_surface_cbs no_callbacks = {0}; @@ -1374,22 +1404,40 @@ bool cocoa_surface_popup_reposition(struct cocoa_surface *popup, return false; } +bool cocoa_surface_start_interactive_move(struct cocoa_surface *s) +{ + if (!s || !s->window) return false; + [s->window performWindowDragWithEvent:[NSApp currentEvent]]; + return true; +} + bool cocoa_display_drag_start(struct cocoa_display *d, struct cocoa_surface *origin, const struct yasl_drag_image *image, const struct yasl_drag_listener *l, void *ud) { - (void)d; (void)origin; (void)image; (void)l; (void)ud; - return false; + (void)image; + if (!d || !origin || d->drag.active || !l) + return false; + d->drag.active = true; + d->drag.origin = origin; + d->drag.carried = NULL; + d->drag.listener = *l; + d->drag.ud = ud; + return true; } -void cocoa_display_drag_cancel(struct cocoa_display *d) { (void)d; } -bool cocoa_surface_drag_carry(struct cocoa_surface *w, struct yasl_vector2i offset) { (void)w; (void)offset; return false; } +void cocoa_display_drag_cancel(struct cocoa_display *d) +{ + drag_end(d, YASL_DRAG_CANCELLED); +} -bool cocoa_surface_start_interactive_move(struct cocoa_surface *s) +bool cocoa_surface_drag_carry(struct cocoa_surface *w, struct yasl_vector2i offset) { - if (!s || !s->window) return false; - [s->window performWindowDragWithEvent:[NSApp currentEvent]]; - return true; + if (!w || w->closed || !darwin_ctx.drag.active) + return false; + darwin_ctx.drag.carried = w; + darwin_ctx.drag.carry_offset = offset; + return cocoa_surface_place_under_pointer(w, offset); } bool cocoa_surface_start_interactive_resize(struct cocoa_surface *s, enum yasl_resize_edge edge) @@ -1691,3 +1739,21 @@ const struct yasl_backend_ops yasl_cocoa_backend = { .surface_get_native_handle = impl_surface_get_native_handle, .surface_get_native_frame = impl_surface_get_native_frame, }; + +NSApplication *yasl_cocoa_display_get_application(struct yasl_display *d) +{ + return (d && yasl_display_backend(d) == YASL_BACKEND_COCOA) ? + (NSApplication *)yasl_display_get_native_handle(d) : nil; +} + +NSWindow *yasl_cocoa_surface_get_window(struct yasl_surface *s) +{ + return (s && s->display && yasl_display_backend(s->display) == YASL_BACKEND_COCOA) ? + (NSWindow *)yasl_surface_get_native_handle(s) : nil; +} + +NSView *yasl_cocoa_surface_get_view(struct yasl_surface *s) +{ + NSWindow *w = yasl_cocoa_surface_get_window(s); + return w ? w.contentView : nil; +} diff --git a/src/common/native.c b/src/common/native.c index 2d9c06f..6e1c69a 100644 --- a/src/common/native.c +++ b/src/common/native.c @@ -1,5 +1,13 @@ #include "backend.h" +#if YASL_HAS_WAYLAND +#include "yasl/native/wayland.h" +#endif + +#if YASL_HAS_X11 +#include "yasl/native/x11.h" +#endif + #include void *yasl_display_get_native_handle(struct yasl_display *d) @@ -14,6 +22,34 @@ void *yasl_surface_get_native_handle(struct yasl_surface *s) s->display->ops->surface_get_native_handle(s) : NULL; } +#if YASL_HAS_WAYLAND +struct wl_display *yasl_wayland_display_get_wl_display(struct yasl_display *d) +{ + return (d && yasl_display_backend(d) == YASL_BACKEND_WAYLAND) ? + (struct wl_display *)yasl_display_get_native_handle(d) : NULL; +} + +struct wl_surface *yasl_wayland_surface_get_wl_surface(struct yasl_surface *s) +{ + return (s && s->display && yasl_display_backend(s->display) == YASL_BACKEND_WAYLAND) ? + (struct wl_surface *)yasl_surface_get_native_handle(s) : NULL; +} +#endif + +#if YASL_HAS_X11 +Display *yasl_x11_display_get_display(struct yasl_display *d) +{ + return (d && yasl_display_backend(d) == YASL_BACKEND_X11) ? + (Display *)yasl_display_get_native_handle(d) : NULL; +} + +uintptr_t yasl_x11_surface_get_window(struct yasl_surface *s) +{ + return (s && s->display && yasl_display_backend(s->display) == YASL_BACKEND_X11) ? + (uintptr_t)yasl_surface_get_native_handle(s) : 0; +} +#endif + struct yasl_surface *yasl_surface_create_native(struct yasl_display *d, const struct yasl_surface_spec *spec, void *graphics_device) { diff --git a/src/wayland/api.h b/src/wayland/api.h index 87e896d..e68aca7 100644 --- a/src/wayland/api.h +++ b/src/wayland/api.h @@ -2,6 +2,7 @@ #define YASL_WAYLAND_API_H #include "common/backend.h" +#include "yasl/native/wayland.h" struct wayland_display; struct wayland_surface; diff --git a/src/x11/window.c b/src/x11/window.c index 85f1d22..5e3dba9 100644 --- a/src/x11/window.c +++ b/src/x11/window.c @@ -89,6 +89,14 @@ struct x11_display { bool xi2; Atom abs_pressure, abs_tilt_x, abs_tilt_y, tool_type, eraser; struct x11_pointer *pointers; + struct { + bool active; + struct x11_surface *origin; + struct x11_surface *carried; + struct yasl_vector2i carry_offset; + struct yasl_drag_listener listener; + void *ud; + } drag; }; static struct x11_display *display_of(const struct yasl_surface *s) @@ -96,6 +104,10 @@ static struct x11_display *display_of(const struct yasl_surface *s) return (struct x11_display *)s->display; } +static bool place(struct yasl_surface *base, struct yasl_vector2i at); +static void drag_end(struct x11_display *d, enum yasl_drag_end how); +static void drag_cancel(struct yasl_display *base); + static void leave(struct x11_display *d) { if (--d->depth) return; @@ -284,6 +296,10 @@ static void xi_event(struct x11_display *d, XGenericEventCookie *cookie) cookie->evtype != XI_ButtonRelease) return; Window window = crossing ? ((XIEnterEvent *)cookie->data)->event : ((XIDeviceEvent *)cookie->data)->event; + if (cookie->evtype == XI_Motion && d->drag.active && d->drag.carried) + place(&d->drag.carried->base, d->drag.carry_offset); + else if (cookie->evtype == XI_ButtonRelease && d->drag.active) + drag_end(d, YASL_DRAG_DROPPED); struct x11_surface *s = d->surfaces; while (s && s->window != window) s = s->next; if (!s) return; @@ -357,6 +373,8 @@ static void surface_destroy(struct yasl_surface *base) struct x11_surface *s = (struct x11_surface *)base; if (s->destroyed) return; struct x11_display *d = display_of(base); + if (d->drag.active && (d->drag.origin == s || d->drag.carried == s)) + drag_end(d, YASL_DRAG_CANCELLED); s->destroyed = true; for (struct x11_surface **p = &d->surfaces; *p; p = &(*p)->next) { if (*p == s) { *p = s->next; break; } @@ -648,6 +666,8 @@ static void dispatch(struct yasl_display *base) mouse.pos = (struct yasl_vector2i){event.xmotion.x, event.xmotion.y}; mouse.kind = YASL_PTR_MOTION; state = event.xmotion.state; + if (d->drag.active && d->drag.carried) + place(&d->drag.carried->base, d->drag.carry_offset); } else if (event.type == LeaveNotify) { mouse.pos = (struct yasl_vector2i){event.xcrossing.x, event.xcrossing.y}; mouse.kind = YASL_PTR_LEAVE; @@ -663,6 +683,8 @@ static void dispatch(struct yasl_display *base) } else { mouse.kind = event.type == ButtonPress ? YASL_PTR_PRESS : YASL_PTR_RELEASE; mouse.button = pointer_button(button); + if (event.type == ButtonRelease && d->drag.active) + drag_end(d, YASL_DRAG_DROPPED); } } d->mods = mouse.mods = modifiers(state); @@ -849,6 +871,7 @@ static void display_close(struct yasl_display *base) { struct x11_display *d = (struct x11_display *)base; assert(!d->surfaces && "destroy every surface before its display"); + drag_cancel(base); cancel_pastes(d, NULL); if (d->loop && d->watch > 0) yasl_loop_del_fd(d->loop, d->watch); if (d->owns_loop && d->loop) { @@ -868,7 +891,8 @@ static void display_close(struct yasl_display *base) static uint32_t caps(const struct yasl_display *d) { - return YASL_CAP_PLACE_WINDOW | YASL_CAP_PRIMARY_SELECTION | + return YASL_CAP_DRAG | YASL_CAP_DRAG_CARRY | YASL_CAP_PLACE_WINDOW | + YASL_CAP_PRIMARY_SELECTION | (((const struct x11_display *)d)->xi2 ? YASL_CAP_TABLET : 0); } @@ -984,6 +1008,49 @@ static bool place(struct yasl_surface *base, struct yasl_vector2i at) return true; } +static void drag_end(struct x11_display *d, enum yasl_drag_end how) +{ + if (!d || !d->drag.active) return; + struct yasl_drag_listener l = d->drag.listener; + void *ud = d->drag.ud; + d->drag.active = false; + d->drag.origin = NULL; + d->drag.carried = NULL; + if (l.end) + l.end(ud, how); +} + +static bool drag_start(struct yasl_display *base, struct yasl_surface *origin, + const struct yasl_drag_image *image, + const struct yasl_drag_listener *l, void *ud) +{ + (void)image; + struct x11_display *d = (struct x11_display *)base; + if (!d || !origin || d->drag.active || !l) + return false; + d->drag.active = true; + d->drag.origin = (struct x11_surface *)origin; + d->drag.carried = NULL; + d->drag.listener = *l; + d->drag.ud = ud; + return true; +} + +static void drag_cancel(struct yasl_display *base) +{ + drag_end((struct x11_display *)base, YASL_DRAG_CANCELLED); +} + +static bool drag_carry(struct yasl_surface *base, struct yasl_vector2i offset) +{ + struct x11_display *d = display_of(base); + if (!d || !d->drag.active) + return false; + d->drag.carried = (struct x11_surface *)base; + d->drag.carry_offset = offset; + return place(base, offset); +} + static bool interactive_move(struct yasl_surface *base) { struct x11_surface *s = (struct x11_surface *)base; @@ -1008,6 +1075,8 @@ const struct yasl_backend_ops yasl_x11_backend = { .display_modifiers = mods, .display_copy = copy, .display_paste = paste, + .display_drag_start = drag_start, + .display_drag_cancel = drag_cancel, .surface_create_toplevel = create_toplevel, .surface_destroy = surface_destroy, .surface_display = surface_display, @@ -1021,6 +1090,7 @@ const struct yasl_backend_ops yasl_x11_backend = { .surface_timing = timing, .surface_frame_time_ns = frame_time, .surface_set_cursor = cursor, + .surface_drag_carry = drag_carry, .surface_place_under_pointer = place, .surface_start_interactive_move = interactive_move, .display_get_native_handle = native_display, -- cgit v1.2.3