diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c index e1bf8c8a5a..1575f78270 100644 --- a/src/video/wayland/SDL_waylandvideo.c +++ b/src/video/wayland/SDL_waylandvideo.c @@ -361,7 +361,7 @@ static const struct zxdg_output_v1_listener xdg_output_listener = { xdg_output_handle_description, }; -static void AddEmulatedModes(SDL_VideoDisplay *dpy, SDL_bool rot_90) +static void AddEmulatedModes(SDL_DisplayData *dispdata, SDL_bool rot_90) { struct EmulatedMode { @@ -413,8 +413,9 @@ static void AddEmulatedModes(SDL_VideoDisplay *dpy, SDL_bool rot_90) int i; SDL_DisplayMode mode; - const int native_width = dpy->desktop_mode.pixel_w; - const int native_height = dpy->desktop_mode.pixel_h; + SDL_VideoDisplay *dpy = dispdata->display ? SDL_GetVideoDisplay(dispdata->display) : &dispdata->placeholder; + const int native_width = dispdata->pixel_width; + const int native_height = dispdata->pixel_height; for (i = 0; i < SDL_arraysize(mode_list); ++i) { SDL_zero(mode); @@ -423,18 +424,19 @@ static void AddEmulatedModes(SDL_VideoDisplay *dpy, SDL_bool rot_90) mode.refresh_rate = dpy->desktop_mode.refresh_rate; mode.driverdata = dpy->desktop_mode.driverdata; - if (rot_90) { - mode.pixel_w = mode_list[i].h; - mode.pixel_h = mode_list[i].w; - } else { - mode.pixel_w = mode_list[i].w; - mode.pixel_h = mode_list[i].h; - } - /* Only add modes that are smaller than the native mode. */ - if ((mode.pixel_w < native_width && mode.pixel_h < native_height) || - (mode.pixel_w < native_width && mode.pixel_h == native_height) || - (mode.pixel_w == native_width && mode.pixel_h < native_height)) { + if ((mode_list[i].w < native_width && mode_list[i].h < native_height) || + (mode_list[i].w < native_width && mode_list[i].h == native_height) || + (mode_list[i].w == native_width && mode_list[i].h < native_height)) { + + if (rot_90) { + mode.pixel_w = mode_list[i].h; + mode.pixel_h = mode_list[i].w; + } else { + mode.pixel_w = mode_list[i].w; + mode.pixel_h = mode_list[i].h; + } + SDL_AddFullscreenDisplayMode(dpy, &mode); } } @@ -452,22 +454,6 @@ static void display_handle_geometry(void *data, { SDL_DisplayData *driverdata = (SDL_DisplayData *)data; - SDL_VideoDisplay *display; - int i; - - if (driverdata->wl_output_done_count) { - /* Clear the wl_output ref so Reset doesn't free it */ - display = SDL_GetVideoDisplay(driverdata->display); - for (i = 0; i < display->num_fullscreen_modes; ++i) { - display->fullscreen_modes[i].driverdata = NULL; - } - - /* Okay, now it's safe to reset */ - SDL_ResetFullscreenDisplayModes(display); - - /* The display has officially started over. */ - driverdata->wl_output_done_count = 0; - } /* Apply the change from wl-output only if xdg-output is not supported */ if (!driverdata->has_logical_position) { @@ -558,14 +544,29 @@ static void display_handle_done(void *data, driverdata->wl_output_done_count = SDL_min(driverdata->wl_output_done_count + 1, event_await_count + 1); - if (driverdata->wl_output_done_count != event_await_count) { + if (driverdata->wl_output_done_count < event_await_count) { return; } + /* If the display was already created, reset and rebuild the mode list. */ + if (driverdata->display != 0) { + int i; + dpy = SDL_GetVideoDisplay(driverdata->display); + + /* Clear the wl_output ref so Reset doesn't free it */ + for (i = 0; i < dpy->num_fullscreen_modes; ++i) { + dpy->fullscreen_modes[i].driverdata = NULL; + } + + /* Okay, now it's safe to reset */ + SDL_ResetFullscreenDisplayModes(dpy); + } + /* The native display resolution */ SDL_zero(native_mode); native_mode.format = SDL_PIXELFORMAT_RGB888; + /* Transform the pixel values, if necessary. */ if (driverdata->transform & WL_OUTPUT_TRANSFORM_90) { native_mode.pixel_w = driverdata->pixel_height; native_mode.pixel_h = driverdata->pixel_width; @@ -577,27 +578,29 @@ static void display_handle_done(void *data, native_mode.refresh_rate = ((100 * driverdata->refresh) / 1000) / 100.0f; /* mHz to Hz */ native_mode.driverdata = driverdata->output; + if (driverdata->has_logical_size) { /* If xdg-output is present... */ + if (video->viewporter) { + /* ...and viewports are supported, calculate the true scale of the output. */ + driverdata->scale_factor = (float)native_mode.pixel_w / (float)driverdata->screen_width; + } else { + /* ...otherwise, the 'native' pixel values are a multiple of the logical screen size. */ + driverdata->pixel_width = driverdata->screen_width * (int)driverdata->scale_factor; + driverdata->pixel_height = driverdata->screen_height * (int)driverdata->scale_factor; + } + } else { + /* Calculate the screen coordinates from the pixel values, if xdg-output isn't present. + * Use the native mode pixel values since they are pre-transformed. + */ + driverdata->screen_width = native_mode.pixel_w / (int)driverdata->scale_factor; + driverdata->screen_height = native_mode.pixel_h / (int)driverdata->scale_factor; + } + /* The scaled desktop mode */ SDL_zero(desktop_mode); desktop_mode.format = SDL_PIXELFORMAT_RGB888; - if (driverdata->has_logical_size) { /* If xdg-output is present, calculate the true scale of the desktop */ - if (video->viewporter) { - driverdata->scale_factor = (float)native_mode.pixel_w / (float)driverdata->screen_width; - } - } else { /* Scale the desktop coordinates, if xdg-output isn't present */ - driverdata->screen_width /= driverdata->scale_factor; - driverdata->screen_height /= driverdata->scale_factor; - } - - /* xdg-output dimensions are already transformed, so no need to rotate. */ - if (driverdata->has_logical_size || !(driverdata->transform & WL_OUTPUT_TRANSFORM_90)) { - desktop_mode.pixel_w = driverdata->pixel_width; - desktop_mode.pixel_h = driverdata->pixel_height; - } else { - desktop_mode.pixel_w = driverdata->pixel_height; - desktop_mode.pixel_h = driverdata->pixel_width; - } + desktop_mode.screen_w = driverdata->screen_width; + desktop_mode.screen_h = driverdata->screen_height; desktop_mode.display_scale = driverdata->scale_factor; desktop_mode.refresh_rate = ((100 * driverdata->refresh) / 1000) / 100.0f; /* mHz to Hz */ desktop_mode.driverdata = driverdata->output; @@ -609,25 +612,23 @@ static void display_handle_done(void *data, } /* Set the desktop display mode. */ - SDL_memcpy(&dpy->desktop_mode, &desktop_mode, sizeof(&dpy->desktop_mode)); + SDL_SetDesktopDisplayMode(dpy, &desktop_mode); - /* If the desktop is scaled... */ - if (driverdata->scale_factor > 1.0f) { - /* ...expose the native resolution if viewports are available... */ - if (video->viewporter != NULL) { - SDL_AddFullscreenDisplayMode(dpy, &native_mode); - } else { - /* ...if not, expose some smaller, integer scaled resolutions. */ - int i; - const int base_pixel_w = desktop_mode.pixel_w / (int)desktop_mode.display_scale; - const int base_pixel_h = desktop_mode.pixel_h / (int)desktop_mode.display_scale; - for (i = 1; i < (int)desktop_mode.display_scale; ++i) { - desktop_mode.pixel_w = base_pixel_w * i; - desktop_mode.pixel_h = base_pixel_h * i; - desktop_mode.display_scale = (float)i; + /* ...expose the unscaled, native resolution if the scale is 1.0 or viewports are available... */ + if (driverdata->scale_factor == 1.0f || video->viewporter != NULL) { + SDL_AddFullscreenDisplayMode(dpy, &native_mode); + } else { + /* ...if not, expose the integer scaled variants of the desktop resolution down to 1. */ + int i; - SDL_AddFullscreenDisplayMode(dpy, &desktop_mode); - } + desktop_mode.pixel_w = 0; + desktop_mode.pixel_h = 0; + desktop_mode.screen_w = driverdata->screen_width; + desktop_mode.screen_h = driverdata->screen_height; + + for (i = (int)driverdata->scale_factor; i > 0; --i) { + desktop_mode.display_scale = (float)i; + SDL_AddFullscreenDisplayMode(dpy, &desktop_mode); } } @@ -635,7 +636,7 @@ static void display_handle_done(void *data, if (video->viewporter && mode_emulation_enabled) { const SDL_bool rot_90 = ((driverdata->transform & WL_OUTPUT_TRANSFORM_90) != 0) || (driverdata->screen_width < driverdata->screen_height); - AddEmulatedModes(dpy, rot_90); + AddEmulatedModes(driverdata, rot_90); } /* Calculate the display DPI */ @@ -974,8 +975,19 @@ static int Wayland_GetDisplayBounds(_THIS, SDL_VideoDisplay *display, SDL_Rect * SDL_DisplayData *driverdata = display->driverdata; rect->x = driverdata->x; rect->y = driverdata->y; - rect->w = display->current_mode->screen_w; - rect->h = display->current_mode->screen_h; + + /* When an emulated, exclusive fullscreen window has focus, treat the mode dimensions as the display bounds. */ + if (display->fullscreen_window && + display->fullscreen_window->fullscreen_exclusive && + display->fullscreen_window == SDL_GetFocusWindow() && + display->fullscreen_window->fullscreen_mode.screen_w != 0 && + display->fullscreen_window->fullscreen_mode.screen_h != 0) { + rect->w = display->fullscreen_window->fullscreen_mode.screen_w; + rect->h = display->fullscreen_window->fullscreen_mode.screen_h; + } else { + rect->w = display->current_mode->screen_w; + rect->h = display->current_mode->screen_h; + } return 0; } diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c index 6e9297bca5..5519a7975d 100644 --- a/src/video/wayland/SDL_waylandwindow.c +++ b/src/video/wayland/SDL_waylandwindow.c @@ -53,70 +53,10 @@ SDL_FORCE_INLINE SDL_bool FloatEqual(float a, float b) return diff <= largest * SDL_FLT_EPSILON; } -static void GetFullScreenDimensions(SDL_Window *window, int *width, int *height, int *drawable_width, int *drawable_height) -{ - SDL_WindowData *wind = window->driverdata; - SDL_VideoDisplay *disp = SDL_GetVideoDisplayForWindow(window); - SDL_DisplayData *output = disp->driverdata; - - int fs_width, fs_height; - int buf_width, buf_height; - const int output_width = wind->fs_output_width ? wind->fs_output_width : output->screen_width; - const int output_height = wind->fs_output_height ? wind->fs_output_height : output->screen_height; - - if (window->fullscreen_exclusive) { - /* If a mode was set, use it, otherwise use the native resolution. */ - const SDL_DisplayMode *mode = SDL_GetWindowFullscreenMode(window); - if (!mode) { - mode = &disp->desktop_mode; - } - fs_width = mode->screen_w; - fs_height = mode->screen_h; - buf_width = mode->pixel_w; - buf_height = mode->pixel_h; - } else { - /* - * Fullscreen desktop mandates a desktop sized window, so that's what - * applications will get. If the application is DPI aware, it will need - * to handle the transformations between the differently sized window - * and backbuffer spaces on its own. - */ - fs_width = output_width; - fs_height = output_height; - - /* If the application is DPI aware, we can expose the true backbuffer size */ - if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) { - buf_width = output->pixel_width; - buf_height = output->pixel_height; - } else { - buf_width = fs_width; - buf_height = fs_height; - } - } - - if (width) { - *width = fs_width; - } - if (height) { - *height = fs_height; - } - if (drawable_width) { - *drawable_width = buf_width; - } - if (drawable_height) { - *drawable_height = buf_height; - } -} - -SDL_FORCE_INLINE SDL_bool FullscreenModeEmulation(SDL_Window *window) -{ - return window->fullscreen_exclusive; -} - -SDL_bool SurfaceScaleIsFractional(SDL_Window *window) +static SDL_bool SurfaceScaleIsFractional(SDL_Window *window) { SDL_WindowData *data = window->driverdata; - const float scale_value = !FullscreenModeEmulation(window) ? data->windowed_scale_factor : window->fullscreen_mode.display_scale; + const float scale_value = !(window->fullscreen_exclusive) ? data->windowed_scale_factor : window->fullscreen_mode.display_scale; return !FloatEqual(SDL_roundf(scale_value), scale_value); } @@ -125,9 +65,8 @@ static SDL_bool WindowNeedsViewport(SDL_Window *window) SDL_WindowData *wind = window->driverdata; SDL_VideoData *video = wind->waylandData; SDL_DisplayData *output = SDL_GetDisplayDriverDataForWindow(window); - const int output_width = wind->fs_output_width ? wind->fs_output_width : output->screen_width; - const int output_height = wind->fs_output_height ? wind->fs_output_height : output->screen_height; - int fs_width, fs_height; + const int output_width = wind->requested_window_width ? wind->requested_window_width : output->screen_width; + const int output_height = wind->requested_window_height ? wind->requested_window_height : output->screen_height; /* * A viewport is only required when scaling is enabled and: @@ -137,9 +76,8 @@ static SDL_bool WindowNeedsViewport(SDL_Window *window) if (video->viewporter != NULL) { if (SurfaceScaleIsFractional(window)) { return SDL_TRUE; - } else if (FullscreenModeEmulation(window)) { - GetFullScreenDimensions(window, &fs_width, &fs_height, NULL, NULL); - if (fs_width != output_width || fs_height != output_height) { + } else if (window->fullscreen_exclusive) { + if (window->fullscreen_mode.screen_w != output_width || window->fullscreen_mode.screen_h != output_height) { return SDL_TRUE; } } @@ -154,16 +92,13 @@ static void GetBufferSize(SDL_Window *window, int *width, int *height) int buf_width; int buf_height; - if (FullscreenModeEmulation(window)) { - GetFullScreenDimensions(window, NULL, NULL, &buf_width, &buf_height); - } else if (WindowNeedsViewport(window)) { - /* Round fractional backbuffer sizes halfway away from zero. */ - buf_width = (int)SDL_lroundf((float)window->w * data->windowed_scale_factor); - buf_height = (int)SDL_lroundf((float)window->h * data->windowed_scale_factor); + if (window->fullscreen_exclusive) { + buf_width = window->fullscreen_mode.pixel_w; + buf_height = window->fullscreen_mode.pixel_h; } else { - /* Integer scaled windowed or fullscreen with no viewport */ - buf_width = window->w * (int)data->windowed_scale_factor; - buf_height = window->h * (int)data->windowed_scale_factor; + /* Round fractional backbuffer sizes halfway away from zero. */ + buf_width = (int)SDL_lroundf((float)data->requested_window_width * data->windowed_scale_factor); + buf_height = (int)SDL_lroundf((float)data->requested_window_height * data->windowed_scale_factor); } if (width) { @@ -204,9 +139,9 @@ static void ConfigureWindowGeometry(SDL_Window *window) SDL_WindowData *data = window->driverdata; SDL_VideoData *viddata = data->waylandData; SDL_DisplayData *output = SDL_GetDisplayDriverDataForWindow(window); - struct wl_region *region; const int old_dw = data->drawable_width; const int old_dh = data->drawable_height; + int window_width, window_height; SDL_bool window_size_changed; SDL_bool drawable_size_changed; @@ -221,52 +156,56 @@ static void ConfigureWindowGeometry(SDL_Window *window) 0, 0); } - if (FullscreenModeEmulation(window)) { - int fs_width, fs_height; - const int output_width = data->fs_output_width ? data->fs_output_width : output->screen_width; - const int output_height = data->fs_output_height ? data->fs_output_height : output->screen_height; + if (window->fullscreen_exclusive) { + /* If the compositor supplied fullscreen dimensions, use them, otherwise fall back to the display dimensions. */ + const int output_width = data->requested_window_width ? data->requested_window_width : output->screen_width; + const int output_height = data->requested_window_height ? data->requested_window_height : output->screen_height; + window_width = window->fullscreen_mode.screen_w; + window_height = window->fullscreen_mode.screen_h; - window_size_changed = data->window_width != output_width || data->window_height != output_height; + window_size_changed = window_width != window->w || window_height != window->h || + data->wl_window_width != output_width || data->wl_window_height != output_height; if (window_size_changed || drawable_size_changed) { - GetFullScreenDimensions(window, &fs_width, &fs_height, NULL, NULL); - if (WindowNeedsViewport(window)) { /* Set the buffer scale to 1 since a viewport will be used. */ wl_surface_set_buffer_scale(data->surface, 1); SetDrawSurfaceViewport(window, data->drawable_width, data->drawable_height, output_width, output_height); - data->window_width = output_width; - data->window_height = output_height; - - data->pointer_scale_x = (float)fs_width / (float)output_width; - data->pointer_scale_y = (float)fs_height / (float)output_height; + data->wl_window_width = output_width; + data->wl_window_height = output_height; } else { + /* Always use the mode dimensions for integer scaling. */ + UnsetDrawSurfaceViewport(window); wl_surface_set_buffer_scale(data->surface, (int32_t)window->fullscreen_mode.display_scale); - data->window_width = fs_width; - data->window_height = fs_height; - - data->pointer_scale_x = 1.0f; - data->pointer_scale_y = 1.0f; + data->wl_window_width = window->fullscreen_mode.screen_w; + data->wl_window_height = window->fullscreen_mode.screen_h; } + + data->pointer_scale_x = (float)window_width / (float)data->wl_window_width; + data->pointer_scale_y = (float)window_height / (float)data->wl_window_height; } } else { - window_size_changed = data->window_width != window->w || data->window_height != window->h; + window_width = data->requested_window_width; + window_height = data->requested_window_height; + + window_size_changed = window_width != window->w || window_height != window->h; if (window_size_changed || drawable_size_changed) { if (WindowNeedsViewport(window)) { wl_surface_set_buffer_scale(data->surface, 1); - SetDrawSurfaceViewport(window, data->drawable_width, data->drawable_height, window->w, window->h); + SetDrawSurfaceViewport(window, data->drawable_width, data->drawable_height, + window_width, window_height); } else { UnsetDrawSurfaceViewport(window); wl_surface_set_buffer_scale(data->surface, (int32_t)data->windowed_scale_factor); } /* Clamp the physical window size to the system minimum required size. */ - data->window_width = SDL_max(window->w, data->system_min_required_width); - data->window_height = SDL_max(window->h, data->system_min_required_height); + data->wl_window_width = SDL_max(window_width, data->system_min_required_width); + data->wl_window_height = SDL_max(window_height, data->system_min_required_height); data->pointer_scale_x = 1.0f; data->pointer_scale_y = 1.0f; @@ -278,16 +217,18 @@ static void ConfigureWindowGeometry(SDL_Window *window) * need to be recalculated if the output size has changed. */ if (window_size_changed) { + struct wl_region *region; + /* libdecor does this internally on frame commits, so it's only needed for xdg surfaces. */ if (data->shell_surface_type != WAYLAND_SURFACE_LIBDECOR && viddata->shell.xdg && data->shell_surface.xdg.surface != NULL) { - xdg_surface_set_window_geometry(data->shell_surface.xdg.surface, 0, 0, data->window_width, data->window_height); + xdg_surface_set_window_geometry(data->shell_surface.xdg.surface, 0, 0, data->wl_window_width, data->wl_window_height); } if (!viddata->egl_transparency_enabled) { region = wl_compositor_create_region(viddata->compositor); wl_region_add(region, 0, 0, - data->window_width, data->window_height); + data->wl_window_width, data->wl_window_height); wl_surface_set_opaque_region(data->surface, region); wl_region_destroy(region); } @@ -296,6 +237,10 @@ static void ConfigureWindowGeometry(SDL_Window *window) Wayland_input_confine_pointer(viddata->input, window); } } + + /* Unconditionally send the window and drawable size, the video core will deduplicate when required. */ + SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, window_width, window_height); + SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED, data->drawable_width, data->drawable_height); } static void CommitLibdecorFrame(SDL_Window *window) @@ -304,7 +249,7 @@ static void CommitLibdecorFrame(SDL_Window *window) SDL_WindowData *wind = window->driverdata; if (wind->shell_surface_type == WAYLAND_SURFACE_LIBDECOR && wind->shell_surface.libdecor.frame) { - struct libdecor_state *state = libdecor_state_new(wind->window_width, wind->window_height); + struct libdecor_state *state = libdecor_state_new(wind->wl_window_width, wind->wl_window_height); libdecor_frame_commit(wind->shell_surface.libdecor.frame, state, NULL); libdecor_state_free(state); } @@ -326,7 +271,7 @@ static void SetMinMaxDimensions(SDL_Window *window, SDL_bool commit) return; } - if (window->fullscreen_exclusive) { + if (window->flags & SDL_WINDOW_FULLSCREEN) { min_width = 0; min_height = 0; max_width = 0; @@ -439,22 +384,23 @@ static void UpdateWindowFullscreen(SDL_Window *window, SDL_bool fullscreen) SDL_WindowData *wind = window->driverdata; if (fullscreen) { - if ((window->flags & SDL_WINDOW_FULLSCREEN) == 0) { + if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { wind->is_fullscreen = SDL_TRUE; + wind->in_fullscreen_transition = SDL_TRUE; SDL_SetWindowFullscreen(window, SDL_TRUE); wind->in_fullscreen_transition = SDL_FALSE; } } else { /* Don't change the fullscreen flags if the window is hidden or being hidden. */ - if (!window->is_hiding && !(window->flags & SDL_WINDOW_HIDDEN)) { - if ((window->flags & SDL_WINDOW_FULLSCREEN) != 0) { - wind->is_fullscreen = SDL_FALSE; - wind->in_fullscreen_transition = SDL_TRUE; - SDL_SetWindowFullscreen(window, SDL_FALSE); - wind->in_fullscreen_transition = SDL_FALSE; - SetMinMaxDimensions(window, SDL_FALSE); - } + if ((window->flags & SDL_WINDOW_FULLSCREEN) && !window->is_hiding && !(window->flags & SDL_WINDOW_HIDDEN)) { + wind->is_fullscreen = SDL_FALSE; + + wind->in_fullscreen_transition = SDL_TRUE; + SDL_SetWindowFullscreen(window, SDL_FALSE); + wind->in_fullscreen_transition = SDL_FALSE; + + SetMinMaxDimensions(window, SDL_FALSE); } } } @@ -474,7 +420,7 @@ static void surface_damage_frame_done(void *data, struct wl_callback *cb, uint32 wind->drawable_width, wind->drawable_height); } else { wl_surface_damage(wind->surface, 0, 0, - wind->window_width, wind->window_height); + wind->wl_window_width, wind->wl_window_height); } wl_callback_destroy(cb); @@ -503,14 +449,12 @@ static const struct wl_callback_listener gles_swap_frame_listener = { gles_swap_frame_done }; -static void Wayland_HandleResize(SDL_Window *window, int width, int height); - static void handle_configure_xdg_shell_surface(void *data, struct xdg_surface *xdg, uint32_t serial) { SDL_WindowData *wind = (SDL_WindowData *)data; SDL_Window *window = wind->sdlwindow; - Wayland_HandleResize(window, window->w, window->h); + ConfigureWindowGeometry(window); xdg_surface_ack_configure(xdg, serial); wind->shell_surface.xdg.initial_configure_seen = SDL_TRUE; @@ -533,6 +477,7 @@ static void handle_configure_xdg_toplevel(void *data, SDL_bool fullscreen = SDL_FALSE; SDL_bool maximized = SDL_FALSE; SDL_bool floating = SDL_TRUE; + SDL_bool focused = SDL_FALSE; wl_array_for_each (state, states) { switch (*state) { case XDG_TOPLEVEL_STATE_FULLSCREEN: @@ -543,6 +488,9 @@ static void handle_configure_xdg_toplevel(void *data, maximized = SDL_TRUE; floating = SDL_FALSE; break; + case XDG_TOPLEVEL_STATE_ACTIVATED: + focused = SDL_TRUE; + break; case XDG_TOPLEVEL_STATE_TILED_LEFT: case XDG_TOPLEVEL_STATE_TILED_RIGHT: case XDG_TOPLEVEL_STATE_TILED_TOP: @@ -557,18 +505,18 @@ static void handle_configure_xdg_toplevel(void *data, UpdateWindowFullscreen(window, fullscreen); if (!fullscreen) { - if ((floating && !wind->was_floating) || width == 0 || height == 0) { - /* This usually happens when we're being restored from a - * non-floating state, so use the cached floating size here. - */ - width = wind->floating_width; - height = wind->floating_height; - } - /* xdg_toplevel spec states that this is a suggestion. - Ignore if less than or greater than max/min size. */ - + * Ignore if less than or greater than max/min size. + */ if ((window->flags & SDL_WINDOW_RESIZABLE)) { + if ((floating && !wind->floating) || width == 0 || height == 0) { + /* This happens when we're being restored from a + * non-floating state, so use the cached floating size here. + */ + width = wind->floating_width; + height = wind->floating_height; + } + if (window->max_w > 0) { width = SDL_min(width, window->max_w); } @@ -586,6 +534,12 @@ static void handle_configure_xdg_toplevel(void *data, height = window->windowed.h; } + /* Store current floating dimensions for restoring */ + if (floating) { + wind->floating_width = width; + wind->floating_height = height; + } + /* Always send a maximized/restore event; if the event is redundant it will * automatically be discarded (see src/events/SDL_windowevents.c) * @@ -594,45 +548,25 @@ static void handle_configure_xdg_toplevel(void *data, SDL_SendWindowEvent(window, maximized ? SDL_EVENT_WINDOW_MAXIMIZED : SDL_EVENT_WINDOW_RESTORED, 0, 0); - - /* Store current floating dimensions for restoring */ - if (floating) { - wind->floating_width = width; - wind->floating_height = height; - } - - /* Store this now so the xdg_surface configure knows what to resize to */ - if (window->w != width || window->h != height) { - window->w = width; - window->h = height; - wind->needs_resize_event = SDL_TRUE; - } } else { - /* For fullscreen, foolishly do what the compositor says. If it's wrong, - * don't blame us, we were explicitly instructed to do this. - * - * UPDATE: Nope, sure enough a compositor sends 0,0. This is a known bug: - * https://bugs.kde.org/show_bug.cgi?id=444962 - */ - if (width && height) { - wind->fs_output_width = width; - wind->fs_output_height = height; - } else { - wind->fs_output_width = 0; - wind->fs_output_height = 0; - } - - if (FullscreenModeEmulation(window)) { - GetFullScreenDimensions(window, &width, &height, NULL, NULL); - } - if (width != 0 && height != 0 && (window->w != width || window->h != height)) { - window->w = width; - window->h = height; - wind->needs_resize_event = SDL_TRUE; + /* If an exclusive fullscreen mode was requested, ensure it is placed on the appropriate output. */ + if (window->fullscreen_exclusive && wind->fullscreen_display != window->fullscreen_mode.displayID) { + SDL_VideoDisplay *disp = SDL_GetVideoDisplay(window->fullscreen_mode.displayID); + if (disp) { + wind->fullscreen_display = disp->id; + xdg_toplevel_set_fullscreen(xdg_toplevel, disp->driverdata->output); + } } } - wind->was_floating = floating; + /* Similar to maximized/restore events above, send focus events too! */ + SDL_SendWindowEvent(window, + focused ? SDL_EVENT_WINDOW_FOCUS_GAINED : SDL_EVENT_WINDOW_FOCUS_LOST, + 0, 0); + + wind->requested_window_width = width; + wind->requested_window_height = height; + wind->floating = floating; } static void handle_close_xdg_toplevel(void *data, struct xdg_toplevel *xdg_toplevel) @@ -826,29 +760,27 @@ static void decoration_frame_configure(struct libdecor_frame *frame, * Always assume the configure is wrong. */ if (fullscreen) { + /* If an exclusive fullscreen mode was requested, ensure it is placed on the appropriate output. */ + if (window->fullscreen_exclusive && wind->fullscreen_display != window->fullscreen_mode.displayID) { + SDL_VideoDisplay *disp = SDL_GetVideoDisplay(window->fullscreen_mode.displayID); + if (disp) { + wind->fullscreen_display = disp->id; + libdecor_frame_set_fullscreen(frame, disp->driverdata->output); + } + } + /* FIXME: We have been explicitly told to respect the fullscreen size * parameters here, even though they are known to be wrong on GNOME at * bare minimum. If this is wrong, don't blame us, we were explicitly * told to do this. */ - if (libdecor_configuration_get_content_size(configuration, frame, - &width, &height)) { - wind->fs_output_width = width; - wind->fs_output_height = height; - } else { - width = window->w; - height = window->h; - wind->fs_output_width = 0; - wind->fs_output_height = 0; + if (!libdecor_configuration_get_content_size(configuration, frame, &width, &height)) { + width = 0; + height = 0; } - - if (FullscreenModeEmulation(window)) { - GetFullScreenDimensions(window, &width, &height, NULL, NULL); - } - } else if (!(window->flags & SDL_WINDOW_RESIZABLE) || (floating && wind->floating_resize_pending)) { + } else if (!(window->flags & SDL_WINDOW_RESIZABLE)) { width = window->windowed.w; height = window->windowed.h; - wind->floating_resize_pending = SDL_FALSE; OverrideLibdecorLimits(window); } else { @@ -862,7 +794,7 @@ static void decoration_frame_configure(struct libdecor_frame *frame, * * https://gitlab.gnome.org/jadahl/libdecor/-/issues/40 */ - const SDL_bool use_cached_size = (floating && !wind->was_floating) || + const SDL_bool use_cached_size = (floating != wind->floating) || (window->is_hiding || !!(window->flags & SDL_WINDOW_HIDDEN)); /* This will never set 0 for width/height unless the function returns false */ @@ -886,13 +818,16 @@ static void decoration_frame_configure(struct libdecor_frame *frame, wind->floating_height = height; } - wind->was_floating = floating; + /* Store the new floating state. */ + wind->floating = floating; - /* Do the resize on the SDL side (this will set window->w/h)... */ - Wayland_HandleResize(window, width, height); + /* Calculate the new window geometry */ + wind->requested_window_width = width; + wind->requested_window_height = height; + ConfigureWindowGeometry(window); /* ... then commit the changes on the libdecor side. */ - state = libdecor_state_new(wind->window_width, wind->window_height); + state = libdecor_state_new(wind->wl_window_width, wind->wl_window_height); libdecor_frame_commit(frame, state, configuration); libdecor_state_free(state); @@ -979,7 +914,7 @@ static void update_scale_factor(SDL_WindowData *window) if (!FloatEqual(new_factor, old_factor)) { window->windowed_scale_factor = new_factor; - Wayland_HandleResize(window->sdlwindow, window->sdlwindow->w, window->sdlwindow->h); + ConfigureWindowGeometry(window->sdlwindow); } } @@ -1296,7 +1231,7 @@ void Wayland_ShowWindow(_THIS, SDL_Window *window) } /* Set the geometry */ - xdg_surface_set_window_geometry(data->shell_surface.xdg.surface, 0, 0, data->window_width, data->window_height); + xdg_surface_set_window_geometry(data->shell_surface.xdg.surface, 0, 0, data->wl_window_width, data->wl_window_height); } else { /* Nothing to see here, just commit. */ wl_surface_commit(data->surface); @@ -1324,8 +1259,8 @@ void Wayland_ShowWindow(_THIS, SDL_Window *window) "Window dimensions (%i, %i) are smaller than the system enforced minimum (%i, %i); window borders will be larger than the content surface.", window->windowed.w, window->windowed.h, data->system_min_required_width, data->system_min_required_height); - data->window_width = SDL_max(window->windowed.w, data->system_min_required_width); - data->window_height = SDL_max(window->windowed.h, data->system_min_required_height); + data->wl_window_width = SDL_max(window->windowed.w, data->system_min_required_width); + data->wl_window_height = SDL_max(window->windowed.h, data->system_min_required_height); CommitLibdecorFrame(window); } } else @@ -1546,7 +1481,7 @@ void handle_preferred_scale_changed(void *data, if (!FloatEqual(new_factor, old_factor)) { window->windowed_scale_factor = new_factor; - Wayland_HandleResize(window->sdlwindow, window->sdlwindow->w, window->sdlwindow->h); + ConfigureWindowGeometry(window->sdlwindow); } } @@ -1647,12 +1582,17 @@ void Wayland_SetWindowFullscreen(_THIS, SDL_Window *window, /* Called from within a configure event or the window is a popup, drop it. */ if (wind->in_fullscreen_transition || wind->shell_surface_type == WAYLAND_SURFACE_XDG_POPUP) { + if (!fullscreen) { + /* Clear the display ID so it will be set next time. */ + wind->fullscreen_display = 0; + } return; } /* Don't send redundant fullscreen set/unset events. */ if (wind->is_fullscreen != fullscreen) { wind->is_fullscreen = fullscreen; + wind->fullscreen_display = fullscreen ? display->id : 0; SetFullscreen(window, fullscreen ? output : NULL); /* Roundtrip required to receive the updated window dimensions */ @@ -1660,11 +1600,16 @@ void Wayland_SetWindowFullscreen(_THIS, SDL_Window *window, } else if (wind->is_fullscreen) { /* * If the window is already fullscreen, this is likely a request to switch between - * fullscreen and fullscreen desktop, or to change the video mode. Update the - * geometry and trigger a commit. + * fullscreen and fullscreen desktop, change outputs, or change the video mode. + * Update the geometry and trigger a commit. */ - ConfigureWindowGeometry(window); - CommitLibdecorFrame(window); + if (wind->fullscreen_display != display->id) { + wind->fullscreen_display = display->id; + SetFullscreen(window, output); + } else { + ConfigureWindowGeometry(window); + CommitLibdecorFrame(window); + } /* Roundtrip required to receive the updated window dimensions */ WAYLAND_wl_display_roundtrip(viddata->display); @@ -1894,6 +1839,8 @@ int Wayland_CreateWindow(_THIS, SDL_Window *window) data->outputs = NULL; data->num_outputs = 0; + data->requested_window_width = window->w; + data->requested_window_height = window->h; data->floating_width = window->windowed.w; data->floating_height = window->windowed.h; @@ -1989,30 +1936,6 @@ int Wayland_CreateWindow(_THIS, SDL_Window *window) return 0; } -static void Wayland_HandleResize(SDL_Window *window, int width, int height) -{ - SDL_WindowData *data = window->driverdata; - const int old_w = window->w, old_h = window->h; - - /* Update the window geometry. */ - window->w = width; - window->h = height; - ConfigureWindowGeometry(window); - - if (data->needs_resize_event || old_w != width || old_h != height) { - /* We have already updated window w/h, so we must override the deduplication logic in the video core */ - window->w = 0; - window->h = 0; - SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, width, height); - window->w = width; - window->h = height; - data->needs_resize_event = SDL_FALSE; - } - - SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED, - data->drawable_width, data->drawable_height); -} - void Wayland_SetWindowMinimumSize(_THIS, SDL_Window *window) { SetMinMaxDimensions(window, SDL_TRUE); @@ -2027,27 +1950,21 @@ void Wayland_SetWindowSize(_THIS, SDL_Window *window) { SDL_WindowData *wind = window->driverdata; -#ifdef HAVE_LIBDECOR_H - /* we must not resize the window while we have a static (non-floating) size */ - if (wind->shell_surface_type == WAYLAND_SURFACE_LIBDECOR) { - if (wind->shell_surface.libdecor.frame && - !libdecor_frame_is_floating(wind->shell_surface.libdecor.frame)) { - /* Commit the resize when we re-enter floating state */ - wind->floating_resize_pending = SDL_TRUE; - return; - } - - OverrideLibdecorLimits(window); - } -#endif - - /* Update the window geometry. */ - ConfigureWindowGeometry(window); - CommitLibdecorFrame(window); - - /* windowed is unconditionally set, so we can trust it here */ + /* + * Unconditionally store the floating size, as it will need + * to be applied when returning from a non-floating state. + */ wind->floating_width = window->windowed.w; wind->floating_height = window->windowed.h; + + /* Don't change the size of static (non-floating) windows. */ + if (wind->floating) { + wind->requested_window_width = window->windowed.w; + wind->requested_window_height = window->windowed.h; + + ConfigureWindowGeometry(window); + CommitLibdecorFrame(window); + } } void Wayland_GetWindowSizeInPixels(_THIS, SDL_Window *window, int *w, int *h) @@ -2201,7 +2118,7 @@ static void EGLTransparencyChangedCallback(void *userdata, const char *name, con if (!newval) { struct wl_region *region = wl_compositor_create_region(wind->waylandData->compositor); - wl_region_add(region, 0, 0, wind->window_width, wind->window_height); + wl_region_add(region, 0, 0, wind->wl_window_width, wind->wl_window_height); wl_surface_set_opaque_region(wind->surface, region); wl_region_destroy(region); } else { diff --git a/src/video/wayland/SDL_waylandwindow.h b/src/video/wayland/SDL_waylandwindow.h index 63a139dca4..d45a61e44c 100644 --- a/src/video/wayland/SDL_waylandwindow.h +++ b/src/video/wayland/SDL_waylandwindow.h @@ -106,14 +106,13 @@ struct SDL_WindowData float windowed_scale_factor; float pointer_scale_x; float pointer_scale_y; + int requested_window_width, requested_window_height; int drawable_width, drawable_height; - int fs_output_width, fs_output_height; - int window_width, window_height; + int wl_window_width, wl_window_height; int system_min_required_width; int system_min_required_height; - SDL_bool needs_resize_event; - SDL_bool floating_resize_pending; - SDL_bool was_floating; + SDL_DisplayID fullscreen_display; + SDL_bool floating; SDL_bool is_fullscreen; SDL_bool in_fullscreen_transition; };