summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/yasl/yasl.h7
-rw-r--r--src/common/backend.h2
-rw-r--r--src/common/events.c2
-rw-r--r--src/common/surface.c24
-rw-r--r--src/common/types.h1
5 files changed, 30 insertions, 6 deletions
diff --git a/include/yasl/yasl.h b/include/yasl/yasl.h
index 6139bc3..931cf7f 100644
--- a/include/yasl/yasl.h
+++ b/include/yasl/yasl.h
@@ -216,7 +216,9 @@ enum yasl_event_type {
YASL_EVENT_CLIPBOARD, /**< text; action is a yasl_transfer. */
YASL_EVENT_DROP, /**< External data: x, y; action is a yasl_drop_action;
* text on RELEASE is a text/uri-list. */
- YASL_EVENT_DRAG, /**< A yasl_drag_start drag: x, y; action is a yasl_drop_action. */
+ YASL_EVENT_DRAG, /**< A yasl_drag_start drag: x, y; action is a yasl_drop_action.
+ * Each drag ends once: RELEASE on the surface it drops on,
+ * or CANCEL on its origin when it ends anywhere else. */
/** The display server went away; action is a yasl_lost. Before the next
* wait, or before returning to the loop, destroy Vulkan surfaces and detach
* GL contexts on every thread; popups close. On RECONNECTING, DISPLAY_RESTORED
@@ -239,7 +241,8 @@ enum yasl_pen_axis {
enum yasl_scroll_source { YASL_SCROLL_UNKNOWN, YASL_SCROLL_WHEEL, YASL_SCROLL_FINGER, YASL_SCROLL_CONTINUOUS };
enum yasl_scroll_phase { YASL_SCROLL_PHASE_NONE, YASL_SCROLL_BEGIN, YASL_SCROLL_UPDATE, YASL_SCROLL_END };
enum yasl_transfer { YASL_TRANSFER_OK, YASL_TRANSFER_UNAVAILABLE, YASL_TRANSFER_FAILED };
-enum yasl_drop_action { YASL_DROP_OVER, YASL_DROP_LEAVE, YASL_DROP_RELEASE };
+/** CANCEL ends a drag that dropped on no YASL surface, or was canceled; x and y are 0. */
+enum yasl_drop_action { YASL_DROP_OVER, YASL_DROP_LEAVE, YASL_DROP_RELEASE, YASL_DROP_CANCEL };
enum yasl_lost { YASL_LOST_RECONNECTING, YASL_LOST_FINAL };
/** Physical key, layout labels (codepoint unshifted, shifted; 0 if none) and the
diff --git a/src/common/backend.h b/src/common/backend.h
index a9076e4..6d0388c 100644
--- a/src/common/backend.h
+++ b/src/common/backend.h
@@ -30,6 +30,8 @@ struct yasl_private_display {
struct yasl_surface *surfaces;
/* Set by the backend: a lost connection comes back (Wayland). */
bool reconnects;
+ /* The drag in progress dropped on one of the display's surfaces. */
+ bool drag_dropped;
/* Set by the public layer, or NULL. pending: an event is ready that the
* queue does not hold, such as an owed REFRESH. deliver: hand queued events
* to the application during the loop step. */
diff --git a/src/common/events.c b/src/common/events.c
index c310f06..a9557c9 100644
--- a/src/common/events.c
+++ b/src/common/events.c
@@ -304,6 +304,8 @@ void yasl_private_surface_notify_state(struct yasl_surface *s, uint32_t flags)
void yasl_private_surface_notify_drag(struct yasl_surface *s, enum yasl_private_drag_event action, struct yasl_private_vector2i pos)
{
struct yasl_private_event event = {.type = YASL_PRIVATE_EVENT_DRAG, .surface = s, .drag = {action, pos}};
+ if (action == YASL_PRIVATE_DRAG_DROP)
+ s->display->drag_dropped = true;
yasl_private_events_push(s->display, s, &event);
}
diff --git a/src/common/surface.c b/src/common/surface.c
index 6b805be..683bbaa 100644
--- a/src/common/surface.c
+++ b/src/common/surface.c
@@ -669,6 +669,18 @@ int yasl_clipboard_request(yasl_surface *s, enum yasl_selection selection)
return -errno;
return STATUS(yasl_private_clipboard_request(s, (enum yasl_private_selection)selection) != 0);
}
+/* A drag that drops on no surface would end unheard, so its origin hears
+ * the end. ud is the origin's id: the origin may be gone by then. */
+static void drag_ended(void *ud, enum yasl_private_drag_end how)
+{
+ (void)how;
+ if (!display || display->drag_dropped)
+ return;
+ yasl_surface *origin = yasl_private_events_find(display, (uint64_t)(uintptr_t)ud);
+ if (origin)
+ yasl_private_surface_notify_drag(origin, YASL_PRIVATE_DRAG_CANCEL,
+ (struct yasl_private_vector2i){0, 0});
+}
int yasl_drag_start(yasl_surface *s, const struct yasl_drag_image *source)
{
if (!yasl_public_connected(s))
@@ -679,7 +691,10 @@ int yasl_drag_start(yasl_surface *s, const struct yasl_drag_image *source)
.pixels = source->pixels,
.size = {source->pixel_width, source->pixel_height},
.hotspot = {source->hotspot_x, source->hotspot_y}};
- return STATUS(yasl_private_display_drag_start(display, s, source ? &image : NULL, NULL, NULL));
+ display->drag_dropped = false;
+ const struct yasl_private_drag_listener listener = {.end = drag_ended};
+ return STATUS(yasl_private_display_drag_start(display, s, source ? &image : NULL, &listener,
+ (void *)(uintptr_t)s->id));
}
int yasl_drag_cancel(void)
{
@@ -886,9 +901,10 @@ static bool convert_event(const struct yasl_private_event *p, yasl_event *e)
return true;
case YASL_PRIVATE_EVENT_DRAG:
e->type = YASL_EVENT_DRAG;
- e->action = p->drag.action == YASL_PRIVATE_DRAG_OVER ? YASL_DROP_OVER
- : p->drag.action == YASL_PRIVATE_DRAG_LEAVE ? YASL_DROP_LEAVE
- : YASL_DROP_RELEASE;
+ e->action = p->drag.action == YASL_PRIVATE_DRAG_OVER ? YASL_DROP_OVER
+ : p->drag.action == YASL_PRIVATE_DRAG_LEAVE ? YASL_DROP_LEAVE
+ : p->drag.action == YASL_PRIVATE_DRAG_CANCEL ? YASL_DROP_CANCEL
+ : YASL_DROP_RELEASE;
e->x = p->drag.pos.x / scale;
e->y = p->drag.pos.y / scale;
return true;
diff --git a/src/common/types.h b/src/common/types.h
index 67aaaa3..034e6fd 100644
--- a/src/common/types.h
+++ b/src/common/types.h
@@ -231,6 +231,7 @@ enum yasl_private_drag_event {
YASL_PRIVATE_DRAG_OVER, /**< Surface drag entered or moved over the target. */
YASL_PRIVATE_DRAG_LEAVE, /**< Surface drag left the target. */
YASL_PRIVATE_DRAG_DROP, /**< Surface drag dropped on the target. */
+ YASL_PRIVATE_DRAG_CANCEL, /**< Surface drag ended on no target; sent to its origin. */
};
/** External text/file drag hover notifications; completed data arrives through input. */