From 9aae258aeb3271fbd1e571fc0da799473f90ff57 Mon Sep 17 00:00:00 2001
From: Frank Praznik <[EMAIL REDACTED]>
Date: Fri, 15 May 2026 14:02:18 -0400
Subject: [PATCH] wayland: Adjust DnD coordinates when dragging over a mask
subsurface
---
src/video/wayland/SDL_waylanddatamanager.h | 1 +
src/video/wayland/SDL_waylandevents.c | 34 ++++++++++++++++++----
src/video/wayland/SDL_waylandwindow.c | 10 ++++---
3 files changed, 35 insertions(+), 10 deletions(-)
diff --git a/src/video/wayland/SDL_waylanddatamanager.h b/src/video/wayland/SDL_waylanddatamanager.h
index 1ac0dbfc2f9ee..c9bc93d1f9e7f 100644
--- a/src/video/wayland/SDL_waylanddatamanager.h
+++ b/src/video/wayland/SDL_waylanddatamanager.h
@@ -98,6 +98,7 @@ struct SDL_WaylandDataDevice
const char *mime_type;
bool has_mime_file, has_mime_text;
SDL_Window *dnd_window;
+ struct wl_surface *dnd_surface;
// Clipboard and Primary Selection
uint32_t selection_serial;
diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c
index 7d0fef62dca75..b38ef441db6ed 100644
--- a/src/video/wayland/SDL_waylandevents.c
+++ b/src/video/wayland/SDL_waylandevents.c
@@ -2843,9 +2843,20 @@ static void data_device_handle_enter(void *data, struct wl_data_device *wl_data_
// 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);
+ data_device->dnd_surface = surface;
+ double dx = wl_fixed_to_double(x);
+ double dy = wl_fixed_to_double(y);
+
+ // If over the mask, adjust the offset.
+ if (surface == window->mask.surface) {
+ dx += (double)window->mask.offset_x;
+ dy += (double)window->mask.offset_y;
+ }
+
+ dx *= window->pointer_scale.x;
+ dy *= window->pointer_scale.y;
+
+ SDL_SendDropPosition(data_device->dnd_window, (float)dx, (float)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),
@@ -2863,6 +2874,7 @@ static void data_device_handle_enter(void *data, struct wl_data_device *wl_data_
}
} else {
data_device->dnd_window = NULL;
+ data_device->dnd_surface = NULL;
// Decline the offer.
if (id) {
@@ -2912,14 +2924,24 @@ 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) * 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_WindowData *window_data = data_device->dnd_window->internal;
+ double dx = wl_fixed_to_double(x);
+ double dy = wl_fixed_to_double(y);
+
+ // If over the mask, adjust the offset.
+ if (data_device->dnd_surface == window_data->mask.surface) {
+ dx += (double)window_data->mask.offset_x;
+ dy += (double)window_data->mask.offset_y;
+ }
+
+ dx *= window_data->pointer_scale.x;
+ dy *= window_data->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
* hammer the DBus interface hundreds or even thousands of times per second.
*/
- SDL_SendDropPosition(data_device->dnd_window, dx, dy);
+ SDL_SendDropPosition(data_device->dnd_window, (float)dx, (float)dy);
SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
". In wl_data_device_listener . data_device_handle_motion on data_offer 0x%08x at %d x %d in window %d serial %d",
WAYLAND_wl_proxy_get_id((struct wl_proxy *)data_device->drag_offer->offer),
diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c
index d7e70c63c4639..dda72ce311472 100644
--- a/src/video/wayland/SDL_waylandwindow.c
+++ b/src/video/wayland/SDL_waylandwindow.c
@@ -543,14 +543,16 @@ static void ConfigureWindowGeometry(SDL_Window *window)
SetSurfaceOpaqueRegion(data->mask.surface, 0, 0);
}
- data->mask.offset_x = -(data->current.logical_width - viewport_width) / 2;
- data->mask.offset_y = -(data->current.logical_height - viewport_height) / 2;
-
// Can't use an offset subsurface with libdecor (yet), or the decorations won't line up properly.
if (data->shell_surface_type != WAYLAND_SHELL_SURFACE_TYPE_LIBDECOR) {
- wl_subsurface_set_position(data->mask.subsurface, data->mask.offset_x, data->mask.offset_y);
+ data->mask.offset_x = -(data->current.logical_width - viewport_width) / 2;
+ data->mask.offset_y = -(data->current.logical_height - viewport_height) / 2;
+ } else {
+ data->mask.offset_x = 0;
+ data->mask.offset_y = 0;
}
+ wl_subsurface_set_position(data->mask.subsurface, data->mask.offset_x, data->mask.offset_y);
wl_surface_commit(data->mask.surface);
if (old_buffer) {