wayland: Send display moved events on display geometry changes

Updates are sent to outputs, however, dispatching the moved event was overlooked and never implemented.
This commit is contained in:
Frank Praznik
2026-03-12 12:24:34 -04:00
parent 98d23660bc
commit 41bce956f0
4 changed files with 61 additions and 46 deletions
+29 -3
View File
@@ -442,6 +442,15 @@ static void Wayland_SortOutputs(SDL_VideoData *vid)
Wayland_SortOutputsByPriorityHint(vid); Wayland_SortOutputsByPriorityHint(vid);
} }
static void Wayland_RefreshWindowPositions()
{
SDL_VideoDevice *vid = SDL_GetVideoDevice();
for (SDL_Window *w = vid->windows; w; w = w->next) {
Wayland_UpdateWindowPosition(w);
}
}
static void handle_wl_output_done(void *data, struct wl_output *output); static void handle_wl_output_done(void *data, struct wl_output *output);
// Initialization/Query functions // Initialization/Query functions
@@ -786,6 +795,7 @@ static void handle_xdg_output_logical_position(void *data, struct zxdg_output_v1
{ {
SDL_DisplayData *internal = (SDL_DisplayData *)data; SDL_DisplayData *internal = (SDL_DisplayData *)data;
internal->geometry_changed |= internal->logical.x != x || internal->logical.y != y;
internal->logical.x = x; internal->logical.x = x;
internal->logical.y = y; internal->logical.y = y;
internal->has_logical_position = true; internal->has_logical_position = true;
@@ -795,6 +805,7 @@ static void handle_xdg_output_logical_size(void *data, struct zxdg_output_v1 *xd
{ {
SDL_DisplayData *internal = (SDL_DisplayData *)data; SDL_DisplayData *internal = (SDL_DisplayData *)data;
internal->geometry_changed |= internal->logical.width != width || internal->logical.height != height;
internal->logical.width = width; internal->logical.width = width;
internal->logical.height = height; internal->logical.height = height;
internal->has_logical_size = true; internal->has_logical_size = true;
@@ -932,6 +943,7 @@ static void handle_wl_output_geometry(void *data, struct wl_output *output, int
// Apply the change from wl-output only if xdg-output is not supported // Apply the change from wl-output only if xdg-output is not supported
if (!internal->has_logical_position) { if (!internal->has_logical_position) {
internal->geometry_changed |= internal->logical.x != x || internal->logical.y != y;
internal->logical.x = x; internal->logical.x = x;
internal->logical.y = y; internal->logical.y = y;
} }
@@ -980,6 +992,7 @@ static void handle_wl_output_mode(void *data, struct wl_output *output, uint32_t
SDL_DisplayData *internal = (SDL_DisplayData *)data; SDL_DisplayData *internal = (SDL_DisplayData *)data;
if (flags & WL_OUTPUT_MODE_CURRENT) { if (flags & WL_OUTPUT_MODE_CURRENT) {
internal->geometry_changed |= internal->pixel.width != width || internal->pixel.height != height;
internal->pixel.width = width; internal->pixel.width = width;
internal->pixel.height = height; internal->pixel.height = height;
@@ -988,6 +1001,7 @@ static void handle_wl_output_mode(void *data, struct wl_output *output, uint32_t
* handle_done and xdg-output coordinates are pre-transformed. * handle_done and xdg-output coordinates are pre-transformed.
*/ */
if (!internal->has_logical_size) { if (!internal->has_logical_size) {
internal->geometry_changed |= internal->logical.width != width || internal->logical.height != height;
internal->logical.width = width; internal->logical.width = width;
internal->logical.height = height; internal->logical.height = height;
} }
@@ -1103,11 +1117,9 @@ static void handle_wl_output_done(void *data, struct wl_output *output)
} }
} else { } else {
// ...otherwise expose the integer scaled variants of the desktop resolution down to 1. // ...otherwise expose the integer scaled variants of the desktop resolution down to 1.
int i;
desktop_mode.pixel_density = 1.0f; desktop_mode.pixel_density = 1.0f;
for (i = (int)internal->scale_factor; i > 0; --i) { for (int i = (int)internal->scale_factor; i > 0; --i) {
desktop_mode.w = internal->logical.width * i; desktop_mode.w = internal->logical.width * i;
desktop_mode.h = internal->logical.height * i; desktop_mode.h = internal->logical.height * i;
SDL_AddFullscreenDisplayMode(dpy, &desktop_mode); SDL_AddFullscreenDisplayMode(dpy, &desktop_mode);
@@ -1141,13 +1153,27 @@ static void handle_wl_output_done(void *data, struct wl_output *output)
if (video->scale_to_display_enabled) { if (video->scale_to_display_enabled) {
Wayland_DeriveOutputPixelCoordinates(video); Wayland_DeriveOutputPixelCoordinates(video);
} }
internal->display = SDL_AddVideoDisplay(&internal->placeholder, true); internal->display = SDL_AddVideoDisplay(&internal->placeholder, true);
Wayland_RefreshWindowPositions();
SDL_free(internal->placeholder.name); SDL_free(internal->placeholder.name);
SDL_zero(internal->placeholder); SDL_zero(internal->placeholder);
} }
} else { } else {
SDL_SendDisplayEvent(dpy, SDL_EVENT_DISPLAY_ORIENTATION, internal->orientation, 0); SDL_SendDisplayEvent(dpy, SDL_EVENT_DISPLAY_ORIENTATION, internal->orientation, 0);
if (internal->geometry_changed) {
if (video->scale_to_display_enabled) {
Wayland_DeriveOutputPixelCoordinates(video);
}
SDL_SendDisplayEvent(dpy, SDL_EVENT_DISPLAY_MOVED, 0, 0);
Wayland_RefreshWindowPositions();
}
} }
internal->geometry_changed = false;
} }
static void handle_wl_output_scale(void *data, struct wl_output *output, int32_t factor) static void handle_wl_output_scale(void *data, struct wl_output *output, int32_t factor)
+1
View File
@@ -150,6 +150,7 @@ struct SDL_DisplayData
int wl_output_done_count; int wl_output_done_count;
bool has_logical_position; bool has_logical_position;
bool has_logical_size; bool has_logical_size;
bool geometry_changed;
}; };
// Needed here to get wl_surface declaration, fixes GitHub#4594 // Needed here to get wl_surface declaration, fixes GitHub#4594
+30 -43
View File
@@ -655,11 +655,10 @@ static void FlushPendingEvents(SDL_Window *window)
* what monitor we're on, so let's send move events that put the window at the * what monitor we're on, so let's send move events that put the window at the
* center of the whatever display the wl_surface_listener events give us. * center of the whatever display the wl_surface_listener events give us.
*/ */
static void Wayland_move_window(SDL_Window *window) void Wayland_UpdateWindowPosition(SDL_Window *window)
{ {
SDL_WindowData *wind = window->internal; SDL_WindowData *wind = window->internal;
SDL_DisplayData *display; SDL_DisplayData *display;
SDL_DisplayID *displays;
if (wind->outputs && wind->num_outputs) { if (wind->outputs && wind->num_outputs) {
display = wind->outputs[wind->num_outputs - 1]; display = wind->outputs[wind->num_outputs - 1];
@@ -668,45 +667,33 @@ static void Wayland_move_window(SDL_Window *window)
return; return;
} }
displays = SDL_GetDisplays(NULL); /* We want to send a very very specific combination here:
if (displays) { *
for (int i = 0; displays[i]; ++i) { * 1. A coordinate that tells the application what display we're on
if (SDL_GetDisplayDriverData(displays[i]) == display) { * 2. Exactly (0, 0)
/* We want to send a very very specific combination here: *
* * Part 1 is useful information but is also really important for
* 1. A coordinate that tells the application what display we're on * ensuring we end up on the right display for fullscreen, while
* 2. Exactly (0, 0) * part 2 is important because numerous applications use a specific
* * combination of GetWindowPosition and GetGlobalMouseState, and of
* Part 1 is useful information but is also really important for * course neither are supported by Wayland. Since global mouse will
* ensuring we end up on the right display for fullscreen, while * fall back to just GetMouseState, we need the window position to
* part 2 is important because numerous applications use a specific * be zero so the cursor math works without it going off in some
* combination of GetWindowPosition and GetGlobalMouseState, and of * random direction. See UE5 Editor for a notable example of this!
* course neither are supported by Wayland. Since global mouse will *
* fall back to just GetMouseState, we need the window position to * This may be an issue some day if we're ever able to implement
* be zero so the cursor math works without it going off in some * SDL_GetDisplayUsableBounds!
* random direction. See UE5 Editor for a notable example of this! *
* * -flibit
* This may be an issue some day if we're ever able to implement */
* SDL_GetDisplayUsableBounds! wind->last_displayID = display->display;
* if (wind->shell_surface_type != WAYLAND_SHELL_SURFACE_TYPE_XDG_POPUP) {
* -flibit if (!wind->waylandData->scale_to_display_enabled) {
*/ SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, display->logical.x, display->logical.y);
} else {
if (wind->last_displayID != displays[i]) { SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, display->pixel.x, display->pixel.y);
wind->last_displayID = displays[i];
if (wind->shell_surface_type != WAYLAND_SHELL_SURFACE_TYPE_XDG_POPUP) {
if (!wind->waylandData->scale_to_display_enabled) {
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, display->logical.x, display->logical.y);
} else {
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, display->pixel.x, display->pixel.y);
}
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_DISPLAY_CHANGED, wind->last_displayID, 0);
}
}
break;
}
} }
SDL_free(displays); SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_DISPLAY_CHANGED, wind->last_displayID, 0);
} }
} }
@@ -779,7 +766,7 @@ static void UpdateWindowFullscreen(SDL_Window *window, bool fullscreen)
/* Send a move event, in case it was deferred while the fullscreen window was moving and /* Send a move event, in case it was deferred while the fullscreen window was moving and
* on multiple outputs. * on multiple outputs.
*/ */
Wayland_move_window(window); Wayland_UpdateWindowPosition(window);
} }
} }
} }
@@ -1788,7 +1775,7 @@ void Wayland_RemoveOutputFromWindow(SDL_WindowData *window, SDL_DisplayData *dis
SDL_free(window->outputs); SDL_free(window->outputs);
window->outputs = NULL; window->outputs = NULL;
} else if (!window->is_fullscreen || window->num_outputs == 1) { } else if (!window->is_fullscreen || window->num_outputs == 1) {
Wayland_move_window(window->sdlwindow); Wayland_UpdateWindowPosition(window->sdlwindow);
Wayland_MaybeUpdateScaleFactor(window); Wayland_MaybeUpdateScaleFactor(window);
} }
} }
@@ -1813,7 +1800,7 @@ static void handle_surface_enter(void *data, struct wl_surface *surface, struct
// Update the scale factor after the move so that fullscreen outputs are updated. // Update the scale factor after the move so that fullscreen outputs are updated.
if (!window->is_fullscreen || window->num_outputs == 1) { if (!window->is_fullscreen || window->num_outputs == 1) {
Wayland_move_window(window->sdlwindow); Wayland_UpdateWindowPosition(window->sdlwindow);
Wayland_MaybeUpdateScaleFactor(window); Wayland_MaybeUpdateScaleFactor(window);
} }
} }
+1
View File
@@ -278,5 +278,6 @@ extern bool Wayland_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern bool Wayland_ReconfigureWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_WindowFlags flags); extern bool Wayland_ReconfigureWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_WindowFlags flags);
extern void Wayland_RemoveOutputFromWindow(SDL_WindowData *window, SDL_DisplayData *display_data); extern void Wayland_RemoveOutputFromWindow(SDL_WindowData *window, SDL_DisplayData *display_data);
extern void Wayland_UpdateWindowPosition(SDL_Window *window);
#endif // SDL_waylandwindow_h_ #endif // SDL_waylandwindow_h_