diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c index 40d97a23a2..c1aad57493 100644 --- a/src/video/wayland/SDL_waylandvideo.c +++ b/src/video/wayland/SDL_waylandvideo.c @@ -442,6 +442,15 @@ static void Wayland_SortOutputs(SDL_VideoData *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); // 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; + internal->geometry_changed |= internal->logical.x != x || internal->logical.y != y; internal->logical.x = x; internal->logical.y = y; 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; + internal->geometry_changed |= internal->logical.width != width || internal->logical.height != height; internal->logical.width = width; internal->logical.height = height; 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 if (!internal->has_logical_position) { + internal->geometry_changed |= internal->logical.x != x || internal->logical.y != y; internal->logical.x = x; 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; if (flags & WL_OUTPUT_MODE_CURRENT) { + internal->geometry_changed |= internal->pixel.width != width || internal->pixel.height != height; internal->pixel.width = width; 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. */ if (!internal->has_logical_size) { + internal->geometry_changed |= internal->logical.width != width || internal->logical.height != height; internal->logical.width = width; internal->logical.height = height; } @@ -1103,11 +1117,9 @@ static void handle_wl_output_done(void *data, struct wl_output *output) } } else { // ...otherwise expose the integer scaled variants of the desktop resolution down to 1. - int i; - 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.h = internal->logical.height * i; 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) { Wayland_DeriveOutputPixelCoordinates(video); } + internal->display = SDL_AddVideoDisplay(&internal->placeholder, true); + Wayland_RefreshWindowPositions(); + SDL_free(internal->placeholder.name); SDL_zero(internal->placeholder); } } else { 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) diff --git a/src/video/wayland/SDL_waylandvideo.h b/src/video/wayland/SDL_waylandvideo.h index 7cda18c8fe..e4247e671c 100644 --- a/src/video/wayland/SDL_waylandvideo.h +++ b/src/video/wayland/SDL_waylandvideo.h @@ -150,6 +150,7 @@ struct SDL_DisplayData int wl_output_done_count; bool has_logical_position; bool has_logical_size; + bool geometry_changed; }; // Needed here to get wl_surface declaration, fixes GitHub#4594 diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c index fdd2920f2f..e45728802b 100644 --- a/src/video/wayland/SDL_waylandwindow.c +++ b/src/video/wayland/SDL_waylandwindow.c @@ -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 * 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_DisplayData *display; - SDL_DisplayID *displays; if (wind->outputs && wind->num_outputs) { display = wind->outputs[wind->num_outputs - 1]; @@ -668,45 +667,33 @@ static void Wayland_move_window(SDL_Window *window) return; } - displays = SDL_GetDisplays(NULL); - if (displays) { - for (int i = 0; displays[i]; ++i) { - if (SDL_GetDisplayDriverData(displays[i]) == display) { - /* We want to send a very very specific combination here: - * - * 1. A coordinate that tells the application what display we're on - * 2. Exactly (0, 0) - * - * Part 1 is useful information but is also really important for - * ensuring we end up on the right display for fullscreen, while - * part 2 is important because numerous applications use a specific - * combination of GetWindowPosition and GetGlobalMouseState, and of - * course neither are supported by Wayland. Since global mouse will - * fall back to just GetMouseState, we need the window position to - * be zero so the cursor math works without it going off in some - * random direction. See UE5 Editor for a notable example of this! - * - * This may be an issue some day if we're ever able to implement - * SDL_GetDisplayUsableBounds! - * - * -flibit - */ - - if (wind->last_displayID != displays[i]) { - 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; - } + /* We want to send a very very specific combination here: + * + * 1. A coordinate that tells the application what display we're on + * 2. Exactly (0, 0) + * + * Part 1 is useful information but is also really important for + * ensuring we end up on the right display for fullscreen, while + * part 2 is important because numerous applications use a specific + * combination of GetWindowPosition and GetGlobalMouseState, and of + * course neither are supported by Wayland. Since global mouse will + * fall back to just GetMouseState, we need the window position to + * be zero so the cursor math works without it going off in some + * random direction. See UE5 Editor for a notable example of this! + * + * This may be an issue some day if we're ever able to implement + * SDL_GetDisplayUsableBounds! + * + * -flibit + */ + wind->last_displayID = display->display; + 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_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 * 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); window->outputs = NULL; } else if (!window->is_fullscreen || window->num_outputs == 1) { - Wayland_move_window(window->sdlwindow); + Wayland_UpdateWindowPosition(window->sdlwindow); 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. if (!window->is_fullscreen || window->num_outputs == 1) { - Wayland_move_window(window->sdlwindow); + Wayland_UpdateWindowPosition(window->sdlwindow); Wayland_MaybeUpdateScaleFactor(window); } } diff --git a/src/video/wayland/SDL_waylandwindow.h b/src/video/wayland/SDL_waylandwindow.h index 0f8f22f767..e061855352 100644 --- a/src/video/wayland/SDL_waylandwindow.h +++ b/src/video/wayland/SDL_waylandwindow.h @@ -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 void Wayland_RemoveOutputFromWindow(SDL_WindowData *window, SDL_DisplayData *display_data); +extern void Wayland_UpdateWindowPosition(SDL_Window *window); #endif // SDL_waylandwindow_h_