summaryrefslogtreecommitdiff
path: root/src/x11/window.c
diff options
context:
space:
mode:
authorBerke Güzel <wenekar1@gmail.com>2026-10-12 10:53:22 +0300
committerBerke Güzel <wenekar1@gmail.com>2026-10-12 10:53:22 +0300
commit5206933399775e5ff46baefbbb3a776c2a9bb84c (patch)
treee7549216ac626c1f3ac22345f79fbf80cc5a90fb /src/x11/window.c
parent881fb6cb1d64868132287bd9637f4ae1cbc3504c (diff)
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.
Diffstat (limited to 'src/x11/window.c')
-rw-r--r--src/x11/window.c72
1 files changed, 71 insertions, 1 deletions
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,