From 0c547f34868639d4f4d0de5dff2cae6a42007c67 Mon Sep 17 00:00:00 2001 From: BearOso Date: Thu, 12 Sep 2024 14:09:00 -0500 Subject: [PATCH] Gtk/Wayland: Different workaround for Gtk damage bug. Instead of completely shutting down the display driver, shrink the subsurface when removing fullscreen so that when the parent window sends events when it receives damage or is resized. --- common/video/opengl/wayland_egl_context.cpp | 10 ++++++ common/video/opengl/wayland_egl_context.hpp | 2 ++ common/video/wayland/wayland_surface.cpp | 39 +++++++++++++++++---- common/video/wayland/wayland_surface.hpp | 2 ++ gtk/src/gtk_display_driver.h | 2 ++ gtk/src/gtk_display_driver_opengl.cpp | 20 +++++++++++ gtk/src/gtk_display_driver_opengl.h | 2 ++ gtk/src/gtk_display_driver_vulkan.cpp | 16 +++++++++ gtk/src/gtk_display_driver_vulkan.h | 2 ++ gtk/src/gtk_s9xwindow.cpp | 22 +++++++----- 10 files changed, 103 insertions(+), 14 deletions(-) diff --git a/common/video/opengl/wayland_egl_context.cpp b/common/video/opengl/wayland_egl_context.cpp index 2b528c5a..1f362a68 100644 --- a/common/video/opengl/wayland_egl_context.cpp +++ b/common/video/opengl/wayland_egl_context.cpp @@ -145,3 +145,13 @@ void WaylandEGLContext::swap_interval(int frames) { eglSwapInterval(egl_display, frames); } + +void WaylandEGLContext::shrink() +{ + wayland_surface->shrink(); +} + +void WaylandEGLContext::regrow() +{ + wayland_surface->regrow(); +} \ No newline at end of file diff --git a/common/video/opengl/wayland_egl_context.hpp b/common/video/opengl/wayland_egl_context.hpp index c8bc023a..95f1f3d4 100644 --- a/common/video/opengl/wayland_egl_context.hpp +++ b/common/video/opengl/wayland_egl_context.hpp @@ -26,6 +26,8 @@ class WaylandEGLContext : public OpenGLContext void swap_buffers(); void swap_interval(int frames); void make_current(); + void shrink(); + void regrow(); bool ready(); EGLDisplay egl_display; diff --git a/common/video/wayland/wayland_surface.cpp b/common/video/wayland/wayland_surface.cpp index 04fc687b..1e694e10 100644 --- a/common/video/wayland/wayland_surface.cpp +++ b/common/video/wayland/wayland_surface.cpp @@ -23,15 +23,15 @@ static void wl_global(void *data, auto wl = (WaylandSurface *)data; if (!strcmp(interface, "wl_compositor")) - wl->compositor = (struct wl_compositor *)wl_registry_bind(wl_registry, name, &wl_compositor_interface, 3); + wl->compositor = (struct wl_compositor *)wl_registry_bind(wl_registry, name, &wl_compositor_interface, version); else if (!strcmp(interface, "wl_subcompositor")) - wl->subcompositor = (struct wl_subcompositor *)wl_registry_bind(wl_registry, name, &wl_subcompositor_interface, 1); + wl->subcompositor = (struct wl_subcompositor *)wl_registry_bind(wl_registry, name, &wl_subcompositor_interface, version); else if (!strcmp(interface, zwp_idle_inhibit_manager_v1_interface.name)) - wl->idle_inhibit_manager = (struct zwp_idle_inhibit_manager_v1 *)wl_registry_bind(wl_registry, name, &zwp_idle_inhibit_manager_v1_interface, 1); + wl->idle_inhibit_manager = (struct zwp_idle_inhibit_manager_v1 *)wl_registry_bind(wl_registry, name, &zwp_idle_inhibit_manager_v1_interface, version); else if (!strcmp(interface, wp_viewporter_interface.name)) - wl->viewporter = (struct wp_viewporter *)wl_registry_bind(wl_registry, name, &wp_viewporter_interface, 1); + wl->viewporter = (struct wp_viewporter *)wl_registry_bind(wl_registry, name, &wp_viewporter_interface, version); else if (!strcmp(interface, wp_fractional_scale_manager_v1_interface.name)) - wl->fractional_scale_manager = (struct wp_fractional_scale_manager_v1 *)wl_registry_bind(wl_registry, name, &wp_fractional_scale_manager_v1_interface, 1); + wl->fractional_scale_manager = (struct wp_fractional_scale_manager_v1 *)wl_registry_bind(wl_registry, name, &wp_fractional_scale_manager_v1_interface, version); } static void wl_global_remove(void *data, @@ -161,10 +161,37 @@ std::tuple WaylandSurface::get_size_for_metrics(Metrics m) return { round(m.width * actual_scale), round(m.height * actual_scale) }; } +void WaylandSurface::shrink() +{ + if (!viewport) + viewport = wp_viewporter_get_viewport(viewporter, child); + + wp_viewport_set_source(viewport, + wl_fixed_from_int(-1), wl_fixed_from_int(-1), + wl_fixed_from_int(-1), wl_fixed_from_int(-1)); + wp_viewport_set_destination(viewport, 2, 2); + + wl_surface_commit(child); + wl_surface_commit(parent); +} + +void WaylandSurface::regrow() +{ + if (!viewport) + viewport = wp_viewporter_get_viewport(viewporter, child); + + wp_viewport_set_source(viewport, + wl_fixed_from_int(-1), wl_fixed_from_int(-1), + wl_fixed_from_int(-1), wl_fixed_from_int(-1)); + wp_viewport_set_destination(viewport, metrics.width, metrics.height); + + wl_surface_commit(child); + wl_surface_commit(parent); +} + void WaylandSurface::resize(Metrics m) { metrics = m; - auto [w, h] = get_size(); wl_subsurface_set_position(subsurface, m.x, m.y); diff --git a/common/video/wayland/wayland_surface.hpp b/common/video/wayland/wayland_surface.hpp index 5239737a..5352e539 100644 --- a/common/video/wayland/wayland_surface.hpp +++ b/common/video/wayland/wayland_surface.hpp @@ -22,6 +22,8 @@ class WaylandSurface bool attach(wl_display *display, wl_surface *surface, Metrics source_metrics); void resize(Metrics new_metrics); + void shrink(); + void regrow(); std::tuple get_size(); std::tuple get_size_for_metrics(Metrics m); diff --git a/gtk/src/gtk_display_driver.h b/gtk/src/gtk_display_driver.h index da0bba7f..e66e571a 100644 --- a/gtk/src/gtk_display_driver.h +++ b/gtk/src/gtk_display_driver.h @@ -25,6 +25,8 @@ class S9xDisplayDriver virtual bool can_throttle() { return false; }; virtual int get_width() = 0; virtual int get_height() = 0; + virtual void shrink() {}; + virtual void regrow() {}; protected: Snes9xWindow *window; diff --git a/gtk/src/gtk_display_driver_opengl.cpp b/gtk/src/gtk_display_driver_opengl.cpp index 039bf38b..bd8e4d6c 100644 --- a/gtk/src/gtk_display_driver_opengl.cpp +++ b/gtk/src/gtk_display_driver_opengl.cpp @@ -522,3 +522,23 @@ bool S9xOpenGLDisplayDriver::is_ready() return false; } + +void S9xOpenGLDisplayDriver::shrink() +{ +#ifdef GDK_WINDOWING_WAYLAND + if (GDK_IS_WAYLAND_WINDOW(gdk_window)) + { + ((WaylandEGLContext *)context)->shrink(); + } +#endif +} + +void S9xOpenGLDisplayDriver::regrow() +{ +#ifdef GDK_WINDOWING_WAYLAND + if (GDK_IS_WAYLAND_WINDOW(gdk_window)) + { + ((WaylandEGLContext *)context)->regrow(); + } +#endif +} diff --git a/gtk/src/gtk_display_driver_opengl.h b/gtk/src/gtk_display_driver_opengl.h index b94fc82f..473167a8 100644 --- a/gtk/src/gtk_display_driver_opengl.h +++ b/gtk/src/gtk_display_driver_opengl.h @@ -42,6 +42,8 @@ class S9xOpenGLDisplayDriver : public S9xDisplayDriver bool can_throttle() override { return true; } int get_width() final override { return output_window_width; } int get_height() final override { return output_window_height; } + void shrink() override; + void regrow() override; private: bool opengl_defaults(); diff --git a/gtk/src/gtk_display_driver_vulkan.cpp b/gtk/src/gtk_display_driver_vulkan.cpp index ce860e4d..54d737e8 100644 --- a/gtk/src/gtk_display_driver_vulkan.cpp +++ b/gtk/src/gtk_display_driver_vulkan.cpp @@ -253,4 +253,20 @@ void S9xVulkanDisplayDriver::save(const char *filename) bool S9xVulkanDisplayDriver::is_ready() { return true; +} + +void S9xVulkanDisplayDriver::shrink() +{ +#ifdef GDK_WINDOWING_WAYLAND + if (GDK_IS_WAYLAND_WINDOW(drawing_area->get_window()->gobj())) + wayland_surface->shrink(); +#endif +} + +void S9xVulkanDisplayDriver::regrow() +{ +#ifdef GDK_WINDOWING_WAYLAND + if (GDK_IS_WAYLAND_WINDOW(drawing_area->get_window()->gobj())) + wayland_surface->regrow(); +#endif } \ No newline at end of file diff --git a/gtk/src/gtk_display_driver_vulkan.h b/gtk/src/gtk_display_driver_vulkan.h index 78761596..cb363675 100644 --- a/gtk/src/gtk_display_driver_vulkan.h +++ b/gtk/src/gtk_display_driver_vulkan.h @@ -31,6 +31,8 @@ class S9xVulkanDisplayDriver : public S9xDisplayDriver bool can_throttle() override { return true; } int get_width() final override { return current_width; } int get_height() final override { return current_height; } + void shrink() override; + void regrow() override; static int query_availability(); diff --git a/gtk/src/gtk_s9xwindow.cpp b/gtk/src/gtk_s9xwindow.cpp index 4c0dafa5..fa3d5db8 100644 --- a/gtk/src/gtk_s9xwindow.cpp +++ b/gtk/src/gtk_s9xwindow.cpp @@ -1144,7 +1144,7 @@ static void set_bypass_compositor(Display *dpy, Window window, unsigned char byp void Snes9xWindow::enter_fullscreen_mode() { - int rom_loaded = config->rom_loaded; + bool rom_loaded = config->rom_loaded; if (config->fullscreen) return; @@ -1152,8 +1152,8 @@ void Snes9xWindow::enter_fullscreen_mode() GdkDisplay *gdk_display = window->get_display()->gobj(); GdkWindow *gdk_window = window->get_window()->gobj(); - config->rom_loaded = 0; - config->fullscreen = 1; + config->rom_loaded = false; + config->fullscreen = true; #ifdef GDK_WINDOWING_X11 if (config->change_display_resolution && GDK_IS_X11_WINDOW(gdk_window)) @@ -1232,7 +1232,7 @@ void Snes9xWindow::enter_fullscreen_mode() void Snes9xWindow::leave_fullscreen_mode() { - int rom_loaded = config->rom_loaded; + bool rom_loaded = config->rom_loaded; if (!config->fullscreen) return; @@ -1250,7 +1250,7 @@ void Snes9xWindow::leave_fullscreen_mode() GdkDisplay *gdk_display = window->get_display()->gobj(); GdkWindow *gdk_window = window->get_window()->gobj(); - config->rom_loaded = 0; + config->rom_loaded = false; #ifdef GDK_WINDOWING_X11 if (config->change_display_resolution && GDK_IS_X11_WINDOW(gdk_window)) @@ -1280,10 +1280,15 @@ void Snes9xWindow::leave_fullscreen_mode() } #endif + // If the window is covered by a subsurface, for some reason Gtk doesn't + // send any resize events or do anything to resize the window. So shrink + // the subsurface's viewport to 2x2 temporarily. + auto driver = S9xDisplayGetDriver(); #ifdef GDK_WINDOWING_WAYLAND if (GDK_IS_WAYLAND_WINDOW(gdk_window)) { - S9xDeinitDisplay(); + if (driver) + driver->shrink(); } #endif @@ -1301,14 +1306,15 @@ void Snes9xWindow::leave_fullscreen_mode() #endif config->rom_loaded = rom_loaded; - config->fullscreen = 0; + config->fullscreen = false; configure_widgets(); window->show(); #ifdef GDK_WINDOWING_WAYLAND if (GDK_IS_WAYLAND_WINDOW(gdk_window)) { - S9xReinitDisplay(); + if (driver) + driver->regrow(); } #endif