diff --git a/src/include/server/mir/scene/null_surface_observer.h b/src/include/server/mir/scene/null_surface_observer.h index b40a6ba94f6..548fc61cfd2 100644 --- a/src/include/server/mir/scene/null_surface_observer.h +++ b/src/include/server/mir/scene/null_surface_observer.h @@ -49,6 +49,7 @@ class NullSurfaceObserver : public SurfaceObserver void application_id_set_to(Surface const* surf, std::string const& application_id) override; void entered_output(Surface const* surf, graphics::DisplayConfigurationOutputId const& id) override; void left_output(Surface const* surf, graphics::DisplayConfigurationOutputId const& id) override; + void rescale_output(Surface const* surf, graphics::DisplayConfigurationOutputId const& id) override; protected: NullSurfaceObserver(NullSurfaceObserver const&) = delete; diff --git a/src/include/server/mir/scene/surface_observer.h b/src/include/server/mir/scene/surface_observer.h index 6d1c7085383..6d94220a5bc 100644 --- a/src/include/server/mir/scene/surface_observer.h +++ b/src/include/server/mir/scene/surface_observer.h @@ -64,6 +64,7 @@ class SurfaceObserver virtual void depth_layer_set_to(Surface const* surf, MirDepthLayer depth_layer) = 0; virtual void entered_output(Surface const* surf, graphics::DisplayConfigurationOutputId const& id) = 0; virtual void left_output(Surface const* surf, graphics::DisplayConfigurationOutputId const& id) = 0; + virtual void rescale_output(Surface const* surf, graphics::DisplayConfigurationOutputId const& id) = 0; protected: SurfaceObserver() = default; diff --git a/src/miroil/surface.cpp b/src/miroil/surface.cpp index 89eecfa20a8..c24d77d8b46 100644 --- a/src/miroil/surface.cpp +++ b/src/miroil/surface.cpp @@ -59,6 +59,7 @@ class miroil::SurfaceObserverImpl : public mir::scene::SurfaceObserver mir::geometry::Size const &window_size) override; void entered_output(mir::scene::Surface const* surf, mir::graphics::DisplayConfigurationOutputId const& id) override; void left_output(mir::scene::Surface const* surf, mir::graphics::DisplayConfigurationOutputId const& id) override; + void rescale_output(mir::scene::Surface const* surf, mir::graphics::DisplayConfigurationOutputId const& id) override; private: std::shared_ptr listener; @@ -171,6 +172,12 @@ void miroil::SurfaceObserverImpl::left_output( { } +void miroil::SurfaceObserverImpl::rescale_output( + mir::scene::Surface const* /*surf*/, + mir::graphics::DisplayConfigurationOutputId const& /*id*/) +{ +} + miroil::Surface::Surface(std::shared_ptr wrapped) : wrapped(wrapped) { diff --git a/src/server/frontend_wayland/fractional_scale_v1.cpp b/src/server/frontend_wayland/fractional_scale_v1.cpp index 3d55c2b48ad..ce29e542d4a 100644 --- a/src/server/frontend_wayland/fractional_scale_v1.cpp +++ b/src/server/frontend_wayland/fractional_scale_v1.cpp @@ -102,6 +102,12 @@ void mf::FractionalScaleV1::output_left(mir::graphics::DisplayConfigurationOutpu recompute_scale(); } +void mf::FractionalScaleV1::scale_change_on_output(mir::graphics::DisplayConfigurationOutput const& config) +{ + surface_outputs[config.id] = config.scale; + recompute_scale(); +} + void mf::FractionalScaleV1::recompute_scale() { auto max_element = std::max_element( diff --git a/src/server/frontend_wayland/fractional_scale_v1.h b/src/server/frontend_wayland/fractional_scale_v1.h index 7eab897c941..658c00dab9b 100644 --- a/src/server/frontend_wayland/fractional_scale_v1.h +++ b/src/server/frontend_wayland/fractional_scale_v1.h @@ -38,6 +38,7 @@ class FractionalScaleV1 : public wayland::FractionalScaleV1 void output_entered(mir::graphics::DisplayConfigurationOutput const& config); void output_left(mir::graphics::DisplayConfigurationOutput const& config); + void scale_change_on_output(mir::graphics::DisplayConfigurationOutput const& config); private: // Houses a set of outputs the surface occupies diff --git a/src/server/frontend_wayland/wayland_surface_observer.cpp b/src/server/frontend_wayland/wayland_surface_observer.cpp index 3d5b55038f1..22b4838299b 100644 --- a/src/server/frontend_wayland/wayland_surface_observer.cpp +++ b/src/server/frontend_wayland/wayland_surface_observer.cpp @@ -134,6 +134,15 @@ void mf::WaylandSurfaceObserver::left_output(ms::Surface const*, graphics::Displ }); } +void mf::WaylandSurfaceObserver::rescale_output(ms::Surface const*, graphics::DisplayConfigurationOutputId const& id) +{ + run_on_wayland_thread_unless_window_destroyed( + [id](Impl* impl, WindowWlSurfaceRole*) + { + impl->window.value().handle_scale_output(id); + }); +} + void mf::WaylandSurfaceObserver::run_on_wayland_thread_unless_window_destroyed( std::function&& work) { diff --git a/src/server/frontend_wayland/wayland_surface_observer.h b/src/server/frontend_wayland/wayland_surface_observer.h index 845336e93fc..05ecd80ec6f 100644 --- a/src/server/frontend_wayland/wayland_surface_observer.h +++ b/src/server/frontend_wayland/wayland_surface_observer.h @@ -57,6 +57,7 @@ class WaylandSurfaceObserver : public scene::NullSurfaceObserver void input_consumed(scene::Surface const*, std::shared_ptr const& event) override; void entered_output(scene::Surface const*, graphics::DisplayConfigurationOutputId const& id) override; void left_output(scene::Surface const*, graphics::DisplayConfigurationOutputId const& id) override; + void rescale_output(scene::Surface const*, graphics::DisplayConfigurationOutputId const& id) override; ///@} /// Should only be called from the Wayland thread diff --git a/src/server/frontend_wayland/window_wl_surface_role.cpp b/src/server/frontend_wayland/window_wl_surface_role.cpp index cc11e2777eb..9d29390ca5c 100644 --- a/src/server/frontend_wayland/window_wl_surface_role.cpp +++ b/src/server/frontend_wayland/window_wl_surface_role.cpp @@ -461,11 +461,20 @@ auto mf::WindowWlSurfaceRole::output_config_changed(graphics::DisplayConfigurati client, [&](OutputInstance* instance) { + if (auto const fractional_scale = surface.value().get_fractional_scale()) + fractional_scale.value().output_entered(config); surface.value().send_enter_event(instance->resource); pending_enter_events.erase(id_it); }); } } + + if (auto id_it{std::ranges::find(pending_rescale_events, config.id)}; id_it != pending_rescale_events.end()) + { + if (auto const fractional_scale = surface.value().get_fractional_scale()) + fractional_scale.value().scale_change_on_output(config); + pending_rescale_events.erase(id_it); + } } return true; @@ -524,7 +533,7 @@ void mf::WindowWlSurfaceRole::handle_enter_output(graphics::DisplayConfiguration auto const& config{global->current_config()}; if (config.id == id) { - if (auto fractional_scale = surface.value().get_fractional_scale()) + if (auto const fractional_scale = surface.value().get_fractional_scale()) fractional_scale.value().output_entered(config); global->for_each_output_bound_by( @@ -552,7 +561,7 @@ void mf::WindowWlSurfaceRole::handle_leave_output(graphics::DisplayConfiguration auto const& config{global->current_config()}; if (config.id == id) { - if(auto fractional_scale = surface.value().get_fractional_scale()) + if(auto const fractional_scale = surface.value().get_fractional_scale()) fractional_scale.value().output_left(config); global->for_each_output_bound_by( @@ -566,6 +575,14 @@ void mf::WindowWlSurfaceRole::handle_leave_output(graphics::DisplayConfiguration } } +void mf::WindowWlSurfaceRole::handle_scale_output(graphics::DisplayConfigurationOutputId id) +{ + if (surface) + { + pending_rescale_events.push_back(id); + } +} + void mf::WindowWlSurfaceRole::apply_client_size(mir::shell::SurfaceSpecification& mods) { if ((!committed_width_set_explicitly || !committed_height_set_explicitly) && surface) diff --git a/src/server/frontend_wayland/window_wl_surface_role.h b/src/server/frontend_wayland/window_wl_surface_role.h index 9bbedee02f5..04fc3c4c777 100644 --- a/src/server/frontend_wayland/window_wl_surface_role.h +++ b/src/server/frontend_wayland/window_wl_surface_role.h @@ -102,6 +102,7 @@ class WindowWlSurfaceRole void handle_enter_output(graphics::DisplayConfigurationOutputId id); void handle_leave_output(graphics::DisplayConfigurationOutputId id) const; + void handle_scale_output(graphics::DisplayConfigurationOutputId id); /// Gets called after the surface has committed (so current_size() may return the committed buffer size) but before /// the Mir window is modified (so if a pending size is set or a spec is applied those changes will take effect) @@ -174,6 +175,7 @@ class WindowWlSurfaceRole void apply_client_size(mir::shell::SurfaceSpecification& mods); std::vector pending_enter_events; + std::vector pending_rescale_events; }; } diff --git a/src/server/scene/basic_surface.cpp b/src/server/scene/basic_surface.cpp index f4bfc52e9ab..3e82471bf0a 100644 --- a/src/server/scene/basic_surface.cpp +++ b/src/server/scene/basic_surface.cpp @@ -169,6 +169,11 @@ class ms::BasicSurface::Multiplexer : public ObserverMultiplexer geo void mir::scene::BasicSurface::track_outputs() { auto const state{synchronised_state.lock()}; - std::set tracked; + decltype(tracked_output_scales) tracked; display_config->for_each_output( [&](mg::DisplayConfigurationOutput const& output) { if (output.valid() && output.used && output.extents().overlaps(state->surface_rect)) { - if (!tracked_outputs.contains(output.id)) + if (!tracked_output_scales.contains(output.id)) { observers->entered_output(this, output.id); } - tracked.insert(output.id); + tracked.emplace(output.id, output.scale); } }); - // TODO: Once std::views::filter is properly supported across compilers, replace the - // creation of `untracked` with iteration over a filtered view of `tracked_outputs` - std::vector untracked; - std::ranges::set_difference(tracked_outputs, tracked, std::back_inserter(untracked)); - std::ranges::for_each(untracked, [&](auto const& id) { observers->left_output(this, id); }); + for (auto const& [id, scale] : tracked_output_scales) + { + if (auto new_entry = tracked.find(id); new_entry == tracked.end()) + { + observers->left_output(this, id); + } + else if (new_entry->second != scale) + { + observers->rescale_output(this, id); + } + } - tracked_outputs = std::move(tracked); + tracked_output_scales = std::move(tracked); } void mir::scene::BasicSurface::linearised_track_outputs() diff --git a/src/server/scene/basic_surface.h b/src/server/scene/basic_surface.h index ddd308046ff..55e928fa574 100644 --- a/src/server/scene/basic_surface.h +++ b/src/server/scene/basic_surface.h @@ -239,7 +239,7 @@ class BasicSurface : public Surface std::shared_ptr> display_config_registrar; std::shared_ptr const display_config_monitor; std::shared_ptr display_config; - std::set tracked_outputs; + std::unordered_map tracked_output_scales; }; } diff --git a/src/server/scene/null_surface_observer.cpp b/src/server/scene/null_surface_observer.cpp index f80fc840754..67ca480e423 100644 --- a/src/server/scene/null_surface_observer.cpp +++ b/src/server/scene/null_surface_observer.cpp @@ -37,5 +37,6 @@ void ms::NullSurfaceObserver::placed_relative(Surface const*, geometry::Rectangl void ms::NullSurfaceObserver::input_consumed(Surface const*, std::shared_ptr const&) {} void ms::NullSurfaceObserver::depth_layer_set_to(Surface const*, MirDepthLayer) {} void ms::NullSurfaceObserver::application_id_set_to(Surface const*, std::string const&) {} -void ms::NullSurfaceObserver::entered_output(Surface const*, graphics::DisplayConfigurationOutputId const&) {}; -void ms::NullSurfaceObserver::left_output(Surface const*, graphics::DisplayConfigurationOutputId const&) {}; +void ms::NullSurfaceObserver::entered_output(Surface const*, graphics::DisplayConfigurationOutputId const&) {} +void ms::NullSurfaceObserver::left_output(Surface const*, graphics::DisplayConfigurationOutputId const&) {} +void ms::NullSurfaceObserver::rescale_output(Surface const*, graphics::DisplayConfigurationOutputId const&){} diff --git a/src/server/symbols.map b/src/server/symbols.map index c84817e704b..bcdc5a9de6d 100644 --- a/src/server/symbols.map +++ b/src/server/symbols.map @@ -488,6 +488,7 @@ global: mir::scene::NullSurfaceObserver::placed_relative*; mir::scene::NullSurfaceObserver::reception_mode_set_to*; mir::scene::NullSurfaceObserver::renamed*; + mir::scene::NullSurfaceObserver::rescale_output*; mir::scene::NullSurfaceObserver::transformation_set_to*; mir::scene::NullSurfaceObserver::window_resized_to*; mir::scene::Observer::?Observer*; @@ -947,6 +948,7 @@ global: non-virtual?thunk?to?mir::scene::NullSurfaceObserver::placed_relative*; non-virtual?thunk?to?mir::scene::NullSurfaceObserver::reception_mode_set_to*; non-virtual?thunk?to?mir::scene::NullSurfaceObserver::renamed*; + non-virtual?thunk?to?mir::scene::NullSurfaceObserver::rescale_output*; non-virtual?thunk?to?mir::scene::NullSurfaceObserver::transformation_set_to*; non-virtual?thunk?to?mir::scene::NullSurfaceObserver::window_resized_to*; non-virtual?thunk?to?mir::scene::Observer::?Observer*; diff --git a/tests/mir_test_framework/window_management_test_harness.cpp b/tests/mir_test_framework/window_management_test_harness.cpp index 0eb445e2e59..2360d239f00 100644 --- a/tests/mir_test_framework/window_management_test_harness.cpp +++ b/tests/mir_test_framework/window_management_test_harness.cpp @@ -83,7 +83,7 @@ struct mir_test_framework::WindowManagementTestHarness::Self : public ms::Surfac void depth_layer_set_to(ms::Surface const*, MirDepthLayer) override {} void entered_output(ms::Surface const*, mg::DisplayConfigurationOutputId const&) override {} void left_output(ms::Surface const*, mg::DisplayConfigurationOutputId const&) override {} - + void rescale_output(ms::Surface const*, mg::DisplayConfigurationOutputId const&) override {} std::mutex mutable mutex; std::vector> streams; std::vector> known_surfaces;