diff --git a/gtk/CMakeLists.txt b/gtk/CMakeLists.txt index 5408d23e..32a8ff05 100644 --- a/gtk/CMakeLists.txt +++ b/gtk/CMakeLists.txt @@ -140,7 +140,9 @@ if(USE_WAYLAND) src/gtk_wayland_egl_context.h src/gtk_wayland_surface.cpp src/gtk_wayland_surface.h - src/wayland-idle-inhibit-unstable-v1.c) + src/wayland-idle-inhibit-unstable-v1.c + src/viewporter-client-protocol.c + src/fractional-scale-v1.c) list(APPEND ARGS ${WAYLAND_CFLAGS}) list(APPEND LIBS ${WAYLAND_LIBRARIES}) endif() diff --git a/gtk/src/gtk_display_driver_vulkan.cpp b/gtk/src/gtk_display_driver_vulkan.cpp index c00ad296..157ef6ca 100644 --- a/gtk/src/gtk_display_driver_vulkan.cpp +++ b/gtk/src/gtk_display_driver_vulkan.cpp @@ -31,26 +31,29 @@ void S9xVulkanDisplayDriver::refresh() if (!context) return; - bool vsync_changed = context->swapchain->set_vsync(gui_config->sync_to_vblank); + context->swapchain->set_vsync(gui_config->sync_to_vblank); + int new_width, new_height; - auto new_width = drawing_area->get_width() * drawing_area->get_scale_factor(); - auto new_height = drawing_area->get_height() * drawing_area->get_scale_factor(); - - if (new_width != current_width || new_height != current_height || vsync_changed) - { #ifdef GDK_WINDOWING_WAYLAND - if (GDK_IS_WAYLAND_WINDOW(drawing_area->get_window()->gobj())) - { - wayland_surface->resize(); - } + if (GDK_IS_WAYLAND_WINDOW(drawing_area->get_window()->gobj())) + { + wayland_surface->resize(); + std::tie(new_width, new_height) = wayland_surface->get_size(); + } + else #endif + { + new_width = drawing_area->get_width() * drawing_area->get_scale_factor(); + new_height = drawing_area->get_height() * drawing_area->get_scale_factor(); + } + + if (new_width != current_width || new_height != current_height) + { context->recreate_swapchain(new_width, new_height); context->wait_idle(); current_width = new_width; current_height = new_height; } - - context->swapchain->set_vsync(gui_config->sync_to_vblank); } int S9xVulkanDisplayDriver::init() diff --git a/gtk/src/gtk_wayland_surface.cpp b/gtk/src/gtk_wayland_surface.cpp index a6d79282..ea393b8c 100644 --- a/gtk/src/gtk_wayland_surface.cpp +++ b/gtk/src/gtk_wayland_surface.cpp @@ -6,10 +6,13 @@ #include #include +#include +#include "fractional-scale-v1.h" #include "gtk_s9x.h" #include "gtk_wayland_surface.h" #include "wayland-idle-inhibit-unstable-v1.h" +#include "viewporter-client-protocol.h" static void wl_global(void *data, struct wl_registry *wl_registry, @@ -25,6 +28,10 @@ static void wl_global(void *data, wl->subcompositor = (struct wl_subcompositor *)wl_registry_bind(wl_registry, name, &wl_subcompositor_interface, 1); 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); + else if (!strcmp(interface, wp_viewporter_interface.name)) + wl->viewporter = (struct wp_viewporter *)wl_registry_bind(wl_registry, name, &wp_viewporter_interface, 1); + 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); } static void wl_global_remove(void *data, @@ -50,6 +57,11 @@ WaylandSurface::WaylandSurface() subsurface = nullptr; idle_inhibit_manager = nullptr; idle_inhibitor = nullptr; + viewporter = nullptr; + viewport = nullptr; + fractional_scale_manager = nullptr; + fractional_scale = nullptr; + actual_scale = 0.0; } WaylandSurface::~WaylandSurface() @@ -68,8 +80,32 @@ WaylandSurface::~WaylandSurface() if (child) wl_surface_destroy(child); + + if (viewporter) + wp_viewporter_destroy(viewporter); + + if (viewport) + wp_viewport_destroy(viewport); + + if (fractional_scale_manager) + wp_fractional_scale_manager_v1_destroy(fractional_scale_manager); + + if (fractional_scale) + wp_fractional_scale_v1_destroy(fractional_scale); } +static void preferred_scale(void *data, + struct wp_fractional_scale_v1 *wp_fractional_scale_v1, + uint32_t scale) +{ + ((WaylandSurface *)data)->actual_scale = scale / 120.0; +} + +wp_fractional_scale_v1_listener fractional_scale_v1_listener = +{ + preferred_scale +}; + bool WaylandSurface::attach(GtkWidget *widget) { GdkWindow *window = gtk_widget_get_window(widget); @@ -79,6 +115,7 @@ bool WaylandSurface::attach(GtkWidget *widget) gdk_window = window; gdk_window_get_geometry(gdk_window, &x, &y, &width, &height); + gdk_scale = gdk_window_get_scale_factor(gdk_window); display = gdk_wayland_display_get_wl_display(gdk_window_get_display(gdk_window)); parent = gdk_wayland_window_get_wl_surface(gdk_window); @@ -98,22 +135,47 @@ bool WaylandSurface::attach(GtkWidget *widget) wl_subsurface_set_desync(subsurface); wl_subsurface_set_position(subsurface, x, y); + if (fractional_scale_manager) + { + fractional_scale = wp_fractional_scale_manager_v1_get_fractional_scale(fractional_scale_manager, child); + wp_fractional_scale_v1_add_listener(fractional_scale, &fractional_scale_v1_listener, this); + } + if (idle_inhibit_manager && gui_config->prevent_screensaver) { printf("Inhibiting screensaver.\n"); zwp_idle_inhibit_manager_v1_create_inhibitor(idle_inhibit_manager, child); } - auto scale = gdk_window_get_scale_factor(window); - wl_surface_set_buffer_scale(child, scale); - return true; } +std::tuple WaylandSurface::get_size() +{ + gdk_window_get_geometry(gdk_window, &x, &y, &width, &height); + + if (actual_scale == 0.0) + { + gdk_scale = gdk_window_get_scale_factor(gdk_window); + + return { width * gdk_scale, height * gdk_scale }; + } + + return { width * actual_scale, height * actual_scale }; +} + void WaylandSurface::resize() { - gdk_window_get_geometry(gdk_window, &x, &y, &width, &height); - auto scale = gdk_window_get_scale_factor(gdk_window); + int w, h; std::tie(w, h) = get_size(); wl_subsurface_set_position(subsurface, x, y); - wl_surface_set_buffer_scale(child, scale); + + if (!viewport) + viewport = wp_viewporter_get_viewport(viewporter, child); + + wp_viewport_set_source(viewport, + wl_fixed_from_int(0), wl_fixed_from_int(0), + wl_fixed_from_int(w), wl_fixed_from_int(h)); + wp_viewport_set_destination(viewport, width, height); + + wl_surface_commit(child); } \ No newline at end of file diff --git a/gtk/src/gtk_wayland_surface.h b/gtk/src/gtk_wayland_surface.h index 05a354c5..ddc5d21e 100644 --- a/gtk/src/gtk_wayland_surface.h +++ b/gtk/src/gtk_wayland_surface.h @@ -7,6 +7,8 @@ #pragma once #include "gtk_compat.h" +#include "viewporter-client-protocol.h" +#include "fractional-scale-v1.h" class WaylandSurface { @@ -15,6 +17,7 @@ class WaylandSurface ~WaylandSurface(); bool attach(GtkWidget *widget); void resize(); + std::tuple get_size(); GdkWindow *gdk_window; @@ -32,7 +35,15 @@ class WaylandSurface int y; int width; int height; + int gdk_scale; + double actual_scale; struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager; struct zwp_idle_inhibitor_v1 *idle_inhibitor; + + struct wp_viewporter *viewporter; + struct wp_viewport *viewport; + + struct wp_fractional_scale_manager_v1 *fractional_scale_manager; + struct wp_fractional_scale_v1 *fractional_scale; };