From bf7c5e2df2228bd0ecb35bb430a1185ec5f46d3f Mon Sep 17 00:00:00 2001
From: Frank Praznik <[EMAIL REDACTED]>
Date: Fri, 15 May 2026 13:51:14 -0400
Subject: [PATCH] wayland: Implement Wayland_AcceptDragAndDrop()
This simply toggles a flag that rejects DnD offers if false. Events were previously dropped silently, but rejecting the offer makes some desktops display a proper icon when the drop will not work.
(cherry picked from commit 336d07c2b78c1f08ff0505ce8184dfeb47ce9d62)
---
src/video/wayland/SDL_waylandevents.c | 71 ++++++++++++++-------------
src/video/wayland/SDL_waylandvideo.c | 1 +
src/video/wayland/SDL_waylandwindow.c | 5 ++
src/video/wayland/SDL_waylandwindow.h | 2 +
4 files changed, 44 insertions(+), 35 deletions(-)
diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c
index 9a03ddb45bf38..231fd70dbb07a 100644
--- a/src/video/wayland/SDL_waylandevents.c
+++ b/src/video/wayland/SDL_waylandevents.c
@@ -2750,15 +2750,18 @@ static void data_device_handle_enter(void *data, struct wl_data_device *wl_data_
wl_fixed_t x, wl_fixed_t y, struct wl_data_offer *id)
{
SDL_WaylandDataDevice *data_device = data;
+ SDL_WindowData *window = surface ? Wayland_GetWindowDataForOwnedSurface(surface) : NULL;
data_device->has_mime_file = false;
data_device->has_mime_text = false;
- uint32_t dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
data_device->drag_serial = serial;
+ // Save the drag offer so it can be freed later.
if (id) {
data_device->drag_offer = wl_data_offer_get_user_data(id);
+ }
+ if (data_device->drag_offer && window && window->accepts_drag_and_drop) {
// TODO: SDL Support more mime types
#ifdef SDL_USE_LIBDBUS
if (Wayland_data_offer_has_mime(data_device->drag_offer, FILE_PORTAL_MIME)) {
@@ -2784,48 +2787,46 @@ static void data_device_handle_enter(void *data, struct wl_data_device *wl_data_
}
}
- // SDL only supports "copy" style drag and drop
if (data_device->has_mime_file || data_device->has_mime_text) {
- dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
+ // SDL only supports "copy" style drag and drop
+ if (wl_data_offer_get_version(data_device->drag_offer->offer) >= WL_DATA_OFFER_SET_ACTIONS_SINCE_VERSION) {
+ wl_data_offer_set_actions(data_device->drag_offer->offer, WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY, WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY);
+ }
+
+ // Set the destination window and send the initial position.
+ data_device->dnd_window = window->sdlwindow;
+ const float dx = (float)(wl_fixed_to_double(x) * data_device->dnd_window->internal->pointer_scale.x);
+ const float dy = (float)(wl_fixed_to_double(y) * data_device->dnd_window->internal->pointer_scale.y);
+ SDL_SendDropPosition(data_device->dnd_window, dx, dy);
+ SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
+ ". In wl_data_device_listener . data_device_handle_enter on data_offer 0x%08x at %d x %d into window %d for serial %d",
+ WAYLAND_wl_proxy_get_id((struct wl_proxy *)id),
+ wl_fixed_to_int(x), wl_fixed_to_int(y), SDL_GetWindowID(data_device->dnd_window), serial);
} else {
- // drag_mime is NULL this will decline the offer
+ // Decline the offer.
wl_data_offer_accept(id, serial, NULL);
- }
- if (wl_data_offer_get_version(data_device->drag_offer->offer) >=
- WL_DATA_OFFER_SET_ACTIONS_SINCE_VERSION) {
- wl_data_offer_set_actions(data_device->drag_offer->offer,
- dnd_action, dnd_action);
- }
-
- // find the current window
- if (surface) {
- SDL_WindowData *window = Wayland_GetWindowDataForOwnedSurface(surface);
- if (window) {
- data_device->dnd_window = window->sdlwindow;
- const float dx = (float)wl_fixed_to_double(x);
- const float dy = (float)wl_fixed_to_double(y);
- SDL_SendDropPosition(data_device->dnd_window, dx, dy);
- SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
- ". In wl_data_device_listener . data_device_handle_enter on data_offer 0x%08x at %d x %d into window %d for serial %d",
- WAYLAND_wl_proxy_get_id((struct wl_proxy *)id),
- wl_fixed_to_int(x), wl_fixed_to_int(y), SDL_GetWindowID(data_device->dnd_window), serial);
- } else {
- data_device->dnd_window = NULL;
- SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
- ". In wl_data_device_listener . data_device_handle_enter on data_offer 0x%08x at %d x %d for serial %d",
- WAYLAND_wl_proxy_get_id((struct wl_proxy *)id),
- wl_fixed_to_int(x), wl_fixed_to_int(y), serial);
+ if (wl_data_offer_get_version(data_device->drag_offer->offer) >= WL_DATA_OFFER_SET_ACTIONS_SINCE_VERSION) {
+ wl_data_offer_set_actions(data_device->drag_offer->offer, WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE, WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE);
}
- } else {
+
SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
". In wl_data_device_listener . data_device_handle_enter on data_offer 0x%08x at %d x %d for serial %d",
- WAYLAND_wl_proxy_get_id((struct wl_proxy *)id),
- wl_fixed_to_int(x), wl_fixed_to_int(y), serial);
+ WAYLAND_wl_proxy_get_id((struct wl_proxy *)id), wl_fixed_to_int(x), wl_fixed_to_int(y), serial);
}
} else {
+ data_device->dnd_window = NULL;
+
+ // Decline the offer.
+ if (id) {
+ wl_data_offer_accept(id, serial, NULL);
+ if (wl_data_offer_get_version(data_device->drag_offer->offer) >= WL_DATA_OFFER_SET_ACTIONS_SINCE_VERSION) {
+ wl_data_offer_set_actions(data_device->drag_offer->offer, WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE, WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE);
+ }
+ }
SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
". In wl_data_device_listener . data_device_handle_enter on data_offer 0x%08x at %d x %d for serial %d",
- -1, wl_fixed_to_int(x), wl_fixed_to_int(y), serial);
+ id ? WAYLAND_wl_proxy_get_id((struct wl_proxy *)id) : -1,
+ wl_fixed_to_int(x), wl_fixed_to_int(y), serial);
}
}
@@ -2863,8 +2864,8 @@ static void data_device_handle_motion(void *data, struct wl_data_device *wl_data
SDL_WaylandDataDevice *data_device = data;
if (data_device->drag_offer && data_device->dnd_window && (data_device->has_mime_file || data_device->has_mime_text)) {
- const float dx = (float)wl_fixed_to_double(x);
- const float dy = (float)wl_fixed_to_double(y);
+ const float dx = (float)(wl_fixed_to_double(x) * data_device->dnd_window->internal->pointer_scale.x);
+ const float dy = (float)(wl_fixed_to_double(y) * data_device->dnd_window->internal->pointer_scale.y);
/* XXX: Send the filename here if the event system ever starts passing it though.
* Any future implementation should cache the filenames, as otherwise this could
diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c
index a984f8db09e78..d0f02b4894830 100644
--- a/src/video/wayland/SDL_waylandvideo.c
+++ b/src/video/wayland/SDL_waylandvideo.c
@@ -677,6 +677,7 @@ static SDL_VideoDevice *Wayland_CreateDevice(bool require_preferred_protocols)
device->SyncWindow = Wayland_SyncWindow;
device->SetWindowFocusable = Wayland_SetWindowFocusable;
device->ReconfigureWindow = Wayland_ReconfigureWindow;
+ device->AcceptDragAndDrop = Wayland_AcceptDragAndDrop;
#ifdef SDL_USE_LIBDBUS
if (SDL_SystemTheme_Init())
diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c
index b56f543d3643c..f939dd1bf9802 100644
--- a/src/video/wayland/SDL_waylandwindow.c
+++ b/src/video/wayland/SDL_waylandwindow.c
@@ -3197,6 +3197,11 @@ bool Wayland_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window)
return true;
}
+void Wayland_AcceptDragAndDrop(SDL_Window *window, bool accept)
+{
+ window->internal->accepts_drag_and_drop = accept;
+}
+
bool Wayland_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable)
{
if (window->flags & SDL_WINDOW_POPUP_MENU) {
diff --git a/src/video/wayland/SDL_waylandwindow.h b/src/video/wayland/SDL_waylandwindow.h
index a82000c2bfd23..903d666f76724 100644
--- a/src/video/wayland/SDL_waylandwindow.h
+++ b/src/video/wayland/SDL_waylandwindow.h
@@ -215,6 +215,7 @@ struct SDL_WindowData
bool scale_to_display;
bool reparenting_required;
bool double_buffer;
+ bool accepts_drag_and_drop;
SDL_HitTestResult hit_test_result;
@@ -251,6 +252,7 @@ extern bool Wayland_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SD
extern bool Wayland_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable);
extern float Wayland_GetWindowContentScale(SDL_VideoDevice *_this, SDL_Window *window);
extern void *Wayland_GetWindowICCProfile(SDL_VideoDevice *_this, SDL_Window *window, size_t *size);
+extern void Wayland_AcceptDragAndDrop(SDL_Window *window, bool accept);
extern bool Wayland_SetWindowHitTest(SDL_Window *window, bool enabled);
extern bool Wayland_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation);